diff --git a/backend/app/services/auth_service.py b/backend/app/services/auth_service.py index 8745c6f..4b4b126 100755 --- a/backend/app/services/auth_service.py +++ b/backend/app/services/auth_service.py @@ -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')})")