united-tattoo/lib/auth.ts

198 lines
5.3 KiB
TypeScript

import { NextAuthOptions } from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import GitHubProvider from "next-auth/providers/github"
import CredentialsProvider from "next-auth/providers/credentials"
import { env } from "./env"
import { UserRole } from "@/types/database"
export const authOptions: NextAuthOptions = {
// Note: Database adapter will be configured via Supabase MCP
// For now, using JWT strategy without database adapter
providers: [
// Credentials provider for email/password login
CredentialsProvider({
name: "credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" }
},
async authorize(credentials) {
console.log("Authorize called with:", credentials)
if (!credentials?.email || !credentials?.password) {
console.log("Missing email or password")
return null
}
console.log("Email received:", credentials.email)
console.log("Password received:", credentials.password ? "***" : "empty")
// Seed admin user for nicholai@biohazardvfx.com
if (credentials.email === "nicholai@biohazardvfx.com") {
console.log("Admin user recognized!")
return {
id: "admin-nicholai",
email: "nicholai@biohazardvfx.com",
name: "Nicholai",
role: UserRole.SUPER_ADMIN,
}
}
// For development: Accept any other email/password combination
console.log("Using fallback user creation")
const user = {
id: "dev-user-" + Date.now(),
email: credentials.email,
name: credentials.email.split("@")[0],
role: UserRole.SUPER_ADMIN, // Give admin access for testing
}
console.log("Created user:", user)
return user
}
}),
// Google OAuth provider (optional)
...(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET ? [
GoogleProvider({
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
})
] : []),
// GitHub OAuth provider (optional)
...(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET ? [
GitHubProvider({
clientId: env.GITHUB_CLIENT_ID,
clientSecret: env.GITHUB_CLIENT_SECRET,
})
] : []),
],
session: {
strategy: "jwt",
maxAge: 30 * 24 * 60 * 60, // 30 days
},
callbacks: {
async jwt({ token, user, account }) {
// Add user role to JWT token
if (user) {
// Use the role from the user object (set in authorize function)
token.role = (user as any).role || UserRole.CLIENT
token.userId = user.id
}
return token
},
async session({ session, token }) {
// Add user role and ID to session
if (token) {
session.user.id = token.userId as string
session.user.role = token.role as UserRole
}
return session
},
async signIn({ user, account, profile }) {
// Custom sign-in logic
return true
},
async redirect({ url, baseUrl }) {
// Follows NextAuth.js best practices for redirect
if (url.startsWith("/")) return `${baseUrl}${url}`
else if (new URL(url).origin === baseUrl) return url
return `${baseUrl}/admin`
},
},
pages: {
signIn: "/auth/signin",
error: "/auth/error",
},
events: {
async signIn({ user, account, profile, isNewUser }) {
// Log sign-in events
console.log(`User ${user.email} signed in`)
},
async signOut({ session, token }) {
// Log sign-out events
console.log(`User signed out`)
},
},
debug: env.NODE_ENV === "development",
}
/**
* Utility function to get server-side session
*/
export async function getServerSession() {
const { getServerSession: getNextAuthServerSession } = await import("next-auth/next")
return getNextAuthServerSession(authOptions)
}
/**
* Route protection utility
* @param requiredRole - Minimum role required to access the route
*/
export async function requireAuth(requiredRole?: UserRole) {
const session = await getServerSession()
if (!session) {
throw new Error("Authentication required")
}
if (requiredRole && !hasRole(session.user.role, requiredRole)) {
throw new Error("Insufficient permissions")
}
return session
}
/**
* Check if user has required role or higher
*/
export function hasRole(userRole: UserRole, requiredRole: UserRole): boolean {
const roleHierarchy = {
[UserRole.CLIENT]: 0,
[UserRole.ARTIST]: 1,
[UserRole.SHOP_ADMIN]: 2,
[UserRole.SUPER_ADMIN]: 3,
}
return roleHierarchy[userRole] >= roleHierarchy[requiredRole]
}
/**
* Check if user is admin (SHOP_ADMIN or SUPER_ADMIN)
*/
export function isAdmin(role: UserRole): boolean {
return role === UserRole.SHOP_ADMIN || role === UserRole.SUPER_ADMIN
}
/**
* Check if user is super admin
*/
export function isSuperAdmin(role: UserRole): boolean {
return role === UserRole.SUPER_ADMIN
}
// Extend NextAuth types
declare module "next-auth" {
interface Session {
user: {
id: string
email: string
name: string
image?: string
role: UserRole
}
}
interface User {
role: UserRole
}
}
declare module "next-auth/jwt" {
interface JWT {
userId: string
role: UserRole
}
}