Loading
feat: validate Keycloak tokens and enforce CAMARA roles, scopes and consent per operation
What this does
OEG now checks the Keycloak access token on every CAMARA route (QoD, Edge Application Management, Location Retrieval) and enforces what each operation's CAMARA spec declares.
Token validation (core/auth/)
- Reads
Authorization: Bearer <JWT>and checks the signature against Keycloak's JWKS, with the algorithm fixed to RS256. It also checksiss,aud(defaultoeg) andexp. - Signing keys are cached per
kid(5-minute lifetime, refetched when an unknownkidarrives) and loaded once at startup. - The caller context is built from the token: tenant from
organization, roles fromrealm_access.roles, and subject fromsub. Two-legged vs three-legged is detected fromauth_time.
Authorization per operation (core/authorization.py)
- One table,
OPERATION_REQUIREMENTS, lists the required role, CAMARA scope and three-legged requirement for every route.- All routes require the
app-providerrole. - Location Retrieval always requires a three-legged (user-consented) token.
- QoD: two-legged tokens don't need a scope, as the spec allows.
- EAM and Location Retrieval always need their scope.
- All routes require the
- A route with no entry in the table fails closed: a server error, never silently allowed through.
Error mapping
- 401: missing or invalid token, or missing required claims.
- 403: wrong role, missing scope, or a two-legged token where consent is required.
- 503: Keycloak's JWKS can't be reached (the caller did nothing wrong).
/platform/readyznow also reports JWKS health.
Rollout switch
KEYCLOAK_SETTINGS__ENFORCEMENT_ENABLED=false logs auth_would_reject / authz_would_reject instead of rejecting. It's meant only for a supervised migration window; the default is true.
Also included
- Location Retrieval receives the caller's
subject_idfor device lookup. For now this is the rawsub. - New dependency: PyJWT.
Configuration
| Env var | Notes |
|---|---|
KEYCLOAK_SETTINGS__ISSUER |
Must equal Keycloak's public URL + /realms/oop (what Keycloak writes into iss), not the in-cluster service name |
KEYCLOAK_SETTINGS__JWKS_URL |
In-cluster URL, e.g. http://keycloak:8080/realms/oop/protocol/openid-connect/certs |
KEYCLOAK_SETTINGS__AUDIENCE |
Default oeg |
KEYCLOAK_SETTINGS__ENFORCEMENT_ENABLED |
Default true |
The defaults point to *.invalid addresses on purpose, so a deployment missing this configuration fails loudly instead of trusting a guessed URL.
Not in this PR
- The GSMA federation routes (
/platform/v1/federation/*) don't have a role check yet. A follow-up will require theoperatorrole on them. OEG → FM stays protected by network policy only.