How to Get User ID from Session in NextAuth (with or without JWTs)


Suppose we’re using NextAuth for user authentication in our Next.js application.

How can we obtain a user’s database ID from the session object?

The session object does not seem to include an ID.

{
  user: {
    name: 'John Travolta',
    email: 'johntravolta@gmail.com',
    image: null
  },     
  expires: '2022-03-19T17:21:46.638Z'
}

The code snippets in this article require NextAuth.js v4. Check out how to upgrade to version 4.

Callbacks in NextAuth

In our NextAuth configuration found in [...nextauth].js, we’ll notice a callbacks field, where we can specify asynchronous functions that will be triggered by some event.

...
callbacks: {
  async signIn({ user, account, profile, email, credentials }) {
    return true;
  },
  async redirect({ url, baseUrl }) {
    return baseUrl;
  },
  async session({ session, user, token }) {
    return session;
  },
  async jwt({ token, user, account, profile, isNewUser }) {
    return token;
  }
...

For instance, when a user logs in using the signIn() function, the signIn() callback above will be triggered.

When a user is redirected to a callback URL on signin or signout, the redirect() callback is triggered.

Similarly, calling getSession() and useSession() will invoke the session() callback.

If we’re not persisting sessions in a database, getSession() and useSession() will invoke the jwt() callback first, then the session() callback.

Read up on callbacks in the NextAuth documentation.

Get user ID from session with JWT

Suppose we’re using JSON Web Token (JWT) sessions.

session: { strategy: "jwt", ... }

Access user ID using token.sub

We can access the user ID from the token.

{
  name: 'John Travolta',
  email: 'johntravolta@gmail.com',
  picture: null,
  sub: 'pwoekfldksfpww10',
  iat: 1645118504,
  exp: 1647710504,
  jti: '1we1rwe1-123e-w1e1-fdgd-fjru389r84ug'
}

We’ll notice that token.sub contains the desired user ID.

iat contains the timestamp of the JWT creation. exp contains the token expiration timestamp. Timestamps are in seconds since epoch. sub contains the unique identifier of the user.

Knowing that, we can forward that data into our session.

async session({ session, token }) {
  if (session?.user) {
    session.user.id = token.sub;
  }
  return session;
}

Access user ID using user

Another way to approach this problem is to pass the user.id through the jwt() callback.

async session({ session, token }) {
  if (session?.user) {
    session.user.id = token.uid;
  }
  return session;
},
async jwt({ token, user }) {
  if (user) {
    token.uid = user.id;
  }
  return token;
}

Get user ID from session without JWT

What if we’re persisting sessions in our database?

session: { strategy: "database", ... }

In this case, we can access the user object for the ID.

{
  id: 'pwoekfldksfpww10',
  name: 'John Travolta',
  email: 'johntravolta@gmail.com',
  emailVerified: 2022-03-10T17:18:34.831Z,
  image: null,
  ... // All other fields on the User model
}

user is simply the User object stored in the database. Naturally, the ID will be found in the id field.

async session({ session, user }) {
  if (session?.user) {
    session.user.id = user.id;
  }
  return session;
}

Using new session from callback

Now, we want to obtain this user ID on the client.

const { data: session, status } = useSession();
if (session?.user) {
  console.log(session.user.id);
}

If we’re using TypeScript, we’ll likely have to extend the session interface to include our new field.

The returned session object is of type Session, which can be seen here.

export interface Session extends DefaultSession {}
export interface DefaultSession {
  user?: {
    name?: string | null;
    email?: string | null;
    image?: string | null;
  };
  expires: ISODateString;
}

We can extend this interface and override the user field to include id.

First, let’s create a file types/next-auth.d.ts in our Next.js project.

Then, we can create new interfaces for Session and JWT (if appropriate).

import NextAuth, { DefaultSession } from "next-auth";
declare module 'next-auth' {
  interface Session {
    user?: { 
      id: string;
    } & DefaultSession["user"];
  }
}
// If we're using JWTs with the `uid` field
declare module 'next-auth/jwt/types' {
  interface JWT {
    uid: string;
  }
}

Read up on extending default interface properties in NextAuth.