Get User ID from session in next-auth client
Asked Answered
W

11

25

I'm using next-auth with Prisma and Graphql, I followed these instructions to set up the adapter and my models accordingly: https://next-auth.js.org/adapters/prisma Authentication works but when I inspect session object from here : const { data: session, status } = useSession() I don't see ID enter image description here

The reason I need the ID is to make further GraphQL queries. I'm using email value for now to fetch the User by email, but having ID available would be a better option.

Weinhardt answered 19/12, 2021 at 5:43 Comment(0)
J
30

Here's the quickest solution to your question:

src/pages/api/auth/[...nextAuth].js

export default NextAuth({
  ...
  callbacks: {
    session: async ({ session, token }) => {
      if (session?.user) {
        session.user.id = token.sub;
      }
      return session;
    },
    jwt: async ({ user, token }) => {
      if (user) {
        token.uid = user.id;
      }
      return token;
    },
  },
  session: {
    strategy: 'jwt',
  },
  ...
});
Janeenjanek answered 2/4, 2022 at 22:32 Comment(3)
This might have changed; it's showing up as token.sub not token.uid for me.Womenfolk
Answer edited to show token.sub rather than token.uidCyrilla
Hi @prestondocks can you please answer this question which is similar? #78624209Insignificancy
C
23

I just referred to the NextAuth docs (this page) and finally got it working the right way

callbacks: {
  jwt({ token, account, user }) {
    if (account) {
      token.accessToken = account.access_token
      token.id = user?.id
    }
    return token
  }
  session({ session, token }) {
      // I skipped the line below coz it gave me a TypeError
      // session.accessToken = token.accessToken;
      session.user.id = token.id;

      return session;
    },
}

If you use TypeScript, add this to a new file called next-auth.d.ts

import NextAuth from 'next-auth';

declare module 'next-auth' {
  interface Session {
    user: {
      id: string;
    } & DefaultSession['user'];
  }
}
Cheetah answered 15/2, 2023 at 22:46 Comment(0)
O
16

None of these solutions solved my problem, but during debugging, I discovered that 'token.sub' represents the user ID. Here's what I did:

callbacks: {
    session: ({ session, token }) => ({
      ...session,
      user: {
        ...session.user,
        id: token.sub,
      },
    }),
  },
Orphrey answered 6/5, 2023 at 1:31 Comment(5)
I can validate this being the right answer for this question as of now. Thank you!Rabato
do you store that token.sub id in the database?Comenius
@Comenius no, I don't.Orphrey
but you said it represents the user id. what do you store in the database as the user id?Comenius
This works, but it doesn't take into account the Typescript. The fastest way around it I used was extending the TS type of session and adding the id: interface User extends DefaultSession { id: string; } const user: User = session?.user as User; const userID = user?.id; Plagioclase
B
10

Here's the quickest solution that worked for me

import NextAuth from "next-auth"
import { MongoDBAdapter } from "@next-auth/mongodb-adapter"
import clientPromise from "../../../lib/mongodb"

export const authOptions = {
    providers: [
      <...yourproviders>
    ],
    callbacks: {
      session: async ({ session, token, user }) => {
        if (session?.user) {
          session.user.id = user.id;
        }
        return session;
      },
    },
    adapter: MongoDBAdapter(clientPromise),
}
Barogram answered 12/11, 2022 at 15:6 Comment(0)
M
4

This worked for me.

callbacks: {
    async jwt({token, user, account, profile, isNewUser}) {
        user && (token.user = user)
        return token
    },
    async session({session, token, user}) {
        session = {
            ...session,
            user: {
                id: user.id,
                ...session.user
            }
        }
        return session
    }
}
Mana answered 12/4, 2022 at 23:58 Comment(0)
L
4

I'm late to this, but I faced the same issue and this is the workaround I found that works.

So with Next.js@13^ or App router, they ask you not to modify the next-env.d.ts. They say it's to help them easily add updates/features of Next in the future, so they regenerate it for every build.

Specifically, the original file says,

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

So the way around this is to create a new module with any name. Following convention, it would be a good idea to name it next-auth-extensions.d.ts in the same location/folder as next-env.d.ts and then add that to the includes property in the tsconfig.json.

So the following files are the end result for my config across Next, Typescript and Next-Auth.

// next-auth-extensions.d.ts
import NextAuth, { DefaultSession } from "next-auth";

declare module "next-auth" {
  /**
   * Returned by `useSession`, `getSession`, and received as a prop on the `SessionProvider` React Context
   */
  interface Session {
    user: {
      /** The user's id */
      id: string;
    } & DefaultSession["user"];
  }
}
// tsconfig.json
{
  ...
  "compilerOptions": {
    ...
  },
  "include": ["next-env.d.ts", "next-auth-extensions.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
  ...
}

// /api/auth/[...nextauth]/route.ts
import { db } from "@/db";
import { PrismaAdapter } from "@auth/prisma-adapter";

import NextAuth, { NextAuthOptions } from "next-auth";

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(db),
  providers: [
    ...
  ],
  callbacks: {
    async session({ session, user }) {
      console.log(user);
      session.user.id = user.id;
      return session;
    },
  },
  ...
};

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };

I'm not sure about any adaptations for GraphQL, since I've never worked with that before but this works for me. Look at my package.json below just in case there might be discrepancies with regard to versions. Let me know if it works.

Note: This isn't the official way or anything, it's just something that I've researched and tried to get it to work, so if anybody has suggestions or anything, let me know.

{
   ...
   "dependencies": {
    "@auth/prisma-adapter": "^1.0.3",
    "@prisma/client": "^5.4.2",
   ...
    "next": "13.5.4",
    "next-auth": "^4.23.2",
    "react": "^18",
    ...
  },
Lauraine answered 14/10, 2023 at 6:16 Comment(1)
For next 14 app router and typescript codebases this is perfectly working answer.Reliance
D
2

Very late for the party, but for those using getServerSession (in opposition to useSession) you need to pass in the authOptions for any of the solutions above to work and give you a userID.

You also need to move authOptions to a separate file, so that the export doesn't cause issues during build.

For instance:

app/api/auth/[...nextauth]/authOptions.ts

import GoogleProvider from "next-auth/providers/google";

type SessionProps = {
  session: any;
  token: any;
};

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
  ],
  callbacks: {
    session: async ({ session, token }: SessionProps) => {
      if (session?.user) {
        session.user.id = token.sub;
        delete session.user.email; // sanitize data for security
      }
      return session;
    },
  },
  
};

~/app/api/auth/[...nextauth]/route.tsx

import NextAuth from "next-auth";
import { authOptions } from "./authOptions";

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };

And then where you are using your getServerSession:

import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions";
const session = await getServerSession(authOptions);

Now session will have an id field!

I'm still having trouble with the types of session and token though. Can't seem to appease Typescript...

Duro answered 6/2 at 20:16 Comment(0)
B
1

This work for me in auth.js, v5:

providers: [
  Credentials({
    async authorize(credentials) {
        //...
        return { name: userid };
    },
  }),
],

// In server component:
import { auth } from "@/auth";
const session = await auth();
{session?.user?.name}
Borgerhout answered 30/4 at 6:12 Comment(0)
F
0

I believe you can change the callbacks so it includes the user's ID in the session: https://next-auth.js.org/configuration/callbacks.

You will need to change the JWT callback so it also include the userId and the session callback so the id is also persisted to the browser session.

Feeling answered 20/12, 2021 at 15:35 Comment(0)
M
0
I fetched same problem. But now I solved the problem by using this code:

 callbacks: {
    session: async ({ session, token }) => {
      if (session?.user) {
        session.user.id = token.sub;// token.uid or token.sub both work
      }
      return session;
    },
    jwt: async ({ user, token }) => {
      if (user) {
        token.sub = user.id; // token.uid or token.sub both work
      }
      return token;
    },
  },

Thanks @Frahim
Morale answered 28/10, 2023 at 12:28 Comment(0)
G
0

this is what works for me on my nextjs 14, firebase and using next-auth authentication /app/api/auth/[...nextauth]/route.tsx

callbacks: {
    session: ({ session, token }) => ({
      ...session,
      user: {
        ...session.user,
        id: token.sub,
      },
    }),
  },

To get ride of the typescript error when using it on my application like the below to see if everything is working

 const { data: session } = useSession();
  if (session) {
    alert(session?.user?.id);
  }

If you are using using TypeScript, add this to a new file called it should be same in the root of your project or same root with your package.json files [next-auth.d.ts]

  import NextAuth from 'next-auth';
   declare module 'next-auth' {
   interface Session {
    user: {
     id: string;
      } & DefaultSession['user'];
     }
    }

i can't explain why but this work for me

Griskin answered 11/5 at 20:57 Comment(1)
Hi can you help me do this with azure ad but also display job title?Insignificancy

© 2022 - 2024 — McMap. All rights reserved.