Skip to content

change(auth): require configured jwt claims, harden empty claims_to_verify and key-auth anonymous fallback#13468

Merged
shreemaan-abhishek merged 6 commits into
apache:masterfrom
shreemaan-abhishek:feat/auth-claims-and-keyauth-hardening
Jun 9, 2026
Merged

change(auth): require configured jwt claims, harden empty claims_to_verify and key-auth anonymous fallback#13468
shreemaan-abhishek merged 6 commits into
apache:masterfrom
shreemaan-abhishek:feat/auth-claims-and-keyauth-hardening

Conversation

@shreemaan-abhishek

@shreemaan-abhishek shreemaan-abhishek commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Description

This PR contains hardening fixes in the auth plugins, each with tests.

1. jwt-auth: treat an empty claims_to_verify array as unset

verify_claims() falls back to the default claims (exp/nbf) when claims_to_verify is not configured. The guard used if not claims then, but an explicitly empty array ("claims_to_verify": []) is truthy in Lua, so it skipped the fallback and the validation loop iterated over nothing, meaning an expired token was accepted when claims_to_verify was set to []. The schema allows an empty array (no minItems), so this configuration is reachable.

The guard now treats an empty array the same as an unset value (if not claims or #claims == 0 then), restoring the default exp/nbf checks. Behaviour for tokens that legitimately omit exp/nbf is unchanged (they are validated only if present).

2. jwt-auth: require explicitly configured claims to be present (behavior change)

Previously verify_claims() validated a configured claim only if it was present in the token (if claim then ...), so a route with e.g. claims_to_verify: ["exp"] would still accept a token that omitted exp. With this change, when claims_to_verify is explicitly configured the listed claims are required: a token missing a configured claim is rejected. The default path (no claims_to_verify, or an empty array) is unchanged and still validates exp/nbf only if present.

Warning

This is a behavior change. A route configured with claims_to_verify: ["exp"] (or any claim) will now reject a token that omits that claim, where it was previously accepted. It only affects routes that explicitly set claims_to_verify; the default behavior is unchanged. jwt-auth-more-algo.t TEST 13 is updated to reflect this (a token missing the configured nbf claim is now rejected). Happy to discuss on the dev mailing list if preferred.

3. key-auth: strip credentials on anonymous-consumer fallback

When a request carried an invalid API key and key-auth fell back to the configured anonymous_consumer, the (invalid) credential was still forwarded upstream even with hide_credentials enabled: find_consumer() only strips credentials on the successful-auth path. The fix strips the credential before falling back to the anonymous consumer when hide_credentials is true. Since a request can carry the key in both the header and the query string, both are cleaned up.

Which issue(s) this PR fixes:

Fixes #

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible (item 2 is an intentional behavior change for explicitly-configured claims_to_verify; called out above)

An explicitly empty `claims_to_verify` array ([]) is truthy in Lua, so
the `if not claims` guard in verify_claims() did not fall back to the
default claims (exp/nbf). As a result the validation loop iterated over
nothing and expired tokens were accepted when the array was set to [].

Treat an empty array the same as an unset value and fall back to the
default exp/nbf checks. Add tests covering the default-claims path, the
empty-array case, and tokens that legitimately omit exp.
When a request carried an invalid API key and key-auth fell back to the
configured anonymous consumer, the credential was forwarded upstream even
with hide_credentials enabled: find_consumer() only strips credentials on
the successful-auth path.

Strip the credential before falling back to the anonymous consumer when
hide_credentials is true. A request may carry the key in both the header
and the query string, so clean up both. Add tests covering the header,
query, and both-present cases on anonymous fallback.
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Jun 3, 2026
The anonymous-fallback tests scanned the error log (no_error_log:
invalid-key) and used a non-existent --- response_args section. nginx
always logs the original client request line (".../echo?auth=invalid-key"),
so no_error_log matched that logged request line rather than a forwarded
credential, failing in CI even though the credential was correctly
stripped. response_args is not a Test::Nginx section, so the query-strip
path was effectively unasserted.

Add a print_request_received upstream that echoes the received request
URI and headers, and assert the proxied request carries the anonymous
marker but not the credential. This checks what actually reaches the
upstream and fails closed if stripping regresses.
membphis
membphis previously approved these changes Jun 5, 2026
AlinsRan
AlinsRan previously approved these changes Jun 5, 2026
Comment thread apisix/plugins/key-auth.lua
verify_claims previously validated a configured claim only if it was
present in the token (if claim then ...). With this change, when
claims_to_verify is explicitly configured the listed claims are required:
a token missing a configured claim is rejected. The default path (no
claims_to_verify, or an empty array) is unchanged and still validates
exp/nbf only if present.

This is a behavior change: a route with e.g. claims_to_verify: ["exp"]
will now reject a token that omits exp instead of accepting it. Updated
jwt-auth-more-algo.t TEST 13 accordingly (a token without the configured
nbf claim is now rejected).
@shreemaan-abhishek
shreemaan-abhishek dismissed stale reviews from AlinsRan and membphis via 9764411 June 8, 2026 09:29
@shreemaan-abhishek shreemaan-abhishek changed the title fix(auth): harden jwt-auth empty claims_to_verify and key-auth anonymous fallback credential stripping change(auth): require configured jwt claims, harden empty claims_to_verify and key-auth anonymous fallback Jun 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comment thread apisix/plugins/jwt-auth/parser.lua
Comment thread t/lib/server.lua
TEST 12 leaves claims_to_verify=["nbf"] on the /hello route. Since
configured claims are now required, the EdDSA token in TEST 17 (which
carries exp but no nbf) would be rejected by that stale requirement.
Restore a clean jwt-auth route config at the start of the EdDSA test
group so TEST 17 exercises signature verification as intended.
When claims_to_verify lists a claim that is absent from the JWT payload,
the code returned "claim <name> is not a number", which is misleading: the
claim is missing, not the wrong type (and in multi-auth mode that message
propagates to the caller). Return "claim <name> is missing" instead.
@shreemaan-abhishek
shreemaan-abhishek merged commit b6f80f5 into apache:master Jun 9, 2026
19 checks passed
@shreemaan-abhishek shreemaan-abhishek mentioned this pull request Jun 10, 2026
5 tasks
wistefan pushed a commit to wistefan/apisix that referenced this pull request Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants