Sign-in and verification
Action Challenge API
Issues a one-time challenge to a user (a code by email or SMS, a magic link, or a token) and checks the answer. The challenge policy, picked by its alias, decides the type, length, lifetime and retries.
https://{tenant-host}/action-challenge-service/api/v1audience action-challenge-service13 operationsWhen to use it
- Send a one-time code by email or SMS for a password reset, a recovery or a step-up.
- Verify the code the user typed, then complete the challenge so the action can go ahead.
- Read or manage the tenant's challenge policies and the service's global settings.
Connect
- Base URL
https://{tenant-host}/action-challenge-service/api/v1- Token
POST https://{tenant-host}/oauth/v1/tokenwithgrant_type=client_credentials. The client ID and secret go in the form body. The published spec says/oauth/token: that path is wrong.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
action-challenge-service. Notaction-challenge.checked live - Default scopes
- One
challenge:*scope per operation, listed below. Some tenants don't define these scopes in Access yet: create them first, then add them to the client.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}/action-challenge-service/api/v1/configuration/policies?page=0&size=20" \
-H "Authorization: Bearer $ACCESS_TOKEN"Operations
Open an operation to see its parameters and code, and send it to your tenant. The token needs the listed scope. Request bodies use snake_case field names, for example challenge_alias and contact_details.
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
Answering the code does not finish the challenge
not confirmedA right answer moves the challenge to VERIFIED. Call /complete next to move it to COMPLETED. Complete, resend and cancel answer 422 (ACS-103) when the challenge is in the wrong state.
A wrong code answers 401
not confirmedThe answer endpoint returns 401 for a wrong code, an expired challenge or too many retries. Don't treat it as a token problem: check the error body before you fetch a new token.
The policy list returns ids only
checked liveGET /configuration/policies replies {"policyIds":[...],"hasNext":true}, not the ids / page / size / total shape in the published spec. page starts at 0. Read each policy by id to get its alias and type.
Policy replies don't match the published schema
checked liveA policy comes back with camelCase fields (actionScope, templateRef) and conditions as a list: [{"type":"TTL","value":300,"isDefault":true}, ...] with types TTL, MAX_RETRIES, TOKEN_LENGTH and CHARSET (value DIGITS). The spec shows snake_case and a conditions object. Read a policy before you write one, and check the result after.
The global configuration reply is not the PUT body
checked liveGET /configuration returns version, rateLimit, encryption, redirectUriWhitelist and client, with values as strings ("100", "true"). The PUT body in the spec uses rate_limit and redirect_uri_wl with numbers and booleans. Don't send the GET reply back as is.
An unknown alias returns 404 ACS-404
checked livechallenge_alias must match a policy on the tenant. Tenants come with seeded policies such as password-reset-email and password-reset-sms. Create a policy for any other alias first.
EMAIL_LINK and TOKEN challenges send nothing
not confirmedOnly EMAIL_OTP and SMS_OTP are delivered by the service. For EMAIL_LINK and TOKEN the code comes back in the create reply, and your app must send the link itself. The global setting client.magic_link_enabled (default false) must be true to create EMAIL_LINK challenges.
Set subject.type to IDENTITY
not confirmedThe spec lists USER and DEVICE. In the service code, the verification token only carries the subject when subject.type is IDENTITY; with USER the call succeeds but the subject is missing. Pass the Identity Store id as subject.id.
The action scope is signed, not enforced
not confirmedA policy's action scope ends up in the verification token, but the service never checks it. The service that consumes the token must check the scope itself.
Updating a policy needs its current version
not confirmedPUT /configuration/policies/{policyId}/version/{version} takes the version from the last GET. A stale version answers 409.
Errors
Error bodies carry status, errorCode, details and timestamp (seen live). The published spec shows code and message instead. 401 on a call other than answer, and 403, come from the security layer.
| 400 | ACS-100 | Invalid or missing parameters. |
| 400 | ACS-400 | Request validation failed. |
| 401 | — | On answer: wrong code, expired challenge or too many retries. Elsewhere: token missing or without the audience. |
| 404 | ACS-404 | "The element requested does not exist": unknown challenge, policy or alias. |
| 409 | — | Policy alias already exists, or a stale version on a policy update. |
| 422 | ACS-103 | The challenge is in the wrong state for this step. |
| 429 | ACS-410 | Rate limit reached (rate_limit.max_req per tenant). |
| 500 | ACS-500 | Service error. Seen on a configuration read on a tenant where the service wasn't set up. |
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.