Users and credentials
TOTP API
Authenticator-app MFA: enrolls TOTP devices for an identity, validates their codes at sign-in, and holds the tenant's TOTP settings (algorithm, digits, period, issuer).
https://{tenant-host}/totp-service/api/v1audience totp-service10 operationsWhen to use it
- Enroll an authenticator app: start enrollment, show the QR code, check the first code.
- Check a TOTP code at sign-in or step-up.
- List, rename or delete a user's authenticator devices.
- Import TOTP secrets when you migrate users from another system.
Connect
- Base URL
https://{tenant-host}/totp-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
totp-servicechecked live - Default scopes
- The
totp:*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}/totp-service/api/v1/totp/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. enroll is spelled with two l's.
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 totp-service and the service answers 403, not 401. The client lacks the totp:* scope, often because Access doesn't define TOTP scopes on that tenant yet. Create them in Access, then add them to the client.
Sign-in validation returns 200 for a wrong code
from sourcePOST /validate answers 200 with valid: false for a wrong code, a replayed code or a user without devices. Only valid: true (with deviceId) means the user passed. Don't branch on the status or on message.
Enrollment failures are 422, not 400
from sourceA wrong first code is 422 TOTP-104. An expired or unknown transaction is 422 TOTP-103. Paused or disabled TOTP is 422 TOTP-108 / TOTP-107. Retry only on TOTP-104.
An enrollment lasts ten minutes
from sourceStart enrollment returns enrollmentTransactionId. The user can retry a wrong first code with it for ten minutes. A wrong code doesn't extend that time. After it, start again: the old QR code is useless.
The secret is inside otpUri
from sourceStart enrollment returns otpUri, deviceId, enrollmentTransactionId, algorithmName, codeDigits and timePeriodSeconds. There is no secret or QR image field: draw the QR code from otpUri. The URI contains the shared secret, so never log it and clear it after enrollment.
Send codes as strings
from sourcecode is a string of 6–8 digits. A number loses leading zeros.
The first code can't be reused at sign-in
from sourceEnrollment marks its code's time step as used. A sign-in with the same code right after returns valid: false. Wait for the next code.
Clock skew counts whole periods
from sourceThe service accepts clockSkewSeconds / timePeriodSeconds steps on each side, rounded down. A skew below the period (e.g. 20 s with 30 s codes) means only the current code works. Enrollment always uses 30 s of skew.
Settings only apply to new enrollments
from sourceDevices keep the algorithm, digits and period they were enrolled with. Changing the config doesn't rewrite them. The config PATCH is partial: omitted fields stay as they are.
Import needs every parameter and an empty identity
from sourceEach imported device needs sharedSecret (Base32), algorithmName, codeDigits and timePeriodSeconds: no tenant defaults. If the identity already has any device, import answers 409 TOTP-106 instead of appending.
Errors
Error bodies carry id (trace id), code, message and details. A missing device on GET or DELETE is a 404 with an empty body. 401 and 403 come from the security layer and have no TOTP code.
| 400 | TOTP-100 | Invalid or missing parameters. Field errors are in details. |
| 400 | TOTP-101 | Invalid or mismatched tenant. |
| 404 | TOTP-102 | TOTP config not found for the tenant. |
| 422 | TOTP-103 | Enrollment transaction missing or expired. Start again. |
| 422 | TOTP-104 | Wrong first code during enrollment. |
| 404 | TOTP-105 | Device not found (rename). |
| 409 | TOTP-106 | Import refused: the identity already has a device. |
| 422 | TOTP-107 | TOTP is disabled for the tenant. |
| 422 | TOTP-108 | TOTP enrollments are paused. Sign-in still works. |
| 404 | TOTP-109 | 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.