1
0
Files
spring-auth-demo/docs/authorization-server/02-minimum-provider.md
Ankur Mhatre 38c0a5f358 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:12:36 +05:30

4.7 KiB

← 01 Versions · index · next: 03 — Clients, PKCE and the defaults that moved

The minimum working provider

The two imports that break every tutorial

Two classes moved out of the Spring Authorization Server jar and into spring-security-config:

1.5.8 7.1.1
OAuth2AuthorizationServerConfiguration o.s.s.oauth2.server.authorization.config.annotation.web.configuration o.s.s.config.annotation.web.configuration
OAuth2AuthorizationServerConfigurer o.s.s.oauth2.server.authorization.config.annotation.web.configurers o.s.s.config.annotation.web.configurers.oauth2.server.authorization

And one method was deleted. javap on both jars:

# 1.5.8
public static void applyDefaultSecurity(HttpSecurity) throws Exception;

# 7.1.1
  (absent)

OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http) is the one-liner in essentially every article and sample written before September 2025. It is gone.

src-broken/LegacySasConfig.java.txt is that configuration, kept out of the build. scripts/compile-legacy.sh compiles it against the real 7.1.1 classpath and commits the compiler's own words to docs/output/as-legacy-compile-failure.txt:

error: package org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration does not exist
error: package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers does not exist
error: cannot find symbol
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
  symbol:   variable OAuth2AuthorizationServerConfiguration
4 errors

Four errors from nine lines of copied configuration.

What replaces it

OAuth2AuthorizationServerConfigurer authorizationServer =
        new OAuth2AuthorizationServerConfigurer();

http
    .securityMatcher(authorizationServer.getEndpointsMatcher())
    .with(authorizationServer, server -> server
        .oidc(Customizer.withDefaults())
        .authorizationEndpoint(endpoint -> endpoint.consentPage("/oauth2/consent")))
    .authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
    .exceptionHandling(...)
    .oauth2ResourceServer(rs -> rs.jwt(Customizer.withDefaults()));

Source: AuthorizationServerConfig.java.

Why two filter chains

The protocol chain carries securityMatcher(getEndpointsMatcher()), so it declines every request that is not an OAuth2 or OIDC endpoint. Something has to serve the login form and the consent page, and it needs a completely different authentication mechanism — a browser session rather than a bearer token. That is DefaultSecurityConfig.

Order is load-bearing. The protocol chain must be @Order(HIGHEST_PRECEDENCE). Swap them and the catch-all form-login chain matches /oauth2/token first: a token request 302s to /login and the token endpoint is never reached. That redirect is the fingerprint. /diag/chains prints the live ordering.

OIDC is not on by default

.oidc(Customizer.withDefaults()) is one line and omitting it costs you /userinfo, the id_token, and /.well-known/openid-configuration. You still get the OAuth2 metadata document at /.well-known/oauth-authorization-server — the two are different documents, and as-discovery.txt prints both.

The bean that is not a bean

A custom consent page needs to read OAuth2AuthorizationConsentService. It is not exposed as an injectable bean. The configurer creates one for its own use; a controller that constructor-injects it fails the context at startup, and the real message is kept in as-missing-consent-service.txt:

No qualifying bean of type 'org.springframework.security.oauth2.server.authorization
.OAuth2AuthorizationConsentService' available: expected at least 1 bean which qualifies
as autowire candidate.

Declare OAuth2AuthorizationService and OAuth2AuthorizationConsentService yourself. That also forces the storage decision into the open: the in-memory implementations mean a second replica of the authorization server cannot complete a code exchange started on the first.

Next: 03 — Clients, PKCE and the defaults that moved