Reputation: 11
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.
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
},
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
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