spring-security icon indicating copy to clipboard operation
spring-security copied to clipboard

Simplify OAuth2ResourceServerJwtConfiguration

Open michaldo opened this issue 1 month ago • 2 comments

Expected Behavior

3 sources to validate JWT signature:

  1. issuer-uri
  2. jwk-set-uri
  3. public-key-location

No IssuerUriCondition, no KeyValueCondition Each source creates JWT Decoder bean, when more than one is created, Spring context will fail due autowiring ambiguity New property spring.security.oauth2.resourceserver.jwt.issuer used to verify clam iss. If issuer-uri is defined, then it is also default value for issuer

Current Behavior

Currently spring.security.oauth2.resourceserver.jwt.issuer-uri is used both for validate JWT signature as well as claim iss When both public-key-location and issuer-uri/jwk-set-uri is present, the public-key-location is silently ignored

Context

Documentation says explicit that issuer-uri can be used both for verification JWT signature and claim iss, but under some circumstances signature is verified by jwk-set-uri. Implementation based on IssuerUriCondition is understandable, but not optimal. Dual meaning of issuer-uri creates complexity which could be avoided by simple separation claim validation and signature validation.

Later public-key-location was introduced and the dualism was extended by implementation KeyValueCondition. When IssuerUriCondition was not optimal but implemented JWT requirement, KeyValueCondition, as explained in https://github.com/spring-projects/spring-boot/issues/15814#issuecomment-3595440698, does not implement JWT requirement. The goal of KeyValueCondition is silently hide misconfiguration (misconfiguration: 2 signature verifications configured together).

When misconfiguration is detected, there are 2 options: fail-fast or hide as a backdoor. I do not agree with second option.

Conclusion. I understand why double meaning of issuer-uri was implemented, but it created unnecessary priority complexity. It would be better to split issuer for claim verification and issuer-urifor signature verification. When public-key-location was added, it became worse.

I propose separation issuer and issuer-uri and clear configuration, without priority complexity, without undocumented hiding JWT decoders, fast-fail when misconfiguration detected.

michaldo avatar Dec 01 '25 11:12 michaldo

Thanks for this suggestion and the analysis you provided, @michaldo. I see this in the following way:

  1. Use issuer-uri and audience to validate those claims
  2. issuer-uri may also double as a way to validate the signature

The fact that Boot can also infer a signature verification strategy from issuer-uri is an optimization that also exists in JwtDecoders#fromIssuerLocation, thus aligning the Boot properties with the Spring Security API.

If you choose to pursue this further, I have a couple of questions. You said:

The goal of KeyValueCondition is silently hide misconfiguration (misconfiguration: 2 signature verifications configured together).

I don't see the misconfiguration just yet. Only one NimbusJwtDecoder is produced with only one signature verification strategy. Can you clarify, please?

Each source creates JWT Decoder bean, when more than one is created, Spring context will fail due autowiring ambiguity

Are you actually seeing this happen; for example, when declaring public-key-location and jwk-set-uri, is startup failing due to multiple JwtDecoder beans being defined?

jzheaux avatar Dec 15 '25 16:12 jzheaux

1

The goal of KeyValueCondition is silently hide misconfiguration (misconfiguration: 2 signature verifications configured together).

I don't see the misconfiguration just yet. Only one NimbusJwtDecoder is produced with only one signature verification strategy. Can you clarify, please?

spring.security.oauth2.resourceserver.jwt:
   public-key-location: classpath:my-key.pub
   issuer-uri: https://idp.example.com/issuer

This is misconfiguration - 2 source of truth together and application does not fail.

2

Are you actually seeing this happen; for example, when declaring public-key-location and jwk-set-uri, is startup failing due to multiple JwtDecoder beans being defined?

That is exactly what I observe: no startup failing, one JwtDecoder with issuer-uri: https://idp.example.com/issuer, public key silently ignored

Basically, when I see application with 2 or 3 source of truth, I should not wonder which is working and which is shadow - I should be sure application fails when more than 1 source of truth are present

michaldo avatar Dec 15 '25 20:12 michaldo