A FastAPI dependency that validates the Authorization token, and returns an AuthenticatedUser object with the user's ID and pro status. Raises HTTPException if validation fails.
(request: Request)
| 36 | |
| 37 | # --- The Updated FastAPI Dependency --- |
| 38 | async def get_current_user(request: Request) -> AuthenticatedUser: |
| 39 | """ |
| 40 | A FastAPI dependency that validates the Authorization token, |
| 41 | and returns an AuthenticatedUser object with the user's ID and pro status. |
| 42 | Raises HTTPException if validation fails. |
| 43 | """ |
| 44 | credentials_exception = HTTPException( |
| 45 | status_code=status.HTTP_401_UNAUTHORIZED, |
| 46 | detail="Could not validate credentials", |
| 47 | headers={"WWW-Authenticate": "Bearer"}, |
| 48 | ) |
| 49 | |
| 50 | auth_header = request.headers.get("Authorization") |
| 51 | if not auth_header or not auth_header.startswith("Bearer "): |
| 52 | logger.warning("Authorization header is missing or does not start with Bearer") |
| 53 | raise credentials_exception |
| 54 | |
| 55 | token = auth_header.split(" ")[1] |
| 56 | |
| 57 | try: |
| 58 | signing_key = jwks_client.get_signing_key_from_jwt(token) |
| 59 | |
| 60 | payload = jwt.decode( |
| 61 | token, |
| 62 | signing_key.key, |
| 63 | algorithms=ALGORITHMS, |
| 64 | audience=API_AUDIENCE, |
| 65 | issuer=ISSUER, |
| 66 | options={"verify_exp": True} |
| 67 | ) |
| 68 | |
| 69 | user_id = payload.get("sub") |
| 70 | if user_id is None: |
| 71 | logger.warning("Token is valid but 'sub' (user ID) is missing.") |
| 72 | raise credentials_exception |
| 73 | |
| 74 | # 1. Define the claim name for the whole metadata object |
| 75 | app_metadata_claim = f"{CUSTOM_CLAIM_NAMESPACE}app_metadata" |
| 76 | |
| 77 | # 2. Get the metadata dictionary from the token (it will be None if not present) |
| 78 | app_metadata = payload.get(app_metadata_claim) or {} # Use empty dict as fallback |
| 79 | |
| 80 | # 3. Get the specific values from the metadata dictionary |
| 81 | is_pro_status = app_metadata.get("is_pro", False) |
| 82 | is_max_status = app_metadata.get("is_max", False) |
| 83 | is_plus_status = app_metadata.get("is_plus", False) |
| 84 | |
| 85 | # 4. Get email from namespaced claim (set by Auth0 Action) |
| 86 | email_claim = f"{CUSTOM_CLAIM_NAMESPACE}email" |
| 87 | user_email = payload.get(email_claim) |
| 88 | |
| 89 | logger.info(f"User is pro: {is_pro_status}, is max: {is_max_status}, is plus: {is_plus_status}") |
| 90 | |
| 91 | # Return the structured user data |
| 92 | return AuthenticatedUser( |
| 93 | id=user_id, |
| 94 | email=user_email, |
| 95 | is_pro=is_pro_status, |
nothing calls this directly
no test coverage detected