93 lines
2.3 KiB
TypeScript
93 lines
2.3 KiB
TypeScript
import NextAuth, { type DefaultSession } from "next-auth"
|
|
import Credentials from "next-auth/providers/credentials"
|
|
import { ZodError } from "zod"
|
|
|
|
import type { UserRole } from "@/generated/prisma/client"
|
|
import { SIGN_IN_URL, TOKEN_EXPIRATION_SECONDS } from "@/lib/constants"
|
|
import { verifyPassword } from "@/lib/security"
|
|
import { signInSchema } from "@/schemas/auth.schema"
|
|
import { getUserByUsername } from "@/services/user.service"
|
|
|
|
declare module "next-auth" {
|
|
interface Session {
|
|
user: {
|
|
id: string
|
|
role: UserRole
|
|
} & DefaultSession["user"]
|
|
}
|
|
|
|
interface User {
|
|
role: UserRole
|
|
}
|
|
}
|
|
|
|
export const { handlers, signIn, signOut, auth } = NextAuth({
|
|
session: {
|
|
strategy: "jwt",
|
|
maxAge: TOKEN_EXPIRATION_SECONDS,
|
|
},
|
|
providers: [
|
|
Credentials({
|
|
credentials: {
|
|
username: {},
|
|
password: {},
|
|
},
|
|
authorize: async (credentials) => {
|
|
try {
|
|
const { data, success } = signInSchema.safeParse(credentials)
|
|
|
|
if (!success) throw new Error("Invalid username or password")
|
|
|
|
const user = await getUserByUsername(data.username)
|
|
|
|
if (!user) {
|
|
throw new Error("Invalid username or password")
|
|
}
|
|
|
|
if (!user.isActive) {
|
|
throw new Error("Invalid username or password")
|
|
}
|
|
|
|
if (!(await verifyPassword(data.password, user.password)))
|
|
throw new Error("Invalid username or password")
|
|
|
|
return {
|
|
...user,
|
|
role: user.role,
|
|
}
|
|
} catch (error) {
|
|
if (error instanceof ZodError) {
|
|
return null
|
|
}
|
|
throw new Error("Invalid username or password")
|
|
}
|
|
},
|
|
}),
|
|
],
|
|
pages: {
|
|
signIn: SIGN_IN_URL,
|
|
},
|
|
callbacks: {
|
|
async session({ session, token }) {
|
|
if (!token) return session
|
|
session.user.id = token.sub as string
|
|
session.user.name = token.name as string
|
|
session.user.email = token.email as string
|
|
session.user.role = token.role as UserRole
|
|
return session
|
|
},
|
|
async jwt({ token, user }) {
|
|
if (user) {
|
|
token.sub = user.id
|
|
token.name = user.name
|
|
token.email = user.email
|
|
token.role = user.role
|
|
}
|
|
return token
|
|
},
|
|
authorized: async ({ auth }) => {
|
|
return !!auth
|
|
},
|
|
},
|
|
})
|