# Admin Authentication and RBAC Operations

Phase 8 provides a separate, server-verified Admin security boundary. Telegram user identity never grants Admin access, and request bodies, headers, or browser state can never supply trusted permissions.

## Security contract

| Control               | Implementation                                                                                 |
| --------------------- | ---------------------------------------------------------------------------------------------- |
| Password storage      | Native Node.js Argon2id, PHC-style encoded parameters, random 16-byte salt, server-side pepper |
| Argon2id baseline     | 19,456 KiB memory, 2 passes, parallelism 1, 32-byte output                                     |
| First factor          | Normalized email plus password; unknown and invalid accounts return the same response          |
| Second factor         | RFC 6238 TOTP, 30-second step, one-step clock tolerance, accepted-step replay prevention       |
| TOTP storage          | AES-256-GCM encrypted seed with admin-specific authenticated data and key version              |
| Recovery              | Ten single-use, peppered HMAC-SHA-256 recovery-code digests; plaintext shown once              |
| Session               | Random 32-byte opaque token; only SHA-256 token digest is stored in MySQL                      |
| Login challenge       | Separate `HttpOnly` cookie, five-minute lifetime, not authorized for Admin APIs                |
| Authenticated session | Separate `HttpOnly`, `Secure`, `SameSite=Strict` cookie with idle and absolute expiry          |
| Authorization         | Permissions loaded from server-side role mappings on every authenticated request               |
| CSRF                  | Session-bound signed double-submit token on unsafe authenticated Admin requests                |
| Abuse controls        | IP/global route limits, exponential account lockout, optional exact/CIDR Admin IP allowlist    |
| Evidence              | Login attempts, security events, session revocation, and immutable Admin audit logs            |

The password verifier bounds all decoded Argon2 parameters before deriving a key. This prevents a malformed database value from requesting unreasonable memory or CPU. The current baseline follows the [OWASP Password Storage Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html). TOTP behavior follows [RFC 6238](https://www.rfc-editor.org/rfc/rfc6238).

## Required runtime

Phase 8 requires Node.js `>=24.11.0 <25`. Password hashing uses the native asynchronous `node:crypto` Argon2id API, avoiding an additional native addon while keeping the event loop non-blocking.

## One-time Super Admin creation

Apply migrations and seeds first. Then set the two one-time bootstrap values in the deployment environment:

```dotenv
ADMIN_BOOTSTRAP_EMAIL=admin@example.com
ADMIN_BOOTSTRAP_PASSWORD=replace-with-a-unique-strong-password
ADMIN_BOOTSTRAP_TOTP_REQUIRED=true
```

Run:

```bash
npm run verify:environment
npm run admin:create
```

The command:

1. acquires a MySQL advisory lock;
2. refuses to continue if any Admin already exists;
3. creates one active `super_admin` with an Argon2id password;
4. encrypts and stores a new TOTP seed;
5. stores only recovery-code digests;
6. records the system security event;
7. prints the authenticator URI and recovery codes once.

Immediately enroll the URI in a trusted authenticator, store recovery codes offline, and remove `ADMIN_BOOTSTRAP_EMAIL` and `ADMIN_BOOTSTRAP_PASSWORD` from the environment. Re-running the command cannot create another first Admin. Later administrator lifecycle operations must go through audited, permission-protected Admin APIs in the Admin Panel phase.

## Login API sequence

### 1. Password challenge

```http
POST /api/v1/admin/auth/login
Content-Type: application/json

{"email":"admin@example.com","password":"your-password"}
```

A successful response is `202 Accepted`, sets the short-lived challenge cookie, and returns `requires_two_factor: true`. It does not create an authorized Admin identity.

### 2. Complete second factor

Use exactly one field:

```http
POST /api/v1/admin/auth/two-factor
Content-Type: application/json

{"code":"123456"}
```

or:

```json
{ "recovery_code": "ABCDE-FGHIJ" }
```

Success atomically consumes the accepted TOTP step or recovery code and promotes the same opaque challenge token into a full Admin session. A failed factor revokes that challenge.

### 3. Issue Admin-bound CSRF token

```http
GET /api/v1/admin/auth/csrf
```

The browser must copy the returned token into `X-CSRF-Token` for unsafe authenticated requests. The signed token is bound to the Admin session and does not validate for a Telegram user session.

### 4. Identity and logout

| Method and path                  | Purpose                                                            | Protection                                           |
| -------------------------------- | ------------------------------------------------------------------ | ---------------------------------------------------- |
| `GET /api/v1/admin/auth/me`      | Returns verified Admin profile and current server-side permissions | Admin session                                        |
| `POST /api/v1/admin/auth/logout` | Revokes the session and clears Admin cookies                       | Admin session, CSRF, sensitive rate limit, audit log |

## Permission use

Business routes must declare their exact permission:

```js
router.patch(
  '/users/:id',
  adminAuthentication.required,
  requirePermissions('users.update'),
  validateRequest({ body: updateUserSchema }),
  asyncHandler(controller.update),
);
```

Authentication routes are the only exception: a user must be able to log in, inspect their own permission set, and log out without a business permission. All Phase 9+ Admin business endpoints must use `adminAuthentication.required` plus one or more explicit permission checks.

## Lockout behavior

Failed credentials are counted only for a real active account, while the public response stays identical for unknown, disabled, locked, and incorrect credentials. From the fifth failure, lock duration starts at 30 seconds and doubles up to 15 minutes. Correct passwords still perform verification while locked so response behavior does not reveal account state.

## Optional Admin IP policy

`ADMIN_ALLOWED_IPS` accepts comma-separated exact IPv4/IPv6 addresses or CIDR ranges. An empty value disables the allowlist. Examples:

```dotenv
ADMIN_ALLOWED_IPS=203.0.113.10,10.20.0.0/16,2001:db8:1234::/48
```

Only use this when Admin operators have stable trusted egress addresses. A proxy/CDN deployment must configure `TRUST_PROXY` correctly so `request.ip` cannot be forged.

## Incident actions

- Revoke affected `admin_sessions` and disable or suspend the Admin account.
- Rotate the password after verifying operator identity.
- Replace TOTP and recovery codes if the second factor may be exposed.
- Rotate `ENCRYPTION_MASTER_KEY` through a planned key-version migration; do not overwrite the old key until all encrypted values are re-encrypted.
- Review `login_attempts`, `security_events`, and `admin_audit_logs` using the server-side request/session references.
- Never put passwords, raw session tokens, TOTP seeds, or recovery codes into logs, tickets, screenshots, or database notes.

## Verification

```bash
npm run test:unit
npm run test:integration
npm run test:security
npm run check
```

The Phase 8 test suite verifies the native Argon2id path, AES-GCM context binding, RFC TOTP vector, replay prevention, recovery digests, opaque challenge/session cookies, generic login errors, evidence commits, CSRF enforcement, RBAC trust, CIDR policy, and one-time bootstrap lock.
