Files
stock-manager/src/lib/auth.ts
T

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
},
},
})