Skip to main content
Version: 2.0.0

Auth0 and Permit Integration

Introduction

Auth0 is one of the leading authentication platforms for developers, and Permit.io is the leading authorization platform for developers. By integrating Auth0 with Permit, you can easily add secure, feature-rich, and scalable authentication and authorization systems to your application.

You can see more information about RBAC Permit and Auth0 in this great blog post by Gabriel L. Manor

What you’ll build

In this tutorial, you’ll learn how to integrate Auth0 with Permit. We will create a WebApp that uses Auth0 for secure authentication and Permit.io for easily manageable authorization, allowing us to change our application's permissions on the fly.

Prerequisites

Getting Started

1. Setting up Permit

1.1. Setup roles in Permit

To sync users and roles from Auth0 to Permit, we must recreate identical roles in Permit.

info

If you don't use Auth0's roles, you can skip this step and create your roles according to your application's needs.

Setting up roles and resources can be done through the Permit dashboard, by using the Permit API, or with one of our SDKs.

1.2. Create the right resources in Permit

Create a corresponding resource in Permit for each of your application's resources. For instance, if you have a user management section in your app, you will need a user resource with create, delete, and get actions. If you have a task management section, you will need a task resource with create, delete, get, and check actions.

Setting up resources can be done through the Permit dashboard, by using the Permit API, or with one of our SDKs.

1.3. Set up the right permissions in Permit.io:

For each role you created in step 1.1, you will need to set up the right permissions.

Check each action you want to allow for each resource in each role.

As noted earlier, these permissions can be set up through either the Permit policy editor or by using the Permit API.

2. Integrate your own application

2.1. Auth0

As we want to use Auth0 for authentication, we will use the Auth0 SDK to get each user's information and access token. You will need to add in your FE code a check to see if the user is logged in and redirect them to the login page if they are not.

In our demo application it look like this: We have the Auth0 useUser at index.tsx that will give us the user's information, and the userProvider in _app.tsx that redirects the user to the login page if they have not logged in.

// index.tsx
//...
const { user, isLoading } = useUser();
//...
// _app.tsx
//...
<UserProvider>
<Component {...pageProps} />
</UserProvider>
//...

On your backend side, you will need to add code that will check the validity of the Auth0 JWT and retrieve user information from it.

In our demo app, we used an Auth0 and NextJs integration to achieve this with very few lines of code (as you can see in [auth0].ts and the getSession in tasks.ts).

// pages/api/auth/[...auth0].ts
import { handleAuth } from "@auth0/nextjs-auth0";

export default handleAuth();
// pages/api/tasks.ts
import { getSession } from "@auth0/nextjs-auth0";
//...
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const session = await getSession(req, res); // if the user is not logged in, session will be null
if (!session?.user) {
res.status(401).json({ message: 'unauthorized' });
return;
}
//...

2.2. Permit

As we want to use Permit for authorization, we will use the Permit SDK to get the user's permissions and check if they have the right ones.

Authorization is only done on the backend side, so we will use the permit checks before each action. You can add them to your CRUD APIs or as a middleware for the entire API.

All you need to do is add the permit.check function before each action you want to protect.

permit.check(
session?.user.sub, // the user's id
"delete", // the action name
"task" // the resource name
);

In the demo app, you can see it at pages/api/tasks.ts as a middleware that manages all task CRUD requests.

// pages/api/tasks.ts
import { Permit } from "permitio";

export const permit = new Permit({
pdp: "https://cloudpdp.api.permit.io",
token: process.env.PERMIT_SDK_TOKEN,
});
export default withApiAuthRequired(async function handler(
req: NextApiRequest,
res: NextApiResponse<Task | Task[] | Response>
) {
// Auth0 is checking if the user is logged in (who the user is)
const session = await getSession(req, res);
if (!session?.user) {
res.status(401).json({ message: "unauthorized" });
return;
}
// Permit is checking if the user has the right permissions (what the user can do)
const isAllowedForOperation = await permit.check(
(session?.user?.sub as string) || "", // the user identifier (Permit user id / or Permit user key, in this example we set the Auth0 user id as the key)
req.method?.toLowerCase() as string, // the action (can be: get, post, delete)
"task" // our resource key
);
if (!isAllowedForOperation) {
res.status(403).json({ message: "forbidden" });
return;
}
//... handle the request
});

3. The integration

Now that we have the user's information and permissions, we can integrate them. We need to make sure that Permit is synced with Auth0 so that each user who logs in will have the right roles and permissions.

3.1. Sync Auth0 with Permit

First, we need to make sure each user that logs into our app is synced with Permit. This can be done by adding a postLogin function that will be called after each login.

This function will make sure that the users and roles are synced with Permit. You can check the NextJS demo app for the full code.

If you are using Auth0 roles you will need to add them to the user's token in order to easily sync them with Permit. This can be done following this Auth0 guide.

Here's a quick recap on how this can be done (Please use the Auth0 guide for a more detailed description):

  • Go to your Auth0 dashboard > Applications > API.
  • Create an API if you don't have one.
  • Click on the three dots on the right of your API and click on settings.
  • Scroll down to RBAC Settings and enable Add Permissions in the Access Token.
  • Click on Save Changes.

In short, you need to navigate to the Auth0 dashboard and create a new rule https://manage.auth0.com/dashboard/[your-region]/[your-tenant-name]/rules. The Auth0 rule that you need to add will look like this:

function (user, context, callback) {
const namespace = 'my_app_name';
const assignedRoles = (context.authorization || {}).roles;

let idTokenClaims = context.idToken || {};
let accessTokenClaims = context.accessToken || {};

idTokenClaims[`${namespace}/roles`] = assignedRoles;
accessTokenClaims[`${namespace}/roles`] = assignedRoles;

context.idToken = idTokenClaims;
context.accessToken = accessTokenClaims;

callback(null, user, context);
}

Now you can see that the session.user object from Auth0 has my_app_name/roles section with our roles (of course, you can set <my_app_name> to whatever you want):

auth0User {
'my_app_name/roles': [ 'admin' ],
nickname: 'test',
name: 'test@permit.io',
picture: 'https://s.gravatar.com/avatar/test.png',
updated_at: '2023-07-05T10:56:35.831Z',
email: 'test@permit.io',
email_verified: false,
sub: 'auth0|64a139da377fefdcbxxxxxxx',
sid: 'I9xntALjR3M37Iw62Lgc3xxxxxxxxxx'
}

Then when a user logs into our app, we can also sync their roles with Permit by using permit.api.syncUser function and then assign the roles to the user using permit.api.assignRole function.

// sync the user with Permit
const userObj = {
key: auth0User.sub, // the user's key needs to be unique, you can use the Auth0 user id, we will use it to perform the permit checks.
email: auth0User.email,
first_name: auth0User.name,
attributes: {}, // you can add more attributes here, mostly used for ABAC
};

const permitUser = await permit.api.syncUser(userObj);

auth0User.roles.map(async (role) => {
// assign the roles to the user
const role = await permit.api.assignRole(
permitUserObj.key, // the user's key
role, // the role name
[tenantKey] // the tenant key set `default` if you don't use multi-tenancy
);
});

Now if you assign a role to a user (Either in Auth0 or in Permit), the user will have the correct roles and permissions. (Make sure you use the exact same role names in both Auth0 and Permit - they are case-sensitive!)

Check out the postLogin page in this NextJS example to see a working example.

3.2. Removing a user

If you want to remove a user from your app, make sure you remove the user from both Auth0 and Permit. You can do this by adding the following code to your remove user function:

const removedUser = await permit.api.deleteUser(permitUserObj.key);

4. Let's test it!

Create a new user in Auth0 and assign them with the manager role. Then, log in to the app with the new user. You should be able to create tasks, but not delete them. Now go to the Permit policy editor and add the delete action to the manager role and save the policy.

Otherwise, you can go to the Permit user management screen and add the admin role to the logged-in user. You should now be able to delete tasks immediately without logging out and logging in again.

info

If you remove a role in Permit but the user still has the role in Auth0, our code will reassign the role to the user. You can fix this by adding the following code at the begging of the postLogin function:

const session = await getSession(req, res);
// check if user exists in permit
const user = await permit.api.getUser(session?.user?.sub);
if (user) {
console.log("user exists");
redirect(res, 302, "/");
return { props: {} };
}