Errors
Every error your app can meet while it signs members in, where it shows up, what it means, and a message you can show. The messages are suggestions in plain US English: change the words, keep the meaning.
On your redirect URI
When a sign-in is refused after Avonao ID has accepted your client and redirect URI, the member comes back to your redirect URI with the error in the query string, together with your state (check it first):
https://shop.example.com/auth/avonao/callback?error=access_denied&error_description=app_in_development&state=...| error | error_description | What it means | Message to show |
|---|---|---|---|
access_denied | (none) | The member chose "Don't allow" on the consent screen. | Sign-in was cancelled. You can try again whenever you like. |
access_denied | app_in_development | Your app is in development mode and this member is not in your business or on the app's tester list. Add them as a tester, or take the app live. | Sign in with Avonao ID is not open to everyone on this site yet. |
access_denied | app_unavailable | Your business is suspended or deleted. Check the email sent to the business owners. | Sign in with Avonao ID is not available on this site right now. Please contact us. |
access_denied | account_pending_deletion | The member asked to delete their Avonao ID and is inside the 28-day restore window. | This Avonao ID is scheduled for deletion. Sign in at id.avonao.com to restore it, then try again. |
login_required | (none) | You sent prompt=none and the member has no Avonao ID session. | Nothing: show your normal Sign in with Avonao ID button. |
consent_required | (none) | You sent prompt=none and the member has not consented to what you asked for. | Nothing: start a normal sign-in, without prompt=none. |
invalid_scope | (none) | The scope is missing openid, or asks for something other than openid, email and profile. A bug in your start route. | Sign-in failed. Please try again. |
invalid_request | PKCE S256 required, or none | code_challenge is missing, not 43 base64url characters, or code_challenge_method is not S256; or the nonce is longer than 256 characters. A bug in your start route. | Sign-in failed. Please try again. |
unsupported_response_type | (none) | response_type is not code. A bug in your start route. | Sign-in failed. Please try again. |
Not every one of these needs a message for the member. The last three are your own bugs: log them, show the generic message, and fix the route.
At /authorize (an error page, no redirect)
Avonao ID never redirects to an address it cannot trust. If the request cannot be tied to one of your registered redirect URIs, or it has no state, the member stays at Avonao ID and gets a 400 with a JSON body:
| error | What it means | What to do |
|---|---|---|
invalid_client_or_redirect_uri | client_id is unknown or the app is disabled, or redirect_uri is not exactly one of the redirect URIs registered for the app (an exact string match: scheme, host, port, path and query). | Fix the client ID or the redirect URI in your start route or in the console. |
invalid_request with state is required | state is missing or longer than 512 characters. | Send a random state with every request. |
At /token
Errors are JSON, { "error": "<code>" }, with these statuses. They happen on your server, so the message below goes to the member through your own error page.
| Status | error | What it means | Message to show |
|---|---|---|---|
| 401 | invalid_client | Unknown or disabled client, wrong or missing secret. The response has WWW-Authenticate: Basic. Did you rotate the secret and not deploy the new one? | Sign-in failed. Please try again. |
| 400 | unsupported_grant_type | grant_type is not authorization_code. Refresh tokens do not exist. | Sign-in failed. Please try again. |
| 400 | invalid_request | code is missing or too long, or code_verifier is not 43 to 128 characters from A-Z a-z 0-9 - . _ ~. | Sign-in failed. Please try again. |
| 400 | invalid_grant | The code is unknown, already used, older than 60 seconds, or was issued to another app; or redirect_uri differs from the one in the authorization request; or the verifier does not match the challenge; or the member signed out, revoked your app or is no longer active in the meantime. | Sign-in expired. Please try again. |
| 429 | rate_limited | Too many token requests from one address (120 a minute). The response has retry-after (seconds). | Too many attempts. Please wait a minute and try again. |
| 503 | region_unavailable | The place where this member's data lives cannot be reached for the moment. Nothing was changed. | Avonao ID is temporarily unavailable. Please try again in a few minutes. |
At /userinfo
| Status | error | What it means | Message to show |
|---|---|---|---|
| 401 | invalid_token | The Authorization: Bearer header is missing or malformed, or the access token is unknown, older than 10 minutes, or the member has since revoked your app or been deleted. The response has WWW-Authenticate: Bearer error="invalid_token". | Nothing for a member: the ID token already has the claims. Sign them in again if you need fresh ones. |
Checks your own code makes
These are not Avonao ID responses; they are what the Node example throws, and what a library reports in its own words.
| Code in the example | What it means | Message to show |
|---|---|---|
state_mismatch | The state on the redirect is not the one stored for this browser: an expired or missing cookie (10 minutes), a second tab, or a forged request. | Sign-in could not be completed. Please start again. |
invalid_response | The redirect had neither a code nor an error. | Sign-in failed. Please try again. |
token_exchange_failed | The token response was not usable. | Sign-in failed. Please try again. |
invalid_token | The ID token failed validation: algorithm, signature, unknown key, issuer, audience, expiry or nonce. Never sign the member in, and look at the log message. | Sign-in failed. Please try again. |
jwks_unavailable | The signing keys could not be fetched. | Avonao ID is temporarily unavailable. Please try again in a few minutes. |
Showing errors
- Map error codes to fixed messages of your own, as above. Never put
errororerror_descriptioninto HTML, a redirect or a log line as they are: anyone can send a member to your callback with a crafted query string. - Show one clear message and one button to try again, and a different way in (your other sign-in options) when you have one. Never retry automatically in a loop.
- For every error that is a bug in your integration, show the generic message to the member and log the code with enough detail for you to find the request.

