Next.js Discord

Discord Forum

Adding "admin" role for role based auth

Unanswered
TK2 posted this in #help-forum
Open in Discord
TK2OP
Having trouble exposing the role value in my session for role based auth.
Here's my schema.prisma
model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime
  user         User     @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  password      String
  username      String    @unique
  isAdmin       Boolean   @default(false)
  role          String?   
  createdAt     DateTime  @default(now())
  updatedAt     DateTime  @updatedAt
  accounts      Account[]
  sessions      Session[]
}

type dec
import NextAuth from "next-auth"

declare module "next-auth" {
  interface User {
    username: string
    id: string
  }
  interface Session {
    user: User & {
        username: string,
        role: string
      }
    token: {
      username: string,
    }
  }
}

And these are my two jwt and session callbacks
    callbacks: {
      async jwt({ token, user, profile, account }) {
        // console.log("token in jwt callback", token);
        if(user) {
          return {
            ...token,
            username: user.username,
            
          }
        }
        return token;
      },
      async session({ session, token, user }) {
        // console.log("session callback", session, token);
        if(session.user) {
          return {
            ...session,
            user: {
              ...session.user,
              username: token.username,
              role: session.user.role
            }
          }
        }
        return session;
      }
    }

24 Replies

TK2OP
I'm trying to limit access to an admin dashboard but when I fetch the session and try to match the role as string === "admin" I can't find role in the object.
const AdminPage = async () => {
  const session = await getServerSession(options);

  if(session?.user.role === "admin") {
    return <h2> Admin Page - welcome back {session.user.username}</h2>
  }

  return (
    <h2> You don't have admin permission to see this page </h2>
  )
}
For more context here's my authorize function
            async authorize(credentials) {
              console.log("credentials inside authorize function", credentials);
              if (!credentials?.email || !credentials.password) {
                return null;
              }
              const existingUser = await db.user.findUnique({
                where: { email: credentials?.email }
              })
              if(!existingUser) {
                return null;
              }
              const passwordMatch = await compare(credentials.password, existingUser.password); 

              if(!passwordMatch) {
                return null;
              }

              // Check role somewhere?
              const admin = existingUser.role === "admin"
              console.log("isAdmin?", admin)
              console.log("typeof", typeof admin);

              return {
                id: `${existingUser.id}`,
                username: existingUser.username,
                email: existingUser.email,
                role: admin
              }
            }
For this I had to query my db from user email and it gave me access to the role, I’m sure it’s nowhere near the most optimal solution but it works
User email that was in my session
@TK2
TK2OP
@redbabinks Where in your auth flow did you check the role? I feel like something can be added into the authorize function above to achieve what I want but the logic for it is escaping me
You need to map the role inside session callback. Try logging with different possibilities to see how it appears. As far as I remember the flow was like:
User from providers > signIn callback > jwt callback > session callback
From your credentials provider, you need to send the entire object
Then, in the jwt, you need to read all properties and make sure they are not lost in the multiple calls
And then you need to put them in the session callback
To access properties using session.user.x, you will need to set them in the session callback by session.user.x = something
TK2OP
@Riday 💙
    callbacks: {
      async jwt({ token, user, profile, account, session }) {
        if(user) {
          return {
            ...token,
            username: user.username,
            role: user.role
          }
        }
        return token;
      },
      async session({ session, token, user }) {
          return {
            ...session,
            user: {
              ...session.user,
              username: token.username,
              role: session.user.role
            }
          }
      }
I found the solution actually I needed to update the type in next auth types file
TK2OP
@redbabinks care to share?
import "next-auth"

declare module "next-auth"{
  interface Session{
    user:{
      name:string;
      email:string;
      image:string;
      role:string;
    }
  }
}
@/types/next-auth.d.ts
you can ofcourse put everything you want inside of your session
TK2OP
@redbabinks Sweet thanks, can you also share your jwt and session callbacks?
The session is just following this type
Oh, lmao. You could technically access it. You just didn't get the types
Forgot about that part