How to Extend User and Session in NextAuth.js (Typescript)
Jasser Mark Arioste
Hello, hustler! Sometimes we need to extend the user and session object in NextAuth.js to get more information about the user. Do you need a role
, or a subscribed
property to check for access?
In this tutorial, you'll learn how to extend the User
and Session
object in nextauth.js using Typescript's declaration merging and module augmentation. So that you'll be able to access them when using useSession
, getSession
, getToken, or anywhere in the application.
The technique used in this tutorial is not only applicable to nextauth
but to any library/module that needs it.
What is Declaration Merging? #
I won't delve too much into the details here but in Typescript, it's possible to combine multiple interface definitions into one. For example, if you have the following definitions of the interface Animal:
interface Animal { legs: number } interface Animal { canFly: boolean }
1234567
Typescript combines both definitions into a single interface so that we'll have access to both properties.
//no error const cat: Animal = { canFly: false, legs: 4 };
12345
What is Module Augmentation #
Module augmentation is a type of declaration merging where you add or modify definitions to a specific module or library to make it more suitable for your application. This is exactly what we need to modify Nextauth User
interface definition.
Enough talk, how do you apply this in nextauth.js
?
Augmenting nextauth.js
#
For this example, we'll add the role and subscribed properties for both JWT and the User interfaces. First, create a file in the root directory of your project nextauth.d.ts
(the filename can be anything you like)
Create a Role
enum:
// nextauth.d.ts export enum Role { user = "user", admin = "admin", }
12345
Augment the next-auth
module to modify session.user
:
// nextauth.d.ts declare module "next-auth" { interface User { role?: Role; subscribed: boolean; } interface Session extends DefaultSession { user?: User; } }
1234567891011
Augment the next-auth/jwt
module to modify the JWT
interface:
// nextauth.d.ts declare module "next-auth/jwt" { interface JWT { role?: Role; subscribed: boolean; } }
1234567
That's it. You should now be able to access these fields anywhere in your application.
For example, in [...nextauth].ts you should add these fields to the token and session callbacks:
// pages/api/auth/[...nextauth].ts ... callbacks: { async jwt({ token, user }) { /* Step 1: update the token based on the user object */ if (user) { token.role = user.role; token.subscribed = user.subscribed; } return token; }, session({ session, token }) { /* Step 2: update the session.user based on the token object */ if (token && session.user) { session.user.role = token.role; session.user.subscribed = token.subscribed; } return session; }, }, ...
123456789101112131415161718192021
In your component, you can access the fields when using useSession
:
const HomePage: NextPage = () => { const { data: session } = useSession(); console.log(session?.user?.role) // no errors return null }
12345
Refactoring Duplicate Code #
In our augmented modules, you can see there's a bit of duplicate code for User
and JWT
. Let's create a common interface to solve this code smell.
Here's the refactored and the full augmented code:
// nextauth.d.ts import { DefaultSession, DefaultUser } from "next-auth"; export enum Role { user = "user", admin = "admin", } interface IUser extends DefaultUser { /** * Role of user */ role?: Role; /** * Field to check whether a user has a subscription */ subscribed?: boolean; } declare module "next-auth" { interface User extends IUser {} interface Session { user?: User; } } declare module "next-auth/jwt" { interface JWT extends IUser {} }
12345678910111213141516171819202122232425
We created a common interface IUser
and used it across all the modules.
Caveats #
Just remember that since you're adding fields to JWT
, it will increase the size of the nextauth
cookie and this will be included in every request.
Conclusion #
We learned how to extend the User and JWT interfaces in the nextauth
modules using module augmentation. Module augmentation is an important concept to learn in typescript that you can apply to many packages. Packages and Libraries are also aware of this and provide some interfaces to be extended by the developer.
If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on Twitter.
Resources #
- Typescript Docs
- Nextauth Module Augmentation Docs
- The full code is available on GitHub
Credits: Image by Monika Iris from Pixabay