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.
This commit is contained in:
67
docs/output/as-authcode-nochallenge.txt
Normal file
67
docs/output/as-authcode-nochallenge.txt
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
------------------------------------------------------------------
|
||||
== PKCE parameters (RFC 7636)
|
||||
------------------------------------------------------------------
|
||||
code_verifier RnM5322FJWXI9zUZDUkmRmK-x_rjKT6HHFQyIhSEqjtVkb527uqjZmfQCwrNmavk (64 chars)
|
||||
code_challenge rzSywj6JKmWWVVBuZsD26hYR4_3PK0zZRCXrn_4sU9M
|
||||
code_challenge_method S256
|
||||
|
||||
The verifier never leaves the client until the token request. The challenge is
|
||||
all the authorization request carries, and it is a one-way hash of the verifier.
|
||||
|
||||
------------------------------------------------------------------
|
||||
== 1. Log in to the authorization server (browser session)
|
||||
------------------------------------------------------------------
|
||||
$ curl -c jar -d username=alice -d password=password -d _csrf=<token> http://localhost:9000/login
|
||||
HTTP/1.1 302
|
||||
Location: http://localhost:9000/
|
||||
|
||||
------------------------------------------------------------------
|
||||
== 2. GET /oauth2/authorize (client=demo-spa)
|
||||
------------------------------------------------------------------
|
||||
NO_CHALLENGE=1: the authorization request carries no code_challenge.
|
||||
|
||||
$ curl -b jar 'http://localhost:9000/oauth2/authorize?response_type=code&client_id=demo-spa&redirect_uri=http%3A%2F%2F127.0.0.1%3A8080%2Fauthorized&scope=openid%20orders.read&state=xyz123'
|
||||
-> 302 http://127.0.0.1:8080/authorized?code=ydxIVNbSbrjWRlj1YxX6LS4cnXgDdzxTIyZgqoqlmX6cD8q-AYnL_Sdy6m5gqheZ-yd8qKUgSrrgMtg331sYHjblO3IYqGb1i94OxMypdyLr6RMqm_O_XuVZX2g1FSeD&state=xyz123
|
||||
|
||||
------------------------------------------------------------------
|
||||
== 3. No consent page
|
||||
------------------------------------------------------------------
|
||||
The authorization endpoint went straight back to the client. Either consent is
|
||||
off for this client, or every requested scope was already approved.
|
||||
|
||||
------------------------------------------------------------------
|
||||
== 5. The authorization code
|
||||
------------------------------------------------------------------
|
||||
code = ydxIVNbSbrjWRlj1YxX6LS4cnXgDdzxTIyZgqoqlmX6cD8q-AYnL_Sdy6m5gqheZ-yd8qKUgSrrgMtg331sYHjblO3IYqGb1i94OxMypdyLr6RMqm_O_XuVZX2g1FSeD
|
||||
state = xyz123 (the client's own value, returned untouched - compare it)
|
||||
|
||||
------------------------------------------------------------------
|
||||
== 6a. Exchange the code WITHOUT the verifier
|
||||
------------------------------------------------------------------
|
||||
This is the request an attacker who stole the code can make.
|
||||
HTTP 401
|
||||
(empty response body)
|
||||
|
||||
>>> Rejected. invalid_grant is deliberately vague: the server will not tell
|
||||
>>> a caller whether the code was wrong, expired, already used, or missing a
|
||||
>>> verifier, because each of those is information an attacker can use.
|
||||
Note: this consumed the code. Authorization codes are single-use, so the
|
||||
successful exchange below needs a fresh one.
|
||||
|
||||
------------------------------------------------------------------
|
||||
== 6b. A fresh code, exchanged properly
|
||||
------------------------------------------------------------------
|
||||
fresh code = uvr0H49ByWte7NfWDoDnZ61-11rMP0wxYFJMY8eM73Lm2dcc2Edl-oFdW3DwJNSqUBMyIRX8rY9JqPDZIvwnEo4grUU-D5Z-GTwYYV1B8Xvosdbg-vAjiss1wbRsvD03
|
||||
|
||||
$ curl -d grant_type=authorization_code -d code=... http://localhost:9000/oauth2/token
|
||||
(no code_verifier - there was no challenge to verify against)
|
||||
HTTP 401
|
||||
(empty response body)
|
||||
|
||||
>>> No token, even though the client is registered with requireProofKey(false)
|
||||
>>> and the authorization request carried no challenge. The reason is that a
|
||||
>>> public client has no other way to authenticate at the token endpoint:
|
||||
>>> PublicClientAuthenticationProvider delegates entirely to
|
||||
>>> CodeVerifierAuthenticator, and raises invalid_client when there is nothing
|
||||
>>> to verify. requireProofKey(false) relaxes the AUTHORIZATION endpoint only.
|
||||
Reference in New Issue
Block a user