Three modules on Spring Boot 4.1.1 with Spring Authorization Server 7.1.1: the provider
itself, a relying party, and an API that trusts its tokens. Client registration, PKCE,
a custom consent page and token customisation, with profiles that make each failure
reproducible.
Every claim is backed by captured output in docs/output/as-*.txt, regenerated by
authorization-server/scripts/run-all.sh. Notable findings, verified against the jars:
- OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(HttpSecurity) was deleted
in 7.0, and both configuration classes moved into spring-security-config
- ClientSettings.requireProofKey flipped from false to true, on the authorization server
(1.5.8 -> 7.1.1) and on the OAuth2 client (6.5.1 -> 7.1.1)
- requireProofKey(false) does not make PKCE optional for a public client; the code
verifier is that client's only authentication at the token endpoint
- MediaTypeRequestMatcher(TEXT_HTML) matches Accept: */*, so the token endpoint answers
API callers with 302 -> /login unless setIgnoredMediaTypes(ALL) is called
Also renames the repository to spring-auth-demo and cross-links the new chapter set from
the existing documentation.
88 lines
4.9 KiB
Markdown
88 lines
4.9 KiB
Markdown
# 18 — Resource server checklist
|
|
|
|
[← Keycloak setup](17-keycloak-setup.md) · [README](../README.md)
|
|
|
|
The list for a Spring Security resource server specifically. For the token-minting side, see
|
|
[10 — Production checklist](10-production-checklist.md).
|
|
|
|
## Claims
|
|
|
|
- [ ] **`aud` is validated.** It is not by default. One property, one bean, or one wrapped
|
|
validator — [12](12-issuer-and-audience.md). Without it, any token from your realm
|
|
works against any of your services.
|
|
- [ ] **`issuer-uri` matches the issuer string byte for byte**, from wherever the resource
|
|
server runs. Trailing slashes count. Pin `KC_HOSTNAME` — [17](17-keycloak-setup.md).
|
|
- [ ] **The clock skew is a decision.** 60 seconds by default, in both directions. If your
|
|
revocation story is short-lived tokens, the real worst case is the lifetime plus a
|
|
minute — [12](12-issuer-and-audience.md).
|
|
- [ ] **You know whether your issuer emits `typ: at+jwt`.** The default stack refuses it —
|
|
[12](12-issuer-and-audience.md).
|
|
- [ ] **`setJwtValidator` is never called with a bare validator.** It replaces the whole
|
|
stack. Wrap with `JwtValidators.createDefaultWithValidators` — [13](13-validator-stack.md).
|
|
- [ ] **The default stack is pinned by a test**, so an upgrade that moves it goes red rather
|
|
than quiet — [13](13-validator-stack.md).
|
|
|
|
## Authorities
|
|
|
|
- [ ] **Roles actually arrive.** Keycloak's live under `realm_access.roles` and
|
|
`resource_access.<client>.roles`; the default converter reads neither —
|
|
[14](14-authentication-converter.md).
|
|
- [ ] **Hyphenated client ids in SpEL expressions are quoted.** `['reports-api']`, not
|
|
`[reports-api]`. The failure is a silent empty authority list — [14](14-authentication-converter.md).
|
|
- [ ] **You have either a `JwtAuthenticationConverter` bean or the properties, not both.**
|
|
The bean silently disables the properties — [14](14-authentication-converter.md).
|
|
- [ ] **Realm and client roles that share a name do not collide** into one `ROLE_`
|
|
namespace in a way that grants something — [14](14-authentication-converter.md).
|
|
|
|
## JWKS and rotation
|
|
|
|
- [ ] **If you supplied a Spring `Cache`, it has a TTL.** Supplying one disables Nimbus's
|
|
five-minute cache; a `ConcurrentMapCache` never expires and a retired key stays
|
|
trusted — [15](15-jwks-caching-and-rotation.md).
|
|
- [ ] **The JWKS cache is not shared with anything else.** A refresh calls
|
|
`cache.invalidate()`, which clears the whole cache — [15](15-jwks-caching-and-rotation.md).
|
|
- [ ] **You know your revocation window.** It is the cache TTL, not zero, and not the token
|
|
lifetime — [15](15-jwks-caching-and-rotation.md).
|
|
- [ ] **Your issuer's rotation leaves a publish window** long enough for every resource
|
|
server to see the new key before it starts signing with it —
|
|
[15](15-jwks-caching-and-rotation.md).
|
|
- [ ] **Someone is watching the JWKS endpoint's request rate.** A rate that tracks request
|
|
volume rather than instance count means unknown-`kid` traffic is amplifying through
|
|
you — [16](16-jwks-amplification.md).
|
|
- [ ] **You have decided about rate limiting.** Spring Security disables Nimbus's. Restoring
|
|
it means building the `JWKSource` yourself and giving up discovery —
|
|
[16](16-jwks-amplification.md).
|
|
- [ ] **You have decided about outage tolerance.** It is off. When the cache expires and the
|
|
issuer is unreachable, every request fails — [15](15-jwks-caching-and-rotation.md).
|
|
|
|
## Endpoints
|
|
|
|
- [ ] **`/api/public/decoder` is deleted.** The diagnostic in this repository reveals your
|
|
JWK Set URI and cache timings to anyone who can reach it.
|
|
- [ ] **You know `/.well-known/oauth-protected-resource` exists.** Spring Security 7
|
|
publishes it, unauthenticated, without being asked — [12](12-issuer-and-audience.md).
|
|
- [ ] **CSRF is disabled deliberately**, because a stateless bearer-token API has no
|
|
ambient credential to protect — and for no other reason
|
|
— [04](04-csrf-permitall-403.md).
|
|
|
|
## Should you build this at all
|
|
|
|
If your services are minting their own tokens for their own users, you do not need a
|
|
resource server and you do not need an identity provider; the hand-written filter in
|
|
[`jwt-authentication/`](../jwt-authentication) is less code and fewer moving parts. See
|
|
[09 — manual filter vs resource server](09-manual-filter-vs-resource-server.md).
|
|
|
|
The resource server earns its complexity when tokens come from somewhere you do not control:
|
|
several services trusting one issuer, an identity provider you did not write, key rotation
|
|
that has to happen without redeploying anything. If that is not your situation, most of this
|
|
document is a list of ways to get something wrong that you could simply not have.
|
|
|
|
---
|
|
|
|
[← Keycloak setup](17-keycloak-setup.md) · [README](../README.md)
|
|
|
|
---
|
|
|
|
If you also own the issuer, the matching list for that side is
|
|
[`authorization-server/10`](authorization-server/10-should-you.md).
|