> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Account security model

> The hardening layer behind the dashboard — TOTP 2FA, step-up re-authentication challenges, backup codes, an org-wide per-role 2FA policy, and the password and audit surfaces that complete it.

# Account security model

Every Orbit organization controls its own security posture: two-factor
enrollment, step-up re-authentication, backup codes, a per-role 2FA
requirement, and the audit trail that records each transition. Orbit exposes
these as tenant-owned controls — none of them are platform mandates, and the
defaults are open. This page explains how the pieces fit; the mechanics of
each endpoint are in
[Settings endpoints](/api-reference/endpoints/settings).

## Layers of the model

Three layers separate concern.

**Dashboard sign-in.** Email and password (or SAML, where enabled)
authenticate the person. The credential vocabulary — session tokens vs API
keys vs the SCIM token — and how each request's class resolves is owned by
[Authentication and session model](/concepts/authentication-model).

**API-key access.** Server-to-server integrations use `dv_`-prefixed keys,
rotated in place with grace windows. That lifecycle lives in
[API key lifecycle and rotation](/concepts/api-key-rotation-lifecycle).

**The org security posture.** This page. Everything layered on top of
sign-in for the people in your organization: TOTP 2FA, step-up challenges
that gate sensitive mutations, backup codes, a per-role require-2FA policy,
password rotation signals, and the audit trail every transition writes.

## TOTP 2FA

Each member enrolls a time-based one-time password (TOTP) authenticator in
the dashboard under **Settings → Security**. The authenticator secret lives
with the auth provider; Orbit's API wraps enrollment status, disable, and
backup codes behind step-up-gated routes.

Two facts matter about how disabling works:

1. Disabling is a **full removal**, not a flag flip. When a disable succeeds,
   Orbit removes the enrolled authenticator factor and the backup-code factor
   at the provider — a leftover factor can otherwise be re-presented as a
   second factor the user can never satisfy.
2. **Backup codes are single-use recovery codes.** The status endpoint
   (`GET /api/v1/settings/security/2fa/backup-codes/status`) returns only
   whether codes exist and how many remain — never the codes themselves.
   Regenerating a set (`POST /api/v1/settings/security/2fa/backup-codes/regenerate`)
   returns the new codes exactly once and invalidates the prior set the same
   instant, so an attacker inside a stolen session cannot quietly take over
   the recovery path.

## Step-up challenges

2FA disable and backup-code regeneration are gated by a step-up challenge,
not by session recency alone. A live browser session is proof of possession,
not proof of identity — so before either mutation runs, Orbit asks for a
**fresh credential**: the current password OR a current TOTP code.

The sequence:

```mermaid theme={null}
sequenceDiagram
    participant Client as Dashboard client
    participant API as Orbit API
    participant IdP as Auth provider

    Client->>API: POST /settings/security/2fa/challenge<br/>(op, password or totp_code)
    API->>IdP: Verify the fresh credential
    IdP-->>API: Verified
    API-->>Client: { token, expires_at } (single-use, 5 minutes)
    Client->>API: POST /settings/security/2fa/disable<br/>X-Reauth-Challenge: token
    Note over API: token consumed (GETDEL) — op must match
    API-->>Client: 200 { ok: true, disabled_at }
```

The minted token is single-use, expires five minutes after mint, and binds
to exactly one operation (`disable` or `regenerate_backup_codes`). Tokens
live in a dedicated step-up namespace; legacy re-auth tokens minted on
session-recency alone can never satisfy a 2FA-mutating endpoint. A missing,
expired, or wrong-op token returns `401 REAUTH_REQUIRED`.

## Org-wide require-2FA

An owner can require a second factor for any subset of roles through
`GET`/`POST /api/v1/settings/security/require-2fa`. The policy is a bitmap
over the five roles — `owner`, `admin`, `developer`, `viewer`, `billing` —
and each role flips independently. Requiring a role blocks its members from
the dashboard until they enroll a second factor; the `/me` response carries
the enrollment redirect so the blocked member knows where to land. Unset
roles stay not-required, and the read returns the full map so you can
round-trip.

Two guards keep the policy safe to roll out:

* **Self-lockout guard.** An owner cannot enable `owner=true` while their
  own account has no enrolled second factor — the write is rejected with a
  `422` explaining why.
* **Role-at-a-time merge.** The write merges a partial bitmap, so the
  dashboard can toggle one role without re-sending the whole map.

A staged rollout works well: announce to the team, enforce role by role
(usually admins and developers first, viewers last), then audit the trail.
The policy is a default-off, tenant-owned control — Orbit does not mandate
it for any organization.

## Password policy and session audit

Password rotation is recorded the same way 2FA transitions are. The
dashboard's change-password card rotates the credential and then records the
rotation server-side via
`POST /api/v1/settings/security/password-changed`, which fans out a
security-critical "your password was changed" alert — a bell-row in the
Notification Center, email, and mobile push — with the requester IP and
user-agent attached. A genuine rotation therefore never passes silently.

The dashboard **Settings → Security** page is the single surface where all
of this is visible: your own 2FA enrollment and backup-code status, the
org's per-role require-2FA toggles (owner-only write), session management,
and the SSO/SCIM panels — which keep their own pages
([SAML/SCIM federation](/concepts/identity-federation-saml-scim)).

## Audit trail

Every security transition — a granted or failed step-up challenge, a
disable, a backup-code regeneration, a require-2FA bitmap change, a password
rotation — writes an audit record with the requester IP and user-agent. The
out-of-band alerts (bell, email, push) are the user-visible half; the audit
trail is the tamper-evident half. For a SOC 2-style review, the events group
like this:

| Event                      | What it records                              |
| -------------------------- | -------------------------------------------- |
| Step-up granted / failed   | Operation, credential method, IP, user-agent |
| 2FA disabled               | IP, user-agent, provider teardown outcome    |
| Backup codes regenerated   | Count of new codes, IP, user-agent           |
| Require-2FA bitmap changed | Per-role before→after diff, actor            |
| Password changed           | Source surface, IP, user-agent               |

Each of those rows answers the two questions an auditor always asks: **who
changed it**, and **from where**.

## Related pages

* [Authentication and session model](/concepts/authentication-model) — sign-in flows and credential classes.
* [API key lifecycle and rotation](/concepts/api-key-rotation-lifecycle) — the `dv_` key lifecycle.
* [Identity federation (SAML/SCIM)](/concepts/identity-federation-saml-scim) — SSO enrollment and provisioning.
* [Settings endpoints](/api-reference/endpoints/settings) — route-by-route request/response reference.
* [Roles, teams, and permissions](/concepts/roles-teams-permissions) — the role vocabulary the bitmap references.
