> ## Documentation Index
> Fetch the complete documentation index at: https://docs.frostauth.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# Two-Factor Authentication

> Set up, enable, and disable TOTP two-factor authentication for Frost Auth console accounts.

# Two-Factor Authentication

Enrolment is **two steps on purpose**: setup stores the secret unconfirmed, and enable only trusts it once a generated code proves the authenticator holds it — so an abandoned setup cannot lock anyone out.

***

## Setup

```http theme={null}
POST /api/v1/auth/2fa/setup
```

Generates a TOTP secret and returns an `otpauth://` URI for scanning with an authenticator app. Requires an active `session_token`.

### Idempotent Behavior

Calling setup twice returns the **same** secret rather than generating a new one. This prevents a QR already scanned into a phone from silently stopping to match. Pass `?regenerate=true` as a query parameter to force a fresh secret.

### Responses

```text 200 Success theme={null}
{
  "success": true,
  "response": {
    "data": {
      "secret": "JBSWY3DPEHPK3PXP",
      "uri": "otpauth://totp/FrostAuth:alex@example.com?secret=JBSWY3DPEHPK3PXP&issuer=FrostAuth"
    }
  }
}
```

```text 401 Not Authenticated theme={null}
{
  "error": true,
  "response": {
    "code": 401,
    "message": "Not authenticated"
  }
}
```

```text 404 Not Found theme={null}
{
  "error": true,
  "response": {
    "code": 404,
    "message": "User doesn't exist"
  }
}
```

```text 409 Conflict theme={null}
{
  "error": true,
  "response": {
    "code": 409,
    "message": "Two-factor is already enabled — disable it first to re-enrol"
  }
}
```

```text 429 Rate Limited theme={null}
{
  "error": true,
  "response": {
    "code": 429,
    "message": "Too many requests"
  }
}
```

```text 500 Server Error theme={null}
{
  "error": true,
  "response": {
    "code": 500,
    "message": "Failed to start two-factor setup"
  }
}
```

### Errors

| HTTP | Message | Cause |
| - | - | - |
| `401` | `Not authenticated` | No valid session cookie. |
| `404` | `User doesn't exist` | Session's user ID not found. |
| `409` | `Two-factor is already enabled — disable it first to re-enrol` | 2FA is already active. |
| `429` | `Too many requests` | 10 per minute per account. |
| `500` | `Failed to start two-factor setup` | Unhandled exception. |

***

## Enable

```http theme={null}
POST /api/v1/auth/2fa/enable
Content-Type: application/json
```

Confirms the TOTP secret generated by `/api/v1/auth/2fa/setup` by requiring a valid code from the authenticator. Once confirmed, two-factor is enforced on all future logins.

### Request

```json theme={null}
{
  "code": "123456"
}
```

### Parameters

| Field | Required | Description |
| - | - | - |
| `code` | Yes | A six-digit TOTP code from the authenticator app that scanned the setup URI. |

### Responses

```text 200 Success theme={null}
{
  "success": true,
  "response": {
    "data": {
      "totpEnabled": true,
      "recoveryCodes": [
        "ABCD-1234-EFGH",
        "IJKL-5678-MNOP",
        "QRST-9012-UVWX",
        "YZAB-3456-CDEF",
        "GHIJ-7890-KLMN",
        "OPQR-1234-STUV",
        "WXYZ-5678-ABCD",
        "EFGH-9012-IJKL",
        "MNOP-3456-QRST",
        "UVWX-7890-YZAB"
      ],
      "otherSessionsRevoked": true
    }
  }
}
```

```text 400 Bad Request theme={null}
{
  "error": true,
  "response": {
    "code": 400,
    "message": "Start the setup first"
  }
}
```

```text 401 Unauthorized theme={null}
{
  "error": true,
  "response": {
    "code": 401,
    "message": "That code did not match — check your authenticator and try again"
  }
}
```

```text 404 Not Found theme={null}
{
  "error": true,
  "response": {
    "code": 404,
    "message": "User doesn't exist"
  }
}
```

```text 409 Conflict theme={null}
{
  "error": true,
  "response": {
    "code": 409,
    "message": "Two-factor is already enabled"
  }
}
```

```text 429 Rate Limited theme={null}
{
  "error": true,
  "response": {
    "code": 429,
    "message": "Too many requests"
  }
}
```

```text 500 Server Error theme={null}
{
  "error": true,
  "response": {
    "code": 500,
    "message": "Failed to enable two-factor"
  }
}
```

### Behavior

* Activates two-factor authentication on the account.
* Signs out every other active session (bumps `tokenVersion`), then reissues the caller's cookie.
* Returns **10 single-use recovery codes** — shown **exactly once**. The server stores only their hashes.
* The clock-drift message is returned when the code matches a step several windows away, indicating a time sync problem rather than a wrong secret.

### Errors

| HTTP | Message | Cause |
| - | - | - |
| `400` | `Start the setup first` | No pending TOTP secret — call `/2fa/setup` first. |
| `401` | `Not authenticated` | No valid session. |
| `401` | `That code did not match — check your authenticator and try again` | Code doesn't match the pending secret. |
| `401` | `That code is out of date — your device clock looks off. Try the code showing right now.` | Code is valid but several steps stale (clock issue). |
| `404` | `User doesn't exist` | Session's user ID not found. |
| `409` | `Two-factor is already enabled` | Already active. |
| `429` | `Too many requests` | 10 per minute per account. |
| `500` | `Failed to enable two-factor` | Unhandled exception. |

***

## Disable

```http theme={null}
POST /api/v1/auth/2fa/disable
Content-Type: application/json
```

Removes two-factor authentication from the operator account. Requires both the current password and a valid TOTP or recovery code — both factors to remove a factor, so a borrowed session cannot quietly strip the protection.

### Request

```json theme={null}
{
  "password": "••••••••",
  "code": "123456"
}
```

### Parameters

| Field | Required | Description |
| - | - | - |
| `password` | Yes | Current account password. |
| `code` | Yes | Valid TOTP code or one of the 12-character recovery codes. |

### Responses

```text 200 Success theme={null}
{
  "success": true,
  "response": {
    "data": {
      "totpEnabled": false,
      "otherSessionsRevoked": true
    }
  }
}
```

```text 401 Unauthorized theme={null}
{
  "error": true,
  "response": {
    "code": 401,
    "message": "Your password is incorrect"
  }
}
```

```text 404 Not Found theme={null}
{
  "error": true,
  "response": {
    "code": 404,
    "message": "User doesn't exist"
  }
}
```

```text 409 Conflict theme={null}
{
  "error": true,
  "response": {
    "code": 409,
    "message": "Two-factor is not enabled"
  }
}
```

```text 429 Rate Limited theme={null}
{
  "error": true,
  "response": {
    "code": 429,
    "message": "Too many requests"
  }
}
```

```text 500 Server Error theme={null}
{
  "error": true,
  "response": {
    "code": 500,
    "message": "Failed to disable two-factor"
  }
}
```

### Behavior

* Disables two-factor authentication.
* Clears the TOTP secret and all stored recovery code hashes.
* Signs out every other active session (bumps `tokenVersion`), then reissues the caller's cookie.

### Errors

| HTTP | Message | Cause |
| - | - | - |
| `401` | `Not authenticated` | No valid session. |
| `401` | `Your password is incorrect` | Password verification failed. |
| `401` | `Invalid two-factor code` | Code doesn't match. |
| `404` | `User doesn't exist` | Session's user ID not found. |
| `409` | `Two-factor is not enabled` | Nothing to disable. |
| `429` | `Too many requests` | 10 per minute per account. |
| `500` | `Failed to disable two-factor` | Unhandled exception. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.