meti-frontend/lib/auth.ts

82 lines
2.1 KiB
TypeScript
Raw Normal View History

2025-08-15 23:03:15 +07:00
import { SignJWT, jwtVerify } from "jose"
import { cookies } from "next/headers"
import { APP_CONFIG } from "./config"
const secretKey = APP_CONFIG.JWT_SECRET
const key = new TextEncoder().encode(secretKey)
export interface SessionPayload {
userId: string
username: string
role: string
expiresAt: Date
}
export async function encrypt(payload: SessionPayload) {
return await new SignJWT(payload)
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("24h")
.sign(key)
}
export async function decrypt(input: string): Promise<SessionPayload> {
const { payload } = await jwtVerify(input, key, {
algorithms: ["HS256"],
})
return payload as SessionPayload
}
export async function createSession(userId: string, username: string, role: string) {
const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000) // 24 hours
const session = await encrypt({ userId, username, role, expiresAt })
cookies().set("session", session, {
expires: expiresAt,
httpOnly: true,
secure: APP_CONFIG.NODE_ENV === "production",
sameSite: "lax",
path: "/",
})
}
export async function verifySession() {
const cookie = cookies().get("session")?.value
if (!cookie) return null
try {
const session = await decrypt(cookie)
if (new Date(session.expiresAt) < new Date()) {
return null
}
return session
} catch {
return null
}
}
export async function deleteSession() {
cookies().delete("session")
}
// Rate limiting store (in production, use Redis)
const rateLimitStore = new Map<string, { count: number; resetTime: number }>()
export function rateLimit(identifier: string, limit = 5, windowMs: number = 15 * 60 * 1000) {
const now = Date.now()
const key = identifier
const record = rateLimitStore.get(key)
if (!record || now > record.resetTime) {
rateLimitStore.set(key, { count: 1, resetTime: now + windowMs })
return { success: true, remaining: limit - 1 }
}
if (record.count >= limit) {
return { success: false, remaining: 0, resetTime: record.resetTime }
}
record.count++
return { success: true, remaining: limit - record.count }
}