Auth0 Organizations make multi-tenant apps feel less like a hack and more like a first-class citizen, letting each customer get their own isolated slice of your application.
Let’s see it in action. Imagine a project management tool where each company gets its own workspace.
First, we set up an Auth0 Application. This is the app your users will log into.
{
"name": "ProjectManager",
"grant_types": [
"authorization_code",
"refresh_token"
],
"callbacks": [
"https://localhost:3000/callback"
],
"allowed_logout_urls": [
"https://localhost:3000"
],
"app_type": "regular_web"
}
Next, we enable Organizations in your Auth0 tenant settings. This is a simple toggle. Once enabled, you’ll see a new "Organizations" section in your Auth0 dashboard.
Now, let’s create an Organization. This represents a customer, say "Acme Corp."
{
"name": "Acme Corp",
"branding": {
"logo_url": "https://example.com/acme-logo.png"
}
}
When a user from Acme Corp logs in, they need to be associated with this Organization. Auth0 handles this via "Organization Matching Rules" or by explicitly assigning users to organizations. For a managed login experience, you might use a custom login flow where users select their company, or you could integrate with an IdP (like Azure AD) that’s already configured for Acme Corp.
Here’s a simplified example of how you might assign a user to an organization during a social login flow using a post-login action:
/**
* @param {Event} event - Details about the context of the event.
* @param {PostLoginAPI} api - Represents the post login API.
*/
exports.onExecutePostLogin = async (event, api) => {
const namespace = 'https://your-app.com/roles'; // A custom namespace for your claims
const organizationName = event.request.body.connection; // Placeholder: In a real app, this would be determined by user input or IdP configuration
if (organizationName) {
const orgs = await api.management.organizations.getMemberships({ id: event.user.user_id });
const existingOrg = orgs.some(org => org.name === organizationName);
if (!existingOrg) {
const org = await api.management.organizations.getByName(organizationName);
if (org) {
await api.management.organizations.addMember(org.id, { user_id: event.user.user_id });
api.idToken.setCustomClaim('https://your-app.com/organization', organizationName);
}
} else {
api.idToken.setCustomClaim('https://your-app.com/organization', organizationName);
}
}
};
This action checks if the user is already associated with an organization. If not, it attempts to find the organization by name and add the user as a member. Crucially, it then adds a custom claim to the ID token, identifying which organization the user belongs to.
Your application backend then uses this organization claim in the ID token to scope data. When a user from "Acme Corp" requests their projects, your API query would look something like:
SELECT * FROM projects WHERE organization_id = 'acme-corp-id' AND user_id = 'user-abc';
This isolation is key. Users from "Beta LLC" should never see "Acme Corp’s" projects. Auth0 Organizations provide this boundary.
The most surprising aspect is how seamlessly Auth0 handles the user-to-organization mapping beyond simple direct assignment. You can configure rules or use custom actions to dynamically associate users with organizations based on their email domain, the Identity Provider they use, or even user profile attributes provided by that IdP. This allows for sophisticated onboarding where users from a specific company might be automatically placed into the correct organization without explicit user interaction.
You can also manage roles and permissions within an organization. So, "Acme Corp’s" administrator can have different permissions than "Beta LLC’s" administrator, even if they both have the "admin" role in your application’s conceptual model. This is handled by creating roles and assigning them to users within the context of a specific organization.
The next logical step is to explore how to manage API access for these organizations, ensuring that your backend services can also enforce organizational boundaries.