Users and credentials
Recovery codes API
One-time backup codes for users who lose their authenticator. Generates, counts, validates and deletes an identity's codes, and holds the tenant's recovery code settings.
https://{tenant-host}/recovery-code-service/api/v1audience recovery-code-service8 operationsWhen to use it
- Give the user a set of backup codes right after they enroll an authenticator.
- Let a user sign in with a backup code when the authenticator app isn't available.
- Show how many codes are left, or replace the set.
- Import existing codes when you migrate users from another system.
Connect
- Base URL
https://{tenant-host}/recovery-code-service/api/v1- Token
POST https://{tenant-host}/oauth/v1/tokenwithgrant_type=client_credentials. The client ID and secret go in the form body.checked live- Client to create
- Access admin → Web clients → a client with the grant type Client credentials. Not an API client: those are only for Access's own configuration API.checked live
- Default audiences
- must include
recovery-code-servicechecked live - Default scopes
- The
recovery-codes:*scope each operation lists below. A tenant may not define these scopes in Access yet: then even the config read answers 403 "scope missing", and an Access admin must create the scopes first. Keep the client's scopes, joined with spaces, within 1024 characters.checked live - Tenant-Id header
- Send
Tenant-Id: <tenant-uuid>as for the other platform services. Tenant context comes from a shared library, so the exact header rules aren't confirmed.not confirmed
curl -s https://{tenant-host}/oauth/v1/token \
-d grant_type=client_credentials \
-d client_id=$CLIENT_ID \
-d client_secret=$CLIENT_SECRET
curl -s https://{tenant-host}/recovery-code-service/api/v1/recovery-codes/config \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Tenant-Id: $TENANT_ID"Operations
Open an operation to see its parameters and code, and send it to your tenant. The token needs the listed scope.
Connect to your tenant
Then open any operation below and send it.Sends grant_type=client_credentials to https://{tenant-host}/oauth/v1/token through this site's server. The secret and token stay in this tab only; host and client ID are remembered.
What trips people up
403 "scope missing" with the audience present
checked liveThe token endpoint accepts recovery-code-service and the service answers 403, not 401. The client lacks the recovery-codes:* scope, often because Access doesn't define these scopes on that tenant yet. Create them in Access, then add them to the client.
Codes are shown once
from sourcePOST and PUT return the plaintext codes in recoveryCodes, with count. The service stores only hashes: GET returns a count, never the codes. Show the set to the user right away. Don't retry PUT to get the codes again: each PUT makes a new set.
Generate refuses while any code is left
from sourcePOST answers 409 RCS-104 if even one code remains. PUT replaces the set and the old codes stop working. Ask the user before you replace a set: they may have printed it.
No codes left is a 404, not a count of 0
from sourceGET answers 404 RCS-103 once every code is used or deleted. Treat that 404 as zero codes left.
A wrong code is HTTP 422, unlike TOTP
from sourceValidate answers 200 with valid: true only on success. A wrong code is 422 RCS-106, and so is a user with no codes at all. TOTP returns 200 valid: false instead, so don't share one error branch between them.
Lockout blocks the right code too
from sourceAfter maxValidationAttempts wrong codes, validate answers 422 RCS-105 before it checks the code, until autoUnlockSeconds pass. The unlock happens on the next request after that time.
autoUnlockSeconds 0 doesn't lock forever
from sourceThe setting's description says 0 means never unlock. The service adds 0 seconds, so the lock ends right away. Use a large value if you want a long lockout.
Codes match exactly
from sourceCodes mix upper- and lower-case letters and digits. The service doesn't trim, change case or remove separators. Send exactly what the user typed, and don't apply TOTP's digits-only rule.
Import takes plaintext and skips blanks
from sourceSend {"codes": [...]} with the codes themselves, not hashes. Blank entries are dropped without an error and duplicates are kept. The reply is 201 with an empty body: check the count afterwards. Import also answers 409 RCS-104 if codes remain.
Paused stops new codes, not sign-in
from sourceWith recovery codes paused for the tenant, generate, regenerate and config changes answer 422 RCS-108, while validate still works. Disabled also blocks validate, with 422 RCS-109.
Errors
Error bodies carry id (trace id), code, message and details. 401 and 403 come from the security layer and have no RCS code.
| 400 | RCS-100 | Invalid parameters, e.g. a blank code. Field errors are in details. |
| 400 | RCS-101 | Invalid tenant. |
| 404 | RCS-102 | Recovery code config not found for the tenant. |
| 404 | RCS-103 | No codes left for this identity (GET). |
| 409 | RCS-104 | Codes already exist (generate, import). |
| 422 | RCS-105 | Locked after too many wrong codes. |
| 422 | RCS-106 | Wrong code, or no codes left (validate). |
| 422 | RCS-108 | Recovery codes paused or disabled: no new codes or config changes. |
| 422 | RCS-109 | Recovery codes disabled: validation refused. |
| 404 | RCS-110 | Authenticator state missing for the tenant. |
When a call fails
| Reply | Means | What to change |
|---|---|---|
| The service answers 401 | The token lacks this service's audience, or it expired. | Add the audience to the client's Default audiences (Access admin → Web clients → the client), then get a new token. |
| The service answers 403 and the token has the audience | A scope the operation needs is missing. | Add one of the operation's scopes to the client's Default scopes. Use the scope names your tenant lists under Access admin → Scopes. |
| Token endpoint: 401 invalid_client | Unknown client ID or wrong secret. | Check the client ID and paste the secret again. |
| Token endpoint: 400 unauthorized_client | The client may not use client credentials. | Add the grant type Client credentials to the web client. |
| Token endpoint: 400 invalid_request about an audience | You asked for an audience the client doesn't have. | Add it to Default audiences, or stop asking for it. |
| Token endpoint: 500 right after scopes were added | The client's Default scopes, joined with spaces, are over about 1024 characters. | Move scopes to Additional scopes and request them per call, or split the work over two clients. |
| {"code":"not_found","message":"The requested location was not found"} | That service isn't deployed on this tenant. | Nothing to change in the client. Ask whoever runs the tenant. |
Two kinds of client exist in Access. Service APIs like this one take a web client with the Client credentials grant. Access's own configuration API takes an API client: a web client sent there gets 401 invalid_credentials.