Single Sign-On (SSO)
Let admins and end users authenticate through a corporate identity provider over SAML 2.0 or OIDC.
Overview
Single Sign-On lets people authenticate through an external identity provider (IdP) such as Okta, Microsoft Entra, or Google Workspace, instead of a Canopy password. Canopy speaks both SAML 2.0 and OIDC, and verifies every assertion or token before trusting it.
Three surfaces
Who is signing in, and who configures it
There are three SSO surfaces. Admin SSO lets your Account's admins sign into the Canopy Console through your corporate IdP, configured for the Account; whether an administrator's own second factor is still asked for after it is the Account's choice, under Administrator Sign-In in the Admin Workspace. End-user SSO lets your application's end users sign in through their corporate IdP, two ways: during the hosted OAuth flow (configured per OAuth client, so one client can accept several upstream IdPs) or through the Direct-API flow with a publishable key (bound per environment). The third is not a different kind of sign-in but a different configurer: in the organization admin portal your customer's own IT administrator sets up end-user SSO for their organization without you. A connection they create is owned by that organization: it shows as Customer-owned in the Console, it cannot be moved to another organization or Environment, or unbound or unlinked from them (disable or delete it instead), and when you change its identity provider settings, disable it or delete it from the Console or the API, that organization's administrators are emailed. Every such change is in the audit log with who made it. Run any of them, or none.
Connections
Each integration is a connection holding the IdP's details: for SAML its issuer, sign-in URL and signing certificate, or for OIDC a discovery URL plus client credentials. A connection moves through a simple lifecycle:
Setup checklist
Work this checklist top to bottom. It's a two-sided exchange between Canopy and your IdP, plus a little routing on the Canopy side. The last step before activation differs for admin, hosted end-user, and Direct-API end-user SSO:
Attribute mapping
Known providers are seeded with the names they really send
Canopy resolves an email, a stable subject, a name and group claims out of each assertion, using a list of source names per field. The protocol defaults cover the common spellings, but a provider that names its claims differently would resolve nothing, and the first test sign-in would fail on a missing attribute. So for SAML connections Canopy recognises Okta, Microsoft Entra and Google Workspace from the host their sign-in URL or entity ID points at, and seeds the connection with the claim names each of them actually sends. Entra matters most here: it sends WS-Federation claim URIs such as http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress, which match none of the generic defaults. The generic spellings stay in the list behind the provider's own, so an administrator who renamed a claim in their console still resolves. A provider Canopy does not recognise keeps the protocol defaults, and a mapping you set yourself is never overwritten.
Test sign-in
Prove it works before anyone depends on it
A test sign-in runs the real round trip against a connection that is configured but not yet live. Nothing is provisioned, no session is minted, and nothing changes for anyone. It reports whether the provider accepted the sign-in, the email and name it sent back, and whether that email's domain is verified, which is a separate question from the sign-in working. A test that fails names one thing to change rather than reporting that something went wrong. The last result is kept on the connection as last_test_sign_in until a provider setting changes, and an organization administrator activating from the portal needs it to be a pass.
What a failure can name
Security & provisioning
Verified, and provisioning depends on the surface
Canopy verifies each SAML assertion's signature, audience, and validity window, with IdP certificate pinning and replay protection, and validates OIDC tokens against the IdP's keys. What happens after verification depends on the surface. Admin SSO never auto-creates accounts: an admin must already be a member of the Account before they're admitted (a no-JIT pre-invite gate), so a misconfigured or rogue IdP can't mint Console admins. End-user SSO does just-in-time provisioning by design: on a verified user's first login Canopy creates their federated identity and grants them membership in the environment they signed into, so your app's users sign in without being pre-created. A connection can turn just-in-time provisioning off, and then only people who already exist are admitted. A verified email domain belongs to one organization in an Environment and routes only to it; two products on Canopy can each verify the same customer's domain. Domains for admin SSO are the exception: each is exclusive to one Account. An OIDC connection's client secret is sealed at rest under a master key held outside the database and is never returned once saved, and its discovery URL must be a public https address, because Canopy's own servers fetch it.
Organizations
One connection per tenant, landing in its organization
A connection stays an Account-level object, but with the organizations container on it can be bound to one organization per Environment. A verified sign-in through a bound connection lands the identity in that organization as a member with the binding's role and starts the session there. The organization's own policy can also require SSO outright: password and email-code sign-ins are then refused for any email whose domain routes to the bound connection, before a credential is ever checked, and for a member whose session would start in the organization or switch into it. The organization owns the email domains its people sign in with: routing and landing both need the domain verified by that organization with its _sso-verification DNS record, and only one organization per Environment can verify a given domain. With the container on, every end-user connection must be bound to an organization: a connection bound to none cannot provision anyone there, a default connection cannot be set, and organizations cannot be turned on while a default exists. Both are refused with 409, because a default would route every unmatched sign-in to one identity provider across every tenant. A connection an organization owns can never be the Environment's default.
Tell us how we can improve this guide.