By default, NextAuth returns minimal information in the session
object. However, there are use cases where you might want to return extra information to the client.
This post shows you how to extend the session
object and return additional fields to the client.
NextAuth Callbacks
NextAuth callbacks enable you to control what happens when certain actions are performed (sign in, redirect, client accesses a session, etc.) in the application.
NextAuth has 4 callbacks:
signIn()
: This is triggered when a user signs in.redirect()
: This is triggered whenever NextAuth.js has to redirect a user.session()
: This is triggered whenever a session is checked, either via theuseSession
,getSession
, orsession
callbacks.jwt()
: This is triggered whenever a JSON Web Token is created or updated.
In our case, we're interested in the "session" callback, which is called whenever the client checks a session. So, you can use this callback to pass extra information to the client, which is what we'll do.
JWT Callback
Before the data is available in the session callback, you must add it to the token
in the jwt()
callback.
The jwt()
callback is triggered whenever a JWT token is created or updated. For example, a JWT token is created when the user signs in or updated when a session is accessed in the client.
Recently, I worked on the "email verification" feature at Documenso, and I had to expose an extra field - emailVerified
- to the client. The emailVerified
field represents whether the users have verified their email or not.
The logic of adding emailVerified
in the session
object is as follows:
- merge the 2 objects -
token
anduser
- add the
emailVerified
property to themerged
object - return the new object
callbacks: {
async jwt({ token, user }) {
const merged = {
...token,
...user,
};
....
return {
id: merged.id,
name: merged.name,
email: merged.email,
lastSignedIn: merged.lastSignedIn,
emailVerified: merged.emailVerified,
};
},
Since the jwt()
callback is called before the session()
callback, anything that you add to the JWT token is available in the session
callback. That means you can now send it to the client in the session
object.
Session Callback
The last step involves returning the emailVerified
in the session
object as follows:
session({ token, session }) {
if (token && token.email) {
return {
...session,
user: {
id: Number(token.id),
name: token.name,
email: token.email,
emailVerified:
typeof token.emailVerified === 'string' ? new Date(token.emailVerified) : null,
},
} satisfies Session;
}
return session;
},
The emailVerified
field in the code checks if the email verification date is a string and, if so, converts it to a Date object. Otherwise, it sets it to null.
Now, you can check if the user has a verified email by accessing the emailVerified
value. In our case, we want to disable the ability to upload documents if the user doesn't have a verified email.
<DocumentDropzone
className="min-h-[40vh]"
disabled={remaining.documents === 0 || !session?.user.emailVerified}
onDrop={onFileDrop}
/>
The above code snippet shows the emailVerified
field being used to determine whether the user can upload documents or not.
All the code shown in this post is available in this pull request. You can also see the full implementation of the email verification in this PR.