1
0
Files
spring-auth-demo/docs/authorization-server/04-consent-page.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

3.3 KiB

← 03 Clients and PKCE · index · next: 05 — Token customisation

The consent page

Wiring a custom consent page is one line:

.authorizationEndpoint(endpoint -> endpoint.consentPage("/oauth2/consent"))

The path is your own MVC controller, served by the browser chain, not the protocol chain. Source: ConsentController.java and consent.html.

The form contract

The undocumented part is what the form has to send back. Getting any of it wrong produces a redirect loop rather than an error.

requirement consequence of getting it wrong
POST to /oauth2/authorize, not to the consent path 404 or a fresh authorization request
echo state as the consent page received it redirect loop
echo client_id invalid_request
one scope parameter per approved scope consent appears to succeed, token comes back short
include the CSRF token 403
omit openid from the checkboxes harmless, but unticking it does nothing

The state is not the client's state

This is the one that costs an afternoon. From as-authcode-pkce.txt:

GET /oauth2/authorize?…&state=xyz123
-> 302 /oauth2/consent?scope=openid%20orders.read&client_id=demo-spa
        &state=RXHrz8avEvUmNxYMLZoT0CyJS2E0t99pJtMJ5fyJBVM%3D

The client sent state=xyz123. The consent page is handed RXHrz8avEvUmNxYMLZoT0CyJS2E0t99pJtMJ5fyJBVM= — the authorization server's own correlation handle for the pending request. Echo the client's value instead and the endpoint cannot find the pending authorization, so it starts a new one, which redirects to the consent page again. The loop looks like a session problem and is not.

The client's state comes back at the end, untouched, in the redirect to the client:

-> 302 http://127.0.0.1:8080/authorized?code=B6iUSZ…&state=xyz123

Approving and denying

Approve: POST with one scope parameter per approved scope. Deny: POST with no scope parameters at all. The endpoint then redirects to the client with error=access_denied.

OAuth2AuthorizationConsentService stores what the user approved, keyed by client and principal. A second authorization for scopes already approved skips the page entirely. That is why run-all.sh restarts the authorization server between the two client-flow runs — otherwise the second one silently takes the no-consent path and proves nothing.

The in-memory implementation loses all of it on restart, and is per-instance. Two replicas of your authorization server will ask the same user twice.

The noconsent profile sets requireAuthorizationConsent(false) (as-authcode-noconsent.txt). Correct for a first-party client you own and ship together with the provider. Wrong the moment a third party registers, because consent is the only point at which the user is told what they are agreeing to.

Next: 05 — Token customisation