Users and credentials
Credential aggregator API
One view of every sign-in method a user has: password, TOTP, SMS and email OTP, passkeys, recovery codes. It also keeps login history and the tenant's list of enabled methods. It reads events from the credential services; it doesn't hold the credentials themselves.
https://{tenant-host}/credential-aggregation-service/api/v1audience credential-aggregation-service13 operationsWhen to use it
- Show a user which sign-in methods they have and when they last signed in.
- Remove a user's TOTP device, passkey or recovery codes through one API.
- Enable, disable or pause a sign-in method for the whole tenant.
Connect
- Base URL
https://{tenant-host}/credential-aggregation-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
credential-aggregation-service(aggregation, not aggregator). Not yet seen on a tenant.from source - Default scopes
credential-aggregation-service:get,:post,:delete,:patch, as each operation lists below. Access may not define them yet: create them in Access first.from source- Availability
- Not deployed on every tenant. Where it's missing, the gateway answers
{"code":"not_found",...}with HTTP 511.checked live
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}/credential-aggregation-service/api/v1/management/authenticators \
-H "Authorization: Bearer $ACCESS_TOKEN"Operations
Open an operation to see its parameters and code, and send it to your tenant. The token needs any one of the listed scopes. {userId} and {id} are the Identity Store user ID. {authenticatorId} is the aggregator's own row ID (id in the list), except in the management PATCH, where it is a method name such as TOTP.
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
Body fields are camelCase, not the snake_case of the reference
from sourceThe service reads enabledMethods, enabled, active and includedInRemainingAuthCount. enabled_methods, is_enabled and default_challenge from the older reference don't exist. Replies use camelCase too: lastLogin, availableAuthenticators, authnId, authnMethod.
POST on management replaces every method
from sourceMethods you leave out are removed from the tenant list, not kept as disabled. PASSWORD is always put back. To change one method, use the PATCH instead.
Enabling a method without active leaves it paused
from sourcePATCH accepts only enabled and active. {"enabled":true} on a disabled method gives active: false, so users still can't enroll. Send {"enabled":true,"active":true}. {"active":false} pauses an enabled method.
PASSWORD can't be disabled or paused
from sourceEither request returns 400 CAS-112. Pausing a method that isn't enabled returns 400 CAS-111; an empty PATCH returns 400 CAS-104.
The method is FIDO, the delete path says passkey
from sourceUse FIDO in bodies and in the management PATCH path. PASSKEY is obsolete. The user delete route is still /authenticators/passkey/{authenticatorId}.
Deletes return 204 and the TOTP delete takes no OTP
from sourceThe reference says 200 and a required otp query parameter. The service returns 204 and reads no OTP. A passkey row that doesn't exist also returns 204, and nothing is deleted.
authenticatorId is the aggregator's ID, not the credential's
from sourcePass id from the user's list. reference (and credId in the detail) is the passkey credential ID or TOTP device ID; the aggregator looks it up itself.
It shows recorded metadata, not live credentials
from sourceRows come from events sent by the credential services, so they can lag or be stale. A recovery-codes row can stay after every code is used: ask the recovery code service for the real count. A 204 from a delete doesn't prove the credential is gone.
Writing records doesn't enroll or sign anyone in
from sourcePOST .../authenticators and POST .../logins only store metadata. Each call adds a new row, so repeats create duplicates. Use the credential services to enroll a user.
Empty answers differ per endpoint
from source/authenticators and /logins return 200 with an empty array. /last-login and an unknown /authenticators/{authenticatorId} return 404 with an empty body. is_include_disabled on the tenant list defaults to true.
Errors
Error bodies carry id, code, message and details. Some 404s (no last login, unknown user authenticator) have an empty body instead. 401 and 403 from the security layer have no CAS code.
| 400 | CAS-100 | Invalid parameters, including an unknown method name or a malformed UUID. |
| 400 | CAS-104 | PATCH without enabled or active. |
| 400 | CAS-111 | Pause requested on a method that isn't enabled. |
| 400 | CAS-112 | Tried to disable or pause PASSWORD. |
| 401 | CAS-110 | A credential service the aggregator called refused it. |
| 404 | CAS-102 | Not found. An unsupported HTTP method also maps here. |
| 404 | CAS-106 | Method not configured for the tenant, or TOTP row not found. |
| 422 | CAS-109 | Invalid method value stored or sent. |
| 500 | CAS-101 / CAS-105 / HD-102 | Invalid tenant, database failure, or a failed call to another service. Quote id when you report it. |
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.