Migrate WeConnect auth from deprecated BFF endpoints to direct OIDC#237
Open
oldgitdaddy wants to merge 4 commits into
Open
Migrate WeConnect auth from deprecated BFF endpoints to direct OIDC#237oldgitdaddy wants to merge 4 commits into
oldgitdaddy wants to merge 4 commits into
Conversation
VW shut down the legacy BFF endpoints (emea.bff.cariad.digital/user-login/*) at the Azure WAF layer, returning 403 for all clients. This broke WeConnect authentication. Replace the deprecated BFF-based auth flow with direct OIDC endpoints, following the same pattern already used by MyCupraSession: - Authorization: direct OIDC identity.vwgroup.io/oidc/v1/authorize (removed authorizationUrl() override to use parent class implementation) - Token exchange: identity.vwgroup.io/oidc/v1/token with grant_type=authorization_code (standard OIDC) - Token refresh: identity.vwgroup.io/oidc/v1/token (standard OIDC) - Updated User-Agent to Volkswagen/3.61.0-android/14 to match latest APK Fixes tillsteinbach#155 in CarConnectivity
Owner
|
Is it working for you? I don't get past the token fetching from /oidc/v1/token. The post request gives me a 401 |
The direct POST to identity.vwgroup.io/oidc/v1/token with grant_type=authorization_code returns 401 access_denied because Auth0 binds the authorization code to the CARIAD BFF as the authorized exchanger. Switch to the OIDC hybrid flow (response_type=code id_token token) where access_token and id_token are delivered directly in the callback URL — no server-side token exchange is needed. The parent class OpenIDSession.authorizationUrl() already uses this response type so the authorization URL was already correct. Key changes: - fetchTokens(): extract tokens from callback directly, skip the broken token exchange POST to /oidc/v1/token - refresh(): trigger full re-login since hybrid flow issues no refresh_token (~2h access token lifetime) - refreshTokens(): simplified to delegate to login() for graceful fallback - _handle_new_auth_flow(): add action=default to login form POST (required by Auth0 Universal Login, per PR #333 finding) - Scope: add offline_access - Remove unused imports (requests, InsecureTransportError, etc.) Aligned with robinostlund/volkswagencarnet#333. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author
|
At least for my small test program it works now. With the additional commit I can connect and query vehicles. token refresh not tried.... |
Author
|
ok refresh also works. I am very curious how VW is thinking about how to handle this on the long run. The refresh now is much more expensive. Let´s see if it pays off to cut off frontend APIs and to force everyone to do a full new login. VW good luck! 🥇 |
The OIDC hybrid flow has no refresh_token, but the websession preserves Auth0 SSO cookies from the initial login. refresh() now: 1. First tries prompt=none — if the Auth0 session cookie is still valid, tokens are returned silently with no login form. 2. Falls back to full credential-based login if the session expired. This makes token refresh transparent when the Auth0 session outlasts the 2h access_token (which is common). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The --refresh test now: 1. Captures WeConnectSession log messages during refresh() 2. Simulates token expiry (expires_at=0) to force the refresh path 3. Reports whether silent re-auth (prompt=none via Auth0 cookie) was sufficient, or a full credential-based re-login was required 4. Shows the failure reason if silent auth fell back Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Author
|
added test script, which we of course can remove again. With this we can trigger refresh and force token expiry. |
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.
Problem
VW shut down the legacy BFF (Backend-for-Frontend) auth endpoints at the Azure WAF layer. The endpoints
emea.bff.cariad.digital/user-login/v1/authorizeandemea.bff.cariad.digital/user-login/login/v1now return 403 Forbidden for all clients. This broke WeConnect authentication for all Volkswagen accounts.Fixes: tillsteinbach/CarConnectivity#155
Solution — Updated: OIDC Hybrid Flow
Update (2026-05-31): The original approach (direct OIDC authorization code flow) didn't work — POSTing to
identity.vwgroup.io/oidc/v1/tokenwithgrant_type=authorization_codereturned 401 access_denied. Per feedback from @tillsteinbach, the fix wasn't working because Auth0 binds the authorization code to the CARIAD BFF as the authorized exchanger — only CARIAD BFF can exchange codes for tokens.This PR has been updated to align with the working solution in robinostlund/volkswagencarnet#333 by @s1gmund80, which uses the OIDC hybrid flow (
response_type=code id_token token).Why Hybrid Flow Works
With the hybrid flow, Auth0 delivers
access_tokenandid_tokendirectly in the callback URL (URL fragment). No separate server-side token exchange is needed — we skip thePOST /oidc/v1/tokenentirely, avoiding the 401 that the CARIAD BFF token exchange would return.The parent class
OpenIDSession.authorizationUrl()already hardcodesresponse_type='code id_token token', so the authorization URL was already correct. The issue was thatfetchTokens()was still attempting the broken token exchange.Trade-off: No Refresh Token
The OIDC hybrid flow does not return a
refresh_tokenfor security reasons. When theaccess_tokenexpires (~2 hours), a full re-login is required. This matches the behavior in robinostlund/volkswagencarnet#333. All alternative paths were tested and failed:POST /auth/v1/idk/oidc/token(inner opaque code, Bearer JWT)400 Bad RequestPOST /auth/v1/idk/oidc/token(inner code, no Bearer)400 Bad RequestPOST /auth/v1/idk/oidc/token(JWT ascode)400 invalid assertion headersPOST identity.vwgroup.io/oidc/v1/token(direct Auth0, no PKCE)401 access_deniedPOST identity.vwgroup.io/oidc/v1/token(direct Auth0, with PKCE)401 access_deniedPOST /user-login/login/v1(VW-specific endpoint)403 ForbiddenChanges
weconnect/auth/vw_web_session.py3.61.0(old UA caused 401 errors)action=defaultto login form POST body in_handle_new_auth_flow(). This is required by Auth0 Universal Login to distinguish credential submission from other form actions — without it the login POST can be silently ignored (per fix(auth): switch to OIDC hybrid flow after CARIAD BFF token exchange brake robinostlund/volkswagencarnet#333 finding).weconnect/auth/we_connect_session.pyoffline_access— required for refresh token issuance (per PR #333)fetchTokens()parseFromFragment(). No longer POSTs to/oidc/v1/token— skips the broken token exchange entirely.refresh()refresh_tokenrefreshTokens()login()— graceful fallback for the no-refresh-token caseVolkswagen/3.61.0-android/14Key Design Decisions
client_secret: WeConnect is a public OIDC client (unlike MyCupra)access_tokenandid_tokenfrom the Auth0 callback URL are the session tokens — no transformation neededrefreshTokensis preserved in case VW restores a usable code exchange path on the BFF — it will work automatically if refresh tokens become available againauthorizationUrl()override: Parent class already constructs the correct OIDC URL withresponse_type=code id_token tokenReferences
Testing
response_type=code id_token token🤖 Generated with Claude Code