Skip to content

OIDC token parser never verifies audience or issuer #6669

Description

@larrysingleton007

The observation

The feature server's OIDC token parser verifies the token signature (JWKS via discovery) and expiry, but audience verification is explicitly off and there is no issuer check (sdk/python/feast/permissions/auth/oidc_token_parser.py, _decode_token):

audience="account",
options={
    "verify_aud": False,
    "verify_signature": True,
    "verify_exp": True,
},

The consequence is that any validly-signed, unexpired token from the configured IdP authenticates against the feature server, regardless of which resource it was minted for. In a multi-app tenant, a token issued to a completely different application passes authentication, and role matching in RoleBasedPolicy is the only remaining gate. Authorization ends up doing all of the work; authentication is only proving the token came from the right IdP, not that it was meant for Feast.

Why the current default is reasonable

This looks deliberate, and changing the default would break real deployments. Identity providers commonly put claim values in tokens that differ from their discovery metadata. The clearest case is Microsoft Entra ID, which issues v1.0 tokens (iss: https://sts.windows.net/<tenant-id>/, aud: api://<app-id-uri>) that are routinely validated against a v2.0 discovery URL. That works today precisely because discovery is only used to source JWKS keys and no aud/iss comparison runs. #6631 added Entra support on top of that behavior, so this is proposing configurability, not calling the default a bug.

Proposal

Two optional OidcAuthConfig fields, audience and issuer, both unset by default:

  • When set, the corresponding token claim must match or the token is rejected at authentication.
  • When unset, the decode options are identical to today's, so nobody is affected without opting in.
  • Operators state the expected values explicitly rather than the code inferring them from the discovery document, which is what keeps the v1-token-against-v2-discovery setup working.

The accompanying PR includes a test that pins the v1/v2 Entra setup so a future refactor cannot silently regress it.

One scope note: the feast-operator copies a whitelist of OIDC secret keys into the generated feature_store.yaml (OidcOptionalSecretProperties), so exposing these fields through the operator CRD would be a small follow-up if there is interest. The PR keeps to the Python SDK and docs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions