1
0
Files
spring-auth-demo/docs/authorization-server/06-resource-server.md
Ankur Mhatre e9381dc5be Add Spring Authorization Server project: OAuth2/OIDC provider, client and resource server
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.
2026-08-24 08:20:38 +05:30

3.9 KiB
Raw Permalink Blame History

← 05 Token customisation · index · next: 07 — Diagnostics

The resource server side

Source: SecurityConfig.java. The deeper treatment of this half lives in docs/1218; 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