Ivditi Gabeskiria
Ivditi Gabeskiria

Reputation: 402

Get User ID from session in next-auth client

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.

Upvotes: 25

Views: 55936

Answers (12)

stackers
stackers

Reputation: 3270

I dont know what all this token.sub stuff is. I had to do this:

export const authOptions = {
    adapter: PostgresAdapter(pool),
    providers: [
        GoogleProvider({
            clientId: process.env.GOOGLE_CLIENT_ID,
            clientSecret: process.env.GOOGLE_CLIENT_SECRET
        })
    ],
    callbacks: {
        session: ({ session, user}: SessionProps) => {
            if (session?.user) session.user.id = user.id;
            return session;
        }
    }
}

Upvotes: 0

RaySun
RaySun

Reputation: 229

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

Upvotes: 0

jumping_monkey
jumping_monkey

Reputation: 7779

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}

Upvotes: 1

Nando Machado
Nando Machado

Reputation: 342

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...

Upvotes: 2

Frahim
Frahim

Reputation: 379

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',
  },
  ...
});

Upvotes: 31

Maykon Oliveira
Maykon Oliveira

Reputation: 329

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,
      },
    }),
  },

Upvotes: 18

Chanu Abdullah
Chanu Abdullah

Reputation: 11

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

Upvotes: 0

Ayush Nair
Ayush Nair

Reputation: 93

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",
    ...
  },

Upvotes: 4

Usman Sabuwala
Usman Sabuwala

Reputation: 900

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'];
  }
}

Upvotes: 23

Shahriyor Sharifjonov
Shahriyor Sharifjonov

Reputation: 141

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),
}

Upvotes: 11

Gautam Ghai
Gautam Ghai

Reputation: 973

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
    }
}

Upvotes: 4

Kenneth Van den Berghe
Kenneth Van den Berghe

Reputation: 192

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.

Upvotes: 0

Related Questions