99 lines
2.8 KiB
TypeScript
99 lines
2.8 KiB
TypeScript
import { cookies } from "next/headers";
|
|
import bcrypt from "bcryptjs";
|
|
import { query } from "./db";
|
|
|
|
const SESSION_COOKIE_NAME = "aghatis_admin_session";
|
|
const SESSION_SECRET = process.env.ADMIN_SESSION_SECRET || "aghatis_stellar_session_secret_2026_secure_key";
|
|
|
|
interface DBAdminUser {
|
|
id: number;
|
|
username: string;
|
|
password_hash: string;
|
|
full_name: string;
|
|
role: string;
|
|
}
|
|
|
|
export async function verifyUserCredentials(
|
|
user: string,
|
|
pass: string
|
|
): Promise<{ success: boolean; user?: { username: string; role: string; fullName: string } }> {
|
|
try {
|
|
const rows = await query<DBAdminUser[]>(
|
|
"SELECT id, username, password_hash, full_name, role FROM admin_users WHERE username = ? LIMIT 1",
|
|
[user]
|
|
);
|
|
|
|
if (!rows || rows.length === 0) {
|
|
return { success: false };
|
|
}
|
|
|
|
const admin = rows[0];
|
|
|
|
// Check bcrypt hash or fallback to direct comparison
|
|
let isMatch = false;
|
|
if (admin.password_hash.startsWith("$2b$") || admin.password_hash.startsWith("$2a$")) {
|
|
isMatch = bcrypt.compareSync(pass, admin.password_hash);
|
|
} else {
|
|
isMatch = admin.password_hash === pass;
|
|
}
|
|
|
|
if (!isMatch) {
|
|
return { success: false };
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
user: {
|
|
username: admin.username,
|
|
role: admin.role,
|
|
fullName: admin.full_name,
|
|
},
|
|
};
|
|
} catch (err) {
|
|
console.error("Database auth verification error:", err);
|
|
return { success: false };
|
|
}
|
|
}
|
|
|
|
// Generate HMAC session token
|
|
export function createSessionToken(username: string): string {
|
|
const payload = {
|
|
username,
|
|
createdAt: Date.now(),
|
|
};
|
|
const json = JSON.stringify(payload);
|
|
const encoded = Buffer.from(json).toString("base64url");
|
|
const signature = Buffer.from(`${encoded}:${SESSION_SECRET}`).toString("base64url");
|
|
return `${encoded}.${signature}`;
|
|
}
|
|
|
|
export function verifySessionToken(token: string): { username: string } | null {
|
|
try {
|
|
const [encoded, signature] = token.split(".");
|
|
if (!encoded || !signature) return null;
|
|
const expectedSig = Buffer.from(`${encoded}:${SESSION_SECRET}`).toString("base64url");
|
|
if (signature !== expectedSig) return null;
|
|
const json = Buffer.from(encoded, "base64url").toString("utf8");
|
|
const payload = JSON.parse(json);
|
|
return { username: payload.username };
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export async function isAuthenticated(): Promise<boolean> {
|
|
const cookieStore = await cookies();
|
|
const token = cookieStore.get(SESSION_COOKIE_NAME)?.value;
|
|
if (!token) return false;
|
|
return verifySessionToken(token) !== null;
|
|
}
|
|
|
|
export async function getAuthenticatedUser(): Promise<{ username: string } | null> {
|
|
const cookieStore = await cookies();
|
|
const token = cookieStore.get(SESSION_COOKIE_NAME)?.value;
|
|
if (!token) return null;
|
|
return verifySessionToken(token);
|
|
}
|
|
|
|
export { SESSION_COOKIE_NAME };
|