-
Notifications
You must be signed in to change notification settings - Fork 0
SteamAuthenticator
steam.guard.SteamAuthenticator manages Steam Guard 2FA on an account: add a new mobile authenticator, generate TOTP codes, sign trade confirmations, remove an authenticator, and generate emergency backup codes.
The authenticator is bound to an account either via a MobileWebAuth session (WebAPI path — full feature set) or via a logged-in SteamClient (unified-messages path — everything except remove(), which Valve disabled on that channel).
SteamAuthenticator(secrets=None, backend=None)
-
secrets— dict of authenticator secrets, if you already have them. If omitted, you'll get them from.add(). -
backend— aMobileWebAuthorSteamClientinstance, logged in. Required for any network-touching operation. Optional for code-only use — you can hold aSteamAuthenticatorwith just secrets and call.get_code().
import json
import steam.webauth as wa
from steam.guard import SteamAuthenticator
# Step 1: log in with MobileWebAuth (needs your Steam account password).
mobile = wa.MobileWebAuth('steamuser')
mobile.cli_login()
# Step 2: instantiate the authenticator.
sa = SteamAuthenticator(backend=mobile)
# Step 3: request activation. Steam sends an SMS code to the account's phone.
sa.add()
# Step 4: SAVE THE SECRETS. If you lose them here you lose account access.
json.dump(sa.secrets, open('./mysecrets.json', 'w'))
# Step 5: complete the bind by echoing the SMS code back.
sa.finalize('SMS_CODE_FROM_TEXT')
# Step 6: use it.
print(sa.get_code()) # current 2FA codeOnce you have secrets on disk, no backend is needed for pure code generation:
secrets = json.load(open('./mysecrets.json'))
sa = SteamAuthenticator(secrets)
print(sa.get_code())Provision a new authenticator. Populates .secrets with a dict containing shared_secret, identity_secret, revocation_code, serial_number, and other bookkeeping. Triggers Steam to send an SMS code to the account's phone.
Raises SteamAuthenticatorError if:
- The account has no verified phone number (
has_phone_number()first, thenadd_phone_number(...)if needed — see below). - Steam rejects the request (
resp['status'] != EResult.OK).
Save the secrets before calling finalize(). They can't be recovered afterwards.
Adding a new authenticator invalidates any older one on the same account.
Complete the bind using the SMS code Steam sent during .add(). Auto-retries up to 5 times with a +30s time drift adjustment when Steam returns want_more=True, since a large time offset between your clock and Steam's causes early rejections.
Raises SteamAuthenticatorError on final failure.
Generate a Steam 2FA code (5 characters, Steam's custom alphabet). Uses .get_time() internally, which aligns with Steam's server time if align_time_every is set.
Signature: get_code(timestamp=None) -> str
Deactivate the authenticator. revocation_code defaults to self.secrets['revocation_code'] — the R-prefixed code Steam returned in .add().
Only works via MobileWebAuth. Valve disabled this endpoint over the unified-messages channel — trying it via SteamClient raises SteamAuthenticatorError("Only available via MobileWebAuth").
After removal, Steam Guard falls back to email codes.
Generate an HMAC-SHA1 signature over (timestamp, tag) using the authenticator's identity_secret. Used to sign trade confirmations. Valid tags:
-
'conf'— load the confirmations page. -
'details'— load details about a trade. -
'allow'— confirm a trade. -
'cancel'— cancel a trade.
Two-step because Steam requires a fresh SMS code:
sa.create_emergency_codes() # request SMS
sa.create_emergency_codes(code='12345') # returns list of emergency codesInvalidate all emergency codes for the account.
Fetch the current authenticator status from Steam. Returns a dict with state, token_gid, etc.
Steam requires a verified phone number to add an authenticator. If the account doesn't have one:
sa.add_phone_number('+15551234567') # 1. request add
sa.confirm_email() # 2. only if resp said email_confirmation=True
sa.confirm_phone_number('SMS_CODE') # 3. finalize with SMSUtility checks:
-
sa.has_phone_number()— is one already on file? -
sa.validate_phone_number(number)— is this number valid for Steam? (Rejects VoIP.)
TOTP codes are time-sensitive. Steam's servers run in their own time frame; if your clock is off, codes fail. Fixing that:
sa.align_time_every = 60 # re-check Steam time every 60s
sa.steam_time_offset # cached offset, in secondssa.get_time() handles the alignment. Or manually via steam.guard.get_time_offset().
For a rooted Android phone with the Steam Guard app installed:
from steam.guard import extract_secrets_from_android_rooted
secrets_by_steamid = extract_secrets_from_android_rooted()
# {int(steamid): {...secrets dict...}, ...}Requires adb binary + rooted device + debug mode. See docstring in steam/guard.py for details.
sa.secrets is a dict with:
-
account_name— Steam username. -
identity_secret— base64. Used forget_confirmation_key. -
revocation_code— R-prefixed string. Save this — needed forremove(). -
secret_1— base64. Purpose unknown to us; Steam wants it. -
serial_number— decimal string, serial of the authenticator. -
server_time— Unix timestamp of when the authenticator was created. -
shared_secret— base64. Used forget_code(). -
status— int.1= active. -
token_gid— hex GID for the token. -
uri—otpauth://totp/...URL usable by generic TOTP apps (though the alphabet is Steam-custom).
Format is Steam-Desktop-Authenticator (SDA) compatible — you can hand mysecrets.json to SDA or WinAuth and they'll accept it.
- The mobile login flow: WebAuth —
MobileWebAuthis the required backend for full functionality. - Actually signing a trade confirmation? Use
get_confirmation_key()to build thek=<hex>parameter, then POST to the trade-confirmation endpoint yourself.steam.guarddoesn't wrap the full trade-confirmation flow.
H47R15/steam — maintained fork of ValvePython/steam. MIT licensed.