Skip to content

openid-connect: claim_schema is not enforced in the bearer-token authentication path #13397

Description

@shreemaan-abhishek

Description

The openid-connect plugin enforces the claim_schema option only in the session/callback authentication path, not in the bearer-token path.

claim_schema is validated by validate_claims_in_oidcauth_response() in apisix/plugins/openid-connect.lua, and that function has a single call site, inside the openidc.authenticate() branch (session/browser flow). The bearer branch that handles bearer_only / introspection_endpoint / public_key / use_jwks never calls it.

As a result, when a route accepts both authentication modes and a claim_schema is configured:

  • requests authenticated via the session/callback flow are checked against claim_schema
  • requests authenticated via a bearer token are not checked against claim_schema

Why it happens

claim_schema was added together with the OIDC claim validator (#11824) and was wired into the session-flow response, which is the structured object { user, access_token, id_token } returned by openidc.authenticate(). The bearer/introspection/JWKS path returns a flat introspection or JWT payload with a different shape (no id_token, no user wrapper), so it was never routed through the same validation function.

This is plausibly intentional scoping rather than an oversight: the function is named for the openidc.authenticate() response, and claim_schema validates a { user, access_token, id_token } object that only exists in the session flow. The bearer path also already has its own claim controls (claim_validator, required_scopes).

Expected behavior

The behavior of claim_schema across the two authentication paths should be consistent and clearly documented. Either:

  1. document explicitly that claim_schema applies only to the session/callback flow, or
  2. apply an equivalent claim validation step to the bearer/introspection/JWKS path.

A docs-only fix (option 1) is an acceptable resolution for this issue. The current docs describe claim_schema as "JSON schema of OIDC response claim" with no mention of which authentication flow it applies to, which is what invites the wrong assumption. Clarifying that it covers the session/callback flow only is sufficient to close this.

If option 2 is chosen instead, note that the bearer-path response has a different structure than the session-flow response, so the validation object for bearer mode would need to be built accordingly (and the per-flow key differences documented).

Steps to reproduce

  1. Configure a route with the openid-connect plugin, bearer_only: false, an introspection_endpoint, and a claim_schema.
  2. Access the route without credentials and complete the browser login flow: the response is checked against claim_schema.
  3. Access the same route with an Authorization: Bearer <token> header: the response is not checked against claim_schema.

Environment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    Status
    ✅ Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions