import { scryptSync, randomBytes, timingSafeEqual, createHmac } from 'crypto';
const MAX_FAILURES = 5;
const LOCKOUT_MS = 15 * 60 * 1000; // 15 minutes
const TOKEN_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
export class AuthManager {
    authAttempts = new Map();
    pendingAuth = new Map();
    sessionAuth = new Map();
    serverSecret = randomBytes(32);
    // --- Password ---
    hashPassword(password) {
        const salt = randomBytes(16).toString('base64');
        const hash = scryptSync(password, salt, 32).toString('base64');
        return { hash, salt };
    }
    verifyPassword(submitted, storedHash, storedSalt) {
        const computed = scryptSync(submitted, storedSalt, 32);
        const expected = Buffer.from(storedHash, 'base64');
        if (computed.length !== expected.length)
            return false;
        return timingSafeEqual(computed, expected);
    }
    // --- Session password ---
    setSessionPassword(sessionId, hash, salt) {
        this.sessionAuth.set(sessionId, { passwordHash: hash, passwordSalt: salt });
    }
    getSessionAuth(sessionId) {
        return this.sessionAuth.get(sessionId);
    }
    requiresAuth(sessionId) {
        const a = this.sessionAuth.get(sessionId);
        return !!(a?.passwordHash && a?.passwordSalt);
    }
    // --- Token ---
    generateAuthToken(sessionId) {
        const ts = Date.now().toString();
        const hmac = createHmac('sha256', this.serverSecret).update(`${sessionId}:${ts}`).digest('base64url');
        return `${sessionId}:${ts}:${hmac}`;
    }
    verifyAuthToken(token, expectedSessionId) {
        const idx1 = token.indexOf(':');
        const idx2 = token.indexOf(':', idx1 + 1);
        if (idx1 === -1 || idx2 === -1)
            return false;
        const sessionId = token.slice(0, idx1);
        const ts = token.slice(idx1 + 1, idx2);
        const hmac = token.slice(idx2 + 1);
        if (sessionId !== expectedSessionId)
            return false;
        const age = Date.now() - parseInt(ts, 10);
        if (isNaN(age) || age < 0 || age > TOKEN_TTL_MS)
            return false;
        const expected = createHmac('sha256', this.serverSecret).update(`${sessionId}:${ts}`).digest('base64url');
        return hmac === expected;
    }
    // --- Brute-force protection ---
    isLocked(sessionId) {
        const state = this.authAttempts.get(sessionId);
        if (!state?.lockedUntil)
            return false;
        if (Date.now() > state.lockedUntil.getTime()) {
            this.authAttempts.delete(sessionId);
            return false;
        }
        return true;
    }
    recordFailure(sessionId) {
        let state = this.authAttempts.get(sessionId);
        if (!state) {
            state = { failures: 0, lockedUntil: null };
            this.authAttempts.set(sessionId, state);
        }
        state.failures++;
        if (state.failures >= MAX_FAILURES) {
            state.lockedUntil = new Date(Date.now() + LOCKOUT_MS);
            return { locked: true, remaining: 0 };
        }
        return { locked: false, remaining: MAX_FAILURES - state.failures };
    }
    clearFailures(sessionId) {
        this.authAttempts.delete(sessionId);
    }
    // --- Pending auth ---
    setPending(clientId, sessionId) {
        this.pendingAuth.set(clientId, sessionId);
    }
    getPending(clientId) {
        return this.pendingAuth.get(clientId);
    }
    removePending(clientId) {
        this.pendingAuth.delete(clientId);
    }
}
// Singleton instance
export const auth = new AuthManager();
//# sourceMappingURL=auth-manager.js.map