Identity & Access
Technical reference. This page is for integrations, advanced configuration, or platform operations. For portal tasks, start with the user guide.
Orqestra separates identity from authorization. Users, groups, and service accounts can come from enterprise identity systems, while scoped permissions are evaluated by Orqestra against organizations, namespaces, workspaces, workloads, and platform-level operations.
Use this guide when you need to configure login, map IdP groups to Orqestra roles, provision users, debug 403 responses, or decide which role should own an operation.
Identity Sources
| Source | Purpose |
|---|---|
| OIDC | Authenticates users and service principals with bearer tokens and JWKS. |
| SCIM | Provisions users and groups from the enterprise IdP. |
| IdP group mappings | Maps external groups to scoped Orqestra roles. |
| Service accounts | Represents runtime services, agents, and automation identities. |
| Workload principals | Grants invocation, management, observation, or admin access to workloads. |
| Platform admin groups | Grants deployment-wide administration through configured IdP groups. |
OIDC Login
The control plane validates bearer tokens against the configured issuer and JWKS endpoint. Configure these environment variables on the control plane:
| Variable | Description |
|---|---|
ORQESTRA_OIDC_ISSUER_URL | Issuer URL used to validate token issuer claims. |
ORQESTRA_OIDC_JWKS_URL | Optional JWKS endpoint override when services must fetch keys through an internal address. |
ORQESTRA_OIDC_AUDIENCE | Expected token audience. |
ORQESTRA_OIDC_CLIENT_ID | Registered client ID. |
ORQESTRA_OIDC_ACTOR_REF_CLAIM | Stable claim used for the persisted Orqestra actor reference. Defaults to sub; use the SCIM subject. |
ORQESTRA_OIDC_CLOCK_SKEW_LEEWAY_SECONDS | Bounded allowance for IdP/control-plane clock skew when validating iat, nbf, and exp. Defaults to 30; maximum 300. |
Example:
export ORQESTRA_OIDC_ISSUER_URL="https://auth.example.com/realms/orqestra"
export ORQESTRA_OIDC_JWKS_URL="https://auth.example.com/realms/orqestra/.well-known/jwks.json"
export ORQESTRA_OIDC_AUDIENCE="orqestra-control-plane"
export ORQESTRA_OIDC_CLIENT_ID="orqestra-cp"
export ORQESTRA_OIDC_ACTOR_REF_CLAIM="email"
export ORQESTRA_OIDC_CLOCK_SKEW_LEEWAY_SECONDS="30"Local Keycloak deployments commonly use email because development user IDs
can change after a restart. Production deployments should use a claim that is
stable across IdP migrations and SCIM provisioning events.
Expired Portal sessions
When a Portal session expires, API reads and mutations return a structured
401 instead of redirecting the request to the login page. The Portal blocks
the stale shell with a Your session expired dialog and offers an explicit
sign-in action. After OIDC login, the browser returns only to a validated Portal
page path. Orqestra never automatically replays the failed request: review the
page and explicitly retry the action. Workload-profile sandbox discovery
reconciles its draft profile asset and version before requesting recommendations,
so explicitly retrying that step does not duplicate a profile write that committed
before session loss. Other multi-write workflows do not currently provide this
reconciliation guarantee. A 403 remains an authorization denial and does not
start the session-expired flow. A separate endpoint-owned 401 (for example, an
expired share-link ticket) also remains an endpoint error and does not sign the
user out.
Signing out of the Portal
Signing out takes two deliberate steps. The sign-out control in the user menu is
an ordinary link to /logout, which opens a confirmation page; the session ends
only when you submit the confirmation on that page. Simply loading /logout
changes nothing, and GET or HEAD /api/auth/logout answers 405 with
Allow: POST — so a link prefetch, a mail scanner, or a page that embeds the
URL as an image cannot sign you out.
The confirmation page carries a one-use token bound to your current session. It
expires at the sooner of ten minutes and your session’s own idle and absolute
deadlines, and confirming consumes it. So a confirmation page you left open, a
second tab holding an older confirmation, or a resubmitted browser history entry
is refused. Nothing about your session changes when that happens, and the retry
is always the same: reload /logout and confirm again — never resubmit the old
page. A /logout page that offers no confirmation control means there is no
live session left to end; go to /login instead.
If the session store is unavailable while the confirmation page is being
prepared, /logout answers 503 and leaves your cookies untouched. Retry once
the deployment’s session Redis is healthy.
Bounded sign-out failures
The sign-out submission accepts one small, strictly shaped form and refuses everything else with a fixed status and no detail beyond it:
| Response | Cause |
|---|---|
403 | Missing, foreign, or malformed request provenance, or a stale, replayed, or mismatched one-use token. |
401 | No live Portal session to end. |
415 | A body that is not application/x-www-form-urlencoded. |
413 | A body over 1024 bytes. |
408 | A body that takes longer than five seconds to arrive. |
400 | A body that is not exactly one canonical token field. |
503 | The session store failed while ending an already-authorized session. |
Every one of these except the 503 is refused before anything is changed: your
session, your cookies, and the one-use token are exactly as they were. The
503 is the only case that happens after authorization — the sign-out was
accepted but the stored session could not be deleted — so the Portal clears the
four browser auth cookies (orq_access_token, orq_pkce_verifier,
orq_oauth_state, orq_auth_return_to) anyway and does not call the identity
provider. A successful sign-out clears those same four cookies and returns you
to /login.
Sign-out from an embedded Portal page
The sign-out submission requires strict same-origin provenance. A present
Origin header must be exactly one valid, trusted HTTP(S) origin — the Portal’s
own origin or a configured browser-facing base URL. An empty, multi-valued,
non-HTTP(S), or opaque null Origin is rejected outright and never falls back
to Referer; only a genuinely absent Origin may fall back, and the Referer
must then itself be a trusted origin.
That rule denies mutations from an opaque origin, which is exactly what a
sandboxed <iframe> produces. A frame sandboxed without allow-same-origin
sends Origin: null, and that value is indistinguishable from every other
opaque origin — a data: document, an unrelated sandboxed frame, a redirected
cross-origin frame. Accepting null would accept all of them, so any site could
host an invisible sandboxed frame that signs your users out. Orqestra denies it
rather than weakening the check for Origin: null.
A cookie-authenticated embed that needs to make mutations therefore needs a
same-origin-capable context: a frame served from the Portal’s own origin and, if
sandboxed at all, sandboxed with allow-same-origin so it keeps that origin
instead of collapsing to null. A frame hosted on a third-party origin sends
its own Origin, which is not trusted, so it is refused for the same reason. An
embed that must stay opaquely sandboxed, or that lives on another host, can
still render Portal pages through ORQESTRA_EMBED_FRAME_ANCESTORS; signing out
has to happen in a top-level Portal tab.
Scope Chain
Authorization walks the typed scope chain from narrowest to broadest:
WORKSPACE -> NAMESPACE -> ORG -> PLATFORMThe first scope at which the actor has a role that grants the permission
returns ALLOW. A lower scope that lacks a permission does not deny the check;
it continues walking upward. This is the affirmative-grant rule.
Example: a user can be a Developer in one workspace and an Organization Admin on the parent organization. A workspace member-management check can pass through the organization grant even though the workspace Developer role does not include that permission.
Built-In Roles
| Role | Scope | Common use |
|---|---|---|
| Platform Admin | Platform | Deployment-wide administration, retention purge, platform-level settings. |
| Organization Admin | Organization | Namespace management and organization-level grants. |
| Namespace Admin | Namespace | Workspace creation and operations inside a namespace. |
| Workspace Admin | Workspace | Workspace membership, assets, releases, secrets, and connections. |
| Developer | Workspace | Author assets, create draft releases, execute flows. |
| Operator | Workspace | Approve, activate, rollback, observe, and respond to incidents. |
| Viewer | Workspace | Read-only access to workspace resources. |
Permission Boundaries
The most important boundaries are:
- Developers can create assets and draft releases, but cannot approve or activate releases.
- Operators can approve, activate, and rollback releases, but cannot create or edit assets.
- Workspace Admins manage workspace members, secrets, connections, assets, and releases in their workspace.
- Namespace Admins can create and manage workspaces, but cannot make themselves platform or organization administrators.
- Organization Admins can manage namespaces and organization-level grants.
- Only Platform Admins can purge retention or audit data.
Use the repository reference at docs/reference/iam-permissions.md when you
need the complete permission matrix.
IdP Group Mapping
IdP group mappings bind enterprise groups to scoped Orqestra roles. Workspace
role mappings include a workspace. Namespace role mappings omit the workspace.
Platform admin access is configured separately through
ORQESTRA_PLATFORM_ADMIN_GROUPS.
A mapping references the IdP group directory — an issuer-scoped table of
group claims either observed at a real login (verified) or explicitly
pre-provisioned by a Platform Admin (pending_unverified) ahead of a group’s
first login. A mapping cannot be created against a group that isn’t in the
directory: a typo or guessed group name is rejected outright rather than
silently creating a mapping that never matches a login claim.
Platform Admins can manage these mappings in the portal from Administration → IdP group mappings. The IdP group field is a searchable combobox over the directory, with an explicit pre-provisioning option for a group that hasn’t logged in yet. The page lists existing mappings, creates namespace-scoped or workspace-scoped mappings, prevents invalid role/scope combinations before submit, and surfaces duplicate mapping conflicts from the API.
# Find (or pre-provision) the directory candidate first
curl "http://localhost:8000/v1/idp-group-directory/candidates?q=azure-ad"
curl -X POST http://localhost:8000/v1/idp-group-directory \
-H "Content-Type: application/json" \
-d '{"claim_ref": "azure-ad-group-object-id"}'
# Then create the mapping against its directory_group_id
curl -X POST http://localhost:8000/v1/idp-mappings \
-H "Content-Type: application/json" \
-d '{
"directory_group_id": "<uuid from the directory candidate>",
"namespace_id": "namespace-123",
"workspace_id": "workspace-456",
"default_role": "developer"
}'When a user logs in, Orqestra reads group claims from the token, upserts each
one into the directory, and matches them against configured mappings to
provision the matching workspace or namespace membership. A mapping bound to
a still-pending_unverified directory group grants nothing until that
happens.
SCIM Provisioning
SCIM keeps users and groups aligned with the enterprise IdP. The OIDC actor reference claim should match the stable subject SCIM uses for the same human or service identity. If those identifiers drift, the same person can appear as two different actors and permissions will not resolve as expected.
SCIM is off by default and is enabled per tenant in Administer › Access & org ›
Tenant settings (or orqestractl namespace settings set <namespace_id> --scim on), an
audited change that applies to the next request. While it is off, /scim/v2 answers
403 enterprise_capability_required (in the SCIM error format) to an authenticated
provisioning client; an unauthenticated caller still gets 401. In a single-issuer
deployment SCIM is platform-wide, so enabling it needs platform administration;
with Entra tenants it is keyed on the credential’s own organization.
Microsoft Entra ID
Orqestra trusts Entra tenants one at a time, per organization. Nothing is trusted implicitly: a tenant is inert until an operator registers it, and a registered tenant only provisions once it holds a SCIM credential.
For a full step-by-step runbook covering both the platform and customer sides of bringing a new customer onto this profile, see Customer Tenant Onboarding.
Admin consent
The customer’s Entra administrator grants admin consent for the Orqestra application in their own tenant. Consent alone does not grant access to Orqestra — it only allows the tenant’s users to present tokens. Until the tenant is registered (below), those tokens are rejected.
Registering a trusted tenant
Register from Administer → Access & org → Organizations → (organization) → Trusted
tenants, or with orqestractl identity-tenant. You supply the tenant’s
directory ID; Orqestra derives and stores the exact issuer it will accept
and shows it back to you.
The binding is permanent. A tenant can be renamed, disabled and re-enabled, but never deleted and never re-pointed at a different issuer. Historical actors, audit records and group mappings reference it, so detaching it would strand that evidence. If you registered the wrong tenant, disable it and register the correct one.
A disabled tenant stops authenticating immediately and cannot be issued new SCIM credentials.
Qualifying client IDs
ORQESTRA_ENTRA_QUALIFIED_CLIENT_IDS lists the application (client) IDs
eligible for issuer qualification — between 1 and 10 comma-separated
GUIDs. Ordinary sign-in admission checks tenant registration, token
signature, issuer, audience, and expiry. The azp allowlist is a separate,
additional check enforced by the issuer-qualification authority
(POST /v1/issuer-qualifications and friends — see
docs/reference/qualified-issuer-authority.md), which the owner-continuity
and external-AI-ownership workflows require before trusting an actor with
those specific, more sensitive operations. Qualification requires
idtyp == "user", a non-empty scp, and azp in this allowlist.
The allowlist remains the authority for operator-tenant clients and configured
first-party clients. Customer applications can instead be registered by an
organization administrator under Administer → Client applications or with
orqestractl client-application register. Registration is scoped to an active
organization and its registered Entra tenant; Orqestra checks the live client
registration on each request. Registration grants admission only. Workspace
membership, permissions, and human-session qualification still apply.
Disable takes effect on the next request across replicas. Re-enabling advances the authorization revision, so qualifications made under the previous revision remain invalid until a new qualification is approved. Rename does not change the authorization revision.
Use orqestractl client-application scopes to list organizations and tenants
the current caller may administer, then orqestractl client-application list --org-id <id> --identity-tenant-id <id> to inspect registrations. Enable and
disable operations require the current revision with --expected-revision.
Changing this value does not retroactively qualify identities under a new configuration. After changing it, re-qualify affected first-party identities so their qualifications are re-evaluated against the new set. Customer-registered applications are separately bound to the registration’s organization, tenant, client ID, and authorization revision.
SCIM credentials: issuing, rotation, revocation
SCIM credentials are scoped to one organization and one registered tenant, and
are confined to /scim/v2 — a SCIM credential cannot read any other API.
- The secret is displayed once, at issue time. It is never retrievable
afterwards. Orqestra stores only a hash and a short non-secret prefix
(
orqscim_…) used to identify the credential in listings and logs. - Rotate by issuing a second credential, moving the Entra provisioning job onto it, then revoking the first. Two active credentials per tenant is the maximum, and that ceiling exists to make this overlap possible — it is not a quota to sit at. Issuing is refused at two active credentials.
- Revocation takes effect immediately.
Provisioning contract
Configure the Entra enterprise application’s provisioning to Orqestra’s SCIM endpoint using the issued credential as the bearer token.
externalId must be the Entra objectId, and it is required on create.
It is immutable: once a user is provisioned, a later request carrying a
different externalId is rejected rather than silently re-keying the
identity.
Orqestra applies the core SCIM attributes it governs. Attributes outside that set are accepted and ignored rather than rejected, because Entra sends them routinely and rejecting one would take the whole request with it:
- core:
title,nickname,userType,preferredLanguage,locale,timezone,profileUrl,phoneNumbers,addresses,ims,photos name.*:formatted,honorificPrefix,honorificSuffix,middleName- enterprise extension:
employeeNumber,costCenter,organization,division,department,manager
Any attribute outside the governed and accepted sets fails the PATCH with
400. This matters more than it looks: Entra batches attribute updates with
lifecycle changes, so a PATCH carrying an unsupported attribute and
active: false fails as a unit and the user is not disabled. If
deprovisioning appears not to take effect, look for a 400 on the batched
PATCH before looking at the lifecycle logic.
Errors are returned as RFC 7644 Error objects (application/scim+json); a
duplicate userName returns 409 with scimType: "uniqueness".
Group mapping is issuer-scoped
Directory search and pre-provision run against a specific registered tenant,
so group mapping is organization-first: choose the organization, then one
of its active tenants, then the group. The chosen tenant’s issuer is sent
with the lookup — which is why group search does nothing useful until at least
one tenant is registered.
For an organization-wide administrator, map a single Entra group GUID to
organization-admin at organization scope. A mapping targets an organization
or a namespace, never both.
Entra licensing and group shape
Group-based assignment to an enterprise application requires Entra ID P1 or P2. On the free tier, assign users to the application directly.
Only direct group membership is provisioned. Nested groups are not expanded — a user who belongs to a mapped group only through a child group will not be provisioned. Flatten the membership, or map the child group too.
Service Accounts
Service accounts represent runtime services, AI agents, automation clients, and other non-human principals. They are tracked with the same membership model as users but are marked as service accounts.
Use service accounts when:
- a runtime gateway needs to execute on behalf of a workspace,
- an agent needs stable access to scoped workloads,
- CI/CD needs to create releases or synchronize GitOps state,
- automation needs audit attribution separate from a human user.
Do not reuse personal user tokens for automation. Service account activity should have its own actor reference in audit logs.
Workload Principal Bindings
Workload access uses principal bindings for invocation and operational access.
| Access mode | Meaning |
|---|---|
interactive | A human-driven session (for example, a console seat). |
programmatic | An automated or service-to-service caller. |
actor_type is one of user, workload, service, or system.
Example:
curl -X POST http://localhost:8000/v1/workloads/{workload_id}/principal-bindings \
-H "Content-Type: application/json" \
-d '{
"actor_type": "service",
"actor_ref": "service:release-bot",
"access_mode": "programmatic",
"scope_ref": "workspace:payments-prod"
}'Debugging Access Failures
Use this checklist for 401 and 403 failures:
- Confirm the request has a bearer token or an authenticated portal session.
- Verify the token issuer, audience, expiry, and JWKS configuration.
- Check the actor reference claim and make sure it matches the stored member or SCIM subject.
- Confirm the actor is mapped into the expected organization, namespace, or workspace.
- Check whether the permission is evaluated at workspace, namespace, organization, or platform scope.
- For workload operations, inspect workload principal bindings and access modes.
- For gated features, inspect
/v1/capabilitiesand verify the capability is enabled in the deployment.
If a permission should be granted by a broader scope, verify that the broader role is on the same parent chain as the target workspace or workload.
Workspace role permission overrides
An administrator with members.manage on a workspace can set or reset per-role overrides there. These are workspace-role settings, not per-user override lists. Use authenticated API paths relative to your deployment’s control-plane address:
PUT /v1/workspaces/{workspace_id}/permissions/overrides/{role}
DELETE /v1/workspaces/{workspace_id}/permissions/overrides/{role}For example, removing execution permission from the developer role uses this PUT body:
{
"added": [],
"removed": ["execution.run"]
}The wire fields are added and removed. Deleting that role’s override restores its defaults; it does not remove workspace membership. The API validates roles and permission names and rejects platform-admin overrides. Confirm the member’s effective permissions after changing an override; a user’s combined roles and grants can affect the result.