Enterprise SSO enrollment
Users self-enroll with their corporate identity; offboarding is revocation.
Set up SSO
Everything happens in the Anyray console (:3000) under Users → Identity (admin key required). The Identity card holds three tiles: Single sign-on (SSO), Directory sync (SCIM), and SSO configuration.
The Single sign-on (SSO) tile shows Connected only once your IdP connection is actually active in WorkOS. Opening the Admin Portal starts the setup but doesn't complete it — until you finish the provider's steps, the tile stays Off and the standard invite / MDM enrollment paths remain available.
SSO configuration
| Setting | What it does |
|---|---|
| Allowed email domains | Only IdP-verified emails in these domains may enroll (empty = any verified email). |
| Group → team mapping | Map an IdP group to an Anyray team (spend attribution); first match wins, else the default team. At login the user picks from every mapped team plus the default. |
| Group → role mapping | Map an IdP group to a console role; first match wins, else viewer. |
| Default team | Optional. The team a user lands on when no mapped group matches. |
| Session length (hours) | How long a minted ark_ key lasts. Default 30 days; minimum 1 hour, maximum 365 days (8760). A key in active use renews — only idle keys lapse. Console operator sign-ins derive from this value but stay capped at 30 days regardless. |
| Re-enrollment window (hours) | Optional. How long a device can go unused before the person must sign in again through the IdP in the browser. A machine in regular use renews its enrollment certificate silently and never reaches it. Empty = 30 days; only takes effect when set longer than the session length. Needs gateway v1.10.105+ (older gateways fall back to the session length). |
Roles are console permissions only. owner is a fifth, break-glass role held solely by the admin key (ANYRAY_ADMIN_TOKEN) — it passes every check and is never granted through SSO.
Console roles — what each can do
A user's console role sets what they can do, from read-only dashboards up to managing SSO and keys. auditor and operator each build on viewer; security_admin includes both.
| Role | Can do |
|---|---|
viewer | Read-only — dashboards, spend, trace metadata. Fallback for any unmapped user. |
auditor | viewer, plus read the audit log. |
operator | viewer, plus change routing, model aliases, user caps, and run the playground. No audit-log access. |
security_admin | operator and auditor, plus manage SSO/IdP config, provider & client keys, content settings, the optimizer, and updates. |
Assigning roles. Under Users → Identity → SSO configuration, set Group → role mapping — one group=role per line. First matching group wins; unmapped falls back to viewer.
platform-admins=security_admin
devops=operator
On first connect, the account owner gets security_admin so someone can always administer the console, then maps the rest of your groups. To assign by exact person instead of group, use email → role assignment in the admin API.
Group → role and Group → team resolve from the groups your IdP asserts at sign-in. If your provider only sends groups over SCIM directory sync (not in the SSO assertion), map by email instead, or add the group attribute to your WorkOS connection. Changes take effect on each user's next sign-in.
What configuring SSO enforces
Once your IdP connection is active, standalone enrollment stops working and identity is server-verified — enrollment fails closed if the policy can't be read, and client headers can't claim SSO or replace the bound identity. In detail, the Billing backend reads the tenant's SSO config before minting or redeeming enrollment credentials, and:
- Minting a regular
enl_…link orenp_…provisioning token returns409. Rotating an existing provisioning token also returns409; listing and revocation remain available. - Previously issued
enl_…links and both email- and machine-modeenp_…tokens returnsso_required; they never sign a standalone certificate. - Certificates issued before you connected the IdP stop rolling forward, so they lapse at their own expiry and that developer signs in through the IdP instead. Nothing is cut off at the moment you connect — an existing certificate stays valid to its original expiry — but enforcement does reach everyone who enrolled earlier, rather than only new developers.
- MDM distributes the SSO handoff with no embedded credential. Each developer authenticates through the IdP on first use. A genuinely non-human workload uses a separately managed service key.
- SSO status and identity come from durable server-side records and signed bindings.
No policy response contains a raw token, prompt, or response.
Enroll a developer
Once your IdP connection is active, click Add user on the Users page, choose SSO enrollment, and copy the command under Share this command. The link is organization-wide — safe to share in Slack, not tied to one person.
It opens the IdP sign-in, mints a personal ark_… key bound to the verified identity, and configures every supported tool it finds. On first login, Anyray creates the membership and applies the configured role, team, session length, and default budget.
- bash
- powershell
curl -fsSL https://app.anyray.ai/connect.sh | sh -s -- --sso https://app.anyray.ai/sso/<tenantId> --yes
& ([scriptblock]::Create((irm https://app.anyray.ai/connect.ps1))) "--sso" "https://app.anyray.ai/sso/<tenantId>" --yes
The installer downloads a standalone binary to a persistent path (~/.anyray/bin), so the retrieval MCP and hooks it registers keep working. Prefer npm? npx anyray-connect@latest --sso https://app.anyray.ai/sso/<tenantId> --yes works too, but installs to a temporary path — some retrieval-based optimizations stay off until you install durably.
Connect prints a short code and opens the browser; the user signs in. The code and URL are always printed, so the flow works over SSH or headless. A stale or disabled link returns 404.
The terminal reports the verified identity, each detected tool's result, and a final file count. The personal key and refresh credential are stored locally with owner-only permissions.
Gateway-first login compatibility path
anyray-connect login --gateway https://<your-anyray-gateway> remains available for existing
workflows. It signs in, lets the user choose an eligible team, and configures the Claude Code
subscription lane. Run a normal anyray-connect apply afterward to configure every other detected
tool. New onboarding should use the SSO enrollment command above so enrollment and the full apply
happen in one command.
Provision users directly with SCIM
Use the gateway's SCIM 2.0 server when Okta, Microsoft Entra ID, or another IdP should own the user and group lifecycle directly, instead of WorkOS self-enrollment. The base URL is https://<your-anyray-gateway>/scim/v2.
Set up inbound SCIM
Create a high-entropy static token in your secrets manager. The IdP sends it on every SCIM request. Anyray stores only its SHA-256 hash in the shared gateway database.
Set the bearer, the IdP group that grants the Anyray admin role, and any group-to-team mappings. The admin token below is the gateway's existing break-glass credential.
curl -X PUT https://<your-anyray-gateway>/admin/scim/settings \
-H "Authorization: Bearer $ANYRAY_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"bearerToken": "synthetic-scim-bearer-token-replace-me-0001",
"adminGroup": "Anyray Admins",
"groupTeamMap": {
"Platform Engineering": "platform",
"Anyray Admins": "platform"
}
}'
In the IdP, set the SCIM base URL above and use the same static bearer. Enable user and group
pushes. Anyray supports discovery, Users and Groups CRUD, paging, userName eq "…" filters,
user activation, and group membership patches.
Assign a synthetic test user and mapped group in the IdP. The user appears in the Users
roster with source scim, type person or service, the mapped team, and the configured role.
GET /admin/scim/settings returns whether SCIM is configured plus the admin-group and team map. It
never returns the bearer or its hash. Updating mappings without bearerToken keeps the current
token; include a new bearer to rotate it.
If a group push references a user that hasn't arrived yet, include the member's email in its
display field. Anyray creates the identity first, then resolves its team and admin role from the
verified group payload.
Deprovision
- Direct gateway SCIM —
active:falseorDELETE /scim/v2/Users/:idwrites a durable inactive identity. Every replica checks it during client-key verification, so existing keys stop working immediately. Fails closed if the identity store can't be read. - Directory Sync — removing the user in your IdP fires a webhook that revokes them automatically.
- Manual revoke — Users → Identity → SSO configuration → Offboard a person, revoke by email. Re-minting and re-sliding stop within ~15 min. Reinstate with one click.
- Key expiry (backstop) — a revoked key still lapses within the session length, and revocation blocks re-mint.
Keys auto-renew while in use, so active developers are never interrupted (Claude Code refreshes a lapsed key at session start). Renewal re-mints from the local enrollment certificate — it never revives a revoked key, and a leaked key alone can't renew.
The enrollment certificate itself also rolls forward on its own while the device is in use, which is why an active developer never gets sent back to the browser. That renewal is checked against the same revocation list, so revoking someone stops it too: their current key lapses within the session length, and the certificate behind it stops renewing at the same moment.
Because certificates renew, you cannot rely on one aging out to remove access. Deprovision through your IdP (Directory Sync), SCIM, or Offboard a person. Any of those cuts the device off; doing nothing leaves it working.