Skip to content

Usage Authentication

Alex edited this page Oct 2, 2026 · 2 revisions

Authentication

Part of the Usage reference. · ← Previous: Global behavior · Next: Library →

login

Authenticate with Steam and persist the session.

aurelia login [-u <USERNAME>] [-p <PASSWORD>] [-g <GUARD_CODE>] [-c] [-q] [-o]
aurelia login -w [JSON]   # --web-token: store a browser web token (web-only access)
aurelia login -H          # --health: report session status (no login)
aurelia login -r          # --reconnect: rebuild the daemon's shared session
Option Description
-u, --username <USERNAME> Steam account name. Prompted if omitted.
-p, --password <PASSWORD> Account password. Prompted securely if omitted.
-g, --guard <GUARD_CODE> Steam Guard code (email or mobile authenticator), supplied up front.
-c, --code (alias --pin) Enter the Steam Guard code interactively when prompted, instead of approving in the Steam Mobile app. Conflicts with -g.
-q, --qr Log in by scanning a QR code with the Steam Mobile app (no username/password needed). Conflicts with the credential options.
-o, --openid Verify your identity in the browser on the official Steam sign-in page (OpenID). Identity-only, see below. Conflicts with the credential options.
-w, --web-token [JSON] Store a browser web token enabling the web-surface commands (inventory, wallet, market listings) without a client login. Pass the clientjstoken JSON as the value, or omit it to be prompted. See Web-only access. Conflicts with the other login options.
-H, --health Report current session status without logging in (see below). Conflicts with all login options.
-r, --reconnect Rebuild the daemon's shared session from the stored token. Conflicts with all login options.

If you are already logged in and run login without -u, Aurelia warns on stderr before the username prompt: Warning: already logged in as <name> — logging in again replaces that session. Logging in again replaces the stored session.

There are three ways to authenticate (plus a browser-based identity check):

  1. Password + Steam Guard. Provide -u/-p (or be prompted). Then, depending on your account: pass -g <CODE> up front, use --code/--pin to type the code when prompted, or (the default) approve the login in your Steam Mobile app.
  2. --code / --pin. Forces interactive Steam Guard code entry: after you submit credentials, Steam asks for the code (email or authenticator) and you type it in.
  3. --qr. Renders a QR code in the terminal (with a https://s.team/… link as a fallback). Scan it with the Steam Mobile app to approve. No password is entered.
  4. --openid (identity check only). Opens the official Steam sign-in page (steamcommunity.com) in your browser. After you sign in there, Steam redirects back to a localhost-only callback with a signed OpenID assertion, which Aurelia verifies directly with Steam. Your password is only ever typed on Steam's own page, never in Aurelia.

Why --openid can't be a full login. Steam's browser sign-in for third parties is OpenID 2.0 (Valve offers no OAuth2/OpenID Connect endpoint), and it attests identity only: Steam never issues a client session/refresh token through it. So --openid proves who you are (your SteamID64, cross-checked against the stored session's account) but cannot create a session. Commands that need one still require a login via password or --qr. For keeping the password out of Aurelia entirely and getting a session, --qr is the recommended flow. The browser left signed in by --openid can, however, hand over a web token, see --web-token below.

Web-only access (--web-token)

While Valve issues no client tokens to browsers, a signed-in browser session does carry a short-lived web-audience token: https://steamcommunity.com/chat/clientjstoken returns {"logged_in":true,"steamid":…,"account_name":…,"token":…} for whoever is signed in on steamcommunity.com. aurelia login --web-token stores that token (pasted as the flag's value, or at a prompt), after which the web-surface commands (inventory, wallet, market listings) work with no client login. The OpenID success page links to the clientjstoken URL directly, so login --openid → copy JSON → login --web-token is a complete browser-only setup.

Limits, so there are no surprises:

  • Web surface only. CM-backed commands (library, install, launch, friends, cloud) are unaffected. They still need a full login/--qr session.
  • Short-lived (~24 h), no refresh token. When it expires, reload the clientjstoken page in the (still signed-in) browser and re-run login --web-token. The command prints the expiry when the token carries one. Steam issues both JWT-format tokens (readable expiry) and opaque ones (expiry known only to Steam, shown as unknown).
  • Accepted pastes: the full clientjstoken JSON (recommended), a bare token value, or a steamLoginSecure cookie value (<steamid>||<token>, %7C%7C accepted). A bare opaque (non-JWT) token carries no identity of its own, so it binds to the stored session's account. With no stored session, paste the full JSON instead.
  • Account-safe. The paste's identity is cross-checked (JSON/cookie steamid vs. a JWT token's sub claim) and refused if it belongs to a different account than the stored session.
  • A full login supersedes the stored web token (a CM session mints fresh web tokens itself), and logout deletes it along with the rest of the session.

For a GUI driver (e.g. Heroic): render login --openid in your own webview. Once signed in, fetch clientjstoken with the webview's session and feed the JSON to aurelia login --web-token --json (see the NDJSON table below). Keeping the webview profile persistent lets you silently re-fetch a fresh token whenever the stored one expires.

aurelia login --web-token                      # prompts for the pasted JSON
aurelia login --web-token '{"logged_in":true,"steamid":"…","account_name":"…","token":"…"}'
aurelia wallet                                 # now works without a client login

A single log line (shown even without -v) reports which method is being awaited, e.g. Login method awaited: QR code — scan it with the Steam Mobile app or Login method awaited: Steam Guard code. The password may also be supplied via the AURELIA_PASSWORD environment variable.

# Interactive password login (recommended)
aurelia login

# Type the Steam Guard code interactively instead of approving in the app
aurelia login --code            # or: aurelia login --pin

# Scan a QR code with the Steam Mobile app (no password)
aurelia login --qr

# Verify your identity on the official Steam page in the browser (no session)
aurelia login --openid

# Fully non-interactive
aurelia login -u myname -p 'secret' -g ABCDE
AURELIA_PASSWORD='secret' aurelia login -u myname

Session health & reconnect

These two flags inspect or refresh the session rather than logging in, and are aimed at a front-end that drives the daemon:

  • aurelia login --health reports whether a session is currently authenticated, without performing a login. When a daemon is in use it reads the daemon's shared session state (no new logon). Standalone it does a one-off live restore check. Output (--json): { "logged_in", "account", "steam_id", "web_token", "daemon" }, with daemon indicating whether the answer came from the shared daemon session, web_token whether a browser web token is stored (--web-token). account/steam_id are reported from the persisted session even when logged_in is false (e.g. a web-token-only sign-in), so a driver can show who is signed in. A poller can use this to decide whether login is needed.
  • aurelia login --reconnect tears down the daemon's shared session and re-establishes it from the stored token. Use it if the live connection dropped (e.g. after a network blip) and commands start failing with auth errors. It returns the same status object as --health. Without a running daemon it errors (there is no shared session to rebuild: start aurelia daemon first, or just re-run the failing command standalone).
aurelia login --health             # human-readable status
aurelia login --health --json      # {"logged_in":true,"account":"me","steam_id":...,"daemon":true}
aurelia login --reconnect --json   # rebuild the shared session, then report status

Non-interactive --json login (for tooling)

With --json, login becomes a machine-drivable handshake with no TTY prompts: a driver (e.g. a GUI front-end) supplies credentials via flags/AURELIA_PASSWORD and exchanges NDJSON lines on stdout/stdin:

  • Password: aurelia login --json -u <user> -p <pass>. The first line emitted is always {"event":"awaiting_confirmation","message":"…"}, sent before the login attempt blocks, so the driver can immediately tell the user to approve the sign-in on their device (otherwise nothing prints until the attempt completes or times out). Then, if Steam needs a Guard code, a {"event":"guard_required","type":"email"|"device"} line follows. Write the code as a single line to the process's stdin and login retries. Accounts that use mobile-app approval instead emit {"event":"guard_required","type":"device_confirmation"}.
  • QR: aurelia login --qr --json streams {"event":"qr_challenge","url":"https://s.team/…"} (re-emitted whenever Steam rotates the code). Render the URL as a QR and wait.
  • OpenID: aurelia login --openid --json emits {"event":"openid_challenge","url":"https://steamcommunity.com/openid/login?…"}, after which the driver opens the URL in a browser (Aurelia does not auto-open one in --json mode) and waits while the user signs in on the Steam page.
  • Web token: aurelia login --web-token --json (no value) first checks the AURELIA_WEB_TOKEN environment variable for the clientjstoken JSON, the recommended driver channel, immune to shell/argv quoting of the embedded JSON quotes. Without it, the command emits {"event":"web_token_required","url":"https://steamcommunity.com/chat/clientjstoken"} and reads the JSON as one line from stdin. Passing the JSON as the flag's value also works where quoting is under control. --web-token always runs locally (never through the daemon), so the env var is reliably visible.
  • Result: password and QR logins end with {"logged_in":true,"account":"<name>"} on success. --openid ends with {"openid_verified":true,"steam_id":…,"matches_stored_session":true|false|null,"logged_in":false}: logged_in is always false because the OpenID flow verifies identity without creating a session (matches_stored_session is null when no session is stored). Failures always end with {"error":"…","type":"…"} and a non-zero exit (see Exit codes). Invalid credentials and a missing Guard code are auth_required (exit 77), and Steam's login throttling is rate_limited (exit 75).

The complete NDJSON event sequence a driver may observe, in order:

Event line When Driver action
{"event":"awaiting_confirmation","message":"…"} Immediately, on password login, before the attempt blocks. Show the message. Prompt the user to approve on their device if asked.
{"event":"qr_challenge","url":"…"} QR login, re-emitted on each code rotation. Render url as a QR code and wait.
{"event":"openid_challenge","url":"…"} OpenID identity check, once at the start. Open url in a browser. The user signs in on the Steam page.
{"event":"web_token_required","url":"…"} --web-token without a value. Fetch url with the signed-in browser/webview session, write the JSON as one line to the child's stdin.
{"event":"guard_required","type":"email"|"device"} A typed Steam Guard code is needed. Read a code from the user, write it as one line to the child's stdin.
{"event":"guard_required","type":"device_confirmation"} The account approves via the Steam Mobile app. Tell the user to approve in the app. The command then completes or times out.
{"logged_in":true,"account":"<name>"} Terminal: success (password/QR). Done. The session is persisted.
{"openid_verified":true,"steam_id":…,"logged_in":false} Terminal: success (--openid). Identity verified. No session was created.
{"web_token_saved":true,"steam_id":…,"expires_at":…,"logged_in":…} Terminal: success (--web-token). Web commands now work until expires_at. logged_in reflects whether a full client session also exists.
{"error":"…","type":"…"} Terminal: failure (non-zero exit). Surface the error. Branch on type (e.g. wait on rate_limited).

In --json mode the username/password must be provided up front (no interactive prompt), and only the Guard code is exchanged over stdin.

Session storage

The session lives in session.json in the config directory. Since v0.1.37, it is encrypted automatically, with no password and no prompt:

  • On the first successful login, Aurelia creates a random key and stores it in the OS keyring (Secret Service on Linux, Credential Manager on Windows, Keychain on macOS) under service aurelia, account session-key. session.json is then written as a ChaCha20-Poly1305 envelope keyed by it.
  • The CLI and the session daemon both read the key from the keyring themselves, so nothing has to be passed between them.
  • On Linux the keyring is reached over D-Bus (Secret Service), so a D-Bus session and a Secret Service provider (GNOME Keyring, KWallet, KeePassXC, …) must be running. The Nix flake ships dbus for this since v0.1.37-2.
  • Without a reachable keyring (e.g. a headless box with no Secret Service), the session stays plaintext with owner-only file permissions, and a warning is logged. Set AURELIA_DISABLE_KEYRING=1 to choose this on purpose.
  • The envelope keeps the account's display name in plaintext, so the "already logged in" warning works without touching the keyring.
  • If the keyring entry is lost, the session can't be decrypted. Run aurelia login again.

Upgrading from v0.1.36: the old password-based encryption (config session-password and AURELIA_SESSION_PASSWORD) is gone. A session.json encrypted with a session password can't be read any more. Aurelia tells you to run aurelia login again, which re-keys it to the OS keyring.

logout

Clear the stored session.

aurelia logout

Clone this wiki locally