nextauthjs / next-auth

Authentication for the Web.
https://authjs.dev
ISC License
24.21k stars 3.37k forks source link

getServerSession not returning database user / user ID #7658

Closed r-ohan closed 1 year ago

r-ohan commented 1 year ago

Environment

System: OS: macOS 13.0 CPU: (8) arm64 Apple M2 Memory: 79.09 MB / 16.00 GB Shell: 5.8.1 - /bin/zsh Binaries: Node: 18.16.0 - /opt/homebrew/bin/node Yarn: 1.22.19 - /opt/homebrew/bin/yarn npm: 9.5.1 - /opt/homebrew/bin/npm Browsers: Chrome: 113.0.5672.126 Safari: 16.1

Reproduction URL

Describe the issue

getServerSession does not return the full user object when run as a server action.

I am trying to get the user ID from the session.

However, I am only getting the prototype user

{
  "user": {
    "name": "First Name",
    "email": "firstName@gmail.com",
    "image": "https://contrived.url"
  }
}

How to reproduce

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

export async function addNewAccount() {
    const session = await getServerSession(authOptions);
    if (!session) {
        return null;
    }
    console.log(JSON.stringify(session, null, 2));
    await prisma?.bankAccount.create({
        data: {
            id: 'test_id',
            link_token: '1234',
            description: 'testing this out',
            user: {
                connect: {
                    id: session.id,
                }
            }
        }
    })

Expected behavior

According to the docs, when using authJS with a database, the user object should be the database user object

image
balazsorban44 commented 1 year ago

https://next-auth.js.org/getting-started/example#extensibility https://next-auth.js.org/getting-started/typescript#module-augmentation

You can also follow #7443

williamlmao commented 1 year ago

@balazsorban44 I took a look at all 3 of those links but am still very unclear on how to get the userId back into the getServerSession response.

In my callbacks, I am adding the user id as well as the an extra test value

    session: ({ session, token }) => {
      if (token) {
        session.user.id = token.id;
        session.user.name = token.name;
        session.user.email = token.email;
        session.user.image = token.picture;
      }
      session.test = "hi";
      console.log("🚀 ~ file: auth.ts:41 ~ session:", session);
      return session;
    },
You can see it logs: 

```
🚀 ~ file: auth.ts:41 ~ token: {

id: '906613a3-31cb-48d5-a862...', name: 'redacted', email: 'redacted@gmail.com', picture: 'https://cdn.discordapp.com/avatars/.../....gif' }


However when in the server component in nextjs, I am calling useServerSession

import { getServerSession } from "next-auth"; import CreateTrainingSetForm from "../CreateTrainingSetForm";

export default async function Page() { const session = await getServerSession(); console.log("🚀 ~ file: page.tsx:7 ~ Page ~ session:", session); if (!session) { return

Not logged in
; } return ; }


In this console log we only get 

{ "name": "redacted", "email": "redacted@gmail.com", "image": "https://cdn.discordapp.com/avatars/.../....gif" }


I am on 

    "next": "13.4.12",
    "next-auth": "^4.22.5",
Any help is greatly appreciated!
OmidHajizadeh commented 1 year ago

@balazsorban44 I took a look at all 3 of those links but am still very unclear on how to get the userId back into the getServerSession response.

In my callbacks, I am adding the user id as well as the an extra test value

    session: ({ session, token }) => {
      if (token) {
        session.user.id = token.id;
        session.user.name = token.name;
        session.user.email = token.email;
        session.user.image = token.picture;
      }
      session.test = "hi";
      console.log("🚀 ~ file: auth.ts:41 ~ session:", session);
      return session;
    },
You can see it logs: 

```
🚀 ~ file: auth.ts:41 ~ token: {

id: '906613a3-31cb-48d5-a862...', name: 'redacted', email: 'redacted@gmail.com', picture: 'https://cdn.discordapp.com/avatars/.../....gif' }


However when in the server component in nextjs, I am calling useServerSession

import { getServerSession } from "next-auth"; import CreateTrainingSetForm from "../CreateTrainingSetForm";

export default async function Page() { const session = await getServerSession(); console.log("🚀 ~ file: page.tsx:7 ~ Page ~ session:", session); if (!session) { return

Not logged in
; } return ; }


In this console log we only get

{ "name": "redacted", "email": "redacted@gmail.com", "image": "https://cdn.discordapp.com/avatars/.../....gif" }


I am on

"next": "13.4.12", "next-auth": "^4.22.5",

Any help is greatly appreciated!

I'm stuck in the exact scenario. Did the exact same thing with JWT callback, and session gets the user id after the if statement, but when I use getServerSession, it's as if this callback didn't make any difference in the session obejct

OmidHajizadeh commented 1 year ago

@balazsorban44 I took a look at all 3 of those links but am still very unclear on how to get the userId back into the getServerSession response.

In my callbacks, I am adding the user id as well as the an extra test value

    session: ({ session, token }) => {
      if (token) {
        session.user.id = token.id;
        session.user.name = token.name;
        session.user.email = token.email;
        session.user.image = token.picture;
      }
      session.test = "hi";
      console.log("🚀 ~ file: auth.ts:41 ~ session:", session);
      return session;
    },
You can see it logs: 

```
🚀 ~ file: auth.ts:41 ~ token: {

id: '906613a3-31cb-48d5-a862...', name: 'redacted', email: 'redacted@gmail.com', picture: 'https://cdn.discordapp.com/avatars/.../....gif' }


However when in the server component in nextjs, I am calling useServerSession

import { getServerSession } from "next-auth"; import CreateTrainingSetForm from "../CreateTrainingSetForm";

export default async function Page() { const session = await getServerSession(); console.log("🚀 ~ file: page.tsx:7 ~ Page ~ session:", session); if (!session) { return

Not logged in
; } return ; }


In this console log we only get

{ "name": "redacted", "email": "redacted@gmail.com", "image": "https://cdn.discordapp.com/avatars/.../....gif" }


I am on

"next": "13.4.12", "next-auth": "^4.22.5",

Any help is greatly appreciated!

So I finally figured it out. Easy mistake actually. When you're calling getServerSession() in a server component or in any server environment, make sure to pass your authOptions as it's argument.

christo9090 commented 12 months ago

@balazsorban44 I took a look at all 3 of those links but am still very unclear on how to get the userId back into the getServerSession response. In my callbacks, I am adding the user id as well as the an extra test value

    session: ({ session, token }) => {
      if (token) {
        session.user.id = token.id;
        session.user.name = token.name;
        session.user.email = token.email;
        session.user.image = token.picture;
      }
      session.test = "hi";
      console.log("🚀 ~ file: auth.ts:41 ~ session:", session);
      return session;
    },
You can see it logs: 

```
🚀 ~ file: auth.ts:41 ~ token: {

id: '906613a3-31cb-48d5-a862...', name: 'redacted', email: 'redacted@gmail.com', picture: 'https://cdn.discordapp.com/avatars/.../....gif' }


However when in the server component in nextjs, I am calling useServerSession

import { getServerSession } from "next-auth"; import CreateTrainingSetForm from "../CreateTrainingSetForm";

export default async function Page() { const session = await getServerSession(); console.log("🚀 ~ file: page.tsx:7 ~ Page ~ session:", session); if (!session) { return

Not logged in
; } return ; }


In this console log we only get

{ "name": "redacted", "email": "redacted@gmail.com", "image": "https://cdn.discordapp.com/avatars/.../....gif" }


I am on

"next": "13.4.12", "next-auth": "^4.22.5",



Any help is greatly appreciated!

So I finally figured it out. Easy mistake actually. When you're calling getServerSession() in a server component or in any server environment, make sure to pass your authOptions as it's argument.

Love you for this. Very easy to miss in the docs

demeralde commented 6 months ago

Here is the link to the documentation that explains how to pass authProps, for anyone who is wondering: https://next-auth.js.org/configuration/nextjs#in-app-router

import { getServerSession } from "next-auth/next"
import { authOptions } from "pages/api/auth/[...nextauth]"

export default async function Page() {
  const session = await getServerSession(authOptions)
  return <pre>{JSON.stringify(session, null, 2)}</pre>
}
wattsmainsanglais commented 5 months ago

Unfortunately i have the same issue, the docs are clear as mud. I eventually have getServerSession persisiting my userId which i added to the session. But now the signIn / signOut functions no longer work . I'm pulling my hair out and need to step away. I'm on NextJs 14 now so maybe there are some bugs

Unhandled Runtime Error SyntaxError: Unexpected end of JSON input

Call Stack json node_modules\next-auth\react\index.js (409:23) call node_modules\@babel\runtime\helpers\regeneratorRuntime.js (45:15) tryCatch node_modules\@babel\runtime\helpers\regeneratorRuntime.js (133:16) _invoke node_modules\@babel\runtime\helpers\regeneratorRuntime.js (74:20) arg node_modules\@babel\runtime\helpers\asyncToGenerator.js (3:24)

isafranco13 commented 5 months ago

Hi! I'm having a issue, i want that in console to show me the user(email, name, role, etc) in a page after signin, the problem is that the object that has all that is not in the page after sigin, i use getServerSession and all i got in the console is this { name: undefined, email: undefined, image: undefined } this is what i have in route.ts `import NextAuth from 'next-auth' import GoogleProvider from 'next-auth/providers/google' import CredentialsProvider from "next-auth/providers/credentials"; import { connectDB } from "@/libs/mongodb"; import User from "@/models/usuarios"; import { GoogleProfile } from 'next-auth/providers/google';

const handler = NextAuth ({ //const handler = NextAuth({ -> lo que estaba antes providers: [ GoogleProvider ({ profile(profile:GoogleProfile){ return{ ...profile, role: "usuario", id: profile.sub, } }, clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,

    }),
    CredentialsProvider({
        id: 'credentials',
        name: "Credentials",
        credentials: {
          email: { label: "Email", type: "text" },
          password: { label: "Password", type: "text" },
        },

        async authorize(credentials: any) { //credentials: any

        {
          await connectDB();
          try {

            const user = await User.findOne({ correo: credentials.email });
            if (user && user.contrasena === credentials.password) {
                // La contraseña coincide, puedes devolver el usuario
                //console.log(user);
                return user;
            } else {
                // La contraseña no coincide
                return null;
            }
          } catch (err: any) {
            throw new Error(err);
          }}  
        },
      })
],
callbacks: {
    async jwt({ token, account }) {
        // Persist the OAuth access_token to the token right after signin
        if (account) {
          token.accessToken = account.access_token
        }
        return token
      },
      async session({ session, token, user }) {
        // Send properties to the client, like an access_token from a provider.
        //session.accessToken = token.accessToken
        return session
      },
    async signIn({user, account}) {
        if (account?.provider === 'credentials') {
            console.log(user);
            return true;
        }
        if (account && account.provider === 'google') {
            const {name, email} = user;
            try {
                const res = await fetch('http://localhost:3000/api/usuarios', { //http://localhost:3000/api/usuarios
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                    body: JSON.stringify({
                        nombre: name,
                        correo: email,
                        role:  "usuario",
                    }),
                });
                if (res.ok) {
                    console.log(user)
                    return true;
                }
            } catch (error) {
                console.error(error);
                return false;
            }
        }
        return true;
    }
}

}); export {handler as GET, handler as POST}; `

And the signin page `"use client" import Link from "next/link" import Image from 'next/image'; import CustomButton from '@/components/CustomButton'; import {signIn, useSession} from 'next-auth/react' import { Navbar2 } from "@/components"; import { useRouter } from "next/navigation"; import React, {useEffect, useState} from "react"; import CustomAlert from '@/components/CustomAlert';

export default function Form(){ // const session = useSession(); const router = useRouter(); const { data: session, status: sessionStatus } = useSession(); const [alertMessage, setAlertMessage] = useState(""); // Mensaje de alerta const [isVisible, setIsVisible] = useState(false);

useEffect(() => {
    if (sessionStatus === "authenticated") {
    router.replace("/usuario");
    }
}, [sessionStatus, router]);

const handleSubmit = async (e: any) => {
    e.preventDefault();
    const email = e.target[0].value;
    const password = e.target[1].value;

    const res = await signIn('credentials', {
        email,
        password,
        redirect: false,
    });

    if (res?.error) {
        setAlertMessage("Contraseña y/o correo eléctronico incorrecto");
        setIsVisible(true);
        if (res?.url) router.replace("/usuario");
    }
};

if (sessionStatus === "loading") {
    return <h1>Loading...</h1>;
}

return(
    <>
    <Navbar2 />

    <main className="flex flex-col justify-center items-center w-full flex-1 bg-[#F5FFFC]">
        <br /><br /><br /><br />
        <div className="flex max-w-3xl yellowContainer"> {/*div principal */}
            {/*Sección de iniciar sesión*/}
            <div className="logoContainer"> 
                <Image
                    src="/atomo.png"
                    alt="atomo"
                    width={80}
                    height={48}
                    className="object-contain mx-auto logoSignIn" 
                />
                <div className="divYellowContainer"><h1 className="text-3xl font-bold text-center titleSignIn">Iniciar Sesión</h1><br />
                    <div className="flex flex-col w-full pl-4">
                        <form className="flex flex-col items-center w-full" onSubmit={handleSubmit}>                                 
                            <input type="email" className="bg-white rounded-lg outline-none text-base h-12 pl-2 w-3/4 input" placeholder="Correo"/>
                            <br />
                            <input type="password" className="bg-white rounded-lg outline-none text-base h-12 pl-2 w-3/4 input" placeholder="Contraseña"/>

                            <CustomButton
                                btnType="submit"
                                title="Iniciar Sesión"
                                containerStyles="text-white rounded-full bg-[#FC83A1] hover:bg-[#E55E7F] font-medium mt-10 textButton"
                            />
                        </form><br />
                        <div className="flex justify-between">
                            <p className="text-black text-[17px] font-medium prSignIn">¿No tienes cuenta? <Link href="/signup" className="text-[#E55E7F]">Regístrate</Link></p>
                            <p className="text-black text-[17px] font-medium prSignIn"><Link href="" className="text-[#E55E7F]">¿Olvidaste tu contraseña?</Link></p>
                        </div>
                        {/* Renderizar alerta */}
                        {isVisible && (
                            <CustomAlert
                                status="warning"
                                variant="subtle"
                                title="Error"
                                description={alertMessage}
                                setIsVisible={setIsVisible}
                            />
                        )}                            
                    </div>
                </div>
                    <div className="flex flex-nowrap items-center justify-center">
                        <div className="border-2 border-[#FC83A1] inline-block mb-2 pinkLine"></div>
                        &nbsp;&nbsp;&nbsp;
                        <p className="text-black text-[15px] font-bold">O</p>
                        &nbsp;&nbsp;&nbsp;
                        <div className="border-2 border-[#FC83A1] inline-block mb-2 pinkLine"></div>
                    </div>
                    <br />
                    <div className="flex justify-center my-2">&nbsp;
                    <button onClick={() => signIn('google', { callbackUrl: '/terapeuta' })} className="flex items-center justify-center w-[299px] h-[59px] 
                    border-2 border-[#FFFFFF] bg-[#FFFFFF] rounded px-2 py-2 font-light text-center"> <Image
                    src="/buscar.png"
                    alt="mental"
                    width={40}
                    height={40}
                    className="mr-2 googleLogo" 
                    />Continuar con Google</button> {/*'google', { callbackUrl: '/dashboard' }*/ }
                    </div> <br /> 
                    <br />
            </div>
        </div> <br />
    </main>
    </>
)

}`

please help me I am getting on my nerves

lucaspieran commented 2 months ago

I'm only getting the user with name, email, image in a server component with getServerSession and not the entire session that I'm returning. Does anyone know what it could be? @balazsorban44 @r-ohan

async session({ session, token }) {
      if (token) {
        session.access = (token.access as string) || ''
        session.refresh = (token.refresh as string) || ''
      }
      return session
    }
 export function auth(
  ...args:
    | [GetServerSidePropsContext['req'], GetServerSidePropsContext['res']]
    | [NextApiRequest, NextApiResponse]
    | []
) {
  return getServerSession(...args, handler)
}
const Externals = async ({ searchParams }: { searchParams: Pagination }) => {
  const session = await auth()
  console.log(session)`

{
    name: 'Lucas ',
    email: 'lucas@email.com',
    image: 'https://lh3.googleusercontent.com/....'
  }
}
KorigamiK commented 2 days ago

how was this issue resolved?

lucaspieran commented 2 days ago

@KorigamiK I set the access token in an httponly cookie with a server action, but if you use the new version 5 of authjs you won't have any problems, everything works ok there

KorigamiK commented 2 days ago

@KorigamiK I set the access token in an httponly cookie with a server action, but if you use the new version 5 of authjs you won't have any problems, everything works ok there

I'm already on the beta version of authjs. This problem is present with prisma adapter which I finally solved by creative a new session callback in auth options