API reference · Access and identity
Two-factor authentication
9 operations of the Ankra Cloud API: Multi-factor authentication: the second sign-in step (a TOTP code or a recovery code), setting up and turning off an authenticator app, recovery codes, and the account owner's requirement of a second factor for every member.
Your second factor and whether the account requires one#
/v1/account/mfa- Operation
get_account_mfa- Credentials
- Portal session
- Requires
- Permission
self
Session-only; API tokens never see how anyone signs in.
Responses
200Your second factor.application/json · SecondFactorStatus
| Field | Type | Description |
|---|---|---|
totp_enabledrequired | boolean | |
totp_pendingrequired | boolean | A setup was started and waits for its first code. |
enabled_atrequired | string (date-time) | null | |
recovery_codes_remainingrequired | integer | |
required_by_accountrequired | boolean | The owner requires a second factor of every member; it cannot be turned off. |
availablerequired | boolean | False when the control plane has no secret key and cannot seal authenticator secrets. |
- 401Not signed in, or the credential is invalid or expired.
- 403The role lacks the permission, the token is read-only (a read-only token also gets
reason: read_only_token_cannot_read_credentialson every credential read), the CSRF header is missing, a support session may not do this, or the route needs a verified email address and the caller's is not (reason: email_unverified). - defaultAny other error, usually 500.
Example
curl 'https://cloud.ankra.app/v1/account/mfa' \
-b "ankracloud_session=$SESSION"Require a second factor of every member (owner only)#
/v1/account/mfa/policy- Operation
set_account_mfa_policy- Credentials
- Portal session
- Requires
- Permission
members.manage
required: true makes every member without a second factor set one up at their next sign-in; sessions already open keep running until they end. Only the owner may change it, and only with a second factor of their own (409 otherwise). Session-only. Audited as account.mfa_policy_changed.
Request bodyapplication/json
| Field | Type | Description |
|---|---|---|
requiredrequired | boolean |
Responses
200The owner's second factor with the new requirement.application/json · SecondFactorStatus
| Field | Type | Description |
|---|---|---|
totp_enabledrequired | boolean | |
totp_pendingrequired | boolean | A setup was started and waits for its first code. |
enabled_atrequired | string (date-time) | null | |
recovery_codes_remainingrequired | integer | |
required_by_accountrequired | boolean | The owner requires a second factor of every member; it cannot be turned off. |
availablerequired | boolean | False when the control plane has no secret key and cannot seal authenticator secrets. |
- 400The request is invalid;
detailsays why. - 401Not signed in, or the credential is invalid or expired.
- 403The role lacks the permission, the token is read-only (a read-only token also gets
reason: read_only_token_cannot_read_credentialson every credential read), the CSRF header is missing, a support session may not do this, or the route needs a verified email address and the caller's is not (reason: email_unverified). - 409The resource's state does not allow this now.
- defaultAny other error, usually 500.
Example
curl -X PUT 'https://cloud.ankra.app/v1/account/mfa/policy' \
-b "ankracloud_session=$SESSION" \
-H "X-CSRF-Token: $CSRF_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"required": false
}'Replace your recovery codes#
/v1/account/mfa/recovery-codes- Operation
replace_account_recovery_codes- Credentials
- Portal session
- Requires
- Permission
self
Needs a current code or a recovery_code. Every earlier recovery code stops working; ten new ones are returned once. Session-only; refused to support sessions. Audited as account.mfa_recovery_codes_regenerated.
Request bodyapplication/json · SecondFactorProof
| Field | Type | Description |
|---|---|---|
code | string | Six digits from the authenticator app. |
recovery_code | string | One of the single-use recovery codes. |
Responses
200The new recovery codes.application/json · RecoveryCodes
| Field | Type | Description |
|---|---|---|
recovery_codesrequired | array of string | Ten single-use codes, shown once. |
- 400The request is invalid;
detailsays why. - 401Not signed in, or the credential is invalid or expired.
- 403The role lacks the permission, the token is read-only (a read-only token also gets
reason: read_only_token_cannot_read_credentialson every credential read), the CSRF header is missing, a support session may not do this, or the route needs a verified email address and the caller's is not (reason: email_unverified). - 404No such resource in the caller's account.
- 429Rate limited or the email is locked out.
- defaultAny other error, usually 500.
Example
curl -X POST 'https://cloud.ankra.app/v1/account/mfa/recovery-codes' \
-b "ankracloud_session=$SESSION" \
-H "X-CSRF-Token: $CSRF_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"code": "123456",
"recovery_code": "string"
}'Start setting up an authenticator app#
/v1/account/mfa/totp- Operation
start_account_totp_setup- Credentials
- Portal session
- Requires
- Permission
self
A new TOTP secret (RFC 6238: SHA-1, six digits, 30 seconds), returned once as secret and as the otpauth_uri to render as a QR code; it replaces an earlier unconfirmed one and is not active until POST /v1/account/mfa/totp/confirm. 409 while an authenticator is set up. Session-only; refused to support sessions. Audited as account.mfa_setup_started.
Responses
200The secret, shown once.application/json · AuthenticatorSetup
| Field | Type | Description |
|---|---|---|
secretrequired | string | Base32, for typing into the app. |
otpauth_urirequired | string | otpauth://totp/… for the QR code; render it locally. |
expires_atrequired | string (date-time) | Confirm with the first code before this. |
- 401Not signed in, or the credential is invalid or expired.
- 403The role lacks the permission, the token is read-only (a read-only token also gets
reason: read_only_token_cannot_read_credentialson every credential read), the CSRF header is missing, a support session may not do this, or the route needs a verified email address and the caller's is not (reason: email_unverified). - 409The resource's state does not allow this now.
- 503No capacity or address is free, or a host did not answer; try again later.
- defaultAny other error, usually 500.
Example
curl -X POST 'https://cloud.ankra.app/v1/account/mfa/totp' \
-b "ankracloud_session=$SESSION" \
-H "X-CSRF-Token: $CSRF_TOKEN"Turn the authenticator app off#
/v1/account/mfa/totp- Operation
disable_account_totp- Credentials
- Portal session
- Requires
- Permission
self
Needs a current code or a recovery_code. Refused (409) while the account requires a second factor. Session-only; refused to support sessions. Audited as account.mfa_disabled, wrong codes as auth.mfa_failed.
Request bodyapplication/json · SecondFactorProof
| Field | Type | Description |
|---|---|---|
code | string | Six digits from the authenticator app. |
recovery_code | string | One of the single-use recovery codes. |
Responses
204The authenticator and every recovery code are gone.
- 400The request is invalid;
detailsays why. - 401Not signed in, or the credential is invalid or expired.
- 403The role lacks the permission, the token is read-only (a read-only token also gets
reason: read_only_token_cannot_read_credentialson every credential read), the CSRF header is missing, a support session may not do this, or the route needs a verified email address and the caller's is not (reason: email_unverified). - 404No such resource in the caller's account.
- 409The resource's state does not allow this now.
- 429Rate limited or the email is locked out.
- defaultAny other error, usually 500.
Example
curl -X DELETE 'https://cloud.ankra.app/v1/account/mfa/totp' \
-b "ankracloud_session=$SESSION" \
-H "X-CSRF-Token: $CSRF_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"code": "123456",
"recovery_code": "string"
}'Activate the authenticator app with its first code#
/v1/account/mfa/totp/confirm- Operation
finish_account_totp_setup- Credentials
- Portal session
- Requires
- Permission
self
Returns the ten single-use recovery codes, once; they are stored hashed. Session-only; refused to support sessions. Audited as account.mfa_enabled.
Request bodyapplication/json · AuthenticatorCodeRequest
| Field | Type | Description |
|---|---|---|
coderequired | string | Six digits from the authenticator app. |
Responses
200Active; keep the recovery codes.application/json · RecoveryCodes
| Field | Type | Description |
|---|---|---|
recovery_codesrequired | array of string | Ten single-use codes, shown once. |
- 400The request is invalid;
detailsays why. - 401Not signed in, or the credential is invalid or expired.
- 403The role lacks the permission, the token is read-only (a read-only token also gets
reason: read_only_token_cannot_read_credentialson every credential read), the CSRF header is missing, a support session may not do this, or the route needs a verified email address and the caller's is not (reason: email_unverified). - 409The resource's state does not allow this now.
- 429Rate limited or the email is locked out.
- defaultAny other error, usually 500.
Example
curl -X POST 'https://cloud.ankra.app/v1/account/mfa/totp/confirm' \
-b "ankracloud_session=$SESSION" \
-H "X-CSRF-Token: $CSRF_TOKEN" \
-H 'Content-Type: application/json' \
-d '{
"code": "123456"
}'Finish a sign-in with a code from the authenticator app or a recovery code#
/v1/auth/login/mfa- Operation
verify_login_second_factor- Credentials
- None (public)
Takes the challenge from the password step (or from the identity provider sign-in, which lands on the portal's /login/mfa page with it in the URL fragment) and exactly one of code (six digits, each accepted once) and recovery_code (single use). Sets the session cookies like POST /v1/auth/login and applies its end_other_sessions. Limited to 10 attempts per client address per minute; repeated wrong codes lock the user's second factor out. Audited as auth.login (and auth.mfa_recovery_code_used), wrong codes as auth.mfa_failed.
Request bodyapplication/json · SecondFactorLoginRequest
| Field | Type | Description |
|---|---|---|
challengerequired | string | |
code | string | Six digits from the authenticator app. |
recovery_code | string | One of the single-use recovery codes (verify only). |
Responses
200Signed in.application/json · SecondFactorSession
| Field | Type | Description |
|---|---|---|
userrequired | User | |
idrequired | string | |
emailrequired | string | |
name | string | Set for users who signed up themselves. |
account_idrequired | string | |
rolerequired | string | One of owner, admin, member, viewer |
csrf_tokenrequired | string | |
expires_atrequired | string (date-time) | |
ended_sessions | integer | Sign-in only: how many other sessions end_other_sessions ended. |
recovery_codes | array of string | After a setup at sign-in: the ten recovery codes, shown once. |
recovery_codes_remaining | integer | After a code check: how many unused recovery codes are left. |
return_to | string | After an identity provider sign-in: where the portal continues. |
- 400The request is invalid;
detailsays why. - 401Not signed in, or the credential is invalid or expired.
- 409The resource's state does not allow this now.
- 429Rate limited or the email is locked out.
- 503No capacity or address is free, or a host did not answer; try again later.
- defaultAny other error, usually 500.
Example
curl -X POST 'https://cloud.ankra.app/v1/auth/login/mfa' \
-H 'Content-Type: application/json' \
-d '{
"challenge": "string"
}'Start setting up the authenticator app the account requires, before the first session#
/v1/auth/login/mfa/enroll- Operation
start_login_second_factor_setup- Credentials
- None (public)
With an enroll challenge: a new TOTP secret for the user, returned once as secret and as the otpauth_uri the portal renders as a QR code. It is not active until .../enroll/confirm receives its first code.
Request bodyapplication/json · SecondFactorChallengeRequest
| Field | Type | Description |
|---|---|---|
challengerequired | string |
Responses
200The secret, shown once.application/json · AuthenticatorSetup
| Field | Type | Description |
|---|---|---|
secretrequired | string | Base32, for typing into the app. |
otpauth_urirequired | string | otpauth://totp/… for the QR code; render it locally. |
expires_atrequired | string (date-time) | Confirm with the first code before this. |
- 400The request is invalid;
detailsays why. - 401Not signed in, or the credential is invalid or expired.
- 409The resource's state does not allow this now.
- 429Rate limited or the email is locked out.
- 503No capacity or address is free, or a host did not answer; try again later.
- defaultAny other error, usually 500.
Example
curl -X POST 'https://cloud.ankra.app/v1/auth/login/mfa/enroll' \
-H 'Content-Type: application/json' \
-d '{
"challenge": "string"
}'Activate the new authenticator with its first code and sign in#
/v1/auth/login/mfa/enroll/confirm- Operation
finish_login_second_factor_setup- Credentials
- None (public)
The first code activates the authenticator; the ten recovery codes are returned once, and the session cookies are set. Audited as account.mfa_enabled and auth.login.
Request bodyapplication/json · SecondFactorLoginRequest
| Field | Type | Description |
|---|---|---|
challengerequired | string | |
code | string | Six digits from the authenticator app. |
recovery_code | string | One of the single-use recovery codes (verify only). |
Responses
200Signed in; keep the recovery codes.application/json · SecondFactorSession
| Field | Type | Description |
|---|---|---|
userrequired | User | |
idrequired | string | |
emailrequired | string | |
name | string | Set for users who signed up themselves. |
account_idrequired | string | |
rolerequired | string | One of owner, admin, member, viewer |
csrf_tokenrequired | string | |
expires_atrequired | string (date-time) | |
ended_sessions | integer | Sign-in only: how many other sessions end_other_sessions ended. |
recovery_codes | array of string | After a setup at sign-in: the ten recovery codes, shown once. |
recovery_codes_remaining | integer | After a code check: how many unused recovery codes are left. |
return_to | string | After an identity provider sign-in: where the portal continues. |
- 400The request is invalid;
detailsays why. - 401Not signed in, or the credential is invalid or expired.
- 409The resource's state does not allow this now.
- 429Rate limited or the email is locked out.
- 503No capacity or address is free, or a host did not answer; try again later.
- defaultAny other error, usually 500.
Example
curl -X POST 'https://cloud.ankra.app/v1/auth/login/mfa/enroll/confirm' \
-H 'Content-Type: application/json' \
-d '{
"challenge": "string"
}'