Simplify OAuth2ResourceServerJwtConfiguration
Expected Behavior
3 sources to validate JWT signature:
- issuer-uri
- jwk-set-uri
- 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.
Thanks for this suggestion and the analysis you provided, @michaldo. I see this in the following way:
- Use
issuer-uriandaudienceto validate those claims -
issuer-urimay 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?
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