Next.js Discord

Discord Forum

NextAuth data added to session is not present in builded app

Unanswered
Loïs posted this in #help-forum
Open in Discord
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)

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.ts
callbacks: {
    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.
I'm using the CredentialsProvider like this :
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 }
I pushed a bug reproduction repository https://github.com/LoisDuplain/next-auth-session-issue
Masai Lion
oh nice let me take a look
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? 😄
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
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
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 configured
Is this the correct way for the session provider ?
Masai Lion
pass the authoptions in the getServerSession
const authOptions: ....... /// should be export const authOptions
Fuck my life
Masai Lion
then w/e you need to get the session serverside you pass the options in the hook
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 ^^
Yes, yes, now I know I've understood all these things, I've even created my own context...

Thank you so much ❤️
Masai Lion
yeah also be careful with the fetch api
i have no cache rule for the moment by default
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
amm no idea kekw 😄
quick question the method getSession() or getCsrfToken() didnt expose the token?
sorry if this is solved
to the client
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
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
is a async method here is the code of the getSession
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
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