Sitthata
Sitthata

Reputation: 11

Custom Role-Based Access Control on Next.js and NextAuth

I'm working on a Next.js project and looking to implement a role-based authentication system using NextAuth with the Prisma adapter. I aim to allow users to have multiple roles (e.g., admin, user, company) but am encountering difficulties due to the lack of specific examples in the official NextAuth documentation for this use case.

You can see full source code here - https://github.com/Sitthata/next-auth/tree/first-dev-roles

My current Prisma schema models a user with a single role as shown below. I've added a custom role field in the User model, but this approach limits a user to only one role.

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  role          String    @default("user") // Added custom role here
  accounts      Account[]
  sessions      Session[]
}

In authOptions.ts, I'm configuring my authentication providers and callbacks as follows:

export const authOptions = {
  adapter: PrismaAdapter(prisma) as Adapter,
  // Configure one or more authentication providers
  providers: [
    GithubProvider({
      profile(profile: GithubProfile) {
        console.log(profile);
        return {
          id: profile.id.toString(),
          name: profile.login,
          email: profile.email,
          image: profile.avatar_url,
          role: profile.role ?? "user",
        };
      },
      clientId: process.env.GITHUB_ID as string,
      clientSecret: process.env.GITHUB_SECRET as string,
    }),
    // ...add more providers here
  ],
  
  debug: process.env.NODE_ENV === "development",
  session: {
    strategy: "jwt",
  },
  callbacks: {
    // Ref: https://authjs.dev/guides/basics/role-based-access-control#persisting-the-role
    async jwt({ token, user }) {
      if(user) token.role = user.role
      return token
    },
    async session({ session, token }) {
      session.user.role = token.role
      return session
    }
  },
} satisfies NextAuthOptions;

Right now it's very basic. In my understanding, right now I'm using Github provider profile to add role to my database and handle the user session using callbacks

Now, there're some ideas that I have to implement this features but I don't know if it possible.

  1. Customizing the signIn callback: Implement a custom function within the signIn callback to assign default or specific roles to users upon sign-up or sign-in. This function would interact with Prisma to manage role assignments. (These are GPT answer)
callbacks: {
    async signIn({ user, account, profile }) {
      const userRoles = ['default-role']; // Define default roles
      // Check if the user exists and assign roles if new
      const existingUser = await prisma.user.findUnique({
        where: { email: user.email },
      });

      if (!existingUser) {
        const newUser = await prisma.user.create({
          data: {
            email: user.email,
            name: user.name,
            // Other user fields...
          },
        });

        // Assign default roles to new user
        await Promise.all(
          userRoles.map(async (roleName) => {
            let role = await prisma.role.findUnique({
              where: { name: roleName },
            });
            if (!role) {
              // Create the role if it doesn't exist
              role = await prisma.role.create({
                data: { name: roleName },
              });
            }
            await prisma.userRole.create({
              data: {
                userId: newUser.id,
                roleId: role.id,
              },
            });
          })
        );
      }
      return true; // Continue the sign-in process
    },
  1. Maybe manully create seperate layer to handle the updates of roles after user sign in.

Given the above context, I have several questions:

I appreciate any insights or examples from those who have tackled similar challenges.

Upvotes: 0

Views: 630

Answers (1)

Daniel
Daniel

Reputation: 1205

You can try this: https://www.npmjs.com/package/prisma-rbac

And then just use permissions field to adjust UI rendering

Upvotes: 0

Related Questions