from typing import Annotated, Any from anydi.ext.fastapi import Inject from fansifter_common.api.security import get_fan_response_jwt_auth from fansifter_common.auth.exceptions import NotAuthenticated from fansifter_common.constants import HEADER_ORCHARD_IDENTITY_ID from fastapi import Security from fastapi.security import APIKeyHeader, HTTPAuthorizationCredentials, HTTPBearer from jwtauth import JWTAuth from jwtauth.exceptions import JWTAuthError from starlette.requests import Request from email_campaigns.config import Settings, settings JWT_USER_METADATA = "https://grass.theorchard.com/user_metadata" CLAIM_ORCHARD_IDENTITY_ID = "orchardIdentityId" bearer_auth = HTTPBearer( scheme_name="JWTAuthorization", auto_error=False, ) orchard_identity_id_auth = APIKeyHeader( name=HEADER_ORCHARD_IDENTITY_ID, scheme_name="OrchardIdentityId", auto_error=False, ) async def get_identity_id( request: Request, credentials: HTTPAuthorizationCredentials | None = Security(bearer_auth), orchard_identity_id: str | None = Security(orchard_identity_id_auth), jwt_auth: JWTAuth = Inject(), ) -> str: # Local/dev only header authentication if not settings.jwt_auth_enabled: if not orchard_identity_id: raise NotAuthenticated return orchard_identity_id token: dict[str, Any] | None = request.scope.get("token") if not token and credentials: try: token = await jwt_auth.aget_token(credentials.credentials) except JWTAuthError as exc: raise NotAuthenticated(exc.message) from exc if not token: raise NotAuthenticated("Missing Authorization header.") identity_id: str | None = None grass_data = token.get(JWT_USER_METADATA) if grass_data: identity_id = grass_data.get(CLAIM_ORCHARD_IDENTITY_ID) if not identity_id: raise NotAuthenticated(f"Missing token {CLAIM_ORCHARD_IDENTITY_ID} claim.") return identity_id async def validate_fan_response_jwt( credentials: Annotated[HTTPAuthorizationCredentials | None, Security(bearer_auth)], app_settings: Annotated[Settings, Inject()], ) -> None: if not app_settings.jwt_auth_enabled: return if not credentials: raise NotAuthenticated("Missing Authorization header.") fan_response_jwt = get_fan_response_jwt_auth(app_settings.environment) try: await fan_response_jwt.aget_token(credentials.credentials) except Exception as exc: raise NotAuthenticated("Invalid JWT token.") from exc