feat(client): password login via IAuthenticationService over WebSocket CM#25
Open
fabieu wants to merge 7 commits into
Open
feat(client): password login via IAuthenticationService over WebSocket CM#25fabieu wants to merge 7 commits into
fabieu wants to merge 7 commits into
Conversation
Steam no longer accepts plaintext passwords at the CM. Replace the removed login_key flow with the credential auth session flow: encrypt the password with the account RSA key, run BeginAuthSessionViaCredentials over the CM as a NonAuthed unified message, handle Steam Guard codes / mobile confirmation, poll for a refresh token, and log on with it. The acquired refresh token is stored on `refresh_token` and reused by `relogin`. The auth session (client_id/request_id) is cached on the client and reused across `login()` calls while a guard code is being collected, so an email code is not invalidated by a fresh session on each retry, and an accepted code that mints its token slowly is not misreported as a mismatch.
- Split _get_refresh_token's guard-code submission and token polling into _submit_guard_code / _poll_for_refresh_token to cut cognitive complexity. - Drop the redundant tuple() constructor in webauth._encrypt_password. - Use `_` for unpacked return values the login tests do not assert. - Test rsa_encrypt_password against a 2048-bit key and rename the password variable; the round-trip must use PKCS#1 v1.5, the scheme Steam mandates.
Steam only serves the pre-logon IAuthenticationService credential flow over the WebSocket CM endpoints, so the raw TCP CMs drop the non-authed auth call. Add a WebsocketConnection (wss://<cm>/cmsocket/) that frames each Steam message as one binary frame (no VT01 prefix, no AES -- TLS secures the channel) and plugs into the base Connection via _frame/_close_socket hooks. CMClient gains PROTOCOL_WEBSOCKET, sets channel_secured immediately, sends the required ClientHello, stamps realm/steamid/client_sessionid on WS headers, and bootstraps from serverlist_websockets. SteamClient now defaults to WebSocket. Also wait up to confirmation_timeout for an out-of-band approval: ask for a code and poll for a mobile-app confirmation in the background, whichever completes first. Removes the deprecated MobileWebAuth and reuses rsa_encrypt_password in webauth. Requires the websocket-client dependency (client extra) and cooperative sockets -- login recipes now call steam.monkey.patch_minimal().
- Extract _request_guard_code to drop _get_refresh_token cognitive complexity below 15. - Rename TCPChannelEncrypt_Scenario to satisfy the class-name convention. - Explain the empty FakeWS.settimeout stub and use a documentation-range IP in the bootstrap test.
- Tolerate unknown logon eresults via _eresult() instead of raising - Prevent a finishing background poll from orphaning a newer one - Pace failed CM discovery rounds so the websocket loop cannot busy-spin - Add wait_for_confirmation to cli_login so out-of-band approval works, and skip the code prompt when a background poll already logged us on - Drop the impossible-scenario guard around socket.gethostname()
Extract the Steam Guard code prompt and transient-error handling from cli_login into _cli_prompt_guard_code and _cli_handle_unavailable to bring the function under SonarCloud's cognitive complexity limit.
Scope the already-logged-on short-circuit to the same account so a login for a different user no longer silently returns OK, and drop a stored refresh_token / pending auth session on an account switch to avoid a mismatched (username, refresh_token) pair and stale session reuse. Tolerate unknown logon eresults in the base handler instead of raising out of the message greenlet, reject empty input at the "keep retrying" CLI prompt, and fail gracefully when GetCMList omits the WebSocket serverlist instead of raising KeyError.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Steam no longer accepts plaintext passwords at the CM, which broke
SteamClient.login(username, password)and the oldlogin_key"remember password" flow. Steam also only serves the pre-logonIAuthenticationServicecredential flow over its WebSocket CM endpoints, not the raw TCP CMs. This PR rebuilds password login on the modern credential auth session and adds a WebSocket CM transport to carry it.What changed
Password login via
IAuthenticationServicelogin(): when apasswordis supplied, run the credential flow to obtain a JWT refresh token, then log on with it. A previously obtained token can be passed directly viaaccess_token. Dropped thelogin_keyparameter; auth codes are now handled pre-logon rather than onCMsgClientLogon._get_refresh_token/_begin_auth_session):GetPasswordRSAPublicKey→BeginAuthSessionViaCredentials→ optionalUpdateAuthSessionWithSteamGuardCode→ pollPollAuthSessionStatus, all sent as NonAuthed unified messages before logon.confirmation_timeoutfor either a Steam Guard code or a mobile-app approval — ask for a code and poll for a mobile confirmation in the background, whichever completes first.client_id/request_id) is cached on the client and reused acrosslogin()retries while a guard code is collected, so an email code isn't invalidated by a fresh session on each retry. An accepted code whose token mints slowly is no longer misreported as a mismatch.relogin/relogin_available: now backed by the storedrefresh_tokeninstead oflogin_key. RemovedEVENT_NEW_LOGIN_KEYand_handle_login_key.send_um/send_um_and_wait: accept anemsgoverride so pre-logon calls can useServiceMethodCallFromClientNonAuthed(newEMsg9804).WebSocket CM transport
WebsocketConnection(wss://<cm>/cmsocket/): frames each Steam message as one binary WebSocket frame (no VT01 prefix, no AES — TLS secures the channel) and plugs into the baseConnectionvia_frame/_close_sockethooks.CMClient: gainsPROTOCOL_WEBSOCKET, setschannel_securedimmediately, sends the requiredClientHello, stamps realm/steamid/client_sessionid on the WS headers, and bootstraps fromserverlist_websockets.SteamClientnow defaults to the WebSocket transport.websocket-clientdependency (clientextra) and cooperative sockets — login recipes now callsteam.monkey.patch_minimal().Cleanup
rsa_encrypt_password: new helper insteam.core.crypto;webauthreuses it (UTF-8 password encoding).MobileWebAuth.Tests
tests/test_client_login.py— refresh-token flow (success, invalid password, 2FA/email code required and accepted/rejected, mobile confirmation, session reuse, slow token mint, unknown confirmation type),_send_logon/ NonAuthed unified message behavior, and a unicode round-trip forrsa_encrypt_password.tests/test_websocket_transport.py— WebSocket framing and connection behavior.tests/test_core_cm.py— updated for the WebSocket protocol path.Closes #23.