A standalone OAuth Client. Implemented with the help of Spring Boot using Nimbus OAuth 2.0 / OIDC SDK (no Spring Security OAuth client).
Supports Authorization Code flow, Device Authorization flow (RFC 8628), PKCE, confidential client authentication, DPoP and UserInfo retrieval.
The values pre-filled into the form on the start screen - endpoint URLs, client id and credential, scope and the flow options - are configurable, so that a frequently used AS does not have to be typed in over and over. They are only defaults: each one can still be changed in the UI before starting the flow.
The shipped defaults are in src/main/resources/application.properties, under the oauth.client.defaults.*
prefix. To override them locally, copy the example file:
cp client-defaults.properties.example client-defaults.properties
client-defaults.properties is git ignored, so your own AS URLs and credentials stay out of the repository.
It is optional and picked up from the working directory the app is started in (the project root when running
with mvn spring-boot:run). See client-defaults.properties.example for the full list of settings with
comments. Any other Spring Boot property, e.g. server.port, can be set there as well.
One setting is not a form field: oauth.client.defaults.redirect-uri. Left empty, the redirect URI is
computed from the incoming request, honoring the X-Forwarded-Proto and X-Forwarded-Host headers. Set it
only when that autodetection can not work, e.g. behind a proxy which does not send those headers.
The initial screen has a separate Run Device flow button. The device authorization endpoint is required only for that flow. After the initial request, the client shows the verification URL and user code, plus the complete verification URL when the authorization server supplies one. The page then polls the token endpoint automatically until the user approves the request or a terminal error is returned.
The polling interval is configurable on the form and with
oauth.client.defaults.device-polling-interval-seconds. RFC 8628 timing rules are still enforced: a larger
interval returned by the authorization server takes precedence, early browser requests are rate-limited by
the server-side client, and slow_down increases all subsequent intervals by five seconds. Pending responses
stay on the device page; success and terminal errors use the existing token result page. Device flow also
supports the existing confidential-client, DPoP, and UserInfo options. PKCE is specific to Authorization Code
flow and is ignored for Device flow.
DPoP (RFC 9449) is enabled with a checkbox on the initial screen. When enabled:
- A fresh proof-of-possession key is generated for each authentication attempt, of the selected kind:
EC P-256signing withES256RSA 2048signing withRS256OKP Ed25519signing withEdDSA
- A DPoP proof is attached to the token request and, if a UserInfo endpoint is configured, to the
UserInfo request (there with the
athclaim binding the proof to the access token). - Server provided nonces are handled at both endpoints. A server which requires a nonce answers the first
request with
use_dpop_nonce(HTTP 400 with a JSON error at the token endpoint, HTTP 401 with aWWW-Authenticate: DPoPheader at the UserInfo endpoint) and aDPoP-Nonceheader. The request is then retried once with a freshly signed proof carrying that nonce. - Optionally the
dpop_jktparameter (RFC 9449 sec. 10) is sent with the authorization request, binding the authorization code to the DPoP key. - The access token is presented to the UserInfo endpoint with the
DPoPscheme only if the AS actually returned it withtoken_type: DPoP; otherwise it is presented as a bearer token, and the result screen says so.
The result screen summarizes whether DPoP was used, whether a server provided nonce was needed at each
endpoint, the public key with its jkt thumbprint, and the decoded DPoP proofs which were sent.
mvn package
Run from Maven
mvn -pl oauth-sample-client spring-boot:run -DskipTests -Dgpg.skip=true
App listens on port 8085 by default. Open:
http://localhost:8085/