⚠️ The latest release is unstable. Use preview builds instead
Skip to content

Steam Authentication Architecture

The steam-auth service runs in a separate container to isolate Steam credentials from the game server. It handles:

  • Steam authentication and session management
  • Game file downloads from Steam depots
  • Encrypted app ticket generation for GOG Galaxy cross-platform multiplayer

Architecture Diagram

┌─────────────────────────────────────┐
│   Game Server Container             │
│                                     │
│  ┌──────────────────────────────┐  │
│  │  AuthService.cs              │  │
│  │  (SteamAppTicketFetcherHttp) │  │
│  └──────────┬───────────────────┘  │
│             │ HTTP GET              │
│             │ /steam/app-ticket     │
└─────────────┼──────────────────────┘


┌─────────────────────────────────────┐
│   Steam Auth Container              │
│                                     │
│  ┌──────────────────────────────┐  │
│  │  HTTP Server (port 3001)     │  │
│  │  /health, /steam/app-ticket  │  │
│  └──────────┬───────────────────┘  │
│             │                       │
│  ┌──────────▼───────────────────┐  │
│  │  SteamKit2                   │  │
│  │  (login, 2FA, downloads)     │  │
│  └──────────────────────────────┘  │
│                                     │
│  Volumes:                           │
│  - steam-session (refresh tokens)  │
│  - game-data (shared with server)  │
└─────────────────────────────────────┘

Security Benefits

  1. Credential Isolation: Steam username and password never leave the steam-auth container
  2. Token Security: Refresh tokens remain private in a dedicated volume
  3. Minimal Exposure: Only ephemeral encrypted app tickets are exposed to the game server
  4. Shared Game Data: Game files are downloaded once and shared via volume

API Endpoints

The steam-auth HTTP API exposes:

GET /health

Health check endpoint.

json
{
  "status": "ok",
  "logged_in": true,
  "timestamp": "2026-01-16T12:00:00.000Z"
}

GET /steam/app-ticket

Get an encrypted app ticket for GOG Galaxy authentication.

json
{
  "app_ticket": "base64-encoded-ticket",
  "steam_id": "76561198012345678"
}

Available Commands

The steam-auth service supports several commands:

CommandDescription
setupInteractive login + download game (first-time setup)
loginInteractive login only, saves session
downloadDownload/update game files (uses saved session)
ticketOutput encrypted app ticket to stdout
export-tokenExport saved refresh token for CI use
serveRun HTTP API for runtime ticket requests (default)

Token Persistence

Refresh tokens are saved to /data/steam-session/session-{username}.json and reused on container restart. Steam tokens typically last 200 days.

How Invite Codes Work

  1. Game server starts hosting via GOG Galaxy SDK
  2. Galaxy creates a lobby and requests an encrypted app ticket
  3. Game server's AuthService fetches ticket from steam-auth via HTTP
  4. Steam-auth uses SteamKit2 to get an encrypted app ticket from Steam
  5. Ticket is returned and used to generate an invite code with "G" prefix

File Filtering

The download process skips unnecessary files to reduce download size:

  • Large audio files (Wave Bank.xwb ~370MB) — always stripped (the server runs silent)
  • Non-English language files (~50MB) — stripped by default; opt back in per language with STEAM_KEEP_LANGUAGES (see below)
  • Other assets not needed for dedicated server operation

This reduces the download from ~1.5GB to ~600MB.

After filtering, Content/ContentHashes.json is pruned to list only the files that were actually downloaded. The game checks this manifest (not the filesystem) to decide whether an asset exists, so leaving a stripped file listed would make the game attempt to load a missing .xnb and throw ContentLoadException. Pruning keeps the manifest honest so the game's built-in localized→English font fallback works.

Keeping a language's fonts

By default every localized font/content file is stripped and the server falls back to the English font, so non-Latin chat (e.g. Cyrillic, CJK) renders as empty boxes on clients. To keep a language's fonts in the download — so that language renders correctly — set STEAM_KEEP_LANGUAGES to a comma-separated list of language codes:

bash
STEAM_KEEP_LANGUAGES=pt-BR,ru-RU

Valid codes: de-DE, es-ES, fr-FR, hu-HU, it-IT, ja-JP, ko-KR, pt-BR, ru-RU, tr-TR, zh-CN, th-TH. CJK codes (zh-CN, ja-JP, ko-KR) also keep their larger font families, so those add more to the image size than the others. An unrecognized code is logged as a warning and ignored.

CI/CD Usage

For automated builds, use a refresh token instead of interactive login:

bash
# Export token after local setup
docker compose run steam-auth export-token > token.json

# Use in CI (set as secret)
STEAM_REFRESH_TOKEN=xxx STEAM_USERNAME=user docker compose run steam-auth download

Environment Variables

Steam Auth Container

VariableDescriptionDefault
STEAM_USERNAMESteam account username(prompted)
STEAM_PASSWORDSteam account password(prompted)
STEAM_REFRESH_TOKENPre-existing refresh token (for CI)-
PORTHTTP server port3001
SESSION_DIRToken storage directory/data/steam-session
GAME_DIRGame files directory/data/game
FORCE_REDOWNLOADSet to "1" to re-download all files-
STEAM_KEEP_LANGUAGESComma-separated language codes whose fonts to keep in the download (e.g. pt-BR,ru-RU). Default strips all localized content (English-only).-

Game Server Container

VariableDescriptionDefault
STEAM_AUTH_URLURL of steam-auth servicehttp://steam-auth:3001

Released under the MIT License.