전체 코드, 구조 검토 - P2 코드 수정 진행
This commit is contained in:
@@ -7,6 +7,32 @@ import { logger } from "@/lib/logger";
|
||||
|
||||
const AUTH_CTX = "auth";
|
||||
|
||||
type AttemptRecord = { count: number; until: number };
|
||||
const loginAttempts = new Map<string, AttemptRecord>();
|
||||
const MAX_ATTEMPTS = 5;
|
||||
const LOCKOUT_MS = 15 * 60 * 1000;
|
||||
|
||||
function isLockedOut(adminName: string): boolean {
|
||||
const record = loginAttempts.get(adminName);
|
||||
if (!record || record.count < MAX_ATTEMPTS) return false;
|
||||
if (record.until > Date.now()) return true;
|
||||
loginAttempts.delete(adminName);
|
||||
return false;
|
||||
}
|
||||
|
||||
function recordFailure(adminName: string): void {
|
||||
const record = loginAttempts.get(adminName) ?? { count: 0, until: 0 };
|
||||
record.count += 1;
|
||||
if (record.count >= MAX_ATTEMPTS) {
|
||||
record.until = Date.now() + LOCKOUT_MS;
|
||||
}
|
||||
loginAttempts.set(adminName, record);
|
||||
}
|
||||
|
||||
function clearAttempts(adminName: string): void {
|
||||
loginAttempts.delete(adminName);
|
||||
}
|
||||
|
||||
const credentialsSchema = z.object({
|
||||
adminName: z.string().trim().min(1),
|
||||
password: z.string().min(1),
|
||||
@@ -53,6 +79,7 @@ export const {
|
||||
},
|
||||
session: {
|
||||
strategy: "jwt",
|
||||
maxAge: 8 * 60 * 60,
|
||||
},
|
||||
providers: [
|
||||
Credentials({
|
||||
@@ -67,8 +94,14 @@ export const {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { db } = await import("@/lib/db");
|
||||
const { adminName, password } = parsedCredentials.data;
|
||||
|
||||
if (isLockedOut(adminName)) {
|
||||
logger.warn(AUTH_CTX, "login blocked (rate limit)", { adminName });
|
||||
return null;
|
||||
}
|
||||
|
||||
const { db } = await import("@/lib/db");
|
||||
const admins = await db.$queryRaw<AdminAuthRow[]>`
|
||||
SELECT AdminID, Name, Email, AccountType, ActivateStatus
|
||||
FROM admin
|
||||
@@ -81,10 +114,12 @@ export const {
|
||||
const activateStatus = Number(admin?.ActivateStatus);
|
||||
|
||||
if (!admin || activateStatus === ACTIVATE_STATUSES.PENDING) {
|
||||
recordFailure(adminName);
|
||||
logger.warn(AUTH_CTX, "login failed", { adminName });
|
||||
return null;
|
||||
}
|
||||
|
||||
clearAttempts(adminName);
|
||||
logger.info(AUTH_CTX, "login success", { adminName, adminID });
|
||||
return {
|
||||
id: String(adminID),
|
||||
|
||||
Reference in New Issue
Block a user