Skip to content

Comments

feat(auth): allow missing exp per JWKS while rejecting expired tokens#8911

Open
fernando-apollo wants to merge 2 commits intodevfrom
fk/auth-allow-missing-exp
Open

feat(auth): allow missing exp per JWKS while rejecting expired tokens#8911
fernando-apollo wants to merge 2 commits intodevfrom
fk/auth-allow-missing-exp

Conversation

@fernando-apollo
Copy link
Contributor

Summary

Adds a per-JWKS allow_missing_exp configuration option to the JWT authentication plugin. When enabled, tokens without an exp (expiry) claim are accepted for the configured JWKS endpoint. Tokens that do include an exp claim are still validated and rejected if expired.

This is useful for identity providers that issue tokens without expiry, such as long-lived service tokens or certain machine-to-machine flows.

Tracking issue: #8910

Motivation (customer context)

A prospect reported that some of their JWTs are intentionally long-lived and do not include an exp claim (for example, service-to-service tokens). Router currently rejects those tokens because JWT validation requires exp.

They currently work around this with a sidecar and asked for a per-JWKS way to effectively “skip expiry validation”.

This PR implements a safer, narrower version of that request:

  • allow_missing_exp is configured per JWKS
  • Tokens without exp can be accepted (for that JWKS only)
  • Tokens that do include exp are still validated and rejected if expired

Key behaviors

  • Per-JWKS scoping: allow_missing_exp is configured per JWKS entry, not globally. A token without exp is only accepted if it matches a JWKS that has the option enabled.
  • Expired tokens are still rejected: This only skips the presence check for exp. If exp is present and in the past, the token is rejected as usual.
  • Startup warnings: A warning is logged when allow_missing_exp is enabled, and an additional warning if neither issuers nor audiences are configured alongside it.
  • Per-request observability: A debug log is emitted when a JWT without exp is accepted.
  • SearchResult refactor: Changed from a tuple to a named struct to cleanly thread the new field through JWKS search and JWT decoding.

Configuration

authentication:
  router:
    jwt:
      jwks:
        - url: "https://example.com/.well-known/jwks.json"
          allow_missing_exp: true
          issuers: ["https://example.com"]
          audiences: ["my-api"]

Known limitation

Subscriptions currently use JWT expiry to determine a timeout. Tokens without exp are not timed out by JWT expiry. This behavior is intentionally not changed in this PR.

Test plan

  • Rejects tokens without exp by default
  • Accepts tokens without exp when allow_missing_exp: true
  • Rejects expired tokens even when allow_missing_exp: true
  • Accepts tokens with future exp when allow_missing_exp: true
  • Rejects tokens with wrong issuer even when allow_missing_exp: true
  • Rejects tokens with wrong audience even when allow_missing_exp: true
  • Per-JWKS scoping: token without exp accepted by JWKS A (allow_missing_exp: true) but rejected by JWKS B (allow_missing_exp: false)

Verification run

  • cargo fmt --all --check
  • cargo test -p apollo-router plugins::authentication::tests::expiry_validation --no-fail-fast --locked
  • cargo test -p apollo-router plugins::authentication::jwks::test --no-fail-fast --locked

Checklist

Complete the checklist (and note appropriate exceptions) before the PR is marked ready-for-review.

  • PR description explains the motivation for the change and relevant context for reviewing
  • PR description links appropriate GitHub/Jira tickets (creating when necessary)
  • Changeset is included for user-facing changes
  • Changes are compatible1
  • Documentation2 completed
  • Performance impact assessed and acceptable
  • Metrics and logs are added3 and documented
  • Tests added and passing4
    • Unit tests
    • Integration tests
    • Manual tests, as necessary

Exceptions

  • No integration tests added: change is localized to JWT auth validation logic and is covered by targeted unit tests (missing exp, expired exp, issuer/audience enforcement, and per-JWKS scoping).
  • No manual tests performed: unit coverage is sufficient for this change.
  • Subscription handling for no-exp JWTs is intentionally deferred to a follow-up PR.

Notes

Footnotes

  1. It may be appropriate to bring upcoming changes to the attention of other (impacted) groups. Please endeavour to do this before seeking PR approval. The mechanism for doing this will vary considerably, so use your judgement as to how and when to do this.

  2. Configuration is an important part of many changes. Where applicable please try to document configuration examples.

  3. A lot of (if not most) features benefit from built-in observability and debug-level logs. Please read this guidance on metrics best-practices.

  4. Tick whichever testing boxes are applicable. If you are adding Manual Tests, please document the manual testing (extensively) in the Exceptions.

@fernando-apollo fernando-apollo requested review from a team as code owners February 24, 2026 10:00
@apollo-librarian
Copy link

apollo-librarian bot commented Feb 24, 2026

✅ Docs preview ready

The preview is ready to be viewed. View the preview

File Changes

0 new, 2 changed, 1 removed
* graphos/routing/(latest)/security/jwt.mdx
* graphos/routing/(latest)/_sidebar.yaml
- graphos/routing/(latest)/performance/circuit-breaking.mdx

Build ID: 65f0ce02cc91d18519dbc947
Build Logs: View logs

URL: https://www.apollographql.com/docs/deploy-preview/65f0ce02cc91d18519dbc947


⚠️ AI Style Review — 1 Issue Found

Summary

This pull request updates the documentation to align with various style guide requirements. Key changes include adopting active and authoritative framing by using imperative language and 'must' instead of 'strongly recommended' for secure paths. Structural elements were refined by removing punctuation from fragment list items. In terms of formatting and word usage, bolding was restricted to interactive elements, while instances of 'allows' were replaced with 'enables' to better describe feature functionality.

Duration: 2718ms
Review Log: View detailed log

This review is AI-generated. Please use common sense when accepting these suggestions, as they may not always be accurate or appropriate for your specific context.

@fernando-apollo fernando-apollo force-pushed the fk/auth-allow-missing-exp branch from 950d2fd to 168e84c Compare February 24, 2026 10:00
@fernando-apollo fernando-apollo force-pushed the fk/auth-allow-missing-exp branch from 168e84c to 7f1d73f Compare February 24, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant