Skip to content

Fix web login: migrate to /api/v2/auth/web/login + push approval (closes #250)#355

Open
twannooitmeer wants to merge 1 commit into
pytr-org:masterfrom
twannooitmeer:fix/auth-v2-web-login
Open

Fix web login: migrate to /api/v2/auth/web/login + push approval (closes #250)#355
twannooitmeer wants to merge 1 commit into
pytr-org:masterfrom
twannooitmeer:fix/auth-v2-web-login

Conversation

@twannooitmeer

Copy link
Copy Markdown

Summary

Trade Republic deprecated the legacy /api/v1/auth/web/login endpoint. All requests to it currently return HTTP 426 CLIENT_VERSION_OUTDATED regardless of User-Agent, which makes pytr login unusable on a fresh install (refs #250). (Verified on 2026-05-29; the cutover itself happened at some earlier point.)

This PR migrates the auth flow to /api/v2/auth/web/login, which is what app.traderepublic.com currently uses.

What changed

/api/v2/auth/web/login requires four new headers

Captured from the live web app:

x-tr-platform: web
x-tr-app-version: 15.7.0          # web track, distinct from the Android app version
x-tr-device-info: <base64(JSON)>  # browser fingerprint
x-aws-waf-token: <same value as the aws-waf-token cookie>

The x-tr-device-info payload looks like:

{
  "stableDeviceId": "<64 hex chars>",
  "model": "Apple Macintosh",
  "browser": "Chrome",
  "browserVersion": "148.0.0.0",
  "os": "Mac OS",
  "osVersion": "10.15.7",
  "timezone": "Europe/Amsterdam",
  "timezoneOffset": -120,
  "screen": "1800x1169x30",
  "preferredLanguages": ["en", "en-US"],
  "numberOfCores": 12,
  "deviceMemory": 16
}

v2 uses push-approval, not a numeric code

The v2 flow no longer issues an SMS/numeric code. Instead the user approves the login via a push notification in the Trade Republic mobile app, and the web client polls GET /api/v2/auth/web/login/processes/{processId} until the response status indicates approval (and a tr_session cookie is set).

Implementation

  • pytr/api.py
    • Module-level constants TR_WEB_APP_VERSION, TR_WEB_USER_AGENT, TR_WEB_LOGIN_PATH, overridable via PYTR_TR_APP_VERSION / PYTR_TR_USER_AGENT so future TR bumps don't require a code change:
      # If TR bumps the web app version and pytr starts returning CLIENT_VERSION_OUTDATED again:
      PYTR_TR_APP_VERSION=15.8.0 pytr login
      
      # If TR starts gating by a specific browser UA:
      PYTR_TR_USER_AGENT='Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36' pytr login
    • _get_device_id() persists a stable 64-hex device id at ~/.pytr/device_id.
    • _build_device_info_header() constructs the base64-encoded JSON fingerprint.
    • _auth_headers() returns all four required headers (WAF token included when known).
    • await_web_login_approval() polls the new processes/{processId} endpoint every 2 s with a 180 s timeout. Detects approval via either an explicit status field or the presence of a tr_session cookie.
    • initiate_weblogin() now POSTs to v2 with the new headers, kicks off the poll, and returns 0 to signal the v2 flow.
    • complete_weblogin('') is a no-op (approval already happened during initiation).
  • pytr/account.py
    • When initiate_weblogin() returns 0, skip the code/SMS prompts entirely and call complete_weblogin('') to persist cookies.
    • Legacy v1 prompt path is preserved unchanged for the countdown > 0 case (dead branch in practice, but safe to keep).

Tested

Verified locally end-to-end on macOS, Python 3.12: WAF token fetch → processId → push approval → session cookie → Logged in.

Notes

  • Closes App Login Broken #250.
  • Does not yet implement opt-in registration via trustDeviceRegistrationOptions (returned by the process endpoint); that would let users skip the push step on subsequent logins. Happy to follow up in a separate PR.

Trade Republic deprecated the legacy /api/v1/auth/web/login endpoint
(all requests now return HTTP 426 CLIENT_VERSION_OUTDATED regardless of
User-Agent). The current web app uses /api/v2/auth/web/login with:

  * x-tr-platform: web
  * x-tr-app-version: 15.7.0  (web track, distinct from the Android version)
  * x-tr-device-info: base64(JSON) browser fingerprint
  * x-aws-waf-token: same value as the aws-waf-token cookie

The v2 flow also no longer issues a numeric code: the user approves the
login via a push notification in the TR mobile app. The web app polls
GET /api/v2/auth/web/login/processes/{processId} until the push is
acknowledged.

Changes:
  * pytr/api.py: add TR_WEB_APP_VERSION / TR_WEB_USER_AGENT / TR_WEB_LOGIN_PATH
    constants (overridable via env), _get_device_id() persisting a stable
    UUID at ~/.pytr/device_id, _build_device_info_header(), _auth_headers()
    helper, and await_web_login_approval() polling loop.
  * initiate_weblogin() now POSTs to v2 with the new headers, polls for
    push approval, and returns 0 to signal the v2 flow.
  * complete_weblogin('') is a no-op for v2 (approval already happened).
  * pytr/account.py: when initiate_weblogin() returns 0, skip the
    code/SMS prompts and call complete_weblogin('') directly.

Refs: pytr-org#250
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

App Login Broken

1 participant