AETHERLABS

Errors

Every error the OAuth endpoints return, where it lands, and what to do about it.

Errors

Errors follow RFC 6749:

{
  "error": "invalid_grant",
  "error_description": "Invalid PKCE verifier"
}

Where the error lands

This is the part most integrators get wrong, so it is worth stating plainly:

  • Before redirect_uri is proven registered — the error is returned to your app as JSON, with an HTTP status. SSO will not redirect, because redirecting to an unverified URI is exactly the open-redirect bug.
  • After that — the browser is redirected back to redirect_uri with error and error_description appended, plus the same state you sent.

So handle both: a JSON body on the fetch, and error parameters on the callback.

There is one more place failures show up: the token endpoint never redirects. It always answers your backend directly.

Error reference

ErrorStatusRaised atUsual cause
invalid_request400authorize, consent-info, approve, tokenMissing parameter; unknown client; unregistered redirect_uri; PKCE missing or not S256
unsupported_response_type— (redirect)authorizeresponse_type is anything other than code
invalid_scope400 / redirectauthorize, consent-info, approveA scope the client isn't registered for, or one SSO doesn't know
access_denied401consent-info, approve, userinfoNo signed-in user
invalid_client401token, revokeWrong or missing client credentials
invalid_grant400tokenBad/expired/used code; PKCE mismatch; redirect_uri mismatch; invalid, expired, reused, or foreign refresh token
unsupported_grant_type400tokengrant_type other than authorization_code / refresh_token
too_many_requests429token, revokeMore than 60 requests per 15 minutes from one IP
server_error500anySomething broke — check the API logs

The invalid_grant cases, separated

Because the code is the same for several very different problems:

CauseDetail
Code unknownNever issued, or belongs to a different client
Code expiredCodes live 10 minutes
Code already usedSingle-use. A replay also revokes that client's sessions for the user
redirect_uri mismatchMust be byte-identical to the authorize request
PKCE verifier wrongMust hash to the original code_challenge
Refresh token unknown/expiredNever issued, revoked, or past 30 days
Refresh token reusedRotated-out token presented → the whole session family is revoked
Refresh token not yoursclientId claim doesn't match the authenticating client, or the token came from a first-party session

Diagnosing

Start at discovery — it tells you what the server is actually serving:

curl -s https://sso.aetherlabs.agency/api/oauth/.well-known/oauth-authorization-server

Check that the URLs are https:// (if they read http://, the deployment's PUBLIC_API_URL / trust-proxy setting is wrong), that response_types_supported is ["code"], and that code_challenge_methods_supported is ["S256"].

Then, in order:

  1. invalid_request at authorize → is the redirect_uri exactly registered?
  2. invalid_scope → is every requested scope in the client's registered set?
  3. Redirect back with error=access_denied → the user wasn't signed in, or declined.
  4. invalid_client at token → the secret is wrong, rotated, or sent in neither Basic nor the body.
  5. invalid_grant at token → work down the table above; reusing a code or refresh token is the most common.

Common mistakes

  • Reusing an authorization code, or a refresh token after rotation.
  • Sending scope values the client was never registered for.
  • Registering a callback URI that differs from the request by a slash, port, or query string.
  • Treating expires_in as 3600 — it is the real lifetime (86400).
  • Putting the client_secret in frontend code.
  • Expecting the browser to redirect on an invalid redirect_uri — it won't; that call returns JSON.

On this page