OAuth endpoints
ScaiKey exposes a full OIDC provider surface in two flavors: platform (for GLOBAL-scoped apps) and tenant-scoped (for apps that belong to one tenant).
Discovery#
Always start by fetching the discovery document — it's the source of truth for endpoint URLs and supported scopes.
| Flavor | URL |
|---|---|
| Platform | $SCAIKEY/api/v1/platform/oauth/.well-known/openid-configuration |
| Tenant | $SCAIKEY/api/v1/auth/tenants/{slug}/.well-known/openid-configuration |
The discovery doc lists every other endpoint. Read it once at startup with your OIDC library and you mostly don't need this page.
Authorize endpoint#
Starts the authorization_code flow. User-facing — your app redirects the browser here.
1 2 | |
Standard OIDC query parameters: response_type=code, client_id, redirect_uri, scope, state, nonce. PKCE: code_challenge, code_challenge_method=S256.
Optional: prompt=login (force re-auth), prompt=none (silent — fails if no active session), login_hint (prefill email), idp_hint (preselect a federated IdP).
The platform variant redirects unauthenticated users to a tenant-agnostic login page where they enter their email; ScaiKey then determines their tenant. The tenant variant goes straight to that tenant's login page.
Token endpoint#
The workhorse — exchange a grant for tokens.
1 2 | |
Content-Type: application/x-www-form-urlencoded. Client authentication via Authorization: Basic (recommended) or client_id + client_secret form fields.
grant_type=authorization_code#
| Field | Required | Notes |
|---|---|---|
code |
yes | The code from the authorize redirect |
redirect_uri |
yes | Must match what was sent to authorize |
code_verifier |
required for public clients | PKCE verifier |
client_secret |
required for confidential clients |
grant_type=client_credentials#
| Field | Required | Notes |
|---|---|---|
client_secret |
yes | Confidential client only — SPA/NATIVE not allowed |
scope |
no | Space-separated scopes. If omitted, defaults to the app's full allowed_scopes |
grant_type=refresh_token#
| Field | Required | Notes |
|---|---|---|
refresh_token |
yes | The token to exchange |
Refresh tokens are one-time-use — the response includes a new refresh token; the previous one is invalidated.
grant_type=urn:ietf:params:oauth:grant-type:token-exchange#
| Field | Required | Notes |
|---|---|---|
subject_token |
yes | The token to exchange (must be ScaiKey-issued) |
subject_token_type |
yes | urn:ietf:params:oauth:token-type:access_token is the only supported value |
audience |
yes | The target application's client_id or name (case-insensitive lookup) |
requested_token_type |
no | Always returns access_token regardless |
scope |
no | Requested scopes; intersected with subject and target |
client_secret |
yes | Requesting app must be confidential |
The target application's token_exchange_allowed must be true. See Concepts → OAuth and OIDC for the full rules.
grant_type=urn:ietf:params:oauth:grant-type:device_code#
| Field | Required | Notes |
|---|---|---|
device_code |
yes | From the device-authorize call |
Poll this endpoint at the interval in the device-authorize response (default 5 s). Responds with authorization_pending until the user approves, then issues tokens.
UserInfo endpoint#
1 2 | |
Bearer-authenticated. Returns OIDC identity claims for the user the token belongs to. client_credentials tokens (no user) get a 401 here.
JWKS endpoint#
1 2 | |
The platform and tenant flavors return the same keys (signing is shared). Use whichever your library is pointed at.
Revoke endpoint (tenant-scoped only)#
1 | |
Revokes a refresh token. RFC 7009. Returns 200 OK even if the token was unknown (anti-enumeration).
Introspect endpoint (tenant-scoped only)#
1 | |
RFC 7662. Returns { "active": true, ...claims } for a valid access token, { "active": false } otherwise. Useful for opaque-token consumers (though all ScaiKey tokens are JWTs and can be self-verified).
End_session endpoint (RP-initiated logout)#
1 2 | |
Query parameters: post_logout_redirect_uri, id_token_hint, state, client_id.
Terminates the SSO session (revokes the session cookie), then redirects to post_logout_redirect_uri if provided, otherwise to the default logout confirmation page.
For GLOBAL apps that don't have a tenant slug to put in the URL, use the platform variant — it identifies the session via the SSO cookie, not the URL path.