[← 01 Versions](01-versions.md) · [index](README.md) · next: [03 — Clients, PKCE and the defaults that moved](03-clients-and-pkce.md) # 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`](../../authorization-server/src-broken/LegacySasConfig.java.txt) is that configuration, kept out of the build. [`scripts/compile-legacy.sh`](../../authorization-server/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`](../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 ```java 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`](../../authorization-server/auth-server/src/main/java/com/ankurm/authserver/config/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`](../../authorization-server/auth-server/src/main/java/com/ankurm/authserver/config/DefaultSecurityConfig.java). **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`](07-diagnostics.md) 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`](../output/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`](../output/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](03-clients-and-pkce.md)