NextAuth data added to session is not present in builded app
Unanswered
Loïs posted this in #help-forum
LoïsOP
I have a NextJS app using NextAuth.
I use an external api (created in C# .net ) for every requests.
When a user click on a create or update button i need to send/fetch to the external api from the client and add the access token to the request
To do that i need to store the access token into the session
In development (npx next dev) everything works but when I build and start, the session, in the client side, only contains the default NextAuth session params without the sub : email, image, name
-- Informations
Node version: 21.1.0
NextJS: 14.0.1
NextAuth: ^4.24.4 (from package.json)
-- My code
I created a
My UserDTO used in the User :
This is how I fill the session :
In development mode, when i run the app with
Now in the builded app:
When we look at the server side console we get the correct filled session log:
But at the client side, when i try to print the session into a
And the result is only (
I don't have any idea to fix that😢
I use an external api (created in C# .net ) for every requests.
When a user click on a create or update button i need to send/fetch to the external api from the client and add the access token to the request
To do that i need to store the access token into the session
In development (npx next dev) everything works but when I build and start, the session, in the client side, only contains the default NextAuth session params without the sub : email, image, name
-- Informations
Node version: 21.1.0
NextJS: 14.0.1
NextAuth: ^4.24.4 (from package.json)
tsconfig.json include and exclude:{
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "types/**/*.tsx", "types/**/*.ts"],
"exclude": ["node_modules"],
"compilerOptions": [ "..." ]
}-- My code
I created a
nextauth.d.ts file to declare merging nextauth default types:declare module "next-auth" {
interface Session {
user: {
access_token?: AccessToken,
} & User & DefaultSession["user"],
}
interface User extends UserDTO {
access_token: AccessToken,
}
}
declare module "next-auth/jwt" {
interface JWT extends User {
}
}My UserDTO used in the User :
export type UserDTO = EntityDTO & {
id: UUID;
username: string;
first_name: string;
last_name: string;
full_name: string;
email: string;
group_id: UUID;
group?: GroupDTO;
permissions?: PermissionDTO[];
picture?: string | null | undefined;
};This is how I fill the session :
app/api/auth/[...nextauth]/route.tscallbacks: {
async session({ session, token }) {
console.log("route.ts/session/session", session)
console.log("route.ts/session/token", token)
session.user = {
...session.user,
...token,
picture: token.picture,
}
console.log("route.ts/session/--- FILLED SESSION ----", session)
return session
},
async jwt({ token, user, account, profile }) {
console.log("route.ts/jwt/token", token)
console.log("route.ts/jwt/user", user)
console.log("route.ts/jwt/account", account)
console.log("route.ts/jwt/profile", profile)
if (user) {
token = {
...token,
...user,
name: user.username,
}
}
return token
}
},In development mode, when i run the app with
npx next dev everything works (image 1)Now in the builded app:
When we look at the server side console we get the correct filled session log:
route.ts/session/--- FILLED SESSION ---- {
user: {
name: 'aries',
email: 'contact@aries.fr',
image: '/assets/images/avatars/aries.png',
picture: '/assets/images/avatars/aries.png',
sub: '11111111-1111-1111-1111-111111111111',
username: 'aries',
first_name: 'Aries',
last_name: 'Software',
full_name: 'Aries Software',
group_id: '11111111-1111-1111-1111-111111111112',
group: {
id: '11111111-1111-1111-1111-111111111112',
name: 'Administrateur'
},
permissions: [],
id: '11111111-1111-1111-1111-111111111111',
created_at: '2022-12-31T23:00:00Z',
created_by: '11111111-1111-1111-1111-111111111111',
updated_at: '2022-12-31T23:00:00Z',
updated_by: '11111111-1111-1111-1111-111111111111',
deleted_at: null,
deleted_by: null,
access_token: 'eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTUxMiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjExMTExMTExLTExMTEtMTExMS0xMTExLTExMTExMTExMTExMSIsInVzZXJuYW1lIjo...',
iat: 1698967599,
exp: 1701559599,
jti: '831a3d5b-4ddd-4682-a136-dd0885c04524'
},
expires: '2023-12-02T23:35:15.078Z'
}But at the client side, when i try to print the session into a
use client component (same code as the image 1):const { data: session } = useSession();
console.log("EditUserGroupForm/session", session)And the result is only (
image 2)I don't have any idea to fix that😢
50 Replies
Masai Lion
How do you auth the user in ? Aka what providers do you use and the strategy (jwt or db) I guess is jwt.
@Masai Lion How do you auth the user in ? Aka what providers do you use and the strategy (jwt or db) I guess is jwt.
LoïsOP
I'm using the CredentialsProvider like this :
Here is my full
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
username: { label: "Username", type: "text", placeholder: "jdoe" },
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
if (!credentials)
throw new Error("No credentials")
const loginResponse: LoginResponseDTO = await fetch(process.env.NEXT_PUBLIC_COMMON_API_URL + "/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: credentials.username, password: credentials.password })
})
.then(res => res.json())
if (loginResponse.user) {
return {
...loginResponse.user,
access_token: loginResponse.token
}
} else {
return null
}
}
})
],Here is my full
app/auth/[...nextauth]/route.ts:const authOptions: NextAuthOptions = {
providers: [
CredentialsProvider({
name: "credentials",
credentials: {
username: { label: "Username", type: "text", placeholder: "jdoe" },
password: { label: "Password", type: "password" }
},
async authorize(credentials, req) {
if (!credentials)
throw new Error("No credentials")
const loginResponse: LoginResponseDTO = await fetch(process.env.NEXT_PUBLIC_COMMON_API_URL + "/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: credentials.username, password: credentials.password })
})
.then(res => res.json())
if (loginResponse.user) {
return {
...loginResponse.user,
access_token: loginResponse.token
}
} else {
return null
}
}
})
],
secret: process.env.SECRET,
callbacks: {
async session({ session, token, user }) {
console.log("route.ts/session/session", session)
console.log("route.ts/session/token", token)
console.log("route.ts/session/user", user)
session.user = token
session.test = "test"
console.log("route.ts/session/--- FILLED SESSION ----", session)
return session
},
async jwt({ token, user, account, profile }) {
console.log("route.ts/jwt/token", token)
console.log("route.ts/jwt/user", user)
console.log("route.ts/jwt/account", account)
console.log("route.ts/jwt/profile", profile)
if (user) {
token = {
...token,
...user,
name: user.username,
}
}
return token
}
},
pages: {
signIn: "/login",
error: "/login"
},
}
const handler = NextAuth(authOptions)
export { handler as GET, handler as POST, authOptions }LoïsOP
I pushed a bug reproduction repository https://github.com/LoisDuplain/next-auth-session-issue
Masai Lion
oh nice let me take a look
LoïsOP
it is probably not complete but the main parts are in
If something is missing tell me
Masai Lion
hope that the test-next-auth-issue.tsx isnt the same in term of actual session context provider right? 😄
also i think in the nextauth.d.ts you should be extending the default types heres an example
import { DefaultSession, DefaultUser } from "next-auth";
import { JWT, DefaultJWT } from 'next-auth/jwt';
declare module "next-auth" {
interface Session {
user: {
id: number;
name: string;
role: string;
} & DefaultSession
}
interface User extends DefaultUser {
id: number;
name: string;
role: string;
}
}
declare module "next-auth/jwt" {
interface JWT extends DefaultJWT {
id: number;
name: string;
role: string;
}
};@Masai Lion hope that the test-next-auth-issue.tsx isnt the same in term of actual session context provider right? 😄
LoïsOP
The test-next-auth-issue component is for testing only, I was doing that in another component when I published this post but when the repo was published I preferred to put it in a dedicated component
Masai Lion
also in production in your home page
const session = await getServerSession(authOptions); using this and you console.log the session do u get everything as expected ?@Masai Lion import { DefaultSession, DefaultUser } from "next-auth";
import { JWT, DefaultJWT } from 'next-auth/jwt';
declare module "next-auth" {
interface Session {
user: {
id: number;
name: string;
role: string;
} & DefaultSession
}
interface User extends DefaultUser {
id: number;
name: string;
role: string;
}
}
declare module "next-auth/jwt" {
interface JWT extends DefaultJWT {
id: number;
name: string;
role: string;
}
};
LoïsOP
I'll give it a try, but I don't see why it would work in the development environment and not in production
Masai Lion
well the only difference between prod and dev using next auth is the actual URL like if you are using localhost then u should be changing to prod url (if the app is deployed) . other then that idk using the fetch api from next js does some caching may be thats an issue also but im not so sure
@Masai Lion also in production in your home page const session = await getServerSession(authOptions); using this and you console.log the session do u get everything as expected ?
LoïsOP
Yes, I have all the information on the server side
@Masai Lion well the only difference between prod and dev using next auth is the actual URL like if you are using localhost then u should be changing to prod url (if the app is deployed) . other then that idk using the fetch api from next js does some caching may be thats an issue also but im not so sure
LoïsOP
At the moment it's not even deployed on a server, I'm building and starting locally
Masai Lion
If u setup everything the right way ( prod envs for the actual urls , secrets etc) just try to console.log this thing
const session = await getServerSession(authOptions); if this doesnt return the wanted object out of the session then theres a configuration issue. But if it does and your client side auth aka const { data: session } = useSession() not working then the provider-context is miss configuredMasai Lion
pass the authoptions in the getServerSession
const authOptions: ....... /// should be export const authOptionsLoïsOP
Fuck my life
Masai Lion
then w/e you need to get the session serverside you pass the options in the hook
LoïsOP
I had coded this at the beginning and before I always forgot the authOptions
Masai Lion
for the client side part you need to create a provider context
@Loïs bro happened 1-2 twice to me aswell everytime i see someone calling that his next auth session is empty 99% of the time this was the issue ^^
LoïsOP
Yes, yes, now I know I've understood all these things, I've even created my own context...
Thank you so much â¤ï¸
Thank you so much â¤ï¸
Masai Lion
yeah also be careful with the fetch api
Masai Lion
cheers hope i was helpful ( also a tip when you try to deploy it and use
https://yourawesomedomain.com/ remember to change the .env vars before building 😉@Masai Lion cheers hope i was helpful ( also a tip when you try to deploy it and use https://yourawesomedomain.com/ remember to change the .env vars before building 😉
LoïsOP
yep no problem for that thx
(How to mark the problem has solved?)
(How to mark the problem has solved?)
Masai Lion
amm no idea kekw 😄
quick question the method getSession() or getCsrfToken() didnt expose the token?
sorry if this is solved
@Roberto Duran quick question the method getSession() or getCsrfToken() didnt expose the token?
LoïsOP
You mean exposed to whom?
to the client
LoïsOP
I don't think it's a problem that the customer knows his token
Masai Lion
I mean this is stated in the docs of next auth you can read it from there getcsrftoken can be used both on client and server side but the getsession is only for clienside*
Masai Lion
Exactly
So what's the issue
maybe i miss something but the issue was the
useSession is not returning the full session list and thats is expected
and thats why getSession exist
useSession is not returning the full session list and thats is expected
and thats why getSession exist
Masai Lion
thats not correct at all useSession if theres is a context provider setup correctly as well as an authopstions route handler(app router) in any client side component useSession would return the correct stuff like session data , session status etc
since under the hood it does what the getServerSession does... a request to the api auth endpoint
but as you stated you can use getSession as well im not sure if you get the status prop from it also the helper is async which will be a bit different to handle in a client side component
but your use of the "request to the api auth endpoint" is the best solution
at the end both make a fetch request
btw i never use getSession always create my on session provider/endpoint just bcs i have a better contorl over it.
i just was curios if that getSession could work for him
at the end both make a fetch request
btw i never use getSession always create my on session provider/endpoint just bcs i have a better contorl over it.
i just was curios if that getSession could work for him
Masai Lion
its in the docs so it should tbf ive never used it either
yep i just read it
Masai Lion
believe in the docs 😄
is been deprecated
hahah i first look at the code and then the docs XD