> ## 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.

# Security console: IP allowlist, SAML, SCIM, and 2FA

> Walk through the Settings → Security page: the org-wide IP allowlist for dashboard access, SAML SSO enrollment, SCIM provisioning defaults, operator 2FA, and the audit-log trail each control leaves.

# Settings → Security

The **Security** page under **Settings** groups the org-level access controls that decide who can reach your Orbit dashboard and how they authenticate. This guide walks each card on the page — the org-wide IP allowlist, SAML SSO, SCIM provisioning, and operator two-factor authentication — and shows the API calls behind them.

## Scope map

Four controls live on this page, plus one pointer that lives elsewhere:

| Control           | What it gates                                   | API surface                                                                           |
| ----------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------- |
| IP allowlist      | Dashboard browse / SAML login for session users | `GET/POST /api/v1/settings/ip-allowlist`, `DELETE /api/v1/settings/ip-allowlist/{id}` |
| SAML SSO          | Identity-provider login for the whole org       | `GET/PATCH /api/v1/settings/saml`                                                     |
| SCIM provisioning | Directory sync of users, groups, roles          | `GET/PATCH /api/v1/settings/scim`                                                     |
| 2FA               | Second factor for your own operator login       | `POST /api/v1/settings/security/2fa/*`                                                |

The per-key API IP allowlist is a **different** control: it gates programmatic API authentication per API key, not dashboard access. Reach it under **Settings → API keys** — see the [per-API-key IP allowlist guide](/guides/api-key-ip-allowlist).

## Org IP allowlist

The org-wide allowlist restricts which source IPs can use the dashboard and the SAML login flow for your org. It applies to **session (dashboard) traffic only** — programmatic requests authenticated with an API key are governed by each key's own `allowed_ips` list, so you never have to allowlist every CI or SDK egress IP here.

Open **Settings → Security → IP Allowlist**. Each entry is an IPv4/IPv6 address or a CIDR range with a label. An empty list means unrestricted — every IP can open the dashboard.

<Warning>
  The page shows the source IP Orbit actually enforces against (the value resolved from the proxy chain), which can differ from what an external "what's my IP" site reports. Use the "Use my current IP" action on the page before adding your entry — allowlisting a wrong-looking-up IP is the classic self-lockout.
</Warning>

Add an entry over the API:

<RequestExample>
  ```bash theme={null}
  curl -X POST "https://api.orbit.devotel.io/api/v1/settings/ip-allowlist" \
    -H "Authorization: Bearer $SESSION_JWT" \
    -H "Content-Type: application/json" \
    -d '{ "ip": "198.51.100.0/24", "label": "Berlin office" }'
  ```
</RequestExample>

* The server refuses an add or removal that would lock out the IP you are connecting from (`422`), so a safe change can't strand you.
* A blocked session request returns `403 IP_NOT_ALLOWED` and is recorded in the audit log. On the SAML login flow the rejection surfaces as a generic unauthorized error rather than a distinct "not allowlisted" code, so an unauthenticated probe can't enumerate whether an allowlist exists — the same anti-enumeration invariant as the per-key guide's generic `INVALID_API_KEY` response.
* Support can still reach a locked-out org through a logged super-admin override — the override itself lands in your audit log.

Read or list entries with `GET /api/v1/settings/ip-allowlist`; remove one with `DELETE /api/v1/settings/ip-allowlist/{id}` (returns `204`).

## SAML SSO

SAML lets your org sign in through your identity provider (Okta, Entra ID, Google Workspace, Keycloak, …) instead of Orbit-managed credentials. Configuration is owner-only, and the endpoint contract is the same whether you enroll from the dashboard card or the API.

1. **Paste your IdP metadata URL** into the enrollment form — the page probes it with `POST /api/v1/settings/saml/test-connection`, which fetches the metadata, reports whether it's a valid SAML document, and hints the SSO URL and entity ID it found:

<RequestExample>
  ```bash theme={null}
  curl -X POST "https://api.orbit.devotel.io/api/v1/settings/saml/test-connection" \
    -H "Authorization: Bearer $SESSION_JWT" \
    -H "Content-Type: application/json" \
    -d '{ "metadataUrl": "https://idp.example-corp.com/federationmetadata/2007-06/federationmetadata.xml" }'
  ```
</RequestExample>

2. **Save the configuration** with `PATCH /api/v1/settings/saml`. First-time setup must carry the IdP's PEM signing certificate (`x509Cert`); attribute mapping tells Orbit which IdP attributes carry the user's email and display name:

<RequestExample>
  ```bash theme={null}
  curl -X PATCH "https://api.orbit.devotel.io/api/v1/settings/saml" \
    -H "Authorization: Bearer $SESSION_JWT" \
    -H "Content-Type: application/json" \
    -d '{
      "ssoUrl": "https://idp.example-corp.com/sso",
      "entityId": "urn:example-corp:orbit",
      "attrEmail": "mail",
      "attrName": "displayName",
      "enabled": true,
      "enforced": false
    }'
  ```
</RequestExample>

The response echoes the saved config with a `cert_fingerprint_sha256` — the raw certificate is never readable back.

* `enabled` turns SSO on as an available sign-in method. `enforced` makes SSO the **only** method — set it only after `enabled` works for your team in production, or you lock out everyone whose IdP flow fails.
* Once a certificate is stored, omit `x509Cert` on subsequent PATCHes — the stored one is retained.
* Metadata and SSO URLs are validated to block internal or private addresses (SSRF guard), so `test-connection` reports a clear failure instead of fetching something it shouldn't.

Your identity provider calls the login flow on the public `/auth/saml/*` paths listed under [Authentication](/authentication); the `/api/v1/settings/saml` endpoints above only manage the configuration.

## SCIM provisioning

SCIM keeps your Orbit team in sync with your IdP directory: provision users, map groups to roles, deprovision on removal. The Security page links into the SCIM setup surface; the full walkthrough (IdP-side configuration, health checks, sync errors) is in the [SCIM provisioning guide](/guides/scim-provisioning).

The role assignment defaults worth deciding before you enable sync:

| Setting                                             | Default                              | Meaning                                                                                         |
| --------------------------------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------- |
| `defaultRole` (start with `viewer`)                 | role for users with no group mapping | least-privilege floor — every flow still works from it                                          |
| `groupMapping` (e.g. `{ "Orbit Admins": "admin" }`) | IdP group name → Orbit role          | each sync re-applies the mapped role; groups are synthesized from this map                      |
| role ceiling (default `admin`)                      | upper bound provisioning may assign  | a sync targeting a role above the ceiling is refused — keep `owner` assignment in the dashboard |

Generate or rotate the SCIM bearer token from **Settings → SCIM** (`POST /api/v1/settings/scim/generate-token` — shown once) and paste it into your IdP. Reads and toggles are on `GET` / `PATCH /api/v1/settings/scim`.

## Operator 2FA

The two-factor control on this page protects **your Orbit operator login** (TOTP with backup codes). It is not the Verify product's end-user factors — TOTP, passkeys, and push factors for *your application's* end users are API-only by design; the dashboard 2FA card and the Verify factors are separate systems. See the [Verify overview](/verify/overview) for the end-user factor suite.

Disabling 2FA or regenerating backup codes is a step-up operation: prove a fresh credential (password or TOTP code) against `POST /api/v1/settings/security/2fa/challenge`, then replay the single-use challenge token the backend returns. This prevents a stolen session from silently stripping the second factor. Backup codes are shown once on regeneration — store them somewhere durable before closing the dialog.

## Audit-log tie

Every change to this surface lands in **Settings → Audit log** with the actor, the resource, and the operator's source IP:

| Change                                 | Audit action                                  |
| -------------------------------------- | --------------------------------------------- |
| IP allowlist entry added / removed     | `ip_allowlist.added` / `ip_allowlist.removed` |
| Session blocked by the org allowlist   | `auth.ip_blocked`                             |
| Support override past a locked-out org | `auth.ip_allowlist.super_admin_override`      |
| Per-key allowlist replaced             | `api_key.allowed_ips_updated`                 |
| Operator 2FA disabled                  | `security.2fa.disabled`                       |

Filter the audit log by these action names to reconstruct who changed the org's security posture and when. See the [audit log guide](/guides/audit-log) for filtering and export.

## See also

* [Settings API](/api-reference/settings) — endpoint table for SAML, SCIM, and every other Settings surface
* [Per-API-key IP allowlist](/guides/api-key-ip-allowlist) — programmatic API gating per key
* [SCIM provisioning](/guides/scim-provisioning) — full IdP sync walkthrough
* [Verify overview](/verify/overview) — end-user 2FA factors (separate from operator login)
* [Audit log](/guides/audit-log) — query and export the trail these controls write
