Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypeScript issues in session callback #7132

Closed
lionelrudaz opened this issue Apr 2, 2023 · 40 comments
Closed

TypeScript issues in session callback #7132

lionelrudaz opened this issue Apr 2, 2023 · 40 comments
Labels
bug Something isn't working TypeScript Issues relating to TypeScript

Comments

@lionelrudaz
Copy link

lionelrudaz commented Apr 2, 2023

Environment

System:
OS: macOS 13.1
CPU: (8) arm64 Apple M2
Memory: 604.00 MB / 16.00 GB
Shell: 5.8.1 - /bin/zsh
Binaries:
Node: 19.1.0 - /usr/local/bin/node
Yarn: 3.3.0 - /usr/local/bin/yarn
npm: 8.19.3 - /usr/local/bin/npm
Browsers:
Safari: 16.2

Reproduction URL

https://github.com/lionelrudaz/tasters/tree/release

Describe the issue

I've not done any change on my environment lately, but I have a build error with the latest version of Next.

Here it is:

Type error: Property 'id' does not exist on type '{ name?: string; email?: string; image?: string; }'.

  136 |         // Add property to session, like an access_token from a provider.
  137 |         //console.log("Session callback", session, token, user)
> 138 |         session.user.id = token.sub;
      |                      ^
  139 |         session.user.isAdmin = token.isAdmin;
  140 |         session.user.vendorId = token.vendorId;
  141 |         session.user.stripe_id = token.stripeId;

This is in the session callback method.

Here's the complete method:

/**
       * @param  {object} session      Session object
       * @param  {object} token        User object    (if using database sessions)
       *                               JSON Web Token (if not using database sessions)
       * @return {object}              Session that will be returned to the client
       */
      async session({ session, token, user }) {
        // Add property to session, like an access_token from a provider.
        //console.log("Session callback", session, token, user)
        session.user.id = token.sub;
        session.user.isAdmin = token.isAdmin;
        session.user.vendorId = token.vendorId;
        session.user.stripe_id = token.stripeId;
        return session
      },

This is my next-auth.d.ts file:

import NextAuth, { DefaultSession } from "next-auth"
import { JWT } from "next-auth/jwt"
import type UserType from "./user"

declare module "next-auth" {
  /**
   * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
   */
  interface Session {
    user: UserType & DefaultSession["user"]
  }

  interface User {
    image?: string
    isAdmin?: boolean
    vendorId?: number
    stripeId?: string
  }
}

declare module "next-auth/jwt" {
  /** Returned by the `jwt` callback and `getToken`, when using JWT sessions */
  interface JWT {
    /** OpenID ID Token */
    idToken?: string
    isAdmin?: boolean
    vendorId?: number
    stripeId?: string
  }
}

Nothing really different from the documentation.

I've checked my code, looks like session is from type DefaultSession and not Session anymore. I have no clue why.

I've tried to implement a DefaultSession interface, no luck...

My file was in /interfaces/next-auth.d.ts, I've moved it in /types like in https://next-auth.js.org/getting-started/typescript, no luck.

Here's my tsconfig.js file:

{
  "compilerOptions": {
    "target": "es5",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "incremental": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve"
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx"
  ],
  "exclude": [
    "node_modules"
  ]
}

I've checked the docs over and over, there must be something silly that has changed in a recent version, I just don't get it. It looks like it's a different issue than #7033 encountered by @krinklesaurus and @arnekolja.

What I understand is that now in the session() callback, the type is DefaultSession instead of Session. Which means my interface is not used anymore. No idea why it stopped working all the sudden.

Let me know if there's anything useful I can add.

How to reproduce

Explained above.

Expected behavior

Extend the session with additional attributes.

@lionelrudaz lionelrudaz added the triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. label Apr 2, 2023
@jonathanwilke

This comment was marked as off-topic.

@ChristianIvicevic
Copy link

ChristianIvicevic commented Apr 2, 2023

I can confirm a similar regression moving from 4.20.1 to 4.21.0. Just like the T3 template I used to augment the Session type as follows:

import type { DefaultSession } from 'next-auth';

declare module 'next-auth' {
  interface Session {
    user: DefaultSession['user'] & {
      id: string;
    };
  }
}

During build I get this error attempting to write to the session.user.id object in the session callback of the configuration:

Type error: Property 'id' does not exist on type '{ name?: string | null | undefined; email?: string | null | undefined; image?: string | null | undefined; }'.

Thus it's likely the signature of that callback has a breaking change. Checking the types it shows that the session object in the argument of the session callback (beware of the naming) is indeed of type DefaultSession now and not Session anymore causing the augmentation to be useless. The change occurred here:

https://github.com/nextauthjs/next-auth/pull/7056/files#diff-4a09ba4be6baf319f19fb95085e3c72cb52ab694f013b67ddb8861042a0f9b69R319-R327

@ChristianIvicevic

This comment was marked as off-topic.

@mauriciomutte

This comment was marked as off-topic.

@lionelrudaz
Copy link
Author

I can confirm a similar regression moving from 4.20.1 to 4.21.0. Just like the T3 template I used to augment the Session type as follows:

What's funny is that I was running an older version when the problem appeared, something like 4.10, so my first reflex was to update to 4.21.0 😅

Should we stick to 4.20.1 until there's a fix or a documented workaround for 4.21.0?

Thanks in advance folks

@c-ehrlich
Copy link

Can confirm 4.20.1 works. We've pinned create-t3-app to that version for now.

@balazsorban44
Copy link
Member

balazsorban44 commented Apr 3, 2023

Thanks for the heads up. Technically, the incoming session param has always been DefaultSession as can be seen here:

session: {
user: {
name: decodedToken?.name,
email: decodedToken?.email,
image: decodedToken?.picture,
},
expires: newExpires.toISOString(),
},

and here:

// By default, only exposes a limited subset of information to the client
// as needed for presentation purposes (e.g. "you are logged in as...").
session: {
user: { name: user.name, email: user.email, image: user.image },
expires: session.expires.toISOString(),
},

So actually the typing is more correct now.

Only the return type of the session callback is of type Session.

Maybe the true problem is that we've been recommending mutating the incoming param instead of creating a new object on return, so from the top of this thread, this:

async session({ session, token, user }) {
  // Add property to session, like an access_token from a provider.
  //console.log("Session callback", session, token, user)
  session.user.id = token.sub;
  session.user.isAdmin = token.isAdmin;
  session.user.vendorId = token.vendorId;
  session.user.stripe_id = token.stripeId;
  return session
},

should be this:

session({ session, token }) {
  return { ...session,
    user: { ...session.user,
      id: token.sub,
      isAdmin: token.isAdmin,
      vendorId: token.vendorId,
      stripe_id: token.stripeId
    }
  }
},

which is more correct, advertises immutability, and works with both old and new typings.

I understand it's breaking the current behavior though, so tagging @ThangHuuVu for thoughts, maybe we should revert after all to be less disruptive, even though it's kind of a small lie in the session param type.

Open to feedback and thoughts.

NOTE: Please don't comment "same issue" or anything if it has no constructive meaning to the issue. It creates noise. If it's relevant to you, always hit the 👍 on the top post.

@balazsorban44 balazsorban44 added TypeScript Issues relating to TypeScript and removed triage Unseen or unconfirmed by a maintainer yet. Provide extra information in the meantime. labels Apr 3, 2023
@juliusmarminge
Copy link
Contributor

I think this is fair, and I just changed to the object-spread method and it works.

If it's the type that's actually passed, I think it should stay and update documentation to show this new way of module augmentation. The problem is it came out of nowhere without any documentation notice in a minor release that this behavior had been changed.

@ChristianIvicevic
Copy link

ChristianIvicevic commented Apr 3, 2023

I want to share two relevant things to keep in mind regarding your mention of creating a new object instead of mutating it.

The first one is technical in nature and relates to the developer ergonomics. Since session is still of a type that is not augmented with manual types. This would probably (haven't tested it yet) prevent the convenient use of immer for example. Arguably the amount of objects to be spread isn't too bad but the use of immer may have been handy especially considering according to the docs it's technically required to test for the existence of session.user. I personally have this in my code:

session: ({ session, user }) => {
  if (session.user !== undefined) {
    session.user.id = user.id;
  }
  return session;
},

The second point relates to the widespread use of the current approach of handling things. @juliusmarminge already referenced this issue in the PR he posted while I was still drafting this answer, but the create-t3-app is quite popular, being promoted by influencers etc. Many fresh developers with less experience will have picked it up and not necessarily expect such an unfortunate breaking change to begin with which may cause friction in downstream packages so to speak.

Hope these thoughts are of value to your decision process on how to move forward with that particular issue :)

@balazsorban44
Copy link
Member

balazsorban44 commented Apr 3, 2023

The problem is it came out of nowhere without any documentation notice in a minor release that this behavior had been changed.

We do state we don't consider type changes breaking https://next-auth.js.org/getting-started/typescript#contributing

But I guess this take does not age well, so I'll stop doing it from now on... Sorry for the inconvenience... 🙏 💚

Regarding:

session: ({ session, user }) => {
  if (session.user !== undefined) {
    session.user.id = user.id;
  }
  return session;
}

This would never evaluate to false in if, because as shown, session is a hardcoded object! So you can see how the current type might advertise false behavior. Honestly, if it's in the docs, it's likely because even **I_**was tripped by this when I took over maintaining the lib, and it just stuck...

Anyway, revert it is, then! We can correct this in @auth/core instead and explain it better in the migration guide/docs.

@balazsorban44 balazsorban44 added the bug Something isn't working label Apr 3, 2023
@balazsorban44
Copy link
Member

I pushed the fix, it's released in 4.21.1!

@lionelrudaz
Copy link
Author

Thanks a lot for the quick fix @balazsorban44, it works well.

I've noticed also that isNewUser is becoming deprecated in the jwt fallback. I know it should be a separate issue, but sometimes for makers like I am, we're no professional devs and keeping up with breaking changes in libraries we use is pretty hard.

I can only imagine how hard it is at your end to make a library like this live, but I believe sometimes having an updated documentation and more complete release notes help us locate if there's an issue with a new version. Perhaps what I'm saying is non sense, but I definitely value the clarity of the documentation, which is BTW one of the asset of next-auth.

Anyway, if you need a hand documenting a change or another, I can try. Otherwise, keep on the good work, thanks a lot for this amazing library.

@balazsorban44
Copy link
Member

New docs will be driven by source code so it won't be possible to make it outdated (besides manual guides etc.), a lot of effort is being put into this. 🙏

We are considering every breaking changes with Thang and see if we can rather do them in 1 or 2 major later down the line to give enough time for migration.

@Arro
Copy link

Arro commented Apr 11, 2023

I'm using the sveltekit wrapper for this. How would I get these changes into that?

@mikhail-karan
Copy link

I'm using the sveltekit wrapper for this. How would I get these changes into that?

Also using SvelteKit wrapper and still seeing these errors.

@gusvianadev
Copy link

I have 4.22.1 and this is still happening
Session is extending DefaultSession, in which user doesn't have an id

@robreinhard
Copy link

Just wanted to confirm that I am also seeing this happen in 4.22.1

@udos86
Copy link

udos86 commented Jun 22, 2023

Also dealing with this in SvelteKit.

Here is my current workaround using type imports from the module augmentation:

src/hooks.server.ts

import type { Session, User } from "@auth/core/types";
session(params) {
    const session: Session = params.session;
    const user: User = params.user;

    if (session.user !== undefined) {
        session.user.permissions = user.permissions;
    }
    return session;
}

src/types/auth.d.ts

import { DefaultSession } from '@auth/core/types';

declare module '@auth/core/types' {
	interface Session {
		user?: {
			id?: string;
			permissions?: string[];
		} & DefaultSession['user'];
	}
	interface User {
		permissions?: string[];
	}
}

@tobyab
Copy link

tobyab commented Jul 22, 2023

Wondering if there's any progress on this. I'm running into this same issue on version 4.22.3

@mohamedamara1
Copy link

4.23.1 and this still occurs

@jzf21
Copy link

jzf21 commented Sep 11, 2023

any workaround for this?

@pongbao
Copy link

pongbao commented Sep 30, 2023

haven't figured out anything as well -- 4.23.1

@geralddugbatey
Copy link

using version 4.24.1 and this worked for me
session({ session, token }) { return { ...session, user: { ...session.user, role: token.role, }, }; },
adding a role to my session and this worked as previously stated above--> t3-oss/create-t3-app#1330

@doneumark
Copy link

having the same issue here

@Raulj123
Copy link

How do you fix this then?
Screenshot 2024-01-15 at 11 27 11 AM

@koitoror
Copy link

How do you fix this then? Screenshot 2024-01-15 at 11 27 11 AM

+1 i experienced the same below
image

@anthoguille
Copy link

How do you fix this then? Screenshot 2024-01-15 at 11 27 11 AM

+1 i experienced the same below image

I couldn't find a solution.
Captura de pantalla 2024-01-20 080316

@tranhuyviet
Copy link

How do you fix this then? Screenshot 2024-01-15 at 11 27 11 AM

+1 i experienced the same below image

I couldn't find a solution. Captura de pantalla 2024-01-20 080316

you try to use the version: "next-auth": "^5.0.0-beta.4",

@koitoror
Copy link

How do you fix this then? Screenshot 2024-01-15 at 11 27 11 AM

+1 i experienced the same below image

I couldn't find a solution. Captura de pantalla 2024-01-20 080316

you try to use the version: "next-auth": "^5.0.0-beta.4",

i'm using that exact version

@whcender
Copy link

How do you fix this then? Screenshot 2024-01-15 at 11 27 11 AM

+1 i experienced the same below image

I couldn't find a solution. Captura de pantalla 2024-01-20 080316

you try to use the version: "next-auth": "^5.0.0-beta.4",

i'm using that exact version

same issue, Did you find any solution

@enricoblanco
Copy link

changing to version 5.0.0-beta.4 actually worked for me

@jrfullstack
Copy link

image

I have the same problem, I tried different versions and couldn't solve it,

Any ideas?

@koitoror
Copy link

`

TRIED THIS WORKAROUND →

async session({ session, trigger, newSession }) {
  const token = newSession?.user?.token || session?.user?.token;
  console.log('JWT token:', token);

  if (trigger === 'update' && newSession.user) {
    // Handle/Access the new/session update logic here if needed
    // newSession.user.id, newSession.user.role, etc.
  }

  // return {...session};
  return session;
},

`

@jrfullstack
Copy link

`

TRIED THIS WORKAROUND →

async session({ session, trigger, newSession }) {
  const token = newSession?.user?.token || session?.user?.token;
  console.log('JWT token:', token);

  if (trigger === 'update' && newSession.user) {
    // Handle/Access the new/session update logic here if needed
    // newSession.user.id, newSession.user.role, etc.
  }

  // return {...session};
  return session;
},

`
image
the same problem

@xcodewhisperer
Copy link

xcodewhisperer commented Jan 25, 2024

travistylervii had found a solution by downgrading to beta.4. Be carefull: the ^symbol must deleted. In my case, i did: yarn upgrade next-auth@5.0.0-beta.4. (issue 9633)
It worked for me.

@koitoror
Copy link

`
TRIED THIS WORKAROUND →

async session({ session, trigger, newSession }) {
  const token = newSession?.user?.token || session?.user?.token;
  console.log('JWT token:', token);

  if (trigger === 'update' && newSession.user) {
    // Handle/Access the new/session update logic here if needed
    // newSession.user.id, newSession.user.role, etc.
  }

  // return {...session};
  return session;
},

`
image
the same problem

it depends on the shape of your user type if extended to add/carry token field

@jrfullstack
Copy link

thanks this worked for me,

package.json
"next-auth": "5.0.0-beta.4",

auth.config.ts
trustHost: true, // for build

@koitoror
Copy link

"next-auth": "^5.0.0-beta.4", install resolves to the latest undocumented unstable 5.0.0-beta.5 necessitating the workaround

@jarrodmedrano
Copy link

jarrodmedrano commented Feb 3, 2024

I am using PostgresAdapter, I got this far, but only getting an error on Adapter User

declare module 'next-auth' {
  export interface User extends DefaultUser {
    id: string
    isAdmin: boolean
    role: string
  }

  export interface Session {
    user: User
  }

  export interface JWT {
    user: {
      id: string
      role: string
    }
  }
}

declare module '@auth/core/adapters' {
  export interface AdapterUser extends DefaultAdapterUser {
    isAdmin: boolean
    id: string
    role: string
  }
}

Screenshot 2024-02-03 at 9 44 51 AM

@luislealdev
Copy link

changing to version 5.0.0-beta.4 actually worked for me

Thank you so much!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working TypeScript Issues relating to TypeScript
Projects
None yet
Development

No branches or pull requests