Skip to content

Latest commit

 

History

History
103 lines (84 loc) · 7.98 KB

errors.md

File metadata and controls

103 lines (84 loc) · 7.98 KB

Errors

Every OAuth error has a code (error) and a text for developers (error_description). The text is for the person who writes the client. It never contains internals, secrets or stack traces. Show end users your own message.

Where an error goes depends on the endpoint:

  • JSON endpoints (/token, /register, /revoke, /introspect) answer JSON: {"error": "...", "error_description": "..."} with the HTTP status below.
  • /authorize shows an HTML error page while the client or the redirect URI is not valid, because there is nowhere safe to send the person. Once both are valid, an anonymous visitor is sent to the login first, and the error goes back to the client's redirect_uri (with error, error_description, state and iss) automatically only when that redirect URI is trusted; otherwise the error page offers a link to it. See endpoints.
  • Protected resources answer through BearerTokenMiddleware with a WWW-Authenticate challenge. See protecting resources.

The class OAuthException carries the code, description and status. Its toArray() gives the JSON body. An exception also has an internal temporary flag, used to decide whether a stored client may stand in for an unreachable metadata host. The flag never appears in a response.

A description is printable ASCII without " and \, as RFC 6749 and RFC 6750 require: parameter names are quoted with ' ('code_verifier' is missing.), and a value copied from the request (a grant type, a scope, the name of a repeated parameter) has any other character replaced by ? and is cut to 64 characters ('a?b' is not a valid scope.). OAuthException refuses any other description with an InvalidArgumentException; check a text of your own with OAuthException::isValidDescription(). See security.

Error codes

Code HTTP status Where What the client developer should do
invalid_request 400 (405 for a wrong method) Everywhere A parameter is missing, repeated or malformed. Read error_description and fix the request. Form endpoints need application/x-www-form-urlencoded
invalid_client 401 /token, /revoke, /introspect; /authorize as an error page The client is unknown, revoked, uses the wrong authentication method, has the wrong secret, or its metadata document could not be used. Check the id and the secret. For a metadata document client, fix the document (see troubleshooting). A pruned dynamic client must register again
invalid_grant 400 /token The code or refresh token is wrong, expired, used, or was issued to another client or redirect URI. Start the authorization again. Never retry a used refresh token
unauthorized_client 400 /token, /authorize The client may not use this grant: wrong grant_types, a public client asking for client credentials, or no identity for the client. Fix the registration
unsupported_grant_type 400 /token The grant_type is unknown, or client_credentials is disabled. Use authorization_code, refresh_token or client_credentials
unsupported_response_type 400 /authorize Only response_type=code exists
invalid_scope 400 /authorize, /token A scope is malformed, not declared by the resource, not allowed for the client or the person, or none can be granted
invalid_target 400 /authorize, /token The resource is not declared, or does not match the code or the refresh token. Use the exact URL (see troubleshooting)
access_denied 403 (JSON); a redirect at /authorize /authorize, /register, /introspect The person denied the request, canAuthorize() refused, registration is closed, or introspection is disabled. Do not retry without the person's input
invalid_client_metadata 400 /register The registration document is invalid: not JSON, too large, bad grant or response types, bad name or scope. Fix it
invalid_redirect_uri 400 /register A redirect URI is not allowed. See redirect URI rules
invalid_token 401 /register in token mode; protected resources The token is missing, wrong, expired, revoked, for another resource, or its owner lost access. Get a new one (refresh, or authorize again)
insufficient_scope 403 Protected resources The token is valid but lacks a required scope. Ask the person to authorize the missing scope
server_error 500 Everywhere Something failed on the server. The details are in the server log. Retry later
temporarily_unavailable 429 /authorize, /token, /register, /revoke, /introspect Rate limit reached. Wait for the number of seconds in the Retry-After header

One more code exists for pages only: not_found (404) when the connections page is disabled.

Messages of invalid_grant

These are the texts /token sends with invalid_grant:

Text Meaning
The authorization code is invalid. Unknown code, or a code of another client
The code_verifier does not match. The PKCE verifier is wrong or malformed
The authorization code was already used. A replay. The tokens issued from the code were revoked
The authorization code expired. Codes live 60 seconds by default
redirect_uri does not match the authorization request. Send the same value as at /authorize
The resource of this code no longer exists. The resource was removed from the configuration
The person who approved this code no longer has access. findBySubject() returned null
The refresh token is invalid. Unknown token, a token of another client, or not a refresh token
The refresh token is no longer valid. Expired, or revoked
The refresh token was already used. A replay outside the grace window. The family was revoked
The refresh token is being rotated; try again. A retry arrived while the first request was still running
The refresh token was revoked. It was revoked while the new pair was being issued
The resource of this token no longer exists. The resource was removed from the configuration
The owner of this refresh token no longer has access. findBySubject() or forClient() returned null. Nothing was revoked, so the connection works again when access returns

Generic messages

All client authentication failures say Client authentication failed. whatever the cause (unknown client, wrong secret, wrong method, revoked), so an attacker learns nothing about which part was wrong. A request that names no client at all (no client_id, no Basic header) says Client authentication is required. instead; it concerns no client.

A failed metadata document download (network error, timeout, TLS problem, a document that is too large) is reported only as The client metadata document could not be fetched., and a DNS lookup that got no answer (timeout, SERVFAIL) as The client_id host could not be looked up right now. Both are temporary: they are not remembered as a rejection. The text never carries the low level error, so it cannot be used to probe your network. Other problems with a document name the rule that was broken. See troubleshooting.

Handling errors in your own code

OAuthException is thrown by the services (ClientRegistrar::register(), ConnectionService::createPersonalToken(), TokenValidator does not throw but returns a ValidationFailure). If you call those yourself, catch it:

use CakeDC\OAuth\Server\Exception\OAuthException;

try {
    $created = $server->connectionService()->createPersonalToken($owner, 'CI', $resource, ['read'], 30);
} catch (OAuthException $e) {
    return ['error' => $e->error, 'message' => $e->getMessage()]; // for example invalid_scope
}

ClientRegistrar::create() is for administrators and throws InvalidArgumentException for bad input.