Single sign-on (SAML 2.0 and OIDC)
thump supports one identity provider per account, speaking either OIDC or SAML 2.0. Members whose email domain matches the connection sign in through your provider; everyone else keeps using GitHub, Google, or a passkey.
Both are on the free plan. Neither is brokered through a third-party identity service — thump talks to your IdP directly, so nothing about your logins leaves your Cloudflare account.
An account admin configures this at Settings → Single sign-on.
How a member signs in
- They enter their work email on the sign-in page.
- thump looks up the connection whose allowed email domains include that
domain.
- They're redirected to your identity provider.
- On return, thump verifies the response, provisions them into your account as
a member if they're new, and starts a session.
There's nothing to hand out — the sign-in page routes on the email domain alone. If no connection matches, the user falls back to normal sign-in.
SAML 2.0
What to give your identity provider
These are shown in Settings with copy buttons. They're derived from the URL your instance runs on, so a self-hosted deployment sees its own values.
| Field | Value |
|---|---|
| Entity ID / Audience URI | https://<your-instance>/auth/saml/metadata |
| Assertion Consumer Service (Reply) URL | https://<your-instance>/auth/saml/acs |
| Name ID format | Email address |
| Binding | HTTP-POST |
Service-provider metadata is served at /auth/saml/metadata if your provider prefers to import a file.
What thump needs from your identity provider
Paste the provider's metadata XML and thump reads all three values out of it. Or fill them in by hand:
| Field | Where to find it |
|---|---|
| Identity provider Entity ID | Okta: "Identity Provider Issuer". Azure AD: "Microsoft Entra Identifier". |
| Sign-on URL (HTTP-Redirect) | Okta: "Identity Provider Single Sign-On URL". Azure AD: "Login URL". |
| Signing certificate | The X.509 certificate the provider signs with. PEM or bare base64 both work. |
The certificate is parsed when you save it, so a truncated paste fails on the settings page rather than at someone's first login attempt.
Attributes
thump needs an email address and would like a display name. It looks at, in order:
- The
NameID, when its format isemailAddress. - An attribute named
email,emailaddress,mail,e-mail, orupn—
matched on the last segment, so the Azure AD claim URIs (http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress) and the LDAP OIDs (urn:oid:0.9.2342.19200300.100.1.3) work unchanged.
- The
NameID, if it looks like an email address.
Display name comes from displayName, name, cn, or commonName by the same matching.
Certificate rotation
The signing certificate is a list. To rotate without downtime: add the new certificate alongside the old one, wait for your provider to cut over, then remove the old one. Any configured certificate may verify a response, so there is no window where logins fail.
IdP-initiated sign-in
Off by default. When enabled, members can start from your provider's app tile instead of thump's sign-in page.
It's off by default because it gives something up. A normal (SP-initiated) login carries an InResponseTo that names a request thump issued minutes earlier, and consuming that request is what makes a response single-use. An unsolicited response has no such request to bind to, so it's protected only by the assertion-ID replay cache. Turn it on if your users expect the app tile; leave it off otherwise.
What thump refuses
Every one of these has been someone's breach:
- A signature it can't verify against the certificate you configured. The
certificate embedded in the response's own KeyInfo is ignored entirely — trusting it would mean verifying a signature with a key the sender chose.
- A signature that covers a different element than the one being read. The
Reference URI must name the assertion thump goes on to read, and that ID must be unique in the document. This is what closes XML Signature Wrapping.
- HMAC "signatures" and SHA-1. Accepting
hmac-*at all is the
algorithm-confusion bypass, where an attacker MACs the document using your public certificate as the key.
- XPath and XSLT transforms, which would let the document decide which
bytes were signed.
- **XML comments, DOCTYPEs, entity declarations, CDATA, and processing
instructions.** Comments enable the canonicalisation-splitting attacks (CVE-2017-11427 and relatives); DTDs enable XXE and entity expansion. No legitimate assertion contains any of them.
- An assertion with no
AudienceRestriction, which would be a bearer token
for every service provider on the internet.
- **An assertion addressed to a different audience, destination, or
recipient**, expired or not yet valid (two minutes of clock skew allowed), or replayed.
- An email whose domain isn't in the connection's allowed list, even when
the signature is perfectly valid. An identity provider is trusted to authenticate its own users, not to assert arbitrary domains.
Not supported
- Encrypted assertions. thump has no service-provider decryption key. If
your provider has assertion encryption on, turn it off for this app — everything is over TLS regardless. A response with an EncryptedAssertion fails with a message saying so rather than being silently ignored.
- Signed AuthnRequests. thump's requests are unsigned. They carry nothing
worth protecting; the response is what gets verified.
- Single Logout (SLO). Signing out of thump ends the thump session only.
Troubleshooting
Sign-in failures come back to the sign-in page with a short code. The full reason is in your Worker logs (wrangler tail) — deliberately, because a detailed error on a public page is a tool for iterating towards a forgery.
| Code | What happened |
|---|---|
saml_bad_signature | The response didn't verify. Usually a stale certificate after a provider-side rotation. |
saml_wrong_audience | The provider's Audience doesn't match the Entity ID in Settings. |
saml_wrong_destination | The provider's Reply URL doesn't match the ACS URL in Settings. |
saml_unknown_request | The login took longer than 10 minutes, or the response was replayed. |
saml_replayed | This exact assertion was already redeemed. |
saml_unsolicited | The provider sent an unsolicited response and IdP-initiated sign-in is off. |
saml_idp_refused | The provider itself declined — usually the user isn't assigned to the app. |
saml_no_email | No attribute resolved to an email address. Map one in the provider. |
saml_expired / saml_not_yet_valid | Clock skew beyond two minutes, or a genuinely stale response. |
domain_not_allowed | The verified email's domain isn't in the connection's allowed list. |
OIDC
Authorization Code with PKCE against any OIDC provider (Okta, Azure AD, Google Workspace, Auth0, Keycloak…).
| Field | Value |
|---|---|
| Redirect URI | https://<your-instance>/auth/sso/callback |
| Grant type | Authorization Code + PKCE |
| Scopes | openid email profile |
thump needs the issuer URL (it fetches /.well-known/openid-configuration itself), a client ID, and a client secret. The secret is encrypted at rest with the instance vault key and is never returned by the API — leave the field blank when editing to keep the stored one.
id_tokens must be signed with RS256; the signature is checked against the provider's JWKS, along with the standard iss / aud / exp / nonce claims.
Provisioning members
Both protocols provision through the same path: on first successful sign-in the user is created and added to the connection's account as a member. Promoting someone to admin is done in Settings.
For lifecycle management — deactivating a member when they leave your directory — see SCIM 2.0 provisioning, which runs alongside either protocol.