Advanced: run a SAML-to-OIDC bridge (optional, self-hosted)
This page describes an optional, self-hosted pattern for the small set of teams who want an OIDC trust root in front of a SAML-only corporate identity provider. It is for the operator who would rather the engine not hold a SAML service provider at all, and who is willing to run a broker to get that. If that is not you, the native SAML connection is the right default and you can stop after the next paragraph.
The bridge is not a built-in downpipes feature. It is a deployment recipe the customer runs: a broker such as Keycloak, Dex, or Authentik that you stand up and operate outside the Workers boundary, acting as a SAML service provider to your corporate identity provider and re-exposing the result as an OIDC provider to the engine. The engine side is then just the Generic OIDC preset pointed at the broker, so the engine never speaks SAML in this setup.
For almost everyone, the engine’s own native SAML SP is the supported, validated path with the fewest moving parts. Read When to use it before standing up any infrastructure, and see the SSO overview for where this sits among the connection options.
What the bridge is, and what it is not
The engine ships as its own OIDC and OAuth2 relying party and its own native SAML 2.0 service provider (sign-only, SP-initiated only). Speaking SAML natively means the engine pins your provider’s public signing certificate, which is not a secret, verifies signed assertions on a hardened pure-JavaScript XML parser, and consumes the principal. For the vast majority of tenants that is the right default.
A bridge changes the picture by putting a broker in the middle:
corporate IdP bridge (you run it) downpipes engine
(SAML 2.0 IdP) SAML SP <-> OIDC OP native OIDC RP
Entra / ADFS / SAML --> Keycloak / Dex / OIDC --> Generic OIDC preset
PingFederate / ... an identity broker points at the bridge
The broker owns the SAML SP role end to end: its certificate pinning, its assertion verification, and its SAML metadata registration at your corporate provider. The engine sees only a compliant OIDC provider and runs its ordinary authorisation-code flow with PKCE against it. The bridge is not part of the engine, not a Worker, and not in the engine’s Cloudflare account. It is your identity infrastructure, which is the whole point and also the whole cost.
When to use it, and when not to
The default is the engine’s native SAML SP. You create a SAML connection, paste your provider’s signing certificate (one or more, for rollover), download the SP metadata the engine generates, register it at your provider, and you are done. There is no extra service to run, patch, or keep available. Choose the native SP unless one of the reasons below genuinely applies to you.
Reach for a bridge only when one of these is true.
| Reason to bridge | What it looks like |
|---|---|
| One authentication protocol across the estate | Your security team has standardised on OIDC and wants every relying party, downpipes included, to verify an OIDC id_token rather than a SAML assertion, so there is one protocol to review. |
| You already run a broker | You operate Keycloak, Dex, Authentik, Auth0, or PingOne as an identity broker for other applications, with the corporate SAML provider already wired in. Adding downpipes as one more OIDC client is then less work than a fresh SAML registration. |
| A posture preference against an in-engine SAML SP | Your provider emits SAML, but you specifically would rather the engine’s auth root be OIDC. This is a preference, not a capability gap: the engine can verify the SAML assertion itself. |
| A SAML feature the v1 SP does not implement | The engine’s SAML SP is sign-only and SP-initiated, and neither is configurable. Identity-provider-initiated sign-in is refused at the assertion consumer, which requires a server-minted RelayState record that only an SP-initiated request creates, and that refusal runs before any per-connection setting is read. Encrypted assertions are unsupported the same way: no config flag ever accepts an EncryptedAssertion. A broker that supports those can terminate them and hand the engine a clean OIDC id_token. |
Do not use a bridge if you just want SSO working, your provider signs assertions (almost all do), and you have no standing broker. The native SAML connection is fewer moving parts and one less thing to keep available. A bridge you run is a service you must patch, monitor, and keep up, which is real operational surface you are taking on.
The trade-offs, stated honestly
A bridge moves work and risk; it does not remove either. Weigh these before choosing it.
You add a component you own end to end. The bridge is a service in your own infrastructure that you deploy, patch, monitor, and keep available. Its availability is now your responsibility, not the engine’s.
Custody relocates rather than vanishes. With the engine’s native SAML SP, the engine holds no client secret at all, because a SAML SP pins the provider’s public certificate, which is not a secret. With a bridge, the engine becomes an OIDC client of the broker and now holds a client credential for the broker, while the broker in turn holds the SAML SP role and its own session state. You have traded “engine holds a SAML SP, no secret” for “engine holds an OIDC credential for the broker, and you run a SAML SP in the broker”. Decide which custody shape your threat model prefers, because neither shape is nothing.
You add one more hop to trust and to debug. The identity assertion now crosses two protocol boundaries, the corporate provider to the broker over SAML and the broker to the engine over OIDC, instead of one. That is an extra signature to verify, an extra clock to keep in sync, and an extra set of logs to correlate when a login fails.
The stable subject becomes the broker’s subject. The engine keys authorisation on the OIDC subject the broker emits, in the form oidc:<connId>|<bridge-issuer>|<sub>. Configure the broker so that subject is stable and derived from a persistent corporate identifier. If the broker ever changes how it derives the subject, every engine subject changes with it and your role grants would need re-binding. Group-to-role mapping passes through the broker too, so the broker must carry the SAML group attribute into an OIDC claim the engine can read.
Custody caveat for the current build: do not use Secrets Store for the broker secret
The recipe’s general advice is to place a confidential client secret in Cloudflare Secrets Store by reference. That secret mode does not work today: the engine refuses to save a connection proposing the secrets-store mode at all, rejecting it at create time (there is no update route for an existing connection’s client-auth or secret mode), before anything is persisted, so the connection can never exist in a state that only fails at first login. The rejection is a normal {ok: false, reason} response, not an HTTP error status. The same config-time rejection applies to private-key-jwt. The console UI never offers either mode. For a bridge, use one of the two modes that work: a PKCE-public client, so the engine holds no secret at all, or the write-only Durable Object plaintext floor (do-plaintext), which stores the secret write-only and audits it without the value. Prefer the PKCE-public option whenever the broker supports a public client.
Concrete outline
This is a worked outline, not a turnkey script. The exact screens differ by broker, and the corporate provider’s registration flow is yours. The shape is the same for Keycloak, Dex, Authentik, or any broker that can be a SAML SP and an OIDC OP at once.
Stand up the broker outside the Workers boundary
Run the broker somewhere you control and can keep available, such as a small VM, a container platform, or a managed Keycloak. Give it a stable HTTPS hostname with a publicly trusted certificate, because both your corporate provider over SAML and the engine over OIDC discovery and JWKS will reach it. It is your identity infrastructure, not a Worker and not in the engine’s account.
Wire the corporate SAML IdP into the broker as an identity source
In the broker, add your corporate SAML 2.0 provider as an identity source. The broker acts as the SAML SP here: import the corporate provider’s SAML metadata or paste its entityID, SSO URL, and signing certificate. The broker generates its own SP metadata, and you register that at the corporate provider. Choose a persistent NameID so the downstream subject is stable across logins, because a transient NameID would make every login look like a different user.
Expose the broker as an OIDC provider to downpipes
In the broker, register downpipes as an OIDC client of the broker’s own OIDC OP. Set the redirect URI to the engine’s per-connection callback,
https://console.example.com/admin/oidc/callback/<connId>, substituting your console origin and the connection id you will use. Prefer a PKCE-public client so the engine holds no secret for the broker. Ensure the broker emits a stablesub, anemail, anemail_verifiedclaim if you want invites to auto-bind, and, if you map roles, a groups or roles claim with the SAML group attribute mapped into it explicitly. Confirm the broker serves a valid<issuer>/.well-known/openid-configurationand a JWKS on the issuer host, because the engine’s relying party pins the JWKS host to the issuer host.Point the engine's native OIDC RP at the broker
In the console, create an identity provider connection using the Generic OIDC preset. Set the issuer to the broker’s OIDC issuer URL, and the client id to the client you registered in the broker. For client authentication, choose PKCE-public if the broker allows a public client; otherwise use the write-only
do-plaintextfloor, which stores the secret write-only and audits it without the value. Do not select Secrets Store or private-key-JWT: the engine rejects both at connection-create time in this build (there is no separate update route), so the connection cannot be created with either mode. Set the groups or roles claim name the broker emits, or leave it blank for sign-in only and assign per-person roles in the role table.Verify the whole chain, and keep the floors
Sign in once end to end: the corporate provider prompt, back to the broker, back to the engine, landed with the expected role. Confirm in the audit trail that the engine recorded the login against the bridge connection. Keep at least one passkey-bound Owner and the break-glass path independent of the bridge, so a broker outage is a down button and never a lock-out. Treat the bridge as production identity infrastructure: patch it, monitor its availability and certificate expiry, and rotate the engine-to-broker credential on your normal cadence.
Broker outage behaviour
A bridge outage behaves exactly like any other provider outage, and it is not a lock-out. When the broker is down, that single sign-in route is down: the button does not complete a login. The passkey front door and the break-glass token never depend on the broker, so an Owner can still get in and recovery is still possible. This is the same fail-open shape the engine has for a native provider, and it is why you keep a passkey-bound Owner and the break-glass path independent of the bridge. The difference under a bridge is only that the availability of that route is now your responsibility.
Where this fits
Connect a SAML 2.0 IdP
The supported, validated default for a SAML provider: the engine’s own native SAML SP, with no broker to run.
Connect an OIDC or OAuth2 IdP
The Generic OIDC preset the engine side of this recipe uses, and the working client-auth modes.
Single sign-on overview
Where the bridge sits among the connection options, and why the native SAML SP is the default.
Test, troubleshoot, and revoke SSO
Confirming a login lands with the expected role, and what to check when it does not.
Last updated .