From d343d20b3e76b9339e1db6e1f01cd3bcb554808c Mon Sep 17 00:00:00 2001 From: Mabe Date: Sat, 20 Jun 2026 23:13:10 +0200 Subject: [PATCH] oicd_audience removal --- .env.example | 4 +++- backend/app/auth.py | 24 ++++++++++++++++-------- docs/OIDC_AUDIENCE.md | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 docs/OIDC_AUDIENCE.md diff --git a/.env.example b/.env.example index ddd772f..308708e 100644 --- a/.env.example +++ b/.env.example @@ -2,8 +2,10 @@ OLLAMA_API_KEY=your_ollama_api_key_here # OIDC settings +# `OIDC_AUDIENCE` is optional. If set, the backend will validate the token's `aud` claim. +# If you prefer to skip audience validation (not recommended in production), leave +# `OIDC_AUDIENCE` unset. OIDC_ISSUER=https://your-issuer.example.com -OIDC_AUDIENCE=your-api-audience OIDC_CLIENT_ID=your-client-id OIDC_CLIENT_SECRET=your-client-secret OIDC_VERIFY_ISS=true diff --git a/backend/app/auth.py b/backend/app/auth.py index a23ca96..593fe63 100644 --- a/backend/app/auth.py +++ b/backend/app/auth.py @@ -11,8 +11,11 @@ from pydantic import BaseModel, BaseSettings, Field, HttpUrl security = HTTPBearer(auto_error=False) class Settings(BaseSettings): + # OIDC issuer (discovery URL root) oidc_issuer: HttpUrl = Field(..., env="OIDC_ISSUER") - oidc_audience: str = Field(..., env="OIDC_AUDIENCE") + # Optional audience: if set the backend will validate the token 'aud' claim. + # If omitted, audience validation is skipped (use with caution). + oidc_audience: Optional[str] = Field(None, env="OIDC_AUDIENCE") oidc_client_id: str = Field(..., env="OIDC_CLIENT_ID") oidc_client_secret: Optional[str] = Field(None, env="OIDC_CLIENT_SECRET") oidc_verify_iss: bool = Field(True, env="OIDC_VERIFY_ISS") @@ -79,13 +82,18 @@ async def validate_token(credentials: HTTPAuthorizationCredentials = Security(se key = get_signing_key(token) public_key = jwk.construct(key) try: - verified = jwt.decode( - token, - public_key, - algorithms=[key.get("alg", "RS256")], - audience=settings.oidc_audience, - issuer=str(settings.oidc_issuer) if settings.oidc_verify_iss else None, - ) + # Only pass `audience` to the decoder if configured. Some providers + # (or local development setups) may not include the aud claim in a + # way that matches your API identifier; in that case leave + # `OIDC_AUDIENCE` unset and the audience check will be skipped. + jwt_kwargs = { + "algorithms": [key.get("alg", "RS256")], + "issuer": str(settings.oidc_issuer) if settings.oidc_verify_iss else None, + } + if settings.oidc_audience: + jwt_kwargs["audience"] = settings.oidc_audience + + verified = jwt.decode(token, public_key, **jwt_kwargs) except JWTError as exc: raise HTTPException(status_code=401, detail="Invalid or expired token") from exc diff --git a/docs/OIDC_AUDIENCE.md b/docs/OIDC_AUDIENCE.md new file mode 100644 index 0000000..ebc2b01 --- /dev/null +++ b/docs/OIDC_AUDIENCE.md @@ -0,0 +1,35 @@ +# OIDC Audience (`aud`) — explanation and guidance + +Why `aud` matters + +- The `aud` (audience) claim in a JWT indicates the intended recipient(s) of the token. +- Your API should verify `aud` to ensure the token was issued for this service, preventing tokens meant for other services from being used against your backend. + +How this project handles `aud` + +- `OIDC_AUDIENCE` is optional in this scaffold. +- If `OIDC_AUDIENCE` is set in the environment, the backend will validate that the token's `aud` claim matches that value. +- If `OIDC_AUDIENCE` is not set, the backend will skip audience validation (the token still must be valid and optionally matched to `iss`). + +Security guidance + +- Production: set `OIDC_AUDIENCE` to the identifier your provider includes in access tokens for this API. This is typically: + - The API identifier or Resource URI you configured in Auth0 + - The client ID (or `api://`) in Azure AD when you configured an app registration + - The audience or client for Keycloak realm resources + +- Local dev: it can be convenient to omit `OIDC_AUDIENCE` when using non-standard tokens or test setups, but avoid this in production. + +Provider-specific notes + +- Auth0: configure an API and use its Identifier as the `audience` when requesting tokens. The access token will contain that audience. +- Keycloak: check the client/realm settings; the `aud` may be the client id or an array of client ids. +- Azure AD: access tokens may use `aud` = `api://` or your Application (client) ID URI. + +How to discover the correct value + +1. Request a token from your provider (or use the issuer's test token). +2. Decode the JWT (jwt.io or `python-jose`) and inspect the `aud` claim. +3. Set `OIDC_AUDIENCE` to that value in your `.env`. + +If you want, paste an example decoded token (redact sensitive fields) and I can tell you the exact value to use for `OIDC_AUDIENCE`.