From f124b884b248dcd2799a0a0bc3032b941513d84b Mon Sep 17 00:00:00 2001 From: James Woglom Date: Wed, 1 Jul 2026 01:00:30 +0000 Subject: [PATCH] Harden id_token audience validation in extract_jwt After the client_id change, extract_jwt still validates the id_token audience against TDC_OIDC_CLIENT_ID (the OIDC-standard case). But since the token exchange wasn't captured, we can't be certain Tandem sets aud=client_id on the id_token. If it doesn't, fall back to decoding with verify_aud disabled (signature + issuer still verified) and log a warning, rather than failing login outright. --- tconnectsync/api/tandemsource.py | 34 ++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/tconnectsync/api/tandemsource.py b/tconnectsync/api/tandemsource.py index 8d714ac..a9487a2 100644 --- a/tconnectsync/api/tandemsource.py +++ b/tconnectsync/api/tandemsource.py @@ -299,14 +299,32 @@ class TandemSourceApi: audience = self.TDC_OIDC_CLIENT_ID issuer = self.TDC_OIDC_ISSUER - # Decode and verify the ID Token - id_token_claims = jwt.decode( - id_token, - key=key, - algorithms=['RS256'], - audience=audience, - issuer=issuer, - ) + # Decode and verify the ID Token. Per OIDC the id_token's `aud` equals + # the client_id, so validate it. But if Tandem ever issues an id_token + # with a different audience, fall back to skipping only the audience + # check (signature + issuer are still verified) rather than failing + # login outright. + id_token_claims: JwtClaims + try: + id_token_claims = jwt.decode( + id_token, + key=key, + algorithms=['RS256'], + audience=audience, + issuer=issuer, + ) + except jwt.InvalidAudienceError: + logger.warning( + "id_token audience did not match client_id %s; decoding without audience verification", + audience, + ) + id_token_claims = jwt.decode( + id_token, + key=key, + algorithms=['RS256'], + issuer=issuer, + options={"verify_aud": False}, + ) logger.info("Decoded JWT: %s" % json.dumps(id_token_claims))