Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with using getServerSession #7533

Open
stanko-tomic opened this issue May 12, 2023 · 17 comments
Open

Issue with using getServerSession #7533

stanko-tomic opened this issue May 12, 2023 · 17 comments
Labels
triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.

Comments

@stanko-tomic
Copy link

Environment

Env

System:
   OS: Windows 10 10.0.19044
   CPU: (16) x64 AMD Ryzen 7 2700X Eight-Core Processor
   Memory: 2.07 GB / 7.95 GB
 Binaries:
   Node: 16.16.0 - C:\Program Files\nodejs\node.EXE
   Yarn: 1.22.4 - C:\Program Files (x86)\Yarn\bin\yarn.CMD
   npm: 9.6.4 - C:\Program Files\nodejs\npm.CMD
 Browsers:
   Edge: Spartan (44.19041.1266.0), Chromium (113.0.1774.35)
   Internet Explorer: 11.0.19041.1566

Reproduction URL

Private repository

Describe the issue

When using getServerSession with authOptions req and res, the returned session object includes properties name and image, even though they are not defined in the user model or the authentication callbacks.

So when using getServerSession i am expecting the return of

  user: {
    email: 'email@gmail.com',
    role: 'admin',
    id: '6457d97f80860ee0f44ba1b0'
  },
  expires: '2023-06-11T12:16:03.803Z'

This is the response gotten from useSession().

When using getServerSession with auth options the return i get is

  name: undefined,
    email: 'email@gmail.com',
    image: undefined
  },
  expires: '2023-06-11T12:16:03.809Z'

Even though it should return the email role and id as defined in authOptions.

How to reproduce

So these are my authOptions

import NextAuth, { NextAuthOptions, Session } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { mongooseConnect } from "@/lib/mongoose";
import UserModel from "@/models/user";
import type { JWT } from "next-auth/jwt";

export const authOptions: NextAuthOptions = {
  session: {
    strategy: "jwt",
  },
  providers: [
    CredentialsProvider({
      name: "credentials",
      credentials: {
        email: { label: "Email", type: "email" },
        password: { label: "Password", type: "password" },
      },
      authorize: async (credentials) => {
        await mongooseConnect();

        if (!credentials) return;

        const user = await UserModel.findOne({
          email: credentials.email,
        });

        if (!user) {
          throw new Error("No user with a matching email was found.");
        }

        const pwValid = await user.comparePassword(credentials.password);

        if (!pwValid) {
          throw new Error("Your password is invalid");
        }

        return user;
      },
    }),
  ],
  callbacks: {
    async jwt({ user, token }) {
      const newToken = { ...token };
      if (user?.role) {
        newToken.role = user.role;
      }
      if (user?.id) {
        newToken.id = user.id;
      }
      return newToken;
    },
    async session({ session, token }: { session: Session; token: JWT }) {
      const newSession = { ...session };
      newSession.user.role = token.role as "user" | "admin";
      newSession.user.id = token.id as string;
      delete newSession.user.image;
      return newSession;
    },
  },
  pages: {
    signIn: "/login",
  },
};

export default NextAuth(authOptions);

Expected behavior

The getServerSession should return the object containing email,role,id as defined in the authOptions, but it does not.

First time writing an issue sorry for any errors.

@stanko-tomic stanko-tomic added the triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. label May 12, 2023
@bencehusi
Copy link

@stanko-tomic did you find any solution? I have the same issue.

What ever I return from the jwt callback gets overwritten by this blunt {name, email, image} object.

I really can't get my head around how it works or what I'm missing and why I don't have control over what to encrypt in the jwt.

@stanko-tomic
Copy link
Author

@stanko-tomic did you find any solution? I have the same issue.

What ever I return from the jwt callback gets overwritten by this blunt {name, email, image} object.

I really can't get my head around how it works or what I'm missing and why I don't have control over what to encrypt in the jwt.

Nope just used getSession() couldn't get it to work. Don't know if it's problem with the code or what.

@bencehusi
Copy link

I did a small reproduction - maybe @stanko-tomic you want to include in your description - so when contributors come across this issue we have a base for conversation.

Here's a code sandbox: https://codesandbox.io/p/sandbox/falling-smoke-iyhks1

The goal:

  • Being able to encode custom values in the jwt token, then read back from a serverSession

Where to look?

  • Open the sandbox and sign in with any email/pw combination.
  • You should get an alert that it succeeded
  • Go to protected page
  • Your session will be printed and it does NOT contain the fields specified in the jwt callback

The jwt callback:

{
  jwt: ({ token, user }) => {
    let serializeToken = token;
    if (user) {
      serializeToken = {
        ...token,
        ...user,
        jwt: token.jwt,
        bla: 'foo',
        name: 'Foooooo',
        id: '1234567890'
       };
    }
    return serializeToken;
  }
}

What's the issue here?
The fields: from user, 'jwt', 'bla', 'id' are completely missing while they should have been encrypted in the jwt.

@bencehusi
Copy link

@stanko-tomic , I think I found the issue.

Try modifying your jwt callback as so:

{
   jwt({ user = {}, token }) {
      return {
          ...token,
          user
       };
    },
}

After doing this, I was able to get the user from the getServerSession!

I hope it helps!

@Rykuno
Copy link

Rykuno commented May 17, 2023

There is a fix mentioned here for Next 13 #7486 (comment)

@lostchopstik
Copy link

+1 with a similar issue. My custom user properties are not coming across when using getServerSession(authOptions) in the Next.js /app directory server components.

@raver
Copy link

raver commented Jun 30, 2023

hi There,

next-auth:4.22.1
next:13.4.7
node: 18.12

In a credential provider sceneria, I was able to get the custom user property using getServerSession(authOptions) in the Next.js /app directory server components.

I am new to next.auth and next.js. Sharing my code below. Maybe I am missing anything.
Hopefully it can help:

My custom fields in User table are as below -- dropping "email/password" fields, adding "name/studentNo" fields:

 # my schema.prisma
 model User {
   id        Int       @id @default(autoincrement())
   studentNo Int       @unique
   name      String
   Account   Account[]
   Session   Session[]
 }

My "app\api\auth[...nextauth]\route.ts"

import NextAuth, { type NextAuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import prisma from "@/lib/prisma";
import { PrismaAdapter } from "@next-auth/prisma-adapter";

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prisma),
  session: {
    strategy: "jwt",
  },
  providers: [
    CredentialsProvider({
      credentials: {
        // studentNo is the single credential info used to authorize
        studentNo: { label: "studentNo", type: "number" },
      },
      async authorize(credentials) {
        // some logic here
        return user;
      },
    }),
  ],
  callbacks: {
    async jwt({ user , token }) {
      if (user) {  // Note that this if condition is needed
        token.user={...user}
      }
      return token
     },
    async session({ session, token }) {
      if (token?.user) { // Note that this if condition is needed
        session.user = token.user;
      }
      return session
    },
  },
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };

Then in a server component,

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


export default async function Page() {
  const session = await getServerSession(authOptions);
  ...
}

, session object in log is something like below, as expected:

   { user: { id: 2, studentNo: 2, name: 'Jack' } }

@aimehai
Copy link

aimehai commented Aug 3, 2023

@raver your code works, thanks a lot

@cryskram
Copy link

cryskram commented Sep 5, 2023

@raver @bencehusi both your codes work. Thanks :)

@emrekrt1655
Copy link

@raver thanks for your attribution. Code works well.

@tianangthang7
Copy link

thanks @raver

@viniyr
Copy link

viniyr commented Oct 15, 2023

@raver ty, worked

@guri002
Copy link

guri002 commented Oct 19, 2023

thank you so much @raver

@chrismuiruriz
Copy link

@raver your approach worked.

@devOrv876
Copy link

devOrv876 commented Jan 19, 2024

I had a similar issue where getServerSession was returning different values in the user object from what was being returned by useSession on the client

Brilliant !
Thanks @raver 🎉

@farukparhat
Copy link

Passing in the authOptions to getServerSession was the key...

@KelvinyHenrique
Copy link

@raver your approach worked.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime.
Projects
None yet
Development

No branches or pull requests