101 lines
4.4 KiB
Markdown
101 lines
4.4 KiB
Markdown
# Console TOTP Specification
|
|
|
|
## Purpose
|
|
|
|
TOTP enrollment, verification, and disable flows for platform console users. Covers the three-endpoint lifecycle: setup-start, setup-verify, and disable.
|
|
|
|
## Requirements
|
|
|
|
### Requirement: TOTP Enrollment Start
|
|
|
|
The system MUST provide `POST /api/auth/2fa/setup-start` accessible only to authenticated users holding a `platform_*` role.
|
|
|
|
When the user has no active enrollment (`two_factor_enabled = false`), the endpoint MUST return a freshly generated TOTP secret, its QR code as a base64-encoded PNG, and the `otpauth://` provisioning URI.
|
|
|
|
When the user already has `two_factor_enabled = true`, the endpoint MUST return HTTP 409.
|
|
|
|
#### Scenario: No prior enrollment → returns enrollment data
|
|
|
|
- GIVEN an authenticated user with `two_factor_enabled = false`
|
|
- WHEN `POST /api/auth/2fa/setup-start` is called
|
|
- THEN the response is HTTP 200 with `{ secret_base32, qr_code_png_base64, otpauth_uri }`
|
|
- AND no DB write is made at this stage (secret is pending verification)
|
|
|
|
#### Scenario: Already enrolled → 409
|
|
|
|
- GIVEN an authenticated user with `two_factor_enabled = true`
|
|
- WHEN `POST /api/auth/2fa/setup-start` is called
|
|
- THEN the response is HTTP 409 with error code `TwoFactorAlreadyEnabled`
|
|
|
|
#### Scenario: Unauthenticated caller → 401
|
|
|
|
- GIVEN a request with no valid session token
|
|
- WHEN `POST /api/auth/2fa/setup-start` is called
|
|
- THEN the response is HTTP 401
|
|
|
|
#### Scenario: Non-platform role → 403
|
|
|
|
- GIVEN an authenticated user without a `platform_*` role
|
|
- WHEN `POST /api/auth/2fa/setup-start` is called
|
|
- THEN the response is HTTP 403
|
|
|
|
---
|
|
|
|
### Requirement: TOTP Enrollment Verify
|
|
|
|
The system MUST provide `POST /api/auth/2fa/setup-verify` for confirming a pending enrollment.
|
|
|
|
When the submitted 6-digit code is valid against the in-progress secret, the endpoint MUST set `two_factor_enabled = true`, encrypt and store the secret, and return exactly 8 one-time recovery codes (plaintext, shown once).
|
|
|
|
When the code is invalid, the endpoint MUST return HTTP 401 and MUST NOT write anything to the database.
|
|
|
|
#### Scenario: Valid code → enrollment complete, recovery codes returned
|
|
|
|
- GIVEN a user who completed setup-start and has a pending secret
|
|
- WHEN `POST /api/auth/2fa/setup-verify` is called with a currently-valid 6-digit code
|
|
- THEN the response is HTTP 200 with `{ recovery_codes: [8 strings] }`
|
|
- AND `users.two_factor_enabled` is set to `true`
|
|
- AND `users.two_factor_secret_encrypted` holds the AES-256-GCM ciphertext of the secret
|
|
- AND 8 hashed recovery code rows are inserted into `two_factor_recovery_codes`
|
|
|
|
#### Scenario: Invalid code → 401, no DB writes
|
|
|
|
- GIVEN a user who completed setup-start
|
|
- WHEN `POST /api/auth/2fa/setup-verify` is called with a wrong 6-digit code
|
|
- THEN the response is HTTP 401 with error code `TwoFactorFailed`
|
|
- AND `users.two_factor_enabled` remains `false`
|
|
- AND no rows are written to `two_factor_recovery_codes`
|
|
|
|
---
|
|
|
|
### Requirement: TOTP Disable
|
|
|
|
The system MUST provide `POST /api/auth/2fa/disable` for an authenticated user to remove their TOTP enrollment.
|
|
|
|
The request MUST supply both the current password and a current valid TOTP code. Both MUST be verified server-side. If either fails, the endpoint returns HTTP 401 and makes no DB changes.
|
|
|
|
On success, the system MUST clear `two_factor_enabled`, `two_factor_secret_encrypted`, `two_factor_secret_nonce`, `last_totp_step_used`, `totp_locked_until`, and all rows in `two_factor_recovery_codes` for that user.
|
|
|
|
#### Scenario: Valid password + valid TOTP → disabled
|
|
|
|
- GIVEN an authenticated user with `two_factor_enabled = true`
|
|
- WHEN `POST /api/auth/2fa/disable` is called with correct password and valid TOTP code
|
|
- THEN the response is HTTP 200
|
|
- AND `users.two_factor_enabled` is `false`
|
|
- AND `users.two_factor_secret_encrypted` is NULL
|
|
- AND all rows in `two_factor_recovery_codes` for that user are deleted
|
|
|
|
#### Scenario: Valid password, invalid TOTP → 401, no change
|
|
|
|
- GIVEN an authenticated user with `two_factor_enabled = true`
|
|
- WHEN `POST /api/auth/2fa/disable` is called with correct password but wrong TOTP code
|
|
- THEN the response is HTTP 401
|
|
- AND `users.two_factor_enabled` remains `true`
|
|
|
|
#### Scenario: Invalid password → 401, no change
|
|
|
|
- GIVEN an authenticated user with `two_factor_enabled = true`
|
|
- WHEN `POST /api/auth/2fa/disable` is called with an incorrect password
|
|
- THEN the response is HTTP 401
|
|
- AND `users.two_factor_enabled` remains `true`
|