Typescript error when trying to access more user properties in my JWT token

Hello! I am using Next Auth and I have it built so that users will also have a username, and in my auth file, my jwt callback looks like this:
jwt: ({ token, user }) => {
console.log("JWT CALLBACK");
console.log({ user });
if (user) {
token.id = user.id;
token.email = user.email;
token.name = user.name;
token.username = user.username;
}
console.log({ token });
return token;
},
jwt: ({ token, user }) => {
console.log("JWT CALLBACK");
console.log({ user });
if (user) {
token.id = user.id;
token.email = user.email;
token.name = user.name;
token.username = user.username;
}
console.log({ token });
return token;
},
Unfortunately, I am getting this TypeScript error: Property 'username' does not exist on type 'User | AdapterUser'. After looking into it, I found that the user type is defined in the node_modules folder, and this is what both User and AdapterUser looks like:
export interface AdapterUser extends User {
id: string
email: string
emailVerified: Date | null
username: string
}
// and
export interface DefaultUser {
id: string
name?: string | null
email?: string | null
image?: string | null
username?: string | null
}

export interface User extends DefaultUser {}
export interface AdapterUser extends User {
id: string
email: string
emailVerified: Date | null
username: string
}
// and
export interface DefaultUser {
id: string
name?: string | null
email?: string | null
image?: string | null
username?: string | null
}

export interface User extends DefaultUser {}
As you can see I've tried adding username to either or, and both, but I'm still getting the same error (after a ts server refresh too) and my app also will not build because of it. Any help would be appreciated. Thank you!
2 Replies
delavalom
delavalom16mo ago
Next-auth uses Module Augmentation to customize the types, so you don't need to change the node_modules: https://next-auth.js.org/getting-started/typescript#module-augmentation Anyways, to solve this, type the following:
import {
type DefaultSession,
type DefaultUser,
} from "next-auth";
import { type DefaultJWT } from "next-auth/jwt";

declare module "next-auth" {
interface Session extends DefaultSession {
user: {
id: string;
name: string;
};
username: string; // Here you are telling typescript that you session will have the username property, if you want your client to have access to this property
}
interface User extends DefaultUser {
username: string; // the user will now have the property
}
}

declare module "next-auth/jwt" {
interface JWT extends DefaultJWT {
id: string;
username: string; // also my jwt will have the property, I can access this property within the JWT using the getToken() helper
}
}

export const authOptions: NextAuthOptions = {
session: {
strategy: "jwt",
},
callbacks: {
jwt({ token, user, account }) {
if (account && account.access_token) {
token.id = user.id;
token.username = user.username; // asign the value
}
return token;
},
session({ session, token }) {
session.user.id = token.id;
session.username = token.username; // pass the value to the client session

return session;
},
},
import {
type DefaultSession,
type DefaultUser,
} from "next-auth";
import { type DefaultJWT } from "next-auth/jwt";

declare module "next-auth" {
interface Session extends DefaultSession {
user: {
id: string;
name: string;
};
username: string; // Here you are telling typescript that you session will have the username property, if you want your client to have access to this property
}
interface User extends DefaultUser {
username: string; // the user will now have the property
}
}

declare module "next-auth/jwt" {
interface JWT extends DefaultJWT {
id: string;
username: string; // also my jwt will have the property, I can access this property within the JWT using the getToken() helper
}
}

export const authOptions: NextAuthOptions = {
session: {
strategy: "jwt",
},
callbacks: {
jwt({ token, user, account }) {
if (account && account.access_token) {
token.id = user.id;
token.username = user.username; // asign the value
}
return token;
},
session({ session, token }) {
session.user.id = token.id;
session.username = token.username; // pass the value to the client session

return session;
},
},
Read all my comments in the code, also make sure to retrieve the username from the provider in my case using Github I get the username like this:
providers: [
GithubProvider({
clientId: env.NEXT_PUBLIC_CLIENT_ID,
clientSecret: env.CLIENT_SECRET,
profile(profile) {
return {
id: profile.id.toString(),
name: profile.name,
username: profile.login,
email: profile.email,
image: profile.avatar_url,
};
},
}),
],
providers: [
GithubProvider({
clientId: env.NEXT_PUBLIC_CLIENT_ID,
clientSecret: env.CLIENT_SECRET,
profile(profile) {
return {
id: profile.id.toString(),
name: profile.name,
username: profile.login,
email: profile.email,
image: profile.avatar_url,
};
},
}),
],
TypeScript | NextAuth.js
NextAuth.js has its own type definitions to use in your TypeScript projects safely. Even if you don't use TypeScript, IDEs like VSCode will pick this up to provide you with a better developer experience. While you are typing, you will get suggestions about what certain objects/functions look like, and sometimes links to documentation, examples, ...
ruhroh
ruhroh16mo ago
That is extremely helpful, thank you so much!
Want results from more Discord servers?
Add your server