# Staff security

| Page | Path | What it is for |
|---|---|---|
| **Security** | `/admin/security` | Captcha, SSO, IP rules, passwords |
| **Staff & roles** | `/admin/staff` | Staff and roles |
| **Fraud** | `/admin/fraud` | Fraud checks |
| **GDPR** | `/admin/gdpr` | GDPR exports and erasure |
| **Login history** | `/admin/login-history` | Login history |
| **Sessions** | `/admin/sessions` | Your active sessions |

## Roles and permissions

Staff accounts get roles in Admin → Security → **Staff**; each role is a set of permissions such as `tickets.manage` or `gateways.manage`. Every admin API call checks the permission it needs.

## Two-factor authentication

Every account can enable TOTP two-factor authentication (an authenticator app) with one-time backup codes, and passkeys. When **Require 2FA for staff** is on (Admin → Security → **Staff**, stored as `security.staff_require_2fa`), staff without 2FA keep client access only until they enrol.

- **TOTP**: scan the QR code, confirm a code, store the backup codes. A backup code works once in place of a TOTP code.
- **Passkeys** (WebAuthn): register a passkey on the Sessions page (`/sessions`); it can then be used to sign in without a password and for step-up. If a passkey is lost, sign in with your password plus TOTP or a backup code. Staff can revoke a user's passkeys.
- **Duo**: Duo Security's Universal Prompt can be required after the password, for staff only or for everyone. Configure it in Admin → Security → **Security** with the Duo client id, client secret and API host:

| Field | Type | Required | Description |
|---|---|---|---|
| `enabled` | `bool` | yes |  |
| `client_id` | `String` | yes |  |
| `api_host` | `String` | yes |  |
| `scope` | `String` | yes | `staff` or `all`. |
| `secret_enc` | `String` | no | Sealed client secret (base64), never returned. |

The client secret is stored encrypted and never returned. Duo redirects back to `{public URL}/auth/duo/callback`, so `PELTY_PUBLIC_URL` must be right.

## Security policy

Admin → Security → **Security** sets the policy below. Empty values mean "off".

| Field | Type | Required | Description |
|---|---|---|---|
| `password_min_length` | `usize` | yes |  |
| `require_upper` | `bool` | yes |  |
| `require_lower` | `bool` | yes |  |
| `require_digit` | `bool` | yes |  |
| `require_symbol` | `bool` | yes |  |
| `session_idle_minutes` | `Option<i64>` | no |  |
| `session_absolute_hours` | `Option<i64>` | no |  |
| `login_rate_per_minute` | `Option<u32>` | no |  |
| `admin_reauth_minutes` | `Option<i64>` | no | Staff must re-enter their password for sensitive changes when the last full authentication is older than this (step-up / sudo mode). |
| `password_max_age_days` | `Option<i64>` | no | Staff passwords expire after this many days (must be changed). |
| `login_alerts` | `bool` | yes | Audit + notify on staff sign-ins from a new IP address. |

## Step-up (sudo mode)

With `admin_reauth_minutes` set, staff must re-enter their password (or use a passkey) before a change guarded by one of these permissions when their last full sign-in is older than that:

- `settings.manage`
- `plugins.manage`
- `gateways.manage`
- `users.manage`
- `api_keys.manage`
- `servers.manage`

Reading is never blocked; only writes (`POST`, `PUT`, `PATCH`, `DELETE`) ask for it. API keys are not affected.

## IP rules

Admin → Security → **Security** keeps two kinds of IP rule (single addresses or CIDR ranges, optionally expiring):

- `ban`: refuse every request from the network.
- `admin_allow`: once at least one exists, the admin area (and admin-only addon routes) only answers from allowed networks. PeltyCMS refuses to add a rule that would lock out your own address.

Behind a reverse proxy, make sure the real client address reaches PeltyCMS, or every request looks like it comes from the proxy.

## Sessions, sign-in history and audit

**Sessions** lists and revokes active sessions, **Login history** shows sign-ins, and Admin → System & settings → **Audit** records every staff change. With `login_alerts` on, a staff sign-in from a new IP address is audited and notified. Staff can sign in as a client (impersonation) for support; it is audited and has a way back.

## Single sign-on

Staff and clients can sign in with OAuth/OIDC providers (addons of kind `sso`) and SAML (Admin → Addons & integrations → **SAML**). PeltyCMS can also act as an OAuth provider for other apps (**OAuth clients**).

