OakID follows RFC 6749 error responses on /oauth/token and /oauth/userinfo. Errors are JSON with error and optional error_description.
{
  "error": "invalid_grant",
  "error_description": "PKCE verification failed"
}

Error reference

ErrorHTTPEndpointMeaning
invalid_request400/oauth/tokenMissing or malformed parameters
invalid_client401/oauth/tokenWrong client_id / client_secret
invalid_grant400/oauth/tokenCode or refresh token rejected
unsupported_grant_type400/oauth/tokenOnly authorization_code and refresh_token are supported
invalid_token401/oauth/userinfoMissing, expired, or revoked access token
server_error500/oauth/tokenInternal failure while issuing tokens
Authorization codes are single-use and expire within minutes.Common causes:
  • Exchanging the same code twice (page refresh on callback, double-submit)
  • redirect_uri on token request does not match the authorize step exactly
  • client_id does not match the app that received the code
Fix: Start a new login from your app. Never cache or replay authorization codes.
The code_verifier sent to /oauth/token must satisfy:
BASE64URL(SHA256(code_verifier)) === code_challenge
Common causes:
  • Opening a sample authorize URL from the Developer Portal instead of starting login in your app
  • Losing code_verifier between authorize and callback (wrong storage domain, cleared session)
  • Using plain challenge method — OakID only supports S256
Fix: Generate fresh state, code_verifier, and code_challenge per login. Store the verifier in sessionStorage on the same origin that receives the callback. See PKCE.
Refresh tokens rotate on every use. The previous token is revoked immediately.Common causes:
  • Reusing an old refresh token after a successful refresh
  • Token expired or manually revoked in the Developer Portal
  • Client secret was regenerated (invalidates all sessions for that client)
Fix: Persist the new refresh_token from each token response. If refresh fails, redirect the user through the authorize flow again.
Confidential clients must send client_id and client_secret in the token request body or via HTTP Basic auth.Common causes:
  • Typo in OAKID_PANEL_CLIENT_SECRET (Panel integration)
  • Secret was regenerated in Developer Portal but env var was not updated
  • Using client_id from one app with another app’s secret
Fix: Copy credentials from Developer Portal again. Restart the backend after updating .env.
This appears in the browser when state from the callback does not match what Panel stored in sessionStorage.Common causes:
  • Callback landed on a different host than where login began
  • Third-party cookies or storage blocked between your app and id.oakwall.mom
Fix: Always start login from your app’s Sign in with OakID button on your registered redirect domain. See Sign in with OakID.
The access token is missing, expired, or revoked.Fix: Refresh the access token with grant_type=refresh_token, or run the authorize flow again if refresh is unavailable.

Revoke tokens

To invalidate a refresh or access token:
curl -X POST 'https://id.oakwall.mom/oauth/revoke' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'token=TOKEN_TO_REVOKE'
OakID returns 200 even if the token is unknown (per RFC 7009).

Still stuck?

Check discovery

Confirm issuer and endpoint URLs match your environment.

API Reference

Full request/response schemas for every OAuth endpoint.