Understand how Organization domains are used with SSO.
When an Organization is created in the WorkOS Dashboard or the Create Organization API, one or more domains can be associated with the organization.
Domains added to an organization need to be verified. In the API, they can be initially added 'pending'
verification, and later verified using the Domain Verification API. Or, if previously verified through other means, can be added as 'verified'
.
Domains in the WorkOS Dashboard are always considered verified.
During authentication, WorkOS uses these domains to verify the user signing in through the organization’s Connection belongs to one of these domains. If the domain of the user’s email address does not match one of the organization’s domains (or the organization has no verified domains) they will sent to your Redirect URI with a profile_not_allowed_outside_organization
error.
Rejecting users with non-matching email domains prevents the impersonation of users in other organizations. This would otherwise be possible since many Identity Providers allow IT admins to create user accounts with any email address, regardless if the IT admin actually controls the email address or its domain.
For example, an IT admin of an organization with the domain foo.com
can create a user account for user@bar.com
in their Identity Provider and then sign in as that user. If the application were to receive the profile and naively look up the user record using only the email address, then the IT admin will have gained access to the user@bar.com
account.
Since WorkOS cannot guarantee every application handles this scenario correctly, the default behavior is to reject profiles when its email address does not match the associated organization’s domains.
Some applications may have organizations whose users sign in using a list of domains that cannot be statically defined, or a set of domains so large as to be cumbersome to configure.
A common use-case is non-domain guests. Imagine an organization hires a design consultancy and requires the designers to sign in using the organization’s IdP. While the organization can create accounts in their IdP for these guests, they cannot add the designer’s domain to the WorkOS organization since they cannot verify ownership of it. The default organization domain verification policy will prevent these designer’s from signing in.
If your application has a similar use-case, contact support and we can work with you to relax the default organization domain verification policy. SSO will no longer require any domains to be added to the organization. This change can be made for any of your environments and will affect all organizations in the environment.
Your application will be responsible for validating the email addresses of users using SSO, and checking the appropriate fields from the SSO profile.
Important data from the SSO profile includes the id
and the organization_id
. Generally applications will store the Profile id
alongside its users, such as a column on a users
table. Similarly, the organization_id
will be stored on the matching entity, such as a teams
or workspace
table; whichever represents a collection of users that are related to each other.
{ "object": "profile", "id": "prof_01DMC79VCBZ0NY2099737PSVF1", "connection_id": "conn_01E4ZCR3C56J083X43JQXF3JK5", // Your application should restrict any email-based JIT user // provisioning to within the organization that matches this ID. "organization_id": "org_01EHWNCE74X7JSDV0X3SZ3KJNY", // Only match based on email or email domain unless if are // filtering potential matches by the organization ID above. "email": "todd@example.com" // ... }
Here’s a updated version of the WorkOS callback endpoint from the Quick Start guide with examples of these checks added:
const { WorkOS } = require('@workos-inc/node'); const workos = new WorkOS(process.env.WORKOS_API_KEY); app.get('/callback', async (req, res) => { const { profile } = await workos.sso.getProfileAndToken({ code: req.query.code, clientId: process.env.WORKOS_CLIENT_ID, }); // Your application should have a resource that is analogous to the WorkOS // organization, like a "team" or "workspace". const team = TeamsService.findByWorkOsOrganizationId(profile.organizationId); if (!team) { return res.status(401).send({ message: 'Unauthorized', }); } // Your application should use the Profile ID to determine whether there is an // existing user linked to the SSO profile, within the scope of the "team" or // "workspace" associated with the WorkOS organization. // // If none is found, you can then fall back to the `email` address. If one is // found you should link the profile ID to the user for subsequent sign-ins. // // Finally, if no user exists with the email either, you can implement // JIT-provisioning and create a new user record, persisting both the `email` // and the ID from the profile. const { user } = team.users.findOrCreateByWorkOsProfile(profile); // Make sure to verify the user's email address. You may choose to skip // email verification in order to improve UX, but should only do so if // the new user's email address matches an expected domain for the "team". if (!user.emailVerified) { res.redirect('/verify-email'); } else { // Start a session for the user. // ... res.redirect('/'); } });
The above example makes use of JIT-provisioning, which you can read more about in more our dedicated JIT-provisioning guide.
With the above checks in place, your application can safely sign in and verify users through SSO from any domain.
While your users will authenticate with SSO using your application, they might briefly see the WorkOS ACS URL. This can be configured in the production environment to a custom domain of your choosing. For more information see the Custom Domains documentation.