Best Practices for Retrieving User Permissions in a SPA (Vue.js) with Node.js API using Auth0

I am developing a Single Page Application (SPA) using Vue.js with a Node.js API, where I am utilizing Auth0 for authentication and access control. However, I have encountered challenges in obtaining user permissions on the SPA side and am seeking guidance on best practices for handling this situation.

Scenario:

I have two endpoints in my API:

  • GET - /api/v1/projects /* read:project-list */
  • GET - /api/v1/users /* read:user-list */

And two corresponding screens in the SPA:

  • Project List /* read:project-list */
  • User List /* read:user-list */

My goal is to ensure that different users have access to specific functionalities based on their permissions. For example, the user Jhon, who is an administrator, has permissions to access both screens and endpoints, while James, a project manager, has access only to the “Project List” screen and endpoint.

Challenges on the SPA:

While I have successfully implemented permission validation in my Node.js API using features provided by Auth0, I am facing difficulties in finding the best practice for accessing this information on the SPA side.

Considered Options:

  1. Custom Endpoint:
  • Create a custom endpoint in the API that, based on the access token, returns the user’s permissions/roles.
  1. Use Auth0 API:
  • Create an API endpoint to consume https://{yourDomain}/api/v2/users/USER_ID/permissions and retrieve the permissions/roles.
  1. Custom Claims in the Token ID:
  • Add permissions information directly to the Token ID and access the token through the SDK in the SPA { idTokenClaims } = useAuth0().

Challenges with Custom Claims in the Token ID:

I attempted to implement an action that sets the value within the Token ID but encountered difficulties in capturing user permissions, such as read:users. My current code:

exports.onExecutePostLogin = async (event, api) => {
	const namespace = 'http://localhost:5173';
	const assignedRoles = (event.authorization || {}).roles;
	api.idToken.setCustomClaim(`${namespace}/roles`, assignedRoles);
};

Question:
Given these options and challenges, I am seeking guidance on which approach is more recommended or if there is a standard practice for handling the retrieval of user permissions in a SPA with an API using Auth0. Any suggestions or similar experiences that can guide my decision would be greatly appreciated.

1 Like

Hey there @viniciusteixeiradias !

Thanks for the detailed description, very helpful :slight_smile:

I think your best bet is to add the permissions to ID tokens by way of the Management API/Node Management Client by way of an action as outlined here:

Hey @tyf !

Thanks for the suggestion and the reference to using the Management API/Node Management Client to add permissions to ID tokens. I appreciate it.

However, I’d like to delve deeper into the advantages of this approach compared to the solution I’m currently using, which involves creating a custom endpoint in the API that, based on the access token, returns user permissions/roles.

My current solution has been working well for me, but I’m always looking for optimizations and best practices. Could you share some specific advantages of the suggested approach?

Thanks again for the guidance!

1 Like

No problem, happy to help where I can!

If your current solution works well and has auth in place then that’s totally OK as well - Using the custom claim/node management client approach is just the most straightforward without having to implement too much in the way customization :slight_smile:

1 Like

Hey @tyf, thanks again for your help and your time.

I’m going to share my solution here to help anyone else going through the same situation because for me it was difficult to figure out how to do it. So I’m going to provide a solution using client management and also an endpoint to get this information:

  • Client Management:

    • Create M2M App

      1. Go to Dashboard > Applications;
      2. Click Create a M2M (Machine to Machine) application;
      3. Choose your application name and select the API you want to authorize. It probably will
        be “Auth0 management API”;
      4. Select the permissions that you will need when start to use the Cliente Management;
      5. Click to create;
    • Create a new Flow:

      1. Go to Dashboard > Actions > Library;
      2. Click to “Build Custom”
      3. Fill the name of the action, the trigger that I am using is “Login / Post Login” and you can choose the runtime, in my case I chose Node 18 (recommended).
      4. Setup your secrets by clicking on the “key” icon, the secrets that you may need are: “domain”, “clientId” and “clientSecret” (All this information you can get on your M2M app / settings).
      5. Paste the code (that I will provide below) in your Action and change whatever you want:
      6. Run the code and check if you get any errors (it should work just fine).
      7. Access your application’s SPA, obtain the Token ID and validate the information contained therein;

Notes: You must use any user who has permission;

/**
* Uses ManagementClient to get all user permissions
*
* @param {Event} event - Details about the user and the context in which they are logging in.
*/
const fetchPermissionNames = async (event) => {
  const ManagementClient = require('auth0').ManagementClient;
  const management = new ManagementClient({
    domain: event.secrets.domain,
    clientId: event.secrets.clientId,
    clientSecret: event.secrets.clientSecret
  });

  const params = { id: event.user.user_id, page: 0, per_page: 50, include_totals: false };
  const userPermissions = await management.getUserPermissions(params);

  return userPermissions.map(({ permission_name }) => permission_name);
}

/**
* 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) => {
  try {
    const namespace = 'http://localhost:5173';

    const assignedRoles = (event.authorization || {}).roles;
    const permissionNames = await fetchPermissionNames(event);
  
    api.idToken.setCustomClaim(`${namespace}/roles`, assignedRoles);
    api.idToken.setCustomClaim(`${namespace}/permissions`, permissionNames);
  } catch (e) {
    console.error('Error on execute post login: ', e)
  }
};

  • NodeJS API:

    • Create an API:

      1. Go to Dashboard > API;
      2. Click Create an API;
      3. Choose the name / identifier and Signing Algo of your API;
      4. Click to create;
      5. Go to API Settings and enable the “RBAC Settings”: “Enable RBAC” and “Add permissions in the Access Token”"
      6. Start to build you API (You can copy the quick start code)
      7. Paste the endpoint below in your code and you should have access to the user’s permissions based on the Access Token:
app.get('/permissions', jwtCheck, (req, res) => {
  const permissions = req.auth?.payload["permissions"] || []
  res.json(permissions);
})

.

This is basically how you can get these permissions.

1 Like

That’s great @viniciusteixeiradias thanks a bunch for sharing with the community :slight_smile:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.