Is your feature request related to some problem you are facing? Please describe that problem here.
JWT authentication (authentication.jwt) verifies the token signature and expiry, and; once #657 lands: optionally the iss claim via authentication.jwt.issuer. It still does not verify the aud (audience) claim.
Where several services sit behind the same issuer (the usual Keycloak/Auth0/Entra setup), a token legitimately minted for service A authenticates to service B on signature alone; a cross-service token-replay gap. Authorino already validates audiences for kubernetesTokenReview (audiences) but not for JWTs, its main authentication method. RFC 7519 §4.1.3 and RFC 8725 (JWT BCP) §3.9 both call for the recipient to reject a token not intended for it.
Describe the solution you'd like
Add an optional authentication.jwt.audiences []string. When set, a token authenticates only if its aud claim intersects the configured list ("any match"; the same semantics as kubernetesTokenReview.audiences and Envoy's jwt_authn). When omitted (the default), the claim is not verified and existing AuthConfigs behave identically.
- Enforced at the authentication phase, so a wrong-audience token returns
401 (and honours response.unauthenticated), not 403.
- Applies to both the
issuerUrl and jwksUrl paths. go-oidc already parses aud (JSON string or array) into []string, so this is a post-verify intersection check.
spec:
authentication:
"keycloak-users":
jwt:
issuerUrl: https://sso.corp.example/realms/prod
issuer: https://sso.corp.example/realms/prod
audiences:
- my-api.io
This is the natural next field after #657's issuer: same shape (opt-in claim enforcement, default off, enforced in the verifier). It completes the mandatory-claim set (exp/nbf, iss, aud) and reaches parity with Envoy jwt_authn, Istio RequestAuthentication, and Authorino's own kubernetesTokenReview.
Describe alternatives you've considered
A pattern-matching / OPA authorization rule (e.g. "my-api" in auth.identity.aud) can approximate it, but it isn't the same behavior:
- It runs in the authorization phase, so a wrong-audience token returns
403 instead of 401 and bypasses response.unauthenticated.
- The CEL is a correctness trap:
aud may be a JSON string or an array, so a naive rule silently passes on one of the two shapes.
- It composes badly across multiple identity sources (needs per-source
when conditioning to avoid rejecting non-JWT identities).
Additional context
Is your feature request related to some problem you are facing? Please describe that problem here.
JWT authentication (
authentication.jwt) verifies the token signature and expiry, and; once #657 lands: optionally theissclaim viaauthentication.jwt.issuer. It still does not verify theaud(audience) claim.Where several services sit behind the same issuer (the usual Keycloak/Auth0/Entra setup), a token legitimately minted for service A authenticates to service B on signature alone; a cross-service token-replay gap. Authorino already validates audiences for
kubernetesTokenReview(audiences) but not for JWTs, its main authentication method. RFC 7519 §4.1.3 and RFC 8725 (JWT BCP) §3.9 both call for the recipient to reject a token not intended for it.Describe the solution you'd like
Add an optional
authentication.jwt.audiences []string. When set, a token authenticates only if itsaudclaim intersects the configured list ("any match"; the same semantics askubernetesTokenReview.audiencesand Envoy'sjwt_authn). When omitted (the default), the claim is not verified and existing AuthConfigs behave identically.401(and honoursresponse.unauthenticated), not403.issuerUrlandjwksUrlpaths. go-oidc already parsesaud(JSON string or array) into[]string, so this is a post-verify intersection check.This is the natural next field after #657's
issuer: same shape (opt-in claim enforcement, default off, enforced in the verifier). It completes the mandatory-claim set (exp/nbf,iss,aud) and reaches parity with Envoyjwt_authn, IstioRequestAuthentication, and Authorino's ownkubernetesTokenReview.Describe alternatives you've considered
A pattern-matching / OPA authorization rule (e.g.
"my-api" in auth.identity.aud) can approximate it, but it isn't the same behavior:403instead of401and bypassesresponse.unauthenticated.audmay be a JSON string or an array, so a naive rule silently passes on one of the two shapes.whenconditioning to avoid rejecting non-JWT identities).Additional context
jwt.issuerto enforce theissclaim on bothissuerUrlandjwksUrl#657. Touches the same files (api/v1beta3/auth_config_types.go,pkg/evaluators/identity/jwt.go,controllers/auth_config_controller.go), so implementation should land after feat(jwt): addjwt.issuerto enforce theissclaim on bothissuerUrlandjwksUrl#657 merges to avoid conflicts.jwt.issuerto enforce theissclaim on bothissuerUrlandjwksUrl#657 (thenewIdPMockWithIssuertest-mock signature): that is internal to feat(jwt): addjwt.issuerto enforce theissclaim on bothissuerUrlandjwksUrl#657's tests; the audience tests would simply follow whichever mock pattern feat(jwt): addjwt.issuerto enforce theissclaim on bothissuerUrlandjwksUrl#657 settles on.