# Module: Azaion.Common.Requests.MfaRequests ## Purpose Request and response DTOs for the MFA enrollment / login surface introduced in cycle 2 by AZ-534 (Epic AZ-529, TOTP-based 2FA at credential login). All DTOs live in a single `MfaRequests.cs` file. ## Public Interface ### MfaEnrollRequest | Property | Type | Description | |----------|------|-------------| | `Password` | `string` | Re-auth required for enrollment (defends a stolen access token from silently flipping MFA on). | ### MfaEnrollResponse | Property | Type | Description | |----------|------|-------------| | `Secret` | `string` | 32-char base32 TOTP shared secret. Shown once. | | `OtpAuthUrl` | `string` | Standard `otpauth://` URL the authenticator app consumes. | | `QrPngBase64` | `string` | PNG encoding of `OtpAuthUrl` (base64). UI inlines as `data:image/png;base64,…`. | | `RecoveryCodes` | `string[]` | 10 single-use base32 codes (each ≥12 chars). Stored hashed in `users.mfa_recovery_codes`; the plaintext list is unrecoverable after this response. | ### MfaConfirmRequest | Property | Type | Description | |----------|------|-------------| | `Code` | `string` | TOTP code that validates the enrolled secret. On success `users.mfa_enabled` flips to true. | ### MfaDisableRequest | Property | Type | Description | |----------|------|-------------| | `Password` | `string` | Re-auth (same defence as enroll). | | `Code` | `string` | A valid TOTP code (recovery codes are NOT accepted here — disable should be deliberate). | ### MfaRequiredResponse Returned by `POST /login` when the user has MFA enabled instead of `LoginResponse`. | Property | Type | Description | |----------|------|-------------| | `MfaRequired` | `bool` | Always `true`. Lets dual-shape clients branch on a single field. | | `MfaToken` | `string` | Short-lived (5 min) ES256 JWT with audience `azaion-mfa-step2`. Carry to `/login/mfa`. | | `ExpiresIn` | `int` | Step-1 token TTL in seconds (300). | ### MfaLoginRequest | Property | Type | Description | |----------|------|-------------| | `MfaToken` | `string` | The step-1 token from `MfaRequiredResponse`. | | `Code` | `string` | A valid TOTP code OR a single-use recovery code. | ## Internal Logic None — pure data classes. ## Dependencies None. ## Consumers - `Program.cs` `/users/me/mfa/enroll` — `MfaEnrollRequest` → `MfaEnrollResponse`. - `Program.cs` `/users/me/mfa/confirm` — `MfaConfirmRequest`. - `Program.cs` `/users/me/mfa/disable` — `MfaDisableRequest`. - `Program.cs` `/login` — returns `MfaRequiredResponse` when `user.MfaEnabled`. - `Program.cs` `/login/mfa` — `MfaLoginRequest` → `LoginResponse`. - `MfaService` — consumes every request type and produces the responses. ## Data Models None directly. ## Configuration None. ## External Integrations None — but `MfaToken` validation depends on `IJwtSigningKeyProvider` (ES256 keys) and `JwtConfig.Issuer`. ## Security - `Password` fields carry plaintext credentials; HTTPS is mandatory in Production (AZ-538 HSTS / HTTPS-redirect). - `Secret` and `RecoveryCodes` are returned ONCE in `MfaEnrollResponse` — the client must show them immediately and never send them back. - `MfaToken` is narrowly-scoped (audience `azaion-mfa-step2`) so it cannot be used against any non-MFA endpoint even if leaked. ## Tests - `e2e/Azaion.E2E/Tests/MfaEnrollmentTests.cs` — AC-1 (enroll shape), AC-2 (confirm), AC-5 (disable), AC-6 (encrypted at rest). - `e2e/Azaion.E2E/Tests/MfaLoginTests.cs` — AC-3 (two-step + AMR claim), AC-4 (recovery code single-use).