I’m using Next.js with NextAuth.js for authentication in my application. I’ve noticed an inconsistency in the session object structure between client-side and server-side contexts.
Next.js Version: 14.2.4
NextAuth.js Version: 4.24.7
Details(below):
Setup:
I’ve configured NextAuth.js with session callbacks to populate the session object with user details upon login.
This is my […nextauth].js code:
export default NextAuth({
secret: process.env.NEXTAUTH_SECRET,
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: { label: "Email", type: "text" },
password: { label: "Password", type: "password" },
},
authorize: async (credentials, req) => {
const { email, password } = credentials;
try {
console.log(`Attempting to authenticate user with email: ${email}`);
const user = await User.findOne({
where: { email },
include: [{ model: Role, as: "role" }],
});
console.log("user foud", user);
if (!user) {
console.log(`User with email ${email} not found`);
throw new Error("User not found");
}
const passwordMatch = await bcrypt.compare(password, user.password);
if (!passwordMatch) {
console.log(`Invalid credentials for user with email ${email}`);
throw new Error("Invalid credentials");
}
console.log(`User authenticated successfully: ${user.email}`);
const plainUser = user.get({ plain: true });
return plainUser;
} catch (error) {
console.error("Authorization error:", error);
throw new Error(error.message);
}
},
}),
],
session: {
jwt: true,
},
jwt: {
secret: process.env.NEXTAUTH_SECRET,
},
callbacks: {
async jwt({ token, user, session }) {
console.log("jwt before callabck", { token, user, session });
if (user) {
return {
...token,
user_id: user.user_id,
email: user.email,
first_name: user.first_name,
last_name: user.last_name,
phone_number: user.phone_number,
role: user.role ? user.role.role_name : null,
};
}
console.log("token after callback", token);
return token;
},
async session({ session, token, user }) {
console.log("session before callback", { session, token, user });
session.user = {
user_id: token.user_id,
email: token.email,
name: `${token.first_name} ${token.last_name}`,
phone_number: token.phone_number,
role: token.role, // Default or placeholder name
image: token.image, // Default image URL
};
console.log("session after callback", session);
return session;
},
},
});
This is working fine.. see below the console.log statement’s output.
User authenticated successfully: [email protected]
jwt before callabck {
token: {
name: undefined,
email: '[email protected]',
picture: undefined,
sub: undefined
},
user: {
user_id: 2,
first_name: 'Mayank',
last_name: '',
email: '[email protected]',
phone_number: null,
role_id: 2,
password: '$2a$10$D.LUplehs3/iPVSEyWvI5e7O0MXfKj411ucrk25BZHmS5atRlHQTS',
createdAt: 2024-06-19T08:46:54.000Z,
updatedAt: 2024-06-24T04:54:26.000Z,
role: {
role_id: 2,
role_name: 'staff',
createdAt: 2024-06-19T07:16:16.000Z,
updatedAt: 2024-06-19T07:16:16.000Z
}
},
session: undefined
}
POST /api/auth/callback/credentials 200 in 128ms
jwt before callabck {
token: {
email: '[email protected]',
user_id: 2,
first_name: 'Mayank',
last_name: '',
phone_number: null,
role: 'staff',
iat: 1719833537,
exp: 1722425537,
jti: '231000ed-cdb9-4126-abfe-83dffad3c713'
},
user: undefined,
session: undefined
}
token after callback {
email: '[email protected]',
user_id: 2,
first_name: 'Mayank',
last_name: '',
phone_number: null,
role: 'staff',
iat: 1719833537,
exp: 1722425537,
jti: '231000ed-cdb9-4126-abfe-83dffad3c713'
}
session before callback {
session: {
user: { name: undefined, email: '[email protected]', image: undefined },
expires: '2024-07-31T11:32:17.519Z'
},
token: {
email: '[email protected]',
user_id: 2,
first_name: 'Mayank',
last_name: '',
phone_number: null,
role: 'staff',
iat: 1719833537,
exp: 1722425537,
jti: '231000ed-cdb9-4126-abfe-83dffad3c713'
},
user: undefined
}
session after callback {
user: {
user_id: 2,
email: '[email protected]',
name: 'Mayank ',
phone_number: null,
role: 'staff',
image: undefined
},
expires: '2024-07-31T11:32:17.519Z'
}
GET /api/auth/session 200 in 3ms
jwt before callabck {
token: {
email: '[email protected]',
user_id: 2,
first_name: 'Mayank',
last_name: '',
phone_number: null,
role: 'staff',
iat: 1719833537,
exp: 1722425537,
jti: 'c5288cb3-b8c8-451a-b5b6-0e19c413fcf1'
},
user: undefined,
session: undefined
}
token after callback {
email: '[email protected]',
user_id: 2,
first_name: 'Mayank',
last_name: '',
phone_number: null,
role: 'staff',
iat: 1719833537,
exp: 1722425537,
jti: 'c5288cb3-b8c8-451a-b5b6-0e19c413fcf1'
}
session before callback {
session: {
user: { name: undefined, email: '[email protected]', image: undefined },
expires: '2024-07-31T11:32:18.839Z'
},
token: {
email: '[email protected]',
user_id: 2,
first_name: 'Mayank',
last_name: '',
phone_number: null,
role: 'staff',
iat: 1719833537,
exp: 1722425537,
jti: 'c5288cb3-b8c8-451a-b5b6-0e19c413fcf1'
},
user: undefined
}
session after callback {
user: {
user_id: 2,
email: '[email protected]',
name: 'Mayank ',
phone_number: null,
role: 'staff',
image: undefined
},
expires: '2024-07-31T11:32:18.839Z'
}
Problem Description:
However, when I log the session object retrieved from getSession on the client-side and getServerSession on the server-side, the structure differs. The client-side session object includes detailed user information (user_id, email, name,role etc.), which i whereas the server-side session object appears less detailed or incomplete session obejct is there .. see below
export async function getServerSideProps(context) {
let session = null; // Initialize session outside try block
try {
session = await getSession(context);
session = await getServerSession(context.req, context.res); // Attempt to get session
console.log("Session object in getServerSideProps :", session);
......
Client-side session object:
session = await getSession(context);
{
"user": {
"user_id": 2,
"email": "[email protected]",
"name": "Mayank",
"phone_number": null,
"role": "staff",
"image": undefined
},
"expires": "2024-07-31T11:32:18.839Z"
}
Server-Side session object:
session = await getServerSession(context.req, context.res);
{
"user": {
"name": undefined,
"email": "[email protected]",
"image": undefined
},
"expires": "2024-07-31T11:32:18.839Z"
}
Expected Behavior:
I expect the session object structure to be consistent between the client-side and server-side contexts, containing detailed user information as configured in the jwt
and session
callbacks.
Question:
How can I ensure that the session object structure remains consistent and includes detailed user information when accessed via both getSession
on the client-side and getServerSession
on the server-side in NextAuth.js?
Satvik Katoch is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.