-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Next Auth V5 Augment Typescript User Interface doesnt work #9253
Comments
@cexra Try using |
yes the solution works but only partially, the solution solves the problem on jwt and session callback. but why |
import "next-auth";
declare module "next-auth" {
interface User {
role: "SUPER" | "USER";
}
interface Session {
user?: User;
}
}
declare module "@auth/core/jwt" {
interface JWT {
role: "SUPER" | "USER";
}
} This works for me. |
I tried both ways, and i run into the same issue: import NextAuth from "next-auth";
import Discord from "next-auth/providers/discord";
import type { NextAuthConfig } from "next-auth";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { db } from "@/app/db";
import { pgTable } from "./db/schema";
import { env } from "@/env.mjs";
enum UserRole {
Admin = "admin",
User = "user",
Free = "free",
}
declare module "@auth/core/types" {
interface Session {
user: {
id: string;
// ...other properties
role: UserRole;
} & DefaultSession["user"];
}
interface User {
// ...other properties
role: UserRole;
}
}
export const config = {
theme: {
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
},
providers: [
Discord({
clientId: env.DISCORD_CLIENT_ID,
clientSecret: env.DISCORD_CLIENT_SECRET,
}),
],
adapter: DrizzleAdapter(db, pgTable),
callbacks: {
session: ({ session, user }) => ({
...session,
user: {
...session.user,
id: user.id,
role:session.user?.role
},
}),
},
} satisfies NextAuthConfig;
export const { handlers, auth } = NextAuth(config);
|
Have you tried to put your augmented types into a separate |
I needed to update my package to import NextAuth from "next-auth";
import Discord from "next-auth/providers/discord";
import type { NextAuthConfig } from "next-auth";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { db } from "@/app/db";
import { pgTable } from "./db/schema";
import { env } from "@/env.mjs";
enum UserRole {
Admin = "admin",
User = "user",
Free = "free",
}
declare module "@auth/core/types" {
interface Session {
user: {
id: string;
// ...other properties
role: UserRole;
}
}
interface User {
// ...other properties
role: UserRole;
}
}
export const config = {
theme: {
logo: "https://next-auth.js.org/img/logo/logo-sm.png",
},
providers: [
Discord({
clientId: env.DISCORD_CLIENT_ID,
clientSecret: env.DISCORD_CLIENT_SECRET,
}),
],
adapter: DrizzleAdapter(db, pgTable),
callbacks: {
session: ({ session, user }) => ({
...session,
user: {
...session.user,
id: user.id,
role: session.user?.role,
},
}),
},
} satisfies NextAuthConfig;
export const { handlers, auth } = NextAuth(config); |
thanks bro |
I gave this a go today and no luck still unfortunately. How come this works for you guys when this is still open? #8561 |
So I spend quite some time debugging this, as both I managed to solve it by making the
Notice that it also works with |
I've tested various releases of
|
@rvndev did you include your I've added this snippet in my declare module "next-auth" {
interface Session {
user: {
address: string
} & User
}
interface User {
foo?: string
}
} Check this out for more details: https://authjs.dev/getting-started/typescript |
Extending the native Auth.js type + actually passing those extended values to the session is way more of a nightmare than it should be. Crazy this wasn't improved from v4 to v5, in fact, it's actually worse. Why is this issue closed? |
Following the documentation perfectly on the TypeScript section throws errors and fails. types.d.ts import NextAuth, { type DefaultSession } from "next-auth"
declare module "next-auth" {
/**
* Returned by `auth`, `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
*/
interface Session {
user: {
/** The user's postal address. */
address: string
/**
* By default, TypeScript merges new interface properties and overwrites existing ones.
* In this case, the default session user properties will be overwritten,
* with the new ones defined above. To keep the default session user properties,
* you need to add them back into the newly declared interface.
*/
} & DefaultSession["user"]
}
} auth.ts export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [redacted],
session: {
strategy: "jwt",
maxAge: 48 * 60 * 60, // 48 hours
},
callbacks: {
session({ session, token, user }) {
// `session.user.address` is now a valid property, and will be type-checked
// in places like `useSession().data.user` or `auth().user`
return {
...session,
user: {
...session.user,
address: user.address,
},
};
},
},
}); The above example is taken directly from the documentation, and yields:
This issue needs to be re-opened, at a minimum the docs need to be fixed. |
@dir To fix this just augment the module "@auth/core/adapters". You do need to install it though (pnpm add "@auth/core" and make sure it's the latest version by checking the npm releases or on github.) Bellow see an example: declare module "next-auth" {
interface Session {
user: {
role: UserRoles;
id: string;
email: string;
activated: UserActivated;
} & DefaultSession["user"];
}
interface User {
role: UserRoles;
activated: UserActivated;
}
}
//For the adaptor user objcet
//Here add any other fields you have in user table that are not part of the default schema.
declare module "@auth/core/adapters" {
interface AdapterUser extends User {
role: UserRoles;
activated: UserActivated;
}
} |
@ypanagidis I appreciate your help! I installed But... (after restarting my editor, clearing my modules folder, reinstalling, etc), I get
On a simple reproduction of your example:
This is nightmarish just to extend my session and add a couple of types. What a headache. |
@dir I had the same issue in a monorepo, make sure to import anything from '@auth/core" and the error should go away! I think it's getting treeshaken |
I tried everything above and I still couldn't overwrite the interfaces User and Session, any other suggestions? package.json
|
I downgraded to stable version |
Have you come across a solution yet? |
I think it's been tree shaken away. Just import it and export the type |
Trying multiple different way to override the User types with import 'next-auth'
import '@auth/core/types'
import '@auth/core'
declare module '@auth/core' {
// trying to override type of these field from being undefined | null
interface User {
id: string;
name: string;
image: string;
email: string | null;
emailVerified: Date | null;
}
/**
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
*/
interface Session {
user?: User;
}
interface Account {
accessToken: string;
refreshToken: string;
}
}
declare module 'next-auth' {
// trying to override type of these field from being undefined | null
interface User {
id: string;
name: string;
image: string;
email: string | null;
emailVerified: Date | null;
}
/**
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
*/
interface Session {
user?: User;
}
interface Account {
accessToken: string;
refreshToken: string;
}
} Neither solutions work. That farthest I made it was overriding the session user type. This beta is definitely not cross-compatible with version 4 when it comes to overriding the next-auth types. Still not sure why this issue is still closed. The only solution is to downgrade to the stable |
for those of you who still have problems with augmentation, I have a project that successfully performs augmentation, you can see it https://github.com/prave-com/electra. As an illustration, I added role attributes to the session and user objects. You can see the project directly, and here I will provide some of the code for you to see and study, I hope this can help you. as a reminder, I once read that we should only use the same config for all next auth which I saved in
{
"name": "electra",
"version": "0.1.0",
"private": true,
"prisma": {
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
},
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"format:check": "prettier --check --ignore-unknown .",
"format": "prettier --write --ignore-unknown . && prisma format",
"postinstall": "prisma generate"
},
"dependencies": {
"@auth/prisma-adapter": "^2.4.2",
"@emotion/cache": "^11.13.1",
"@emotion/react": "^11.13.0",
"@emotion/styled": "^11.13.0",
"@hookform/resolvers": "^3.9.0",
"@mui/icons-material": "^5.16.7",
"@mui/material": "^5.16.7",
"@mui/material-nextjs": "^5.16.6",
"@prisma/client": "^5.18.0",
"bcryptjs": "^2.4.3",
"next": "14.2.5",
"next-auth": "5.0.0-beta.20",
"react": "^18",
"react-dom": "^18",
"react-hook-form": "^7.53.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/bcryptjs": "^2.4.6",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"csv-parse": "^5.5.6",
"eslint": "^8",
"eslint-config-next": "14.2.5",
"postcss": "^8",
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.6.6",
"prisma": "^5.18.0",
"tailwindcss": "^3.4.1",
"ts-node": "^10.9.2",
"typescript": "^5"
}
}
import { Role } from '@prisma/client'
import NextAuth, { DefaultSession } from 'next-auth'
import { JWT } from 'next-auth/jwt'
declare module 'next-auth' {
interface Session {
user: {
role: Role
} & DefaultSession['user']
}
}
declare module 'next-auth/jwt' {
interface JWT {
role: Role
}
}
import { getUserById } from '@/data/user'
import { getUserByEmail } from '@/data/user'
import db from '@/lib/db'
import { LoginSchema } from '@/schema/login'
import { PrismaAdapter } from '@auth/prisma-adapter'
import bcrypt from 'bcryptjs'
import type { NextAuthConfig } from 'next-auth'
import Credentials from 'next-auth/providers/credentials'
import Google from 'next-auth/providers/google'
export default {
providers: [
Google,
Credentials({
name: 'Credentials',
credentials: {
username: { label: 'Username', type: 'text' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
const validatedFields = LoginSchema.safeParse(credentials)
if (validatedFields.success) {
const { email, password } = validatedFields.data
const user = await getUserByEmail(email)
if (!user || !user.password) return null
const passwordMatch = await bcrypt.compare(password, user.password)
if (passwordMatch) return user
}
return null
},
}),
],
events: {
async linkAccount({ user }) {
await db.user.update({
where: { id: user.id },
data: { emailVerified: new Date() },
})
},
},
pages: {
signIn: '/signin',
},
callbacks: {
async session({ token, session }) {
if (token.sub && session.user) {
session.user.id = token.sub
}
if (token.role && session.user) {
session.user.role = token.role
}
return session
},
async jwt({ token }) {
if (!token.sub) return token
const existingUser = await getUserById(token.sub)
if (!existingUser) return token
token.role = existingUser.role
return token
},
},
adapter: PrismaAdapter(db),
session: { strategy: 'jwt' },
} satisfies NextAuthConfig
import authConfig from '@/auth.config'
import NextAuth from 'next-auth'
export const {
handlers: { GET, POST },
auth,
signIn,
signOut,
} = NextAuth(authConfig)
import authConfig from '@/auth.config'
import {
administratorRoutePrefix,
apiAuthPrefix,
authRoutes,
operatorRoutePrefix,
publicRoutes,
} from '@/routes'
import { Role } from '@prisma/client'
import NextAuth from 'next-auth'
const { auth } = NextAuth(authConfig)
export default auth((req) => {
const { nextUrl } = req
const isLoggedIn = !!req.auth
const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix)
const isPublicRoute = publicRoutes.includes(nextUrl.pathname)
const isAuthRoute = authRoutes.includes(nextUrl.pathname)
const isAdministratorRoute = nextUrl.pathname.startsWith(
administratorRoutePrefix,
)
const isOperatorRoute = nextUrl.pathname.startsWith(operatorRoutePrefix)
const isAdministrator = req.auth?.user.role === Role.ADMINISTRATOR
const isOperator = req.auth?.user.role === Role.OPERATOR
const isGuest = req.auth?.user.role === Role.GUEST
if (isApiAuthRoute) return
if (isAuthRoute) {
if (isLoggedIn && isAdministrator) {
return Response.redirect(new URL(administratorRoutePrefix, nextUrl))
}
if (isLoggedIn && isOperator) {
return Response.redirect(new URL(operatorRoutePrefix, nextUrl))
}
if (isLoggedIn && isGuest) {
return Response.redirect(new URL('/', nextUrl))
}
return
}
if (isLoggedIn) {
if (isAdministratorRoute && !isAdministrator) {
return Response.redirect(
new URL('/signin?message=Not Authorized', nextUrl),
)
}
if (isOperatorRoute && !isOperator) {
return Response.redirect(
new URL(`/signin?message=Not Authorized`, nextUrl),
)
}
return
}
if (!isLoggedIn && !isPublicRoute) {
let callbackUrl = nextUrl.pathname
if (nextUrl.search) callbackUrl += nextUrl.search
const encodedCallbackUrl = encodeURIComponent(callbackUrl)
return Response.redirect(
new URL(`/signin?callbackUrl=${encodedCallbackUrl}`, nextUrl),
)
}
return
})
export const config = {
matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/(api|trpc)(.*)'],
} |
@vexra thanks for chiming in with another example! I just want to clarify one thing - about the This was an old workaround required because many database adapters didn't work in edge runtimes yet (i.e. middlware). So if you're not using a database, you don't need to do this split at all. In addition, many DB pkgs have been updated over the last months / year, for example prisma, and it may not be necessary when using prisma anymore either. Please check out our prisma docs here: https://authjs.dev/getting-started/adapters/prisma#edge-compatibility |
I have issue making |
I think the possible solution is to put the next-auth.d.ts in types folder root this works for me |
Hello, |
What is the improvement or update you wish to see?
next auth js provides documentation that does not work to augment the User interface in the
@auth/core
module.Is there any context that might help us understand?
from the existing documentation we can augment the User interface like the method below
next-auth.d.ts
augment Session interface and JWT interface it works. but augment User interface does not work. my purpose of augmenting the User interface is to persist the role attribute on the User object, so that I can store it on the Session object.
auth.config.ts
Does the docs page already exist? Please link to it.
https://authjs.dev/getting-started/typescript?frameworks=next
The text was updated successfully, but these errors were encountered: