POST /api/v1/auth/login answers such an account with a challenge_token instead of a token. This endpoint exchanges that challenge, plus either a current authenticator (TOTP) code or one recovery code, for the same Sanctum personal access token a plain login returns.POST /api/v1/auth/login.App\Http\Controllers\Api\V1\Auth\AuthController::twoFactorChallenge()api.v1.auth.two-factor-challengechallenge_token in the body is the only credential, and it is not a bearer token: sent as Authorization: Bearer it answers 401 Invalid token.throttle:two-factor-api — 5 requests per minute per challenge and 10 requests per minute per IP address. This budget is separate from the throttle:auth budget shared by register, login, password/forgot and password/reset.POST/api/v1/auth/two-factor-challengeapplication/json{
"challenge_token": "Zx9kQ2mV7bT4nW1cR8yH5pL0dJ3sF6uAeG2tB9vKq4XoM7iNzC5wY1rD8hE3jP6a",
"code": "123456"
}| Field | Type | Required | Validation | Description |
|---|---|---|---|---|
challenge_token | string | Yes | required|string|max:255 | The data.challenge_token returned by POST /api/v1/auth/login |
code | string | One of code / recovery_code | nullable|string|max:16|required_without:recovery_code|prohibits:recovery_code | Current TOTP code from the authenticator app |
recovery_code | string | One of code / recovery_code | nullable|string|max:64|required_without:code | One unused recovery code |
code and recovery_code. Validated by App\Http\Requests\Api\V1\Auth\TwoFactorChallengeRequest.200 OK{
"success": true,
"status": "success",
"message": "OK",
"data": {
"token": "1|raid_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"token_type": "Bearer",
"expires_in": 7775999
}
}POST /api/v1/auth/login for an account without two-factor authentication, and is documented there. The token is named with the device_name sent to the login request that issued the challenge (or the label derived from the User-Agent), not with anything sent here.422 Unprocessable Entity — wrong codecode or recovery_code, matching the field sent. A wrong code counts towards the 5 failures that invalidate the challenge.{
"success": false,
"status": "error",
"message": "Validation failed",
"errors": {
"code": ["The provided two factor authentication code was invalid."]
}
}recovery_code the message is The provided two factor recovery code was invalid.422 Unprocessable Entity — unknown, expired or used challenge{
"success": false,
"status": "error",
"message": "Validation failed",
"errors": {
"challenge_token": ["The two-factor challenge is invalid or has expired."]
}
}422 Unprocessable Entity — validationchallenge_token, neither or both of code and recovery_code. For example, with both sent:{
"success": false,
"status": "error",
"message": "Validation failed",
"errors": {
"code": ["The code field prohibits recovery code from being present."]
}
}429 Too Many Requests{
"success": false,
"status": "error",
"message": "Too many requests"
}Retry-After header present. The throttle:two-factor-api limiter allows 5 requests per minute per challenge_token and 10 per minute per IP address, counting successful and failed attempts alike.401 Invalid credentials at login, and a challenge answers the same 422 whatever happened to it.docs/api/auth/POST_two_factor_challenge.mdcurl --location 'https://test.diveraid.com/api/v1/auth/two-factor-challenge' \
--header 'Content-Type: application/json' \
--data '{}'{
"status": "success",
"message": "string",
"data": {
"token": "string",
"token_type": "Bearer",
"expires_in": 0
}
}