Support both v1.0 and v2.0 Azure AD token issuer formats

Azure AD issues tokens with different issuer formats depending on the
app registration's accessTokenAcceptedVersion setting:
- v1.0: https://sts.windows.net/{tenant}/
- v2.0: https://login.microsoftonline.com/{tenant}/v2.0

Update backend to accept both formats by trying each issuer in sequence.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
michael 2025-12-18 17:03:47 -06:00
parent dd5ee09d07
commit 0e6f5be46d

View file

@ -120,19 +120,35 @@ async def verify_access_token(token: str) -> Optional[dict]:
logger.info(f"[MSAL Backend] Found matching RSA key for kid: {kid}")
# Verify and decode the token
# For ID tokens with OpenID scopes, audience is the client ID
# and issuer uses the v2.0 endpoint
expected_issuer = f"https://login.microsoftonline.com/{settings.AZURE_TENANT_ID}/v2.0"
logger.info(f"[MSAL Backend] Verifying with audience: {settings.AZURE_CLIENT_ID}")
logger.info(f"[MSAL Backend] Verifying with issuer: {expected_issuer}")
# Azure AD can issue tokens with either v1.0 or v2.0 issuer format
# depending on the app registration's accessTokenAcceptedVersion setting
v1_issuer = f"https://sts.windows.net/{settings.AZURE_TENANT_ID}/"
v2_issuer = f"https://login.microsoftonline.com/{settings.AZURE_TENANT_ID}/v2.0"
claims = jwt.decode(
token,
rsa_key,
algorithms=["RS256"],
audience=settings.AZURE_CLIENT_ID,
issuer=expected_issuer,
)
logger.info(f"[MSAL Backend] Verifying with audience: {settings.AZURE_CLIENT_ID}")
logger.info(f"[MSAL Backend] Accepting issuers: {v1_issuer} OR {v2_issuer}")
# Try v1 issuer first (most common for /.default scope tokens)
claims = None
for issuer in [v1_issuer, v2_issuer]:
try:
claims = jwt.decode(
token,
rsa_key,
algorithms=["RS256"],
audience=settings.AZURE_CLIENT_ID,
issuer=issuer,
)
logger.info(f"[MSAL Backend] Token verified with issuer: {issuer}")
break
except JWTError as e:
if "issuer" in str(e).lower():
continue # Try next issuer
raise # Re-raise if it's a different error
if not claims:
logger.warning("[MSAL Backend] Token issuer doesn't match any expected format")
return None
logger.info(f"[MSAL Backend] Token verified successfully!")
logger.info(f"[MSAL Backend] User: {claims.get('name', 'unknown')} ({claims.get('preferred_username', 'unknown')})")