JWT Resource Server¶
Security objective¶
An API accepts a bearer token issued by a trusted authorization server and authorizes the request from verified claims. The API validates the token; it does not issue it and does not hold the signing key.
What a JWT proves¶
A verified JWT proves that a party holding the signing key asserted these claims before expiry. It does not prove the caller is the legitimate holder unless the token is sender-constrained, and it does not prove the token was intended for this API unless the audience is validated.
Required validation¶
| Claim/element | Why it matters |
|---|---|
Signature and alg | Integrity; reject unexpected algorithms and none |
kid | Selects trusted verification key |
iss | Token came from the expected issuer |
aud | Token was intended for this API |
exp, nbf, iat | Time window with bounded clock skew |
sub | Subject identity |
| Scopes/roles | Authorization input, mapped through an allow list |
The trusted issuer and key source must come from server configuration. A token must never select its own verification endpoint, which would enable SSRF and trust confusion.
Spring Security model¶
| Responsibility | Spring API |
|---|---|
| Enable bearer authentication | oauth2ResourceServer().jwt(...) |
| Decode and verify | JwtDecoder, NimbusJwtDecoder |
| Claim validation | OAuth2TokenValidator<Jwt>, JwtTimestampValidator, JwtIssuerValidator, JwtClaimValidator |
| Claims to authorities | JwtAuthenticationConverter, JwtGrantedAuthoritiesConverter |
| Authenticated principal | Jwt in the SecurityContext |
| Missing/invalid token response | BearerTokenAuthenticationEntryPoint (401) |
| Insufficient authority response | Access denied handling (403) |
Spring maps a scope/scp claim to SCOPE_ authorities by default. A custom claim such as roles needs an explicit converter and prefix.
Why this replaces a custom bearer filter¶
A hand-written filter typically misses algorithm restriction, issuer and audience checks, key rotation, clock skew, and consistent error semantics. A process-local symmetric key also breaks horizontal scaling and lets every verifier mint tokens.
Trust and failure cases¶
- Accepting any issuer or fetching keys from a token-supplied URL
- Skipping audience validation, enabling token reuse across APIs
- Accepting unexpected algorithms or unsigned tokens
- Trusting a self-asserted
rolesclaim without an allow list - No key-rotation or unknown-
kidbehavior - Logging full tokens during troubleshooting
Transfer¶
Python and Node JOSE libraries require the same explicit checks, usually with more manual wiring. JwtDecoder, validator composition, and converter-based authority mapping are Spring-specific.
Continue with LAB-009.