Next-Auth getServerSession not retrieving user data in Nextjs 13.4 API Route
Asked Answered
S

5

8

I need to access user session data in a Next-Auth/Nextjs 13.4 API Route. I have configured the JWT and Session callback; however, the user data I specified in the callback function does not translate to what getServerSession is pulling in an API route. However, the session data does correctly reflect in a Client page when using useSession() so I'm not sure what the issue is.

[...nextauth]/route.js

import { connectToDB } from "@/app/server/db";
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import bcrypt from 'bcrypt';

// Authorize function
async function authorize(credentials) {
    const { email, password } = credentials;
    const { db } = await connectToDB("Tenants");
    const user = await db.collection("Users").findOne({ email });

    if (user) {
        const isPasswordValid = await bcrypt.compare(password, user.password);
        if (!isPasswordValid) { return null; }
        await db.collection("Users").updateOne({ email }, { $set: { lastLoggedIn: new Date() } });
        return user
    }
    return null;
}

export const authOptions = NextAuth({
    providers: [
        CredentialsProvider({
            authorize
        })
    ],
    callbacks: {
        async jwt({ token, user }) {
            return { ...token, ...user };
        },
        async session({ session, token }) {
            session.user = token;
            return session;
        }
    },
    session: {
        strategy: "jwt",
    },
    pages: {
        signIn: '/login',
    },
    secret: process.env.SECRET,
    NEXTAUTH_URL: process.env.NEXTAUTH_URL
});

export { authOptions as GET, authOptions as POST };

User Object

{
  "_id": {
    "$oid": "615fb61796e77940b7f7a5e7"
  },
  "email": "[email protected]",
  "password": "$2b$12$fCADz/THWfE7.hThRiFj2eNJxTL78.7zPtyRtERxkBlU2hwlYRJ9K",
  "firstName": "John",
  "lastName": "Smith",
  "lastLoggedIn": {
    "$date": "2023-07-21T17:36:06.893Z"
  },
  "phoneNumber": "",
  "role": "Admin",
  "status": "Active",
  "company": "TestCompany",
  "jobTitle": "TestJobTitle",
  "department": "TestDepartment"
}

API Route

import { getServerSession } from 'next-auth';
import { authOptions } from "@/app/api/auth/[...nextauth]/route"

export async function GET(request) {
    const session = await getServerSession(authOptions);
    console.log(session)
}

Client Page - Console Log Output

user {
  "_id": "615fb61796e77940b7f7a5e7",
  "email": "[email protected]",
  "password": "$2b$12$fCADz/THWfE7.hThRiFj2eNJxTL78.7zPtyRtERxkBlU2hwlYRJ9K",
  "firstName": "John",
  "lastName": "Smith",
  "lastLoggedIn": "2023-07-21T17:36:06.893Z",
  "phoneNumber": "",
  "role": "Admin",
  "status": "Active",
  "company": "TestCompany",
  "jobTitle": "TestJobTitle",
  "department": "TestDepartment"
  "iat": 1689960966,
  "exp": 1692552966,
  "jti": "ae91e614-4fdb-4de4-9e5e-5b9879ca07ba"
}

API Route - Console Log Output

{
  user: { name: undefined, email: '[email protected]', image: undefined }
}

Expected Output

The expected output of the user session data should match the user object.

{    
  user: {
      "_id": "615fb61796e77940b7f7a5e7",
      "email": "[email protected]",
      "password": "$2b$12$fCADz/THWfE7.hThRiFj2eNJxTL78.7zPtyRtERxkBlU2hwlYRJ9K",
      "firstName": "John",
      "lastName": "Smith",
      "lastLoggedIn": "2023-07-21T17:36:06.893Z",
      "phoneNumber": "",
      "role": "Admin",
      "status": "Active",
      "company": "TestCompany",
      "jobTitle": "TestJobTitle",
      "department": "TestDepartment"
    }
}
Starlet answered 21/7, 2023 at 17:54 Comment(0)
H
1

I believe you can simply export the authOptions object from [...nextauth]/route.ts like:

export const authOptions = {
   //...
};

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };
Herb answered 14/9, 2023 at 2:27 Comment(1)
You just saved my life! You are at least10x better than chatGPT. Thank you!Gilemette
D
1

I did it like this. In your auth/[...nextauth].js file include some callbacks with session and jwt like so:

export default NextAuth({
    callbacks: {
        async session({ session, user, token }){
            // Assign the userid and role from the jwt callback below
            if(session?.user) { session.user.id = token.uid; session.user.role=token.roleid }
            return session
        },
        async jwt({ token, user }){
            if(user) { token.uid = user.id; token.roleid=user.role  }
            return token;
        },
    },
    session:{ strategy:'jwt' },
    providers:[
        CredentialsProvider({
            async authorize(credentials, request){
                // await dbconnect()
                // Login to authenticate the user from Database or other data sources

                return({ id: 'userid1', name: 'Patsy Khan', role: '64e3abe6322c1065da3awee4'})
            }
        })
    ]
})

I'm just returning hardcoded data: id, name and role. Accessing it from the client-side is like this:

import { useSession } from 'next-auth/react';

.....
const{data:session, status}=useSession()// from next-auth


{session && status==='authenticated' ? <SomeComponent /> }

<button type="button" onClick=()=>{ handler(session?.user.id) } > {session?.user.name} </button>

From the server side however we need to set authOptions in the auth/[...nextauth].js file. I already included the callbacks above. So simply copy the callbacks from above and include it in an export const authOptions and add it to the ...nextauth file:

export const authOptions={
    callbacks:{
        async session({ session, user, token }){
            // Assign the userid and role from the jwt callback below
            if(session?.user) { session.user.id = token.uid; session.user.role=token.roleid }
            return session
        },
        async jwt({ token, user }){
            if(user) { token.uid = user.id; token.roleid=user.role  }
            return token;
        },
    },
}

Now we could access all the data in the server side like so:

import { getServerSession } from 'next-auth/next'
import { authOptions } from '../[...nextauth]'

export default async function handler(request, response) {

    const session=await getServerSession(request, response, authOptions)
    console.log(JSON.stringify(session, null, 2))

Output:

{
    "user": {
        "name": "Patsy Khan",
        "id": "userid1",
        "role": "64e3abe6322c1065da3awee4"
    },
    "expires": "2023-12-12T02:58:59.567Z"
}

"next-auth": "^4.24.5", "next": "13.5.4",

Dimpledimwit answered 12/11, 2023 at 17:6 Comment(0)
R
0

Upgrading to v5 (which is currently expiremental) fixed this issue for me

Radiograph answered 13/10, 2023 at 14:48 Comment(0)
B
0

I was facing the same problem but searching on the web I found what solved my problem. So, to get the full User object in server side, you need to call getServerSession() in a server component and make sure to pass your authOptions as it's argument, like this: getServerSession(authOptions).

PS: credits for "@balazsorban44" in this link: https://github.com/nextauthjs/next-auth/issues/7658

Bilection answered 26/1 at 0:48 Comment(0)
C
0

this is how i was able to finally used it and modify my code after studying how you implement yours this is the file path [app/api/auth/[...nextauth]/routes

import NextAuth, { getServerSession } from "next-auth";
import GoogleProvider from "next-auth/providers/google";

const handler = NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    }),
  ],
  // secret: process.env.NEXTAUTH_SECRET,
  callbacks: {
    session: ({ session, token }) => ({
      ...session,
      user: {
        ...session.user,
        id: token.sub,
      },
    }),
  },
  //end of the callback
});

export { handler as GET, handler as POST };

// we have to pass in the handler in getServerSession function to get access to the user id // passsing the handlder direct like this [const getNextServerSession = () => //getServerSession(handler);] did not work still on same path [app/api/auth/[...nextauth]/routes] below the the above code that is where the below code is

export const getNextServerSession = () =>
  getServerSession({
    providers: [
      GoogleProvider({
        clientId: process.env.GOOGLE_CLIENT_ID as string,
        clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
      }),
    ],
    // secret: process.env.NEXTAUTH_SECRET,
    callbacks: {
      session: ({ session, token }) => ({
        ...session,
        user: {
          ...session.user,
          id: token.sub,
        },
      }),
    },
    //end of the callback
  });

//this work for me when using it or calling it where you need to implement it do the //below // example using it in dashboard page [app/dashboard/page.tsx]

import { getNextServerSession } from "../api/auth/[...nextauth]/route";

  const session = await getNextServerSession();

to avoid typscript error define the above code same line with the package file [next-auth.d.ts] import NextAuth from "next-auth";

declare module "next-auth" {
  interface Session {
    user: {
      id: string;
    } & DefaultSession["user"];
  }
}
Cradle answered 12/6 at 22:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.