How to use Auth0 for your multi-tenant B2B platform with Next and Vercel
I came across the following problem: building a platform that needed to serve multiple companies in an isolated and customized way, in other words, a multi-tenant B2B platform.
So I built an application in Next.js that uses Auth0 for authentication and Vercel for hosting with subdomains for each client/tenant.
Let's better understand these concepts:
Multi-tenant is an architecture in which a single instance of software (one source code) serves multiple customers or "tenants", in our case, companies. This approach allows multiple users to share the same resources and data, but in an isolated and secure way. For this, it is crucial to implement strict access controls and data segregation to ensure that tenants cannot access each other's data.
- Separate database for each tenant: Each tenant has their own independent database. This offers the highest level of isolation, but can be more expensive and complex to manage.
- Separate schema for each tenant: Each tenant has their own schema within a shared database. This offers a good balance between isolation and efficiency, but requires care in schema management.
- Shared tables with tenant column: All tenants share the same tables, and a tenant identification column is used to separate the data. This approach is highly efficient in terms of resources, but tenant isolation and data security can be more challenging.
- Database-level virtualization: Using virtualization technologies, it is possible to create separate virtual instances for each tenant within a single database. This can provide a good balance between isolation and efficiency.
The multi-tenant architecture is commonly used in SaaS (Software as a Service) applications, allowing efficient scalability and easier customization to meet each client's specific needs.
The idea is to have subdomains under our main domain that will represent the tenants, these subdomains will point to the same application:
Auth0 - Authentication
Authentication is an essential component in any platform, especially when dealing with companies. After all, data security is a top priority to ensure reliability and integrity of information among the different users of a B2B platform.
In this sense, the use of a robust and reliable authentication service is crucial. Auth0 is one of those services that provides a complete solution for authentication and authorization in various applications.
Auth0 has some concepts:
Applications: Represents a client or entity that can request authentication tokens. It can be a mobile app, a web app, a backend server, etc.
Tenant: Refers to an isolated, dedicated instance of the platform that a customer can control and configure. Typically used to represent environments like development, sandbox and production, etc.
Organizations: It is an abstraction that facilitates collaboration and access management in a business context. It is especially useful for representing companies or teams using SaaS applications.
Let's get to work
1. Create an application: First you need to create your Auth0 account and set up your first application.
You can customize the HTML, CSS and JS for this login page. Learn more here.
After your first authentication, you will receive the token with the user information:
2. Configure your application: For security reasons Auth0 requires us to configure some urls:
Application Login URI: This is the main login url, where users will be invited and can login. Unfortunately, we don't have a url per organization but rather a global one per application. Therefore, we'll need to redirect to the specific tenant/organization url via code.
Allowed Callback URLs: These are the allowed urls to be called after login.
Allowed Logout URLs: These are the allowed urls to be called after logout.
Allowed Web Origins: These are the allowed urls to make calls to specific Auth0 functions.
3. Save some information: With your application created, you'll need the domain and Client ID.
In addition, you'll need the Audience API, found in this link.
When accessing any subdomain, the application doesn't know which tenant needs to configure. Therefore, we need to fetch the tenant information from a backend service. This information can be the backend endpoint, tenant name, logo, feature flags, etc, but the most important for this example is the organization ID in Auth0.
With this information we can direct the user to the correct organization, without the user having to enter the organization.
Since the same user in Auth0 can be connected to more than one organization, for the login flow to work as described above, we need to make some configurations.
4. On your application page: select the organization tab, then Business User as user types and Prompt for Organization as login flow.
This functionality allows you to select the organization if you have more than one and also if you start the login flow without indicating which organization you are trying to login to. Imagine we are logging in from our root domain: mydomain.com.
Following our flow, after login and organization selection, we are redirected back to our application. So we need to fetch the logged in user's JWT token and redirect them to the correct organization url, this url needs to be in the JWT. To do this, let's follow these steps:
5. Navigate to the Actions section: In the left nav panel, click on Actions and then click the Flows button.
6. Select the login flow: Click on the login flow to edit it.
7. Create an action: Create an action with trigger Login / Post Login. Name it Add metadata to access token.
8. Insert script to add metadata to access token: Be sure to replace my_application with the namespace you want to use. The namespace should be a URL but doesn't need to be an accessible URL. For example, you can use https://your_domain.com.
You can see the code here.
9. Save the rule: Drag the action to the diagram and click the Deploy button to save your rule.
Using custom namespaces is a recommended practice to avoid collisions with default JWT claims.
The information you are adding to the token should be considered sensitive, so make sure it is handled securely in your application. Now, whenever a user authenticates, their profile and organization information will be included in the access and ID tokens, and you'll be able to access it in your application as needed.
10. Create an organization: Now let's create an organization (tenant). Remember you can automate all these configurations via Auth0 APIs. On the organization -> Create Organization tab, enter the name and display name:
11. Add organization information: Now, on the organization page we can add metadata like tenant_url and tenant_name. We can also add other customized data your application may need to customize an organization.
12. Enable the connection for your organization: Still on the organization page, go to the Connections tab and click Enable Connections.
Select Username-Password-Authentication. If you want to enable authentication using a Google account, you can do it later.
You can choose whether to authorize any user to enter the organization or not.
That's it, now you have your connection configured:
13. Invite your users: On the Invitations tab you can invite users to your organization:
Code for login flow
The first step in our authentication flow is to fetch the tenant/organization settings. For this, we create a context called TenantContext that returns the settings based on the current application URL.
You can see the code here.
In the example above, we are retrieving the settings through an object, but we could have an API for this purpose.
After having the tenant/organization settings we can configure our application and call Auth0's login function indicating the ID of the current organization.
You can see the code here.
Note that this context is a wrapper around Auth0's official context, so we can support our B2B flow.
The full application is in this repository.
When receiving an invitation, the user will be redirected to the following page:
When accepting the invitation, the user must create a password.
And will be redirected to the organization's URL they were invited to:
To access the application in this tutorial, visit one of the companies and login/signin.
After that, if you access the main URL:
You can login and if you have access to both companies, you should choose which one to login to:
Auth0 has improved features to better support B2B flows. A future improvement is to have login URLs per organization instead of globally per application. Therefore, more than mastering a specific tool like Auth0, we need to understand the flows and concepts, to better choose our solutions. I hope this article is useful for your future applications.
Until next time, folks!