Reputation: 402
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
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
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
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
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
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
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
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
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
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
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
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
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
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