103 lines
2.9 KiB
TypeScript
103 lines
2.9 KiB
TypeScript
import NextAuth, { DefaultSession } from "next-auth"
|
|
import { JWT } from "@auth/core/jwt"
|
|
import { PrismaAdapter } from "@auth/prisma-adapter"
|
|
|
|
import { db } from "@/lib/db"
|
|
import authConfig from "@/auth.config"
|
|
import { getUserById } from "./data/user"
|
|
import { User, UserRole } from "@prisma/client"
|
|
import { getImpersonationById } from "./data/impersonation"
|
|
|
|
|
|
declare module "@auth/core/types" {
|
|
/**
|
|
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
|
|
*/
|
|
interface Session {
|
|
user: {
|
|
role: UserRole | null
|
|
impersonation: User | null
|
|
// By default, TypeScript merges new interface properties and overwrite existing ones. In this case, the default session user properties will be overwritten, with the new one defined above. To keep the default session user properties, you need to add them back into the newly declared interface
|
|
} & DefaultSession["user"] // To keep the default types
|
|
}
|
|
}
|
|
|
|
declare module "@auth/core/jwt" {
|
|
/** Returned by the `jwt` callback and `auth`, when using JWT sessions */
|
|
interface JWT {
|
|
role: UserRole | null
|
|
impersonation: User | null
|
|
}
|
|
}
|
|
|
|
|
|
export const {
|
|
handlers: { GET, POST },
|
|
auth,
|
|
signIn,
|
|
signOut,
|
|
} = NextAuth({
|
|
events: {
|
|
async linkAccount({ user }) {
|
|
await db.user.update({
|
|
where: {id: user.id },
|
|
data: { emailVerified: new Date() }
|
|
})
|
|
}
|
|
},
|
|
callbacks: {
|
|
async session({ session, user, token }) {
|
|
if (token.sub && session.user) {
|
|
session.user.id = token.sub
|
|
}
|
|
|
|
if (token.role && session.user) {
|
|
session.user.role = token.role
|
|
} else {
|
|
session.user.role = null
|
|
}
|
|
|
|
if (token.impersonation && session.user) {
|
|
session.user.impersonation = token.impersonation
|
|
} else {
|
|
token.impersonation = null
|
|
}
|
|
|
|
return session
|
|
},
|
|
async jwt({ token, user, account, profile }) {
|
|
if (!token.sub) return token
|
|
|
|
const existingUser = await getUserById(token.sub)
|
|
if (!existingUser) return token
|
|
|
|
// Update Role
|
|
token.role = existingUser.role
|
|
|
|
// Update Impersonation
|
|
const impersonation = await getImpersonationById(existingUser.id)
|
|
if (token.role == "ADMIN" && impersonation && impersonation.targetId != existingUser.id) {
|
|
const impersonationUser = await getUserById(impersonation.targetId)
|
|
if (impersonation) {
|
|
token.impersonation = impersonationUser
|
|
} else {
|
|
token.impersonation = null
|
|
}
|
|
} else if (impersonation && impersonation.targetId == existingUser.id) {
|
|
await db.impersonation.delete({
|
|
where: {
|
|
sourceId: existingUser.id
|
|
}
|
|
})
|
|
token.impersonation = null
|
|
} else {
|
|
token.impersonation = null
|
|
}
|
|
|
|
return token
|
|
}
|
|
},
|
|
adapter: PrismaAdapter(db),
|
|
session: { strategy: "jwt" },
|
|
...authConfig,
|
|
}) |