1. Docs
  2. Single Sign-On

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:

configuring: created but not yet complete; not accepting logins.active: validated and live; users can authenticate through it.disabled: turned off without being deleted.

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:

Create the connection. In Tenant → Single Sign-On, click Add end-user SSO or Add admin SSO, choose SAML 2.0 or OIDC, and name it.Enter your IdP's details. For SAML, the IdP entity ID (issuer), IdP sign-in URL and IdP signing certificate (PEM). For OIDC, the Discovery URL plus Client ID and Client secret. Because Canopy's own servers read the discovery document and call the token, key and userinfo endpoints it names, the discovery URL and those endpoints must be public https addresses; a document naming a loopback, private-range or link-local address is refused. A tenant setting up their own connection in the organization portal can paste a metadata URL instead and skip all three.Copy Canopy's service-provider details into your IdP. Open the connection and copy the ACS (Assertion Consumer Service) URL and SP entity ID / metadata URL for SAML (the entity ID is also the audience your IdP addresses), or the Redirect URI for OIDC. End-user connections list endpoints for both the hosted-login (OAuth) and Direct-API (publishable-key) flows. Use the set that matches how your app signs users in.Route users to the connection. For admin SSO, verify your email domain with a DNS-TXT record so admins route automatically. For end-user SSO, bind the connection to its organization, then add and verify the organization's email domains on its Policy tab; the verified domains route sign-ins to the connection. An Environment that serves one company can instead mark the connection its default to send everyone there. An Environment with organizations has no default: every end-user connection there belongs to an organization, and a default would route every unmatched sign-in to one identity provider across all of them.For Direct-API end-user SSO, bind the environment and allowlist your return URLs. Bind the connection to each environment that should accept it and, optionally, set its placement (a default node and role, or Membership only). Then add every post-login return URL on the Authentication page under Sign-in methods (Direct API), in the Environment's SSO return URLs. Canopy refuses to redirect anywhere that isn't on the allowlist.For a multi-tenant app, bind the connection to its organization. With the organizations container on, bind the Environment-bound connection to the tenant it belongs to and pick the membership role a first sign-in joins with, one of the Environment's roles or one of that organization's own. Sign-ins through it land in that organization. Pick the least privilege that lets a new person in: everyone the tenant's identity provider admits arrives holding it. The Environment's Roles an identity provider may grant (Authentication → Sign-in methods (Direct API)) bounds what a tenant setting up its own connection may choose and preselect the arrival role; an organization's own grantable list, when you set one, replaces it.Run a test sign-in before activating. On the connection page, pick the Environment and run the test: a new tab signs in through your identity provider for real, and the result shows what the provider sent and whether the connection would accept it. Nothing is created and the test never shows in recent logins. The last result is kept on the connection until a provider setting changes, and an organization administrator activating from the portal needs it to be a pass.Activate. Activate the connection once its config is complete. An organization-bound connection also needs its arrival role chosen, since a sign-in through it joins as a member holding that role, and at least one email domain verified by that organization.

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

provider_rejected: the provider declined the sign-in, usually because the person is not assigned to the application there.signature_invalid: the response did not verify against the signing certificate on the connection.assertion_expired: the response fell outside its validity window, so the two clocks disagree by more than the allowed skew.audience_mismatch: the response was addressed to a different service provider's entity ID.issuer_mismatch: the response came from a different issuer than the connection expects.assertion_replayed: that response had already been used.attribute_missing: a required attribute was absent under every configured source name. The result names the field in missing_fields, lists the attribute names the provider sent in received_attributes with the SAML name_id_format beside them, and names the one fix in hint: set_name_id_email (Okta's SAML default, a Name ID in an unspecified format and no email attribute), add_email_attribute, or send_name_id.email_domain_not_allowed: the sign-in worked, but the email's domain is not verified for the organization.assertion_invalid: the response could not be verified for a reason none of the above covers.sso_error: the round trip failed some other way.

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.

Organizations: binding an identity provider
Environment
API version
v1.0
On this page Was this page helpful?

Tell us how we can improve this guide.