User Roles in Session Object

Hello, I have a question about user roles
In my session object, i dont see the roles array ( im assuming its an array)
I created a new api, created one user role (admin) , assigned it to one user, created some dummy permissions (read:dummy) , enabled this along with my management api ( default ), add the new trigger between sign in and complete
added this

exports.onExecutePostLogin = async (event, api) => {
  const namespace = "https://yourapp.com/roles";
  const roles = event.authorization?.roles || [];
  api.idToken.setCustomClaim(namespace, roles);
};

what am i missing, i don’t see the roles in my session object, what am i doing wrong ?

Hi @jsdev305

Thank you for reaching out to us!

Looking at your code, I would say that everything looks to be correct and it should be working well, so in this case, that issue could come from somewhere else. First thing I would check is that RBAC is enabled for your API, as if this is not turned on, the event.authorization.roles array will not be populated. You can check out the steps to enable RBAC in our following documentation : Enable Role-Based Access Control for APIs.

Additionally, in your application’s login request, please make sure the audience parameter matches the Identifier set in your API, as this can later be causing issues as well.

Hope this helped!
Gerald

Hello Gerald ,
I am not using the POST request way to request roles, but in Trigger Actions
Im using NextJs 15 and “@auth0/nextjs-auth0”: “^4.4.2”,

exports.onExecutePostLogin = async (event, api) => {
  const namespace = "https://yourapp.com/roles";
  let roles = [];

  const { ManagementClient } = require("auth0");

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

  const params = { id: event.user.user_id };

  try {
    // 🔥 Always fetch current roles from Auth0
    const assignedRoles = await management.users.getRoles(params);
    roles = assignedRoles.map((r) => r.name);

    // ✅ If no roles assigned, assign default role
    if (roles.length === 0) {
      const data = { roles: [event.secrets.defaultRoleId] };
      await management.users.assignRoles(params, data);

      // 🔁 Re-fetch roles after assignment
      const updatedRoles = await management.users.getRoles(params);
      roles = updatedRoles.map((r) => r.name);
    }
  } catch (e) {
    console.log("Failed to assign or fetch roles:", e);
  }

  // 🎯 Add roles to ID token
  api.idToken.setCustomClaim(namespace, roles);
};


but the thing is i dont event see the roles property in my session object, its always empty

i have two roles User and Admin, i have this

but still not even seeing the roles property

i tried new code, still no luck :face_with_raised_eyebrow:
why is this so difficult, seems easy, just return the user roles assigned to the user

exports.onExecutePostLogin = async (event, api) => {
  const namespace = "https://auth.myapp.local/roles";
  let roles = event.authorization?.roles || [];

  // Assign default role if no roles exist
  if (roles.length === 0) {
    const { ManagementClient } = require("auth0");
    
    const management = new ManagementClient({
      domain: event.secrets.domain,
      clientId: event.secrets.clientId,
      clientSecret: event.secrets.clientSecret,
    });

    const userId = event.user.sub;
    const params = { id: userId };
    //const params = { id: event.user.user_id };
    const data = { roles: [event.secrets.defaultRoleId] };

    try {
      await management.users.assignRoles(params, data);
      const updatedRoles = await management.users.getRoles(params);
      roles = updatedRoles.map((r) => r.name);
    } catch (e) {
      console.log("Failed to assign role:", e);
    }
  }

  // Always inject roles into ID Token
  api.idToken.setCustomClaim(namespace, roles);
};

this is my auth0 config

//src/lib/auth0.ts
import { Auth0Client } from "@auth0/nextjs-auth0/server";

export const auth0 = new Auth0Client({
  domain: process.env.AUTH0_DOMAIN,
  clientId: process.env.AUTH0_CLIENT_ID,
  clientSecret: process.env.AUTH0_CLIENT_SECRET,
  appBaseUrl: process.env.APP_BASE_URL,
  secret: process.env.AUTH0_SECRET,
  authorizationParameters: {
    scope: process.env.AUTH0_SCOPE,
    audience: process.env.AUTH0_AUDIENCE,
    prompt: "consent",
  },
});

Hello,
I have tried to add the exact code from the documentation, and it does not work

exports.onExecutePostLogin = async (event, api) => {
  const namespace = "https://auth.myapp.local";

  if (event.authorization && event.authorization.roles) {
    api.idToken.setCustomClaim(`${namespace}/roles`, event.authorization.roles);
    api.accessToken.setCustomClaim(`${namespace}/roles`, event.authorization.roles);
  }
};

@gerald.czifra

getting so close

exports.onExecutePostLogin = async (event, api) => {
  const namespace = "https://auth.myapp.local";

  console.log("Authorization object:", JSON.stringify(event.authorization, null, 2));

  if (event.authorization && event.authorization.roles) {
    console.log("Roles found:", event.authorization.roles);
    api.idToken.setCustomClaim(`${namespace}/roles`, event.authorization.roles);
    api.accessToken.setCustomClaim(`${namespace}/roles`, event.authorization.roles);
  } else {
    console.log("No roles found");
  }
};


so in the action logs, im seeing the roles, great, but they are never in the session response why ?

@gerald.czifra Is this also because I have the free tier account, I’m seeing the roles in the Action Logger, but not in the app, is this it?

Hi @jsdev305

I have tested things around and I managed to have the user assigned default roles and permissions on initial login. The issue is that since the user is being updated after they have logged in, the assigned roles will not be visible in the ID token on the first login. After the user logs in again, these roles will be visible however. You should be able to go around this issue by performing a silent authentication for the user after they have been assigned the roles.

The action code looks something like this:

exports.onExecutePostLogin = async (event, api) => {

  const namespace = "https://yourapp.com/roles";
  let roles = event.authorization.roles;

if (event.authorization && event.authorization.roles && event.authorization.roles.length > 0) {
    api.idToken.setCustomClaim(namespace, roles);
    return;
  }


  const { ManagementClient } = require("auth0");

  const management = new ManagementClient({
    domain: "{{AUTH0_DOMAIN}}",
    clientId: "{{CLIENT_ID}}",
    clientSecret: "{{CLIENT_SECRET}}",
  });

  const params = { id: event.user.user_id };

  try {
    if (roles.length === 0) {
      const data = { roles: ["rol_JO2OS5ByM085bM3l"] };
      await management.users.assignRoles(params, data);
    }
  } catch (e) {
    console.log("Failed to assign or fetch roles:", e);
  }

  // :dart: Add roles to ID token
  api.idToken.setCustomClaim(namespace, roles);
};

Please keep in mind that you will need to enable RBAC for your API in order for the permissions to be assigned to them.

If you have any other questions, let me know!

Kind Regards,
Nik

@nik.baleca

Hello Nik

exports.onExecutePostLogin = async (event, api) => {
  const namespace = "https://auth.myapp.local";

  console.log("Authorization object:", JSON.stringify(event.authorization, null, 2));

  if (event.authorization && event.authorization.roles) {
    console.log("Roles found:", event.authorization.roles);
    api.idToken.setCustomClaim(`${namespace}/roles`, event.authorization.roles);
    api.accessToken.setCustomClaim(`${namespace}/roles`, event.authorization.roles);
  } else {
    console.log("No roles found");
  }
};

this is my screenshot of the Trigger Action Log

I do not see this in the front end session

// app/layout.tsx
import { auth0 } from "@/lib/auth0";
import { Inter } from "next/font/google";
import "@/css/satoshi.css";
import "@/css/style.css";
import "react-toastify/dist/ReactToastify.css";
import { redirect } from "next/navigation";
import ToastContainerClientWrapper from "./components/Wrapper/ToastContainerClientWrapper";
import QueryWrapper from "@/components/QueryWrapper/QueryWrapper";

const inter = Inter({ subsets: ["latin"] });

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const session = await auth0.getSession();
  if (!session?.user) {
    // Redirect to the login page if the user is not authenticated
    redirect("/auth/login?prompt=consent");
  }

  return (
    <html lang="en">
      <body className={inter.className}>
        {/* add the query wrapper here */}
        <QueryWrapper>
          <ToastContainerClientWrapper />
          {children}
        </QueryWrapper>
      </body>
    </html>
  );
}

Both the action you have provided the the one I have posted above should display the roles inside the ID token.

It appears that perhaps the user object you are trying to display is incorrect.

Could you please try to decode the id token using jwt.io or to see if the same happens using our sample application?

Kind Regards,
Nik

1 Like

@nik.baleca
Ok, look , its there

so that mean i have decode it ?

It indeed appears that your token does contain the roles attribute inside it. Just to confirm, does it also have all the other components of the id token inside it? For reference, this would be a sample ID token:

{
  "iss": "http://my-domain.auth0.com",
  "sub": "auth0|123456",
  "aud": "my_client_id",
  "exp": 1311281970,
  "iat": 1311280970,
  "name": "Jane Doe",
  "given_name": "Jane",
  "family_name": "Doe",
  "gender": "female",
  "birthdate": "0000-10-31",
  "email": "janedoe@example.com",
  "picture": "http://example.com/janedoe/me.jpg"
}

Kind Regards,
Nik

yes, it has other information, but why are roles sensitive , so they are hidden in the jwt , while the name , email , picture, get from the session without decoding it ?

Great!

It appears that the JWT does indeed include the roles, meaning that the action functions as expected.

The information regarding the roles should be available inside the user object in your application under session.user. If this is not the case, could you please console log the user inside your application and let me know what is visible in there?

Kind Regards,
Nik

@nik.baleca This is what surprised me. I was expecting the property roles in the session object, not them to be hidden in the JWT

@nik.baleca Anyways, thanks for the help, now i know the sneaky roles are in jwt, if you find why , please DM or write it here, I think this would be helpful for other devs who are dealing with the same issue. Thanks again. Bye :waving_hand:

Hi!

Inside your application, can you try using the useUser() hook available in our SDK in order to see if the returned user profile contains the custom claims?

The following is an example available in our NextJS sample application which uses this said hook:

'use client';

import React from 'react';
import { Row, Col } from 'reactstrap';
import { useUser } from '@auth0/nextjs-auth0';

import Loading from '../../components/Loading';
import Highlight from '../../components/Highlight';

export default function Profile() {
  const { user, isLoading } = useUser();

  return (
    <>
      {isLoading && <Loading />}
      {user && (
        <>
          <Row className="align-items-center profile-header mb-5 text-center text-md-left" data-testid="profile">
            <Col md={2}>
              <img
                src={user.picture}
                alt="Profile"
                className="rounded-circle img-fluid profile-picture mb-3 mb-md-0"
                decode="async"
                data-testid="profile-picture"
              />
            </Col>
            <Col md>
              <h2 data-testid="profile-name">{user.name}</h2>
              <p className="lead text-muted" data-testid="profile-email">
                {user.email}
              </p>
            </Col>
          </Row>
          <Row data-testid="profile-json">
            <Highlight>{JSON.stringify(user, null, 2)}</Highlight>
          </Row>
        </>
      )}
    </>
  );
}

Alternatively, can you use a random name for the namespace and let me know if it works? Try using something like https://my-website.com/testing.

Let me know what the results are!

Kind Regards,
Nik

@nik.baleca
The user property does not have the roles, only properties such as email, name, image, nickname,
i have to use session idToken => decode it, and then i get the roles, my guess these claims are not part of the regular response, but included in the jwt , so at least i have a way to get the values. So thanks for trying again. :raising_hands: