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.
3.9 KiB
← 05 Token customisation · index · next: 07 — Diagnostics
The resource server side
Source:
SecurityConfig.java.
The deeper treatment of this half lives in docs/12–18;
this chapter is only what changes when the issuer is yours.
One property, and what it buys
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:9000
At startup, Spring fetches /.well-known/openid-configuration, reads jwks_uri from it,
and builds a decoder. You get signature verification, exp/nbf, and an iss check.
You do not get an audience check. See 05 for why that
matters when the default aud is the client id.
The startup coupling nobody mentions
If the authorization server is not reachable, the resource server does not start
(as-rs-startup-failure.txt):
java.lang.IllegalArgumentException: Unable to resolve the Configuration with the provided
Issuer of "http://localhost:9000"
org.springframework.web.client.ResourceAccessException: I/O error on GET request for
"http://localhost:9000/.well-known/openid-configuration": Connection refused
This is deliberate — fail fast rather than serve unauthenticated traffic — but
it means a provider outage during a rolling deploy takes every API with it. If that is not
acceptable, configure jwk-set-uri directly and validate iss yourself, which removes the
discovery call at the cost of pinning the endpoint.
The issuer string must match exactly
http://localhost:9000 and http://localhost:9000/ are different values. A mismatch fails
at validation time with The iss claim is not valid, not at startup, so it looks like a
token problem rather than a configuration one.
Mapping custom claims without deleting the scopes
The provider writes a roles claim. Mapping it is easy to get wrong in one specific way:
converter.setJwtGrantedAuthoritiesConverter(jwt -> {
var authorities = new ArrayList<GrantedAuthority>(scopes.convert(jwt)); // keep these
List<String> roles = jwt.getClaimAsStringList("roles");
if (roles != null) {
roles.forEach(r -> authorities.add(new SimpleGrantedAuthority("ROLE_" + r)));
}
return authorities;
});
Returning a converter that only handles roles silently deletes every SCOPE_* authority,
which turns hasAuthority("SCOPE_orders.read") into a 403 on a perfectly valid token. The
same trap, in its properties-driven form, is
docs/14-authentication-converter.md.
What the authorities actually look like
From a real request (as-authcode-pkce.txt):
["SCOPE_openid","ROLE_USER","SCOPE_orders.read","ROLE_ADMIN",
"FactorGrantedAuthority [authority=FACTOR_BEARER, issuedAt=2026-08-24T01:52:21.799Z]"]
FactorGrantedAuthority is new in Spring Security 7 — it records how the principal
authenticated, for multi-factor authorisation rules. It shows up in every authority list now.
Code that asserts on the exact contents of getAuthorities() will fail on upgrade.
Protected resource metadata, also new
The WWW-Authenticate header now carries a resource_metadata parameter:
WWW-Authenticate: Bearer error="invalid_token", error_description="…",
resource_metadata="http://localhost:8090/.well-known/oauth-protected-resource"
That is RFC 9728, emitted by default. It tells a client where to learn what this API expects. Harmless, but it is a new endpoint on your resource server that you did not add.
Next: 07 — Diagnostics