Setting Up Auth0 with Next.js: User & Admin Roles

Chamidu Himantha
Inforwaves Blogs
Published in
11 min readSep 14, 2024
Photo by Philipp Katzenberger on Unsplash

Why use Auth0 with Next.js?

In today’s digital landscape, user authentication and authorization are critical components of any web application. Auth0 is a flexible, drop-in solution that simplifies adding authentication and authorization services to your applications with minimal setup. It supports various methods, including social login, multi-factor authentication, and enterprise federation, making it highly versatile for web apps.

With Next.js, you get the added benefit of server-side rendering and static site generation. Pairing Auth0 with Next.js allows you to create a secure, scalable, and modern web application that handles user login and role-based access control (RBAC) efficiently.

What This Blog Will Cover

In this blog, we’ll walk you through setting up Auth0 with Next.js, focusing on creating default user and admin roles to manage access control effectively. We’ll cover the following topics:

  1. Setting Up Auth0 with Next.js: Learn how to configure your Next.js application to use Auth0 for authentication.
  2. Creating and Managing Roles in Auth0: Understand how to create user roles like “Admin” and “User” and assign them within the Auth0 dashboard.
  3. Integrating Authentication and Authorization in Next.js: Discover how to implement role-based access control (RBAC) to protect routes and manage user access.
  4. Testing and Debugging: Learn how to test your authentication setup to ensure that everything works as expected.

By the end of this guide, you will have a fully functional authentication system integrated into your Next.js application, with both user and admin roles configured for secure and efficient access management.

GitHub Link : https://github.com/chamidu044/test-auth0.git

1. Setting Up Auth0 with Next.js

Step 1: Create Next.js Project

Firstly, create a Next.js project using the below command on your terminal.

npx create-next-app@latest
What is your project named? test-auth0
Would you like to use TypeScript? (Yes)
Would you like to use ESLint? (Yes)
Would you like to use Tailwind CSS? (Yes)
Would you like your code inside a `src/` directory? (No)
Would you like to use App Router? (recommended) (Yes)
Would you like to customize the import alias (`@/*` by default)? (No)

Step 2: Install Auth0 SDK

Install the Auth0 SDK by running the following command in your Next.js project directory:

npm install @auth0/nextjs-auth0

Step 3: Set Up API Routes

Then on your app directory create a route.ts file within the below folder structure as api/auth/[auth0]/ . (Refer github repository for file structure)

Here’s the code for route.ts to add login, logout, and callback routes:

# sets up the necessary routes for Auth0 to handle login/logout
import { handleAuth } from '@auth0/nextjs-auth0';

export const GET = handleAuth();

Step 4: Configure Auth0 Dashboard

In the Auth0 dashboard:

  1. Create a Regular Web Application and select Next.js as the base.
  2. Under Settings > Application URIs,
  • Update the Allowed Callback URLs to: http://localhost:3002/api/auth/callback (change localhost:3002 if needed).
  • Update the Allowed Logout URLs to:
    http://localhost:3002/, http://localhost:3002/api/auth/logout

Step 5: Set Up Environment Variables

Create a .env.local file in the root directory of your project. Alternatively, you can add these configurations to the code, but using an env is recommended.

Install thedotenv package to support reading from the env:

yarn add dotenv

Paste the following credentials, which you can obtain from your Auth0 dashboard:

# generate a auth0 secret by terminal. 
# Use the below command to generate a random string value
# node -e "console. log (crypto. randomBytes (32) . toString ('hex'))"
AUTH0_SECRET=long string value
# The base url of your application
AUTH0_BASE_URL='http://localhost:3002'
# The url of your Auth0 tenant domain
AUTH0_ISSUER_BASE_URL='https://<YOUR_AUTH0_DOMAIN>.auth0.com'
# Your Auth0 application's Client ID
AUTH0_CLIENT_ID='<YOUR_AUTH0_CLIENT_ID>'
# Your Auth0 application's Client Secret
AUTH0_CLIENT_SECRET='<YOUR_AUTH0_CLIENT_SECRET>'

Step 6: Implement Auth0 Login in Next.js

Update app/page.tsx to direct users to the Auth0 login page. After login, users will be redirected to /dashboard:

"use client";
import React from 'react';
import Link from 'next/link';
import { useUser } from '@auth0/nextjs-auth0/client';

const LoginSignup: React.FC = () => {
const { user, error, isLoading } = useUser();

return (
<div className="flex flex-col items-center justify-center h-screen bg-gray-100">
<h1 className="text-4xl font-bold mb-8 text-black">Welcome to the Platform</h1>
<div className="space-x-4">
<Link href="/api/auth/login?returnTo=/dashboard&prompt=login"
className="px-6 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition">
Login
</Link>
</div>
</div>
);
};

export default LoginSignup;

Step 7: Wrap the App with <UserProvider>

To manage authentication across your app, wrap the entire app in the <UserProvider> component by updating layout.tsx:

wrapping the application with UserProvider

2. Creating and Managing Roles in Auth0

Roles help control access to specific routes or features in your app. Auth0 lets you create and manage these roles in the dashboard.

Step 1: Create Roles in Auth0

In the Auth0 Dashboard, navigate to User Management > Roles to create roles like “User” and “Admin.” You can assign these roles to users manually in the dashboard.

Initializing user roles in auth0

Step 2: Assign Default Role Using Auth0 Actions

To automatically assign a default role to users upon login, we’ll use Auth0 Actions. This is useful for managing RBAC in your app.

If we are integrating role-based access control (RBAC) we have to develop a logic to assign Employee (default role) to users when logging in and we can manually assign Admin role from auth0 dashboard.

Step 2.1: Create a Machine-to-Machine (M2M) Application

First, you’ll need to create a Machine-to-Machine (M2M) application in Auth0 to allow your Post-Login Action to communicate with the Auth0 Management API for assigning roles.

  1. Go to Applications > Create Application, and select Machine-to-Machine (M2M).
  2. In the Applications > APIs > API Permissions section, enable access to the Auth0 Management API with the following scopes:
  • update:users
  • read:users
  • read:roles
  • update:roles
Default System API

If you are using the System API then it is already enabled with permissions. In your M2M Application give access to auth0 management API for access it.

M2m application Auth0 Management API Access

After setting up the M2M application, take note of the Client ID, Client Secret, and your Auth0 domain. You'll need these values in the next step.

Step 2.2: Set Up Post-Login Action

  • Go to Actions > Library > Create Action and choose Build from Scratch and select below options and create.
Login actions creation
  • Paste the following code into the action editor, replacing YOUR_DEFAULT_USER_ID with your role ID:
/**
* Handler that will be called during the execution of a PostLogin Flow.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PostLoginAPI} api - Interface whose methods can be used to change the behavior of the login.
*/
exports.onExecutePostLogin = async (event, api) => {

// Check if the user has a role assigned
if (event.authorization && event.authorization.roles && event.authorization.roles.length > 0) {
return;
}

// Create management API client instance
const ManagementClient = require("auth0").ManagementClient;

const management = new ManagementClient({
domain: event.secrets.DOMAIN,
clientId: event.secrets.CLIENT_ID,
clientSecret: event.secrets.CLIENT_SECRET,
});

const params = { id : event.user.user_id };
const data = { "roles" : ["YOUR_DEFAULT_USER_ID"] };

try {
await management.users.assignRoles(params, data);
} catch (e) {
console.log(e);
}
};
  • Set up the required secrets (DOMAIN, CLIENT_ID, CLIENT_SECRET) in the Secrets tab, using the values from your M2M application.
  • Install auth0 with auth0@latest command in Dependancies Tab.
Auth0 actions Dependancies
  • Finally, deploy the action and enable it in your Post-Login Flow by going to Actions > Flows > Login and adding the custom action.
Login flow with custom action

3. Integrating Role-Based Access Control (RBAC) in Next.js

Once roles are set up, you’ll need to integrate RBAC in Next.js to restrict routes or content based on user roles.

Step 1: Create API Route for Role Management

Install axios depending on your package manager:

npm install axios

In app/api/roles/route.ts, handle requests to fetch user roles:

import { NextRequest, NextResponse } from 'next/server';
import axios from 'axios';

// step 01
const getAuth0Token = async () => {
const response = await axios.post(`https://<YOUR_AUTH0_DOMAIN>.auth0.com/oauth/token`, {
client_id: process.env.NEXT_PUBLIC_AUTH0_M2M_CLIENT_ID,
client_secret: process.env.NEXT_PUBLIC_AUTH0_M2M_CLIENT_SECRET,
audience: 'https://<YOUR_AUTH0_DOMAIN>.auth0.com/api/v2/',
grant_type: 'client_credentials',
});

return response.data.access_token;
};

// step 02
const getUserRolesFromAuth0 = async (userId: string, token: string) => {
const response = await axios.get(`https://<YOUR_AUTH0_DOMAIN>.auth0.com/api/v2/users/${userId}/roles`, {
headers: {
Authorization: `Bearer ${token}`,
},
});

return response.data;
};

// step 03
export async function POST(req: NextRequest) {
try {
const { userId } = await req.json();
const token = await getAuth0Token();
const roles = await getUserRolesFromAuth0(userId, token);
return NextResponse.json({ roles });
} catch (error) {
return NextResponse.json({ error: 'Failed to fetch user roles' }, { status: 500 });
}
}

.env file configuration with M2M Auth0 credentials as below


#M2M AUTH0
NEXT_PUBLIC_AUTH0_M2M_CLIENT_ID='YOUR_CLIENT_ID'
NEXT_PUBLIC_AUTH0_M2M_CLIENT_SECRET='YOUR_CLIENT_SECRET'
  • Step 01 : Fetching an Auth0 Access Token

In the RBAC setup, we need to communicate with the Auth0 Management API to fetch user roles. This requires an access token, which can be obtained by authenticating through the Auth0 Machine-to-Machine (M2M) application. This sends a request to the Auth0 OAuth token endpoint, exchanging the machine-to-machine client ID and secret for an access token.

  • Step 02 : Fetching User Roles from Auth0

Once the token is available, we can use it to query the Auth0 API to get the roles assigned to a specific user. This makes a GET request to the Auth0 Management API to retrieve the roles assigned to the user with the specified userId

  • Step 03 : API Route to Handle Role Requests

Next, we create an API route in Next.js that handles requests to fetch user roles. This API route interacts with the functions above.

On the client side, fetch user roles when a user logs in. Create a dashboard/page.tsx component:

"use client";
import React, { useEffect, useState } from 'react';
import { useUser } from '@auth0/nextjs-auth0/client';
import axios from 'axios';

const Dashboard: React.FC = () => {
const { user, error, isLoading } = useUser();
const [roles, setRoles] = useState<string[]>([]);

// Step 04
useEffect(() => {
const fetchUserRoles = async () => {
if (user) {
try {
const response = await axios.post('/api/roles', { userId: user.sub });
response.data.roles.map((role: any) => role.id);
setRoles(response.data.roles.map((role: any) => role.name));
} catch (err) {
console.error('Error fetching user roles:', err);
}
}
};
fetchUserRoles();
}, [user]);

if (isLoading) return <div>Loading...</div>;
if (error) return <div>{error.message}</div>;

const logout = () => {
const logoutUrl = "http://localhost:3002/api/auth/logout";
window.location.href = logoutUrl;
};

// step 05
return (
<div className="h-screen flex flex-col items-center justify-center bg-gray-100 text-black">
<div className="bg-white p-8 rounded shadow-md w-full max-w-md text-center">
{user ? (
<>
<h1 className="text-2xl font-bold">Welcome, {user.name}!</h1>
<p className="mt-4">Here is your Auth0 user data:</p>
<table className="bg-gray-100 p-4 rounded mt-4 text-left w-full">
<tbody>
<tr>
<td className="font-bold">Name:</td>
<td>{user.name}</td>
</tr>
<tr>
<td className="font-bold">Email:</td>
<td>{user.email}</td>
</tr>
<tr>
<td className="font-bold">Nickname:</td>
<td>{user.nickname}</td>
</tr>
<tr>
<td className="font-bold">Picture:</td>
<td><img src={user.picture ?? ''} alt="User Picture" className="rounded-full w-16 h-16" /></td>
</tr>
<tr>
<td className="font-bold">Auth0 ID:</td>
<td>{user.sub}</td>
</tr>
<tr>
<td className="font-bold">Roles:</td>
<td>{roles.length > 0 ? roles.join(', ') : 'No roles assigned'}</td>
</tr>
</tbody>
</table>
{roles.includes('Admin') && (
<button className="mt-6 bg-blue-500 text-white rounded p-2">Admin Settings</button>
)}
<button onClick={logout} className="mt-6 px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600">
Logout
</button>
</>
) : (
<p>Please log in to view your dashboard.</p>
)}
</div>
</div>
);
};

export default Dashboard;
  • Step 04 : Fetching Roles on the Client-Side

Create the page.tsx file in app > dashboard folder path to create a route as /dashboard from your app directory.

On the client side, we can use React’s useEffect hook to fetch user roles when a user logs in. This ensures that the role data is available as soon as the user accesses the application.

This runs when the user object is available (after the user logs in). It sends the userId to the API route we created, retrieves the user roles, and stores them in the component's state (roles).

  • Step 05 : Displaying User Information and Roles

Once the roles have been fetched, we display the user’s information, including their name, auth0 ID and assigned roles, on the dashboard.

4. Testing and Debugging

  • Create a Test User: Go to the Auth0 Dashboard and create a test user, or use the signup option from auth0 login from web application created.
  • Login as the Test User: Log in to your application using this test account. Upon successful login, your Auth0 Post-Login action should assign the default role to the user.
  • Go to the Auth0 Dashboard.
  • Navigate to User Management > Users.
  • Click on the user and check the Roles tab. You should see the assigned role (e.g., “Employee”).
  • Create another user from auth0 dashboard and assign the role as “Admin”.
  • Try login from these 2 accounts and check how the user details are shown.
auth0 user data

Troubleshooting Tips:

  • Incorrect Callback URL: Double-check the callback URL in your Auth0 dashboard if you’re not being redirected correctly.
  • Roles Not Assigned Automatically: Verify that your Auth0 Post-Login Action is correctly deployed and that the role ID is correct.
  • Environment Variables: Ensure all environment variables are set up properly in your .env.local file.

Below is the folder structure and the github link to my repository for further clarification on setting up the NextJs with Auth0 Authorization.

GitHub Link : https://github.com/chamidu044/test-auth0.git

Useful Links :

Folder structure for NextJs SetUp

Conclusion

In this guide, we’ve walked through integrating Auth0 with Next.js to set up user authentication and role-based access control. You’ve learned how to create and manage user roles in Auth0, implement RBAC in your Next.js app, and test the functionality.

With this setup, you can now manage user roles dynamically and secure your app’s routes and features based on roles like User and Admin. From here, consider expanding the system by adding additional roles, integrating multi-factor authentication (MFA), or exploring more advanced actions with Auth0.

--

--