feat(openid-connect): make client_secret optional for local JWT verification modes#13472
feat(openid-connect): make client_secret optional for local JWT verification modes#13472AlinsRan wants to merge 9 commits into
Conversation
…ication modes In bearer_only + public_key or bearer_only + use_jwks scenarios, the gateway verifies the token locally without contacting the IdP's token or introspection endpoint, so client_secret plays no role. Remove it from the schema's required list and enforce it conditionally in check_schema. Exempt scenarios: - bearer_only=true + public_key: local verification with configured public key - bearer_only=true + use_jwks=true: local verification via JWKS endpoint - token_endpoint_auth_method=private_key_jwt: RSA private key replaces client_secret - use_pkce=true (non-bearer): public-client PKCE flow Also fix claim_schema not being enforced in the bearer-token path (#13397): apply the schema directly to the flat JWT payload / introspection response in the bearer branch. Closes #10563 Closes #13397 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
a222c37 to
bdb5fa2
Compare
…chema bearer tests - Fix client_secret_optional logic: the private_key_jwt exemption for the token endpoint (non-bearer flow) was incorrectly applied to bearer mode. In bearer+introspection mode, client_secret is now only optional when introspection_endpoint_auth_method=private_key_jwt, not when token_endpoint_auth_method=private_key_jwt. - Add TEST 47: bearer_only + introspection_endpoint_auth_method=private_key_jwt → client_secret optional. - Add TEST 5-6 in openid-connect9.t: bearer_only + public_key + claim_schema that requires an absent field returns 401 with WWW-Authenticate header. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t in TEST 6 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rejection test - Replace public key with a freshly generated RSA-512 key pair - Replace JWT (which had line-wrap spaces and invalid signature) with a valid JWT signed by the corresponding private key - Remove the location /hello override in TEST 6's --- config that was inadvertently bypassing APISIX routing, causing the plugin to never run Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Authorization header must be in --- more_headers, not in --- request block; the latter is ignored by Test::Nginx for additional headers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…schema Replace imperative check_schema Lua logic with declarative anyOf constraint. Schema now requires either client_secret or an alternative auth mechanism (public_key, use_jwks, use_pkce, or private_key_jwt method), making the constraint visible to the control plane without custom validation code. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rror messages Without 'required', JSON Schema 'properties' vacuously passes when the field is absent, causing anyOf to always succeed. Add 'required' to each alternative branch so absence of the field correctly fails the branch. Update tests to expect the new anyOf failure message from lua-jsonschema. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
[P2] The The final schema-level For example, a bearer introspection config without Please either restore the mode-aware validation in |
… flow Revert the global anyOf schema constraint and restore mode-aware validation in check_schema. The anyOf accepted any alternative (public_key, use_jwks, use_pkce, private_key_jwt auth methods) as a global replacement for client_secret, but each alternative only applies to a specific flow. For example a bearer introspection config could pass by setting token_endpoint_auth_method=private_key_jwt, which is not the auth method used for introspection. Restore the bearer_only-aware check so each exemption is scoped to its flow, and add negative tests for cross-flow combinations. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@membphis Thanks for catching this. You're right that the global I've reverted the schema-level
Also added negative tests for the cross-flow cases you mentioned:
|
Background
Closes #10563
Closes #13397
In service-to-service scenarios where the gateway only validates an incoming Bearer token locally (via a configured public key or JWKS endpoint),
client_secretplays no role — no call is made to the IdP's token or introspection endpoint. However, the plugin currently requiresclient_secretunconditionally, forcing users to supply a dummy value as a workaround.Changes
apisix/plugins/openid-connect.luaRemove
client_secretfrom the schema'srequiredarray.Add conditional enforcement in
check_schema:client_secretis still required for all flows that need it (session/callback flow, introspection), but is now optional when:bearer_only=true+public_key: local JWT verification with a configured public keybearer_only=true+use_jwks=true: local JWT verification via JWKS endpointtoken_endpoint_auth_method=private_key_jwt: RSA private key replacesclient_secretuse_pkce=true(non-bearer): public-client PKCE flowFix
claim_schemanot being enforced in the bearer-token path (openid-connect: claim_schema is not enforced in the bearer-token authentication path #13397): the schema is now applied directly to the flat JWT payload / introspection response in the bearer branch.t/plugin/openid-connect.tAdd TEST 42–47 covering:
bearer_only=true+public_key→ noclient_secretrequiredbearer_only=true+use_jwks=true→ noclient_secretrequiredbearer_only=true+ introspection endpoint (no local key) →client_secretstill requiredtoken_endpoint_auth_method=private_key_jwt→ noclient_secretrequireduse_pkce=true→ noclient_secretrequiredclient_secretstill requiredBackward Compatibility
All existing configurations remain valid. The change only relaxes the requirement for specific scenarios; any config that previously worked continues to work unchanged.