# Security PIN Flow This document explains how to configure and use a per-user 6-digit Security PIN for sensitive actions. All API payloads follow **JSON:API**. ## 1) Setup Security PIN (first time) Endpoint: `POST /api/v1/auth/pin/setup` Auth: - Requires logged-in session (`AUTH` cookie / access token cookie) Request: ```json { "data": { "type": "auth_pin_setup", "attributes": { "pin": "123456", "confirm_pin": "123456" } } } ``` Success (`200`): ```json { "data": { "type": "auth_pin_setup", "attributes": { "configured": true } } } ``` If PIN already exists (`409`): ```json { "errors": [ { "status": "409", "title": "PIN already configured", "detail": "security PIN is already set" } ] } ``` ## 2) Verify PIN before sensitive action Endpoint: `POST /api/v1/auth/pin/verify` Auth: - Requires logged-in session Request: ```json { "data": { "type": "auth_pin_verify", "attributes": { "pin": "123456", "action": "a546de2f-f1e6-4c2c-ab03-9b4bf39f4d74" } } } ``` Success (`200`): ```json { "data": { "type": "auth_pin_verify", "attributes": { "verified": true, "action_token": "", "action_token_ttl_ms": 300000 } } } ``` Use the returned token on the sensitive API call: - Header: `X-PIN-Verification-Token: ` Example sensitive request: ```http DELETE /api/v1/users/delete/{uuid} X-PIN-Verification-Token: Cookie: wucher_at=... ``` Important: - Token is action-bound (`a546de2f-f1e6-4c2c-ab03-9b4bf39f4d74` only for that action). - Token is one-time use. - Token expires by `AUTH_SECURITY_PIN_ACTION_TOKEN_TTL`. - Maximum failed attempts follow `AUTH_SECURITY_PIN_MAX_ATTEMPTS` (default `5`). - When max attempts are reached, API returns `429` and PIN becomes blocked. - Block is permanent until PIN reset succeeds (`AUTH_SECURITY_PIN_LOCK_DURATION=0` means no auto-unlock timeout). ## 3) Change Security PIN Endpoint: `POST /api/v1/auth/pin/change` Auth: - Requires logged-in session Request: ```json { "data": { "type": "auth_pin_change", "attributes": { "current_pin": "123456", "new_pin": "654321", "confirm_pin": "654321" } } } ``` Success (`200`): ```json { "data": { "type": "auth_pin_change", "attributes": { "updated": true } } } ``` ## 4) Reset Security PIN Flow ### 4.1 Request reset link (blocked PIN button) Endpoint: `POST /api/v1/auth/pin/request-reset` Auth: - Requires logged-in session Request: ```json { "data": { "type": "auth_pin_request_reset", "attributes": { "password": "" } } } ``` Success (`200`): ```json { "data": { "type": "auth_pin_request_reset", "attributes": { "message": "If allowed, a security PIN reset link has been sent to your email." } } } ``` If PIN not blocked (`409`): ```json { "errors": [ { "status": "409", "title": "PIN not blocked", "detail": "security PIN is not blocked" } ] } ``` Invalid password (`401`): ```json { "errors": [ { "status": "401", "title": "Unauthorized", "detail": "invalid password" } ] } ``` Rate limited (`429`): ```json { "errors": [ { "status": "429", "title": "Too many requests", "detail": "please wait before requesting another reset link" } ] } ``` ### 4.2 Request reset link (public forgot endpoint) Endpoint: `POST /api/v1/auth/pin/forgot` Request: ```json { "data": { "type": "auth_forgot_pin", "attributes": { "email": "user@example.com" } } } ``` Response is always generic (`200`): ```json { "data": { "type": "auth_forgot_pin", "attributes": { "message": "If the account exists, a security PIN reset link has been sent." } } } ``` ### 4.3 Reset PIN with password or Microsoft re-auth Endpoint: `POST /api/v1/auth/pin/reset` Notes: - `method` is optional. Default is `password`. - For `method=password`, `token` is required and must be the raw single-use reset token from the email reset link, then send `password`. - For `method=microsoft`, `token` is not required. Send `reauth_token` from Microsoft re-auth directly. Request with password re-auth: ```json { "data": { "type": "auth_reset_pin", "attributes": { "token": "", "password": "", "new_pin": "123456", "confirm_pin": "123456" } } } ``` Request with Microsoft re-auth: ```json { "data": { "type": "auth_reset_pin", "attributes": { "method": "microsoft", "reauth_token": "", "new_pin": "123456", "confirm_pin": "123456" } } } ``` Success (`200`): ```json { "data": { "type": "auth_reset_pin", "attributes": { "message": "Security PIN has been reset successfully.", "reset_success": true, "redirect_url": "https://app.example.com" } } } ``` Invalid/expired token (`400`): ```json { "errors": [ { "status": "400", "title": "Invalid reset link", "detail": "This reset link is invalid or expired." } ] } ``` Invalid password (`401`): ```json { "errors": [ { "status": "401", "title": "Unauthorized", "detail": "invalid password" } ] } ``` ## 5) Sensitive endpoints currently protected by PIN token The following routes require `X-PIN-Verification-Token`: - `POST /api/v1/auth/invite` (`action=2a59e8b4-5a72-4be3-b8fc-4f49941181c3`) - `POST /api/v1/users/create` (`action=2a59e8b4-5a72-4be3-b8fc-4f49941181c3`) - `PATCH /api/v1/users/update/:uuid` (`action=428fdf48-3f99-46ea-bc47-8fb2e312be08`) - `DELETE /api/v1/users/delete/:uuid` (`action=a546de2f-f1e6-4c2c-ab03-9b4bf39f4d74`) - `POST /api/v1/roles/create` (`action=7fb1d693-8692-4e8f-be86-fd0e29e96bc3`) - `PATCH /api/v1/roles/update/:uuid` (`action=18ca5bbf-ce9d-4e24-8ace-793de56c1de0`) - `DELETE /api/v1/roles/delete/:uuid` (`action=b87fca76-783d-45e7-9022-b2d9c95f6da7`) - `POST /api/v1/roles/:uuid/permissions` (`action=48e523f8-d86f-4b24-b0e3-e213e4d33db4`) - `DELETE /api/v1/roles/:uuid/permissions/:permission_uuid` (`action=f2bf59f8-0783-40fc-94ce-a79e40f84c44`) - `POST /api/v1/roles/:uuid/permissions/assign-bulk` (`action=48e523f8-d86f-4b24-b0e3-e213e4d33db4`) - `POST /api/v1/roles/:uuid/permissions/remove-bulk` (`action=f2bf59f8-0783-40fc-94ce-a79e40f84c44`) ## 6) Security controls - PIN must be exactly 6 digits. - PIN is encrypted at rest (`users.security_pin_encrypted`), not stored in plaintext. - Forgot/reset tokens are random, hashed, single-use, and expiring. - Verify attempts are rate-limited per user with lockout (`AUTH_SECURITY_PIN_MAX_ATTEMPTS`, `AUTH_SECURITY_PIN_LOCK_DURATION`). - Forgot endpoint response is generic to reduce account enumeration risk. ## 7) Environment variables - `AUTH_SECURITY_PIN_RESET_URL` - `AUTH_SECURITY_PIN_RESET_TTL` - `AUTH_FORGOT_SECURITY_PIN_EMAIL_COOLDOWN` - `AUTH_FORGOT_SECURITY_PIN_MIN_DURATION` - `AUTH_SECURITY_PIN_MAX_ATTEMPTS` - `AUTH_SECURITY_PIN_LOCK_DURATION` - `AUTH_SECURITY_PIN_ACTION_TOKEN_TTL`