refactor: replace DB-based auth with env-based single admin user
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7eae4fde33
commit
bc300f54f2
1 changed files with 50 additions and 75 deletions
125
lib/auth.ts
125
lib/auth.ts
|
|
@ -1,27 +1,10 @@
|
|||
import bcrypt from "bcrypt"
|
||||
import { cookies } from "next/headers"
|
||||
import {
|
||||
createSession,
|
||||
deleteSession,
|
||||
getSession,
|
||||
getUserByEmail,
|
||||
getUserById,
|
||||
createUser as dbCreateUser,
|
||||
cleanExpiredSessions,
|
||||
type User,
|
||||
} from "./db"
|
||||
|
||||
const SALT_ROUNDS = 10
|
||||
const SESSION_COOKIE_NAME = "session"
|
||||
const SESSION_DURATION_DAYS = 7
|
||||
const SESSION_DURATION_MS = 7 * 24 * 60 * 60 * 1000 // 7 days
|
||||
|
||||
export async function hashPassword(password: string): Promise<string> {
|
||||
return bcrypt.hash(password, SALT_ROUNDS)
|
||||
}
|
||||
|
||||
export async function verifyPassword(password: string, hash: string): Promise<boolean> {
|
||||
return bcrypt.compare(password, hash)
|
||||
}
|
||||
// In-memory session store. Replace this with your own session/token system.
|
||||
const sessions = new Map<string, { email: string; expiresAt: Date }>()
|
||||
|
||||
function generateSessionId(): string {
|
||||
const bytes = new Uint8Array(32)
|
||||
|
|
@ -31,86 +14,78 @@ function generateSessionId(): string {
|
|||
.join("")
|
||||
}
|
||||
|
||||
// ─── AUTH INTERFACE ────────────────────────────────────────────────
|
||||
// To integrate your own auth (JWT, OAuth, etc.), replace these functions.
|
||||
// The rest of the application only calls checkAuth(), login(), and logout().
|
||||
// ───────────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Check if the current request is authenticated.
|
||||
* Replace this function to integrate JWT, OAuth, or any other auth system.
|
||||
*/
|
||||
export async function checkAuth(): Promise<{ authenticated: boolean; email?: string }> {
|
||||
const cookieStore = await cookies()
|
||||
const sessionId = cookieStore.get(SESSION_COOKIE_NAME)?.value
|
||||
|
||||
if (!sessionId) return { authenticated: false }
|
||||
|
||||
const session = sessions.get(sessionId)
|
||||
if (!session) return { authenticated: false }
|
||||
|
||||
if (session.expiresAt < new Date()) {
|
||||
sessions.delete(sessionId)
|
||||
return { authenticated: false }
|
||||
}
|
||||
|
||||
return { authenticated: true, email: session.email }
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate with email and password.
|
||||
* Default: compares against ADMIN_EMAIL/ADMIN_PASSWORD env vars.
|
||||
* Replace this function to integrate your own auth system.
|
||||
*/
|
||||
export async function login(
|
||||
email: string,
|
||||
password: string
|
||||
): Promise<{ success: true; user: User } | { success: false; error: string }> {
|
||||
const user = getUserByEmail(email)
|
||||
): Promise<{ success: true } | { success: false; error: string }> {
|
||||
const adminEmail = process.env.ADMIN_EMAIL
|
||||
const adminPassword = process.env.ADMIN_PASSWORD
|
||||
|
||||
if (!user) {
|
||||
if (!adminEmail || !adminPassword) {
|
||||
return { success: false, error: "Admin-konfiguration mangler (ADMIN_EMAIL/ADMIN_PASSWORD)" }
|
||||
}
|
||||
|
||||
if (email !== adminEmail || password !== adminPassword) {
|
||||
return { success: false, error: "Forkert email eller adgangskode" }
|
||||
}
|
||||
|
||||
const validPassword = await verifyPassword(password, user.passwordHash)
|
||||
if (!validPassword) {
|
||||
return { success: false, error: "Forkert email eller adgangskode" }
|
||||
}
|
||||
|
||||
// Clean up old sessions periodically
|
||||
cleanExpiredSessions()
|
||||
|
||||
// Create new session
|
||||
const sessionId = generateSessionId()
|
||||
const expiresAt = new Date()
|
||||
expiresAt.setDate(expiresAt.getDate() + SESSION_DURATION_DAYS)
|
||||
const expiresAt = new Date(Date.now() + SESSION_DURATION_MS)
|
||||
|
||||
createSession(sessionId, user.id, expiresAt)
|
||||
sessions.set(sessionId, { email, expiresAt })
|
||||
|
||||
// Set cookie
|
||||
const cookieStore = await cookies()
|
||||
cookieStore.set(SESSION_COOKIE_NAME, sessionId, {
|
||||
httpOnly: true,
|
||||
secure: process.env.NODE_ENV === "production",
|
||||
sameSite: "lax",
|
||||
sameSite: "strict",
|
||||
expires: expiresAt,
|
||||
path: "/",
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
user: { id: user.id, email: user.email, name: user.name, createdAt: user.createdAt },
|
||||
}
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
/**
|
||||
* Log out the current session.
|
||||
*/
|
||||
export async function logout(): Promise<void> {
|
||||
const cookieStore = await cookies()
|
||||
const sessionId = cookieStore.get(SESSION_COOKIE_NAME)?.value
|
||||
|
||||
if (sessionId) {
|
||||
deleteSession(sessionId)
|
||||
sessions.delete(sessionId)
|
||||
cookieStore.delete(SESSION_COOKIE_NAME)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getCurrentUser(): Promise<User | null> {
|
||||
const cookieStore = await cookies()
|
||||
const sessionId = cookieStore.get(SESSION_COOKIE_NAME)?.value
|
||||
|
||||
if (!sessionId) return null
|
||||
|
||||
const session = getSession(sessionId)
|
||||
if (!session) return null
|
||||
|
||||
// Check if session is expired
|
||||
if (session.expiresAt < new Date()) {
|
||||
deleteSession(sessionId)
|
||||
return null
|
||||
}
|
||||
|
||||
return getUserById(session.userId)
|
||||
}
|
||||
|
||||
export async function isAuthenticated(): Promise<boolean> {
|
||||
const user = await getCurrentUser()
|
||||
return user !== null
|
||||
}
|
||||
|
||||
// Helper to create users (run from CLI or seed script)
|
||||
export async function createUserWithPassword(
|
||||
email: string,
|
||||
password: string,
|
||||
name: string
|
||||
): Promise<User> {
|
||||
const passwordHash = await hashPassword(password)
|
||||
return dbCreateUser(email, passwordHash, name)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue