Endpoints
All of these are per tenant. The tenant is resolved from the hostname, so
jons.auth.example/token and acme.auth.example/token are different endpoints with different keys.
Discovery
Section titled “Discovery”| Route | |
|---|---|
GET /.well-known/openid-configuration |
the discovery document, including the tenant identity |
GET /.well-known/oauth-authorization-server |
the same document, for clients that look there |
GET /.well-known/jwks.json |
this tenant’s published keys, cached five minutes |
The document names handshake_endpoint alongside the standard ones, so an application discovers
where to ask to be connected rather than writing the path down.
Four fields are worth reading before you write a client against it:
acr_values_supported |
urn:masks:acr:pwd and urn:masks:acr:mfa — ask for one with acr_values |
claims_parameter_supported |
true, for userinfo claims |
request_parameter_supported |
false |
request_uri_parameter_supported |
false |
The last two are declared rather than left to default, because /authorize refuses a request
or request_uri with request_not_supported / request_uri_not_supported rather than ignoring it.
Reading the query and skipping the object would let the unsigned parameters beat the ones the client
signed, which is the wrong direction to fail in.
| Route | |
|---|---|
GET POST /authorize |
starts the code flow; redirects to sign-in or consent as needed |
POST /token |
authorization_code, refresh_token, and token-exchange grants |
POST /revoke |
RFC 7009 — revokes a token and everything exchanged from it |
GET POST /userinfo |
claims for the bearer, requires the openid scope |
/authorize parameters
Section titled “/authorize parameters”response_type |
code |
client_id redirect_uri |
must match a registered URI exactly |
scope |
narrowed to what the client is registered for |
state |
echoed back; you must check it |
nonce |
bound into the id token |
code_challenge code_challenge_method |
S256; required for public clients |
resource |
RFC 8707; may repeat |
prompt |
login forces re-authentication, consent re-asks, none refuses to interact |
max_age |
a ceiling on how long ago the person signed in, checked against auth_time |
acr_values |
space-separated; the id token’s acr names which was used |
claims |
JSON, OIDC Core 5.5; userinfo claims are released by name |
Both methods take the same parameters. POST is form-encoded, and a repeated resource may arrive
in the body as well as the query.
The redirect back always carries iss, so a client with several issuers can tell which answered.
claims asks for a claim by name rather than by the scope that contains it:
&claims={"userinfo":{"name":{"essential":true}}}Essential and voluntary are treated alike — masks releases what it holds either way, because refusing a voluntary request only teaches a client to ask for the broader scope instead.
Sign-in
Section titled “Sign-in”| Route | |
|---|---|
GET POST /login |
identifier and password |
GET POST /login/otp |
TOTP second factor, when the actor has one |
GET POST DELETE /logout |
revokes the session |
GET POST /consent |
scopes and audiences being asked for |
GET / |
who you are signed in as, and what you have authorized |
Registration
Section titled “Registration”| Route | Auth | |
|---|---|---|
GET POST /handshake |
a signed-in actor | approve an app, and mint the token that registers it |
POST /register |
none, or an initial access token | RFC 7591 — create |
GET /register/:client_id |
registration token | read |
PUT /register/:client_id |
registration token | update |
DELETE /register/:client_id |
registration token | archive |
Errors
Section titled “Errors”OAuth error codes, as JSON:
{ "error": "invalid_grant", "error_description": "that code is not valid or has expired" }Errors raised after the client and its redirect_uri are established come back at the redirect
URI rather than as a response body. Errors about the client or the redirect URI itself never do —
see policies.
When there is nowhere safe to send the error, the reader is a person rather than a program, so
/authorize renders an HTML page naming the code and what it means. The JSON body above is kept
for callers that do not ask for HTML.
401 responses carry a WWW-Authenticate header. /userinfo accepts its bearer token in the
Authorization header or as an access_token form parameter, per RFC 6750, and refuses with
invalid_request when both arrive at once.
