OAuth 2.0 and OpenID Connect endpoints
Status:
planned. This page describes the protocol surface of every KeenAuth environment as specified in the plan (§13). The endpoints are being built in phase 2 and are not yet available in a hosted environment. Details can still change before general availability.
Every environment is its own OAuth 2.0 authorization server and OpenID Connect provider. The endpoints are not part of the management API: they live on the environment host, https://<environment-slug>.<region>.keenauth.app, or on your verified custom domain. In the table, <issuer> is the environment's issuer, the value of issuer in the discovery document.
Endpoints
| Endpoint | Method and path | Client authentication |
|---|---|---|
| Discovery | GET <issuer>/.well-known/openid-configuration and GET <issuer>/.well-known/oauth-authorization-server (RFC 8414, same content) |
None |
| Keys | GET <issuer>/.well-known/jwks.json |
None |
| Authorization | GET or POST <login host>/oauth2/authorize |
None (browser) |
| Pushed authorization request (RFC 9126) | POST <issuer>/oauth2/par |
Required for confidential clients; client_id for public clients |
| Token | POST <issuer>/oauth2/token |
Required for confidential clients; client_id for public clients (DPoP is optional) |
| Device authorization (RFC 8628) | POST <issuer>/oauth2/device_authorization |
Same as the token endpoint |
| Revocation (RFC 7009) | POST <issuer>/oauth2/revoke |
Same as the token endpoint |
| Introspection (RFC 7662) | POST <issuer>/oauth2/introspect |
Confidential client only |
| UserInfo | GET or POST <issuer>/oauth2/userinfo |
Bearer or DPoP access token |
| End session | GET or POST <login host>/oauth2/logout |
None (id_token_hint or client_id) |
<login host> is any sign-in domain of the environment: the default host or an added custom domain. The discovery document is served only on the issuer host. On another sign-in host, /.well-known/openid-configuration returns 404 with the issuer URL in the body.
Grant types
| Grant | Use |
|---|---|
authorization_code |
Browser and native sign-in. PKCE with S256 is required. |
refresh_token |
Renews tokens. Every use rotates the refresh token, and reusing an old one revokes the whole token family. |
client_credentials |
Machine-to-machine access. |
urn:ietf:params:oauth:grant-type:device_code |
Devices without a keyboard, such as a TV or a command-line tool. |
urn:ietf:params:oauth:grant-type:token-exchange |
Delegation and impersonation between services. |
Implicit flows (response_type=token and anything that contains id_token), the resource owner password credentials grant and hybrid response types are not supported, now or later. RFC 9700 forbids the first two.
Device authorization
A device without a browser starts the flow with POST <issuer>/oauth2/device_authorization. Only applications of type device or native that list the device grant explicitly can use it. The response carries a device_code, a user_code and a verification address. The user opens the address on another device, signs in and approves. Meanwhile, the device polls the token endpoint with grant_type=urn:ietf:params:oauth:grant-type:device_code. The user_code has 8 characters and is shown as XXXX-XXXX. A device code is valid for 10 minutes and works once. The device polls every 5 seconds at most; faster polling is answered with slow_down, which adds 5 seconds to the interval. The verification_uri is https://<login host>/u/device, and the user is shown the application, the requested scopes and the country of the device that started the request before approving.
Security rules
The environment follows the OAuth 2.0 Security Best Current Practice (RFC 9700) without exception:
- The redirect URI must match a registered one byte for byte. There are no wildcards.
- PKCE accepts only
S256;plainis always rejected. PKCE is required for public clients and is on by default for every application. A confidentialwebapplication can switch it off in its settings, and only if the environment still requiresnoncein that case. - The official SDKs send a 128-bit
stateand anonceand check both on return. - An authorization code is valid for 60 seconds and works once. Presenting a used code returns
invalid_grantand revokes the refresh tokens and the access token that the first use produced. - Pushed authorization requests keep the request parameters out of the browser's address bar. They are available on every plan, and the
require_parsetting makes them mandatory for an application or the whole environment. - Refresh tokens rotate on every use, and reuse detection revokes the family.
- DPoP (RFC 9449) binds an access token to a key held by the client. To verify bound tokens in your API, see Verifying tokens in your API.
Sessions and sign-out
After a successful sign-in the environment keeps one single sign-on session in an HttpOnly cookie on the sign-in host. Applications of the same environment reuse it, so a second application does not ask for a password again unless its policy needs a stronger or newer authentication. Your code never sees the cookie: it only sees the authorization response.
The end-session endpoint (OpenID Connect RP-Initiated Logout 1.0) ends that session, revokes the refresh tokens that belong to it and notifies the applications that registered a back-channel logout address. To list or revoke the sessions of a user from your backend, use the session endpoints of the management API.
If the user signed in through an enterprise connection, add federated=true to the end-session request to sign them out at their identity provider too. The session here ends first; the browser then goes to the identity provider's logout (SAML single logout, or the OpenID provider's end-session endpoint) and comes back to the post_logout_redirect_uri. This happens only when the connection has identity-provider logout turned on and the provider publishes a logout address. Otherwise, or if the provider cannot be reached, the request works as it does without the parameter. Signing out at the identity provider can also end the user's other applications that use it.