Identity and access in downpipes: sign-in, roles and change control
Identity and access in downpipes answers three separate questions: how a person proves who they are, what that person is then allowed to do, and how an account governs changes to either. This page sets the model for the whole section before any how-to page, written for a self-hoster standing up downpipes for their team.
Two facts shape everything that follows. The first is that authority is decided server-side in the engine. It runs inside your own Cloudflare account, and the console only mirrors what the engine would allow. The second is that the engine derives a stable, immutable identity from each sign-in and keys every authority decision on that identity, never on the email a person happens to use today.
Each detail has its own page in this section. This overview connects them and sets out the facts the other pages build on.
Four independent ways in
A request authenticates by one of a few methods, and the engine resolves exactly one of them per request. The point of the design is that the paths are independent: no single sign-in mechanism is a prerequisite for the others, so an outage in one is a sign-in button that does not work, never a lockout.
| Sign-in path | What it is | What it carries |
|---|---|---|
| Passkey front door | The engine’s own WebAuthn login, the free self-hosted front door. A successful login issues a short-lived signed session cookie. | A verified email and a stable subject derived from that email. |
| Cloudflare Access | A verified Cloudflare Access JWT presented as a header, the higher-assurance path attributable to an email. | A verified email and a stable subject built from the Access issuer and subject. |
| Native identity provider | The engine’s own OIDC, OAuth2 or SAML session, minted after the engine itself completes the flow with no Cloudflare Access in the path. Optional, per connection. | A verified email and a connection-scoped stable subject. |
| Break-glass admin token | The bare ADMIN_TOKEN bearer fallback, presented as an explicit header. It is unattributable and all-or-nothing. | No email and no subject, so it resolves to the owner break-glass. |
On the passkey front door, whether the email box is required depends on what you are doing. Setting up a passkey needs your email, so the engine can bind the new credential to it. Signing in does not: leave the box blank and the resident passkey your device already holds is offered without a username.
The precedence at the gate is deliberate. A header-borne Access JWT is checked first, then the engine’s own session cookie (passkey, OIDC or SAML, told apart by the signed payload), then the bare token. A present-but-invalid Access JWT or session cookie is rejected outright and is never quietly downgraded to a weaker method.
An identity-provider outage is never a lockout
The passkey front door and the break-glass token stay independent of any external identity provider by design. If your OIDC or SAML connection is unreachable, that one “Sign in with…” button is down. A passkey-bound owner still works. The break-glass token also works, unless an Owner has retired it or you have removed or disabled it. The bridge recipe and the connect pages restate this rule: keep at least one passkey-bound owner and the break-glass path that do not depend on any identity provider, so a provider outage cannot lock the account out of itself.
The break-glass token is the one path with no attributable identity. Because a shared secret should not pose as an identified person, it does not read the role table at all. It is the owner or it is nothing. That same lack of a stable identity is why the break-glass token can neither propose nor approve a four-eyes change, covered under change control below.
Authority is a capability model, not a rank ladder
Once a person is authenticated, what they may do is decided by an explicit capability map, ROLE_CAPABILITIES. Each role holds a fixed set of named capabilities such as downpipe.write, restore.apply or roles.write. A route allows a caller only when the caller’s role holds the capability that route requires. The decision runs through one primitive, can(role, capability), which is a single map lookup followed by a set membership test. There is no separate ranking that decides a per-route allow.
This matters because two of the six built-in roles are not steps on a cumulative line. restore-operator is recovery-only and access-admin is people-only. Each holds a subset of owner’s powers for one job, so neither is a prefix of owner and neither can be expressed by a single numeric rank. An internal rank does still exist, but only to fold role resolution and grant expiry cleanly, never to decide whether a request is allowed.
On top of the six built-ins, an account can compose its own named capability bundles. These custom roles sit alongside the built-ins and never replace them. An identity-provider group mapping is an optional additive layer that resolves a group to one of these roles. The full table and the composition rules each have their own page.
| Where authority is defined | Page |
|---|---|
| The six built-in roles and the exact capability each holds | Roles and capabilities |
| Composing an account-defined named capability bundle | Custom roles |
| Resolving an identity-provider group to a role | Group-to-role mapping |
Two capabilities, keys.ceremony and posture.riskaccept, are the owner’s alone. No other built-in holds them, no custom role may include them, and no group mapping can confer them, so the break-glass owner stays the single named holder of both. The roles reference page covers the bar in detail.
Authority keys on a stable subject
The engine stores a role against a caller’s stable, immutable subject, under a key of the form role:sub:<subject>, not against the mutable email. The subject is the principal the engine derives from the sign-in. A Cloudflare Access caller’s subject is its issuer joined to its Access subject. A passkey caller’s subject is passkey| followed by its canonical email. A native identity-provider caller’s subject is scoped to the connection it signed in through, so the same person across two connections is two subjects and can never collide.
Keying on the subject has direct consequences for safety. Where the subject does not come from the email, a recycled or reassigned email cannot inherit a departed member’s role. The new person authenticates to a different subject and reads a different or empty role entry. A passkey subject comes from the email, and a SAML subject does too when its NameID is the email address. For those, remove a departed member’s role before you reuse their address.
The same person signing in with a different email case is still recognised as the same subject, so they cannot pose as a distinct second party in dual control. The email is retained for display and for the audit trail, and it is never the axis an authority decision turns on.
An invitation is the one place an email is used as a key, and only until first sign-in. An owner or access-admin invites by email under role:pending:<email>, because the subject is not yet known. On the invitee’s first verified request the engine binds the pending grant to their subject, records the email on the subject-keyed entry, and deletes the pending row. From then on the authority is the subject’s.
What is enforced where
Everything in this section is enforced server-side in the engine and surfaced in the console. The console mirrors the engine’s gates: an action a caller cannot perform is shown disabled with the reason stated, rather than hidden and then failing with a 403 when pressed. When the engine denies a route it returns a structured forbidden body naming the capability the route required and the role the caller resolved to. The console uses the forbidden body to say exactly which capability was missing.
The governance lives in the engine alone. The control-plane and the Go command-line tool carry none of it. The control-plane is a thin surface that does not hold this role model. The command-line tool is an offline recovery and verification aid that operates on archives rather than on the live engine’s authority. If you read about roles, group mappings or four-eyes change control anywhere, it is the engine that is enforcing them.
The console mirror is not the boundary
A surface in the console can be more restrictive than the engine, and that is safe: it simply hides an affordance the engine would have allowed. It can never be more permissive. Every write is gated on the capability set at the engine’s write boundary, so a console that offered an action the engine forbids would be rejected there. Treat the console as guidance and the engine as the control.
Change control sits on top
An account can turn on an opt-in four-eyes gate over config changes, so that a second authorised person must approve a config mutation before it applies. It is off by default, and while it is off a config change applies inline exactly as it would without the feature. When it is on, the second approver must differ from the proposer on the stable-subject axis and must hold the same capability the original change required.
This is a distinct mechanism from the dual control that protects a live-data restore, and from the Cloudflare config backup and restore feature. They are easy to confuse because all three involve approval or configuration, so the boundaries are worth stating once.
| Mechanism | What it gates | Where it lives |
|---|---|---|
| Four-eyes change control | Config mutations such as saving a downpipe, setting a role, or a notify rule, once armed | Change control |
| Restore dual control | Applying a restore over live data, binding to the plan hash | Dual control for restores |
| Cloudflare config backup | Backing up and previewing your Cloudflare account configuration | Covered in the operations section |
A map of the section
The pages below make up the identity section. Read them in roughly this order if you are setting up access for the first time.
- Roles and capabilities is the role-by-capability table for the six built-in roles, and the home for the owner-reserved capabilities.
- Custom roles walks through composing a named capability bundle and the guardrails that keep it from exceeding its creator.
- Group-to-role mapping covers how a verified sign-in’s groups resolve to a role, the strongest-wins combine with a per-person grant, and why a group can never confer owner.
- Change control explains the opt-in four-eyes gate over config changes, what it covers, and the asymmetric off-switch.
Single sign-on with an external identity provider has its own set of pages covering the SSO model and the OIDC, OAuth2 and SAML connection setup. The engine supports eight named providers plus generic OIDC and OAuth2. Its SAML support is sign-only and service-provider-initiated by default. The console creates every SAML connection with identity-provider-initiated sign-in refused, and only a direct API create can allow it. Those pages are part of this section and sit beside the ones above.
The no-custody posture, restated for identity
downpipes holds no secrets it does not need, and the vendor holds nothing at all. Restated for identity, that means the role table, the group mappings and the custom-role catalogue are all metadata. A role entry carries a subject, an email for display, and the grant detail. A custom role carries a name, a label, a capability list, a per-screen visibility map, a presentation hint and a landing screen. None of these records carries a secret or any key material, so they are safe to read back into your own console and safe to record in the audit trail.
Two related points complete the picture. A connection record for an external identity provider holds a secret descriptor, a reference to where the secret lives, rather than the secret value itself. The credential registry that tracks expiries stores labels and dates, not the credentials.
What the engine does hold is the credentials you have given it, kept in its own Durable Object storage and sealed there under the config wrap key when one is configured: the read-only Cloudflare API token you set for account browsing, your destination access keys, your SIEM and metrics push secrets, and an identity-provider client secret where a connection stores one. Where you have added the optional operational key, it holds that private half too, which is what lets it read a sealed archive unattended. All of it lives in your own account. The vendor never holds keys or tokens.
Where this fits
- No-custody trust model explains the custody boundary this section inherits, and what the vendor and the engine each hold.
- Roles and capabilities is the next page to read for the exact authority each built-in role carries.
- First run and setup covers the console’s setup, whose optional team step grants the first roles. Becoming the first Owner is on the self-host runbook.
- Audit log records each privileged action and the operator identity behind it, and is tamper-evident.
Last updated .