Users and credentials
Password Store API
Stores password hashes for identities and the policies that rule them: length, composition, expiry, history, lockout. Verifies and changes passwords at sign-in.
https://{tenant-host}/password-storeaudience password-store18 operationsWhen to use it
- Verify or change a user's password from a backend or an IO flow.
- Set, reset, unlock or delete the password of an identity from an admin tool.
- Import pre-hashed passwords when you migrate users from another system.
- Read or change the tenant's password policies.
Connect
- Base URL
https://{tenant-host}/password-store- 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
password-storechecked live - Default scopes
- The
password:*scope each operation lists below, e.g.password:policy:getfor policy reads. Notpassword-store-admin, which the published spec names. Keep the client's scopes, joined with spaces, within 1024 characters.checked live - Tenant-Id header
- Send
Tenant-Id: <tenant-uuid>. The service rejects a missing or invalid one withINVALID_TENANT_ID. The challenge reset calls require it explicitly.from source
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}/password-store/api/v1/configuration/policies/default \
-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. Two prefixes: verify and change live under /auth/v1, everything else under /api/v1. 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
Verify and change are under /auth/v1, not /api/v1
from sourceCall /password-store/auth/v1/verify/password. There is no /api/v1/verify/password route: only the password and policy management APIs use /api/v1.
Scopes are password:*, not password-store-admin
checked liveThe published spec names one scope, password-store-admin. The service checks one scope per operation instead: password:policy:get for policy reads, password:verify for verify, and so on.
A wrong password is HTTP 422, not 401 or 404
from sourceVerify and change answer 422 with a code and a verificationResult: PST-411 / INCORRECT, PST-422 / LOCKED, PST-466 / POLICY_BLOCKED. A user without a password is PST-413, also 422. Branch on code, not on the status alone.
Verify can succeed and still ask for a reset
from sourceA correct password that breaks the current policy (expired, too short) returns 200 VERIFIED with mustReset: true. mustReset is left out when false. Send the user to a change-password step when it is there.
A locked password fails even when it is right
from sourceAfter lockAttempts wrong tries, every verify returns PST-422 until autoUnlockSeconds pass. Each locked attempt moves that window forward. The unlock call answers 409 PST-417 when the password isn't locked.
Admin reset takes password, not newPassword
from sourcePUT /api/v1/identities/{identityId}/password takes {"password": "..."} and checks neither the old password nor a challenge: your flow must prove who the user is. newPassword belongs to the challenge reset completion. Create is not an upsert: a second create answers 409 PST-415.
Policy values are strings; updates need the current version
checked liveRules come back as strings ("lengthMin": "8", "isDefault": "true") and version as a number. PATCH and PUT go to /policies/{policyId}/version/{version} with the version from your last GET. A stale version answers 500 PST-555, not 409.
The default policy can be the root policy
checked liveOn a tenant without stored policies, the list is {"policies": []} and /policies/default returns the root rules, without id or version. Root defaults: length 8–64, one lower, upper, digit and special character, expiry 90 days, lock after 5, unlock after 300 s.
Stored policies don't inherit root rules
from sourceA custom policy only enforces the rules it contains. Put every rule you need into it. Setting a new default doesn't move passwords that already point to another policy.
Breach checks run later
from sourceWith pwnedCheckOnStore or pwnedCheckOnAuth set, the breach check runs asynchronously. A hit sets mustReset on the stored password afterwards, so a 201 or 200 doesn't prove the password is clean.
Errors
Error bodies carry code, message, and when relevant verificationResult and details. The status depends on the API: verify, change and challenge reset use 422 for password problems, the management APIs use 409. 401 and 403 come from the security layer and have no PST code.
| 422 | PST-411 | Wrong password (verify, change). verificationResult: INCORRECT. |
| 422 | PST-422 | Password locked. verificationResult: LOCKED. |
| 422 | PST-466 | Blocked by policy. verificationResult: POLICY_BLOCKED. |
| 422 | PST-413 | No password for this identity (verify, change). |
| 409 | PST-420 | New password breaks the policy. 422 on change and challenge reset. |
| 409 | PST-424 | New password was used before (history check). |
| 409 | PST-415 | The identity already has a password. Create is not an upsert. |
| 409 | PST-416 | Import over a password that isn't marked migrated. |
| 409 | PST-417 | Unlock called on a password that isn't locked. |
| 404 | PST-431 | Policy not found. |
| 409 | PST-432 | Policy still used by passwords: it can't be deleted. |
| 409 | PST-434 | A policy with this name exists. |
| 500 | PST-555 | Stale policy version on PATCH or PUT. Read the policy again. |
| 400 | PST-234 | Unknown challenge policy alias on reset init. |
| 422 | PST-235 | Challenge verification token invalid, expired, reused or for another identity. |
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.