"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthService = void 0;
const api_locale_1 = require("@hed-hog/api-locale");
const api_prisma_1 = require("@hed-hog/api-prisma");
const common_1 = require("@nestjs/common");
const challenge_service_1 = require("../challenge/challenge.service");
const mail_service_1 = require("../mail/mail.service");
const security_service_1 = require("../security/security.service");
const session_service_1 = require("../session/session.service");
const setting_service_1 = require("../setting/setting.service");
const token_service_1 = require("../token/token.service");
const user_service_1 = require("../user/user.service");
let AuthService = class AuthService {
    constructor(prisma, mail, setting, security, token, session, user, challenge) {
        this.prisma = prisma;
        this.mail = mail;
        this.setting = setting;
        this.security = security;
        this.token = token;
        this.session = session;
        this.user = user;
        this.challenge = challenge;
    }
    async getAuthenticationPayload(locale, userId, ipAddress, userAgent) {
        const { token: refreshToken, session } = await this.session.create(locale, userId, ipAddress, userAgent);
        const payload = { sub: userId, sessionId: session.id };
        const accessToken = await this.token.createAccessToken(payload);
        await this.prisma.user.update({ where: { id: userId }, data: { last_login_at: new Date() } });
        return {
            accessToken,
            refreshToken,
            session,
        };
    }
    hasPendingPasswordReset(user) {
        var _a;
        return Boolean((_a = user === null || user === void 0 ? void 0 : user.user_credential) === null || _a === void 0 ? void 0 : _a.some((credential) => credential.type === 'password' &&
            credential.requires_reset === true &&
            !credential.revoked_at));
    }
    async getRequiresPasswordResetByUserId(userId) {
        const credential = await this.prisma.user_credential.findFirst({
            where: {
                user_id: userId,
                type: 'password',
                requires_reset: true,
                revoked_at: null,
            },
            select: { id: true },
        });
        return Boolean(credential === null || credential === void 0 ? void 0 : credential.id);
    }
    async requiresMfaForLogin(locale, email, user) {
        const settings = await this.setting.getSettingValues([
            'require-mfa',
            'require-email-verification',
            'mfa-email-code-length',
            'mfa-challenge-expiration-minutes'
        ]);
        const code = this.security.generateCode(settings['mfa-email-code-length'] || 6);
        const codeHash = this.security.hashWithPepper(code);
        const identifier = await this.prisma.user_identifier.findFirst({
            where: {
                user_id: user.id,
                type: 'email',
                value: email,
            },
            select: { id: true }
        });
        if (!identifier) {
            throw new common_1.NotFoundException((0, api_locale_1.getLocaleText)('identifierNotFound', locale, 'Email identifier not found or already verified.'));
        }
        const challengeIdentifier = await this.prisma.user_identifier_challenge.create({
            data: {
                hash: codeHash,
                expires_at: new Date(Date.now() + (settings['mfa-challenge-expiration-minutes'] || 15) * 60000),
                user_identifier_id: identifier.id,
            },
            select: {
                id: true,
            }
        });
        const mfa = await this.prisma.user_mfa.create({
            data: {
                name: (0, api_locale_1.getLocaleText)('mfaEmailDefaultName', locale, 'Email MFA'),
                type: 'email',
                user_id: user.id,
                user_mfa_email: {
                    create: {
                        email,
                    }
                },
                user_mfa_challenge: {
                    create: {
                        expires_at: new Date(Date.now() + (settings['mfa-challenge-expiration-minutes'] || 15) * 60000),
                        hash: codeHash,
                    }
                }
            },
            select: {
                user_mfa_challenge: {
                    select: { id: true }
                }
            }
        });
        const challengeMfaId = mfa.user_mfa_challenge[0].id;
        await this.mail.sendTemplatedMail(locale, {
            email,
            slug: 'auth-email-verification-code',
            variables: {
                code,
                name: user.name,
            }
        });
        return {
            requiresMfa: true,
            token: await this.token.createAccessToken({
                challengeIdentifierId: challengeIdentifier.id,
                challengeMfaId,
            })
        };
    }
    async requiresEmailVerificationForLogin(locale, email, user) {
        const settings = await this.setting.getSettingValues([
            'require-mfa',
            'require-email-verification',
            'mfa-email-code-length',
            'mfa-challenge-expiration-minutes'
        ]);
        const code = this.security.generateCode(settings['mfa-email-code-length'] || 6);
        const codeHash = this.security.hashWithPepper(code);
        const identifier = await this.prisma.user_identifier.findFirst({
            where: {
                user_id: user.id,
                type: 'email',
                value: email,
            },
            select: { id: true }
        });
        if (!identifier) {
            throw new common_1.NotFoundException((0, api_locale_1.getLocaleText)('identifierNotFound', locale, 'Email identifier not found or already verified.'));
        }
        const challengeIdentifier = await this.prisma.user_identifier_challenge.create({
            data: {
                hash: codeHash,
                expires_at: new Date(Date.now() + (settings['mfa-challenge-expiration-minutes'] || 15) * 60000),
                user_identifier_id: identifier.id,
            },
            select: {
                id: true,
            }
        });
        await this.mail.sendTemplatedMail(locale, {
            email,
            slug: 'auth-email-verification-code',
            variables: {
                code,
                name: user.name,
            }
        });
        return {
            requiresEmailVerification: true,
            token: await this.token.createAccessToken({
                challengeIdentifierId: challengeIdentifier.id,
                email
            })
        };
    }
    async emailVerificationLoginResend(locale, token) {
        var _a;
        try {
            const payload = await this.token.verify(locale, token);
            const challenge = await this.prisma.user_identifier_challenge.findUnique({
                where: { id: payload.challengeIdentifierId },
                select: {
                    user_identifier: {
                        select: {
                            user_id: true
                        }
                    }
                }
            });
            if (!challenge) {
                throw new common_1.NotFoundException((0, api_locale_1.getLocaleText)('challengeNotFound', locale, 'Challenge not found.'));
            }
            const user = await this.user.findUserById(locale, challenge.user_identifier.user_id);
            return await this.requiresEmailVerificationForLogin(locale, payload.email, user);
        }
        catch (error) {
            if (((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes('jwt expired')) || error.name === 'TokenExpiredError') {
                const expiredPayload = await this.token.decodeExpiredToken(token);
                const newToken = await this.token.createAccessToken({
                    challengeIdentifierId: expiredPayload.challengeIdentifierId,
                    email: expiredPayload.email
                });
                return this.emailVerificationLoginResend(locale, newToken);
            }
            throw error;
        }
    }
    async emailVerificationLogin(locale, token, code, ipAddress, userAgent, res) {
        try {
            const payload = await this.token.verify(locale, token);
            const challenge = await this.prisma.user_identifier_challenge.findUnique({
                where: { id: payload.challengeIdentifierId },
                select: {
                    hash: true,
                    user_identifier_id: true,
                    user_identifier: {
                        select: {
                            user_id: true
                        }
                    }
                }
            });
            if (!challenge) {
                throw new common_1.NotFoundException((0, api_locale_1.getLocaleText)('challengeNotFound', locale, 'Challenge not found.'));
            }
            await this.prisma.user_identifier_challenge.update({
                where: { id: payload.challengeIdentifierId },
                data: {
                    attempts: { increment: 1 }
                }
            });
            if (challenge.hash !== this.security.hashWithPepper(code)) {
                throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('invalidVerificationCode', locale, 'Invalid verification code.'));
            }
            await this.prisma.$transaction(async (tx) => {
                await tx.user_identifier_challenge.update({
                    where: { id: payload.challengeIdentifierId },
                    data: {
                        verified_at: new Date(),
                    }
                });
                await tx.user_identifier.update({
                    where: { id: challenge.user_identifier_id },
                    data: {
                        verified_at: new Date(),
                    }
                });
            });
            const user = await this.user.findUserById(locale, challenge.user_identifier.user_id);
            return this.login(locale, user, ipAddress, userAgent, res);
        }
        catch (error) {
            throw error;
        }
    }
    async login(locale, user, ipAddress, userAgent, res) {
        const emails = await this.prisma.user_identifier.findMany({
            where: {
                user_id: user.id,
                type: 'email',
            },
            select: { value: true }
        });
        if (!emails || emails.length === 0) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
        }
        const { accessToken, refreshToken, session } = await this.getAuthenticationPayload(locale, user.id, ipAddress, userAgent);
        for (const emailObj of emails) {
            this.mail.sendTemplatedMail(locale, {
                email: emailObj.value,
                slug: 'auth-login-new-device',
                variables: {
                    name: user.name,
                    ipAddress,
                    userAgent,
                    location: 'Unknown',
                }
            });
        }
        await this.user.registerUserActivity(user.id, "login");
        await this.token.setRefreshTokenCookie(locale, res, refreshToken, session.expires_at);
        const requiresPasswordReset = this.hasPendingPasswordReset(user);
        if (refreshToken) {
            return { accessToken, refreshToken, requiresPasswordReset };
        }
        return { accessToken, requiresPasswordReset };
    }
    async loginWithEmailAndPassword(res, locale, ipAddress, userAgent, { email, password }) {
        var _a, _b;
        const user = await this.user.findUserByEmail(locale, email);
        if (!user)
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
        const credentials = ((_a = user.user_credential) === null || _a === void 0 ? void 0 : _a.filter((c) => c.type === 'password')) || [];
        const identifier = (_b = user.user_identifier) === null || _b === void 0 ? void 0 : _b.find((i) => i.type === 'email' && i.value === email);
        if (!(await this.security.validatePassword(locale, credentials, password))) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
        }
        const mfaMethods = await this.prisma.user_mfa.findMany({
            where: {
                user_id: user.id,
                verified_at: { not: null }
            },
            include: {
                user_mfa_totp: true,
                user_mfa_email: true
            }
        });
        if (mfaMethods.length > 0) {
            const mfaToken = await this.token.createMfaChallengeToken({
                userId: user.id,
                ipAddress,
                userAgent,
                email
            });
            const emailMfas = mfaMethods.filter(m => m.type === 'email' && m.user_mfa_email && m.user_mfa_email.length > 0);
            if (emailMfas.length > 0) {
                const emails = emailMfas.map(mfa => mfa.user_mfa_email[0].email);
                await this.challenge.sendMfaCodeToMultipleEmails(locale, user.id, emails);
            }
            return {
                requiresMfa: true,
                mfaToken,
                mfaMethods: mfaMethods.map(m => ({ type: m.type, id: m.id }))
            };
        }
        const settings = await this.setting.getSettingValues([
            'require-mfa',
            'require-email-verification',
            'mfa-email-code-length',
            'mfa-challenge-expiration-minutes'
        ]);
        if (settings['require-mfa'] === true && mfaMethods.length === 0) {
            return this.requiresMfaForLogin(locale, email, user);
        }
        else if (settings['require-email-verification'] === true && (identifier === null || identifier === void 0 ? void 0 : identifier.verified_at) === null) {
            return this.requiresEmailVerificationForLogin(locale, email, user);
        }
        return this.login(locale, user, ipAddress, userAgent, res);
    }
    async verifyRoles(_locale, userId) {
        return this.prisma.role.findMany({
            where: {
                role_user: {
                    some: {
                        user_id: userId
                    }
                }
            },
            select: {
                slug: true
            }
        });
    }
    async verifyUser(locale, userId) {
        const user = await this.user.findUserById(locale, userId);
        const requiresPasswordReset = this.hasPendingPasswordReset(user);
        delete user.user_credential;
        return Object.assign(Object.assign({}, user), { requires_password_reset: requiresPasswordReset });
    }
    async refreshAccessToken(locale, refreshToken, ipAddress, userAgent) {
        if (!refreshToken) {
            throw new common_1.UnauthorizedException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
        }
        const { session, token } = await this.session.refresh(locale, refreshToken, ipAddress, userAgent);
        const user = await this.user.findUserById(locale, session.user_id);
        const payload = { sub: user.id, sessionId: session.id };
        const accessToken = await this.token.createAccessToken(payload);
        return { accessToken, refreshToken: token, session };
    }
    async forgotPassword(locale, { email }) {
        const user = await this.user.findUserByEmail(locale, email);
        if (user) {
            await this.user.registerUserActivity(user === null || user === void 0 ? void 0 : user.id, "forgotPassword");
        }
        await this.challenge.forgotEmail(locale, email);
        return { success: true };
    }
    async logout(res, req, refreshToken) {
        var _a, _b, _c;
        const result = await this.session.deleteRefreshTokenCookie(res, refreshToken);
        const logoutUserId = (_c = (_b = (_a = req.auth) === null || _a === void 0 ? void 0 : _a.sub) !== null && _b !== void 0 ? _b : result === null || result === void 0 ? void 0 : result.userId) !== null && _c !== void 0 ? _c : null;
        if (logoutUserId) {
            await this.user.registerUserActivity(logoutUserId, 'logout');
        }
        return { success: true };
    }
    async forgotResetPassword(locale, password, code, ipAddress, userAgent) {
        const challenge = await this.challenge.verifyCodePassword(locale, code);
        const passwordHash = await this.security.hashArgon2(password);
        await this.prisma.user_credential.updateMany({
            where: {
                type: 'password',
                user: {
                    user_mfa: {
                        some: {
                            user_mfa_challenge: {
                                some: { id: challenge.id }
                            }
                        }
                    }
                }
            },
            data: { hash: passwordHash, requires_reset: false },
        });
        const userIdentifier = await this.prisma.user_identifier.findFirst({
            where: {
                type: 'email',
                user_id: challenge.user_mfa.user_id,
                enabled: true
            },
            include: {
                user: {
                    select: { name: true }
                }
            }
        });
        if (!userIdentifier) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('userIdentifierNotFound', locale, 'User identifier not found.'));
        }
        this.mail.sendTemplatedMail(locale, {
            email: userIdentifier.value,
            slug: 'auth-forget-password-confirmation',
            variables: {
                name: userIdentifier.user.name,
            }
        });
        const { accessToken, refreshToken, session } = await this.getAuthenticationPayload(locale, challenge.user_mfa.user_id, ipAddress, userAgent);
        await this.user.registerUserActivity(userIdentifier.id, "resetPassword");
        return { accessToken, refreshToken, session };
    }
    async verifyMfaCode(locale, mfaToken, code, ipAddress, userAgent, methodType) {
        var _a, _b, _c, _d;
        const payload = await this.token.verifyMfaChallengeToken(mfaToken);
        const user = await this.prisma.user.findUnique({
            where: { id: payload.userId },
            include: {
                user_mfa: {
                    where: { verified_at: { not: null } },
                    include: {
                        user_mfa_totp: true,
                        user_mfa_email: true
                    }
                },
                user_recovery_code: true
            }
        });
        if (!user) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
        }
        let isValid = false;
        const settings = await this.setting.getSettingValues([
            'mfa-issuer',
            'mfa-window',
            'mfa-setp',
        ]);
        if (methodType === 'totp') {
            const totpMfa = user.user_mfa.find(m => m.type === 'totp' && m.user_mfa_totp);
            if (totpMfa && totpMfa.user_mfa_totp[0]) {
                const secretBuffer = totpMfa.user_mfa_totp[0].secret;
                const secret = secretBuffer instanceof Uint8Array
                    ? Buffer.from(secretBuffer).toString('utf-8')
                    : String(secretBuffer);
                const window = (_a = settings['mfa-window']) !== null && _a !== void 0 ? _a : 0;
                const step = (_b = settings['mfa-setp']) !== null && _b !== void 0 ? _b : 30;
                const speakeasy = require('speakeasy');
                isValid = speakeasy.totp.verify({
                    secret,
                    encoding: 'base32',
                    token: code,
                    window,
                    step
                });
            }
        }
        else if (methodType === 'email') {
            const emailMfa = user.user_mfa.find(m => m.type === 'email');
            if (emailMfa) {
                isValid = await this.challenge.verifyMfaEmailCode(locale, user.id, code);
            }
        }
        else if (methodType === 'recovery') {
            for (const recoveryCode of user.user_recovery_code) {
                if (await this.security.verifyArgon2(code, recoveryCode.hash)) {
                    isValid = true;
                    await this.prisma.user_recovery_code.delete({
                        where: { id: recoveryCode.id }
                    });
                    break;
                }
            }
        }
        else {
            const totpMfa = user.user_mfa.find(m => m.type === 'totp' && m.user_mfa_totp);
            if (totpMfa && totpMfa.user_mfa_totp[0]) {
                const secretBuffer = totpMfa.user_mfa_totp[0].secret;
                const secret = secretBuffer instanceof Uint8Array
                    ? Buffer.from(secretBuffer).toString('utf-8')
                    : String(secretBuffer);
                const window = (_c = settings['mfa-window']) !== null && _c !== void 0 ? _c : 0;
                const step = (_d = settings['mfa-setp']) !== null && _d !== void 0 ? _d : 30;
                const speakeasy = require('speakeasy');
                isValid = speakeasy.totp.verify({
                    secret,
                    encoding: 'base32',
                    token: code,
                    window,
                    step
                });
            }
            if (!isValid) {
                const emailMfa = user.user_mfa.find(m => m.type === 'email');
                if (emailMfa) {
                    isValid = await this.challenge.verifyMfaEmailCode(locale, user.id, code);
                }
            }
        }
        if (!isValid) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('invalidMfaCode', locale, 'Invalid MFA code.'));
        }
        const { accessToken, refreshToken, session } = await this.getAuthenticationPayload(locale, user.id, ipAddress, userAgent);
        this.mail.sendTemplatedMail(locale, {
            email: payload.email,
            slug: 'auth-login-new-device',
            variables: {
                name: user.name,
                ipAddress,
                userAgent,
                location: 'Unknown',
            }
        });
        await this.user.registerUserActivity(user.id, "login");
        const requiresPasswordReset = await this.getRequiresPasswordResetByUserId(user.id);
        return { accessToken, refreshToken, session, requiresPasswordReset };
    }
    async verifyMfaRecoveryCode(locale, mfaToken, recoveryCode, ipAddress, userAgent) {
        const payload = await this.token.verifyMfaChallengeToken(mfaToken);
        const user = await this.prisma.user.findUnique({
            where: { id: payload.userId },
            include: {
                user_recovery_code: true
            }
        });
        if (!user) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('accessDenied', locale, 'Access denied.'));
        }
        let isValid = false;
        for (const code of user.user_recovery_code) {
            if (await this.security.verifyArgon2(recoveryCode, code.hash)) {
                isValid = true;
                await this.prisma.user_recovery_code.delete({
                    where: { id: code.id }
                });
                break;
            }
        }
        if (!isValid) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('invalidRecoveryCode', locale, 'Invalid recovery code.'));
        }
        const { accessToken, refreshToken, session } = await this.getAuthenticationPayload(locale, user.id, ipAddress, userAgent);
        await this.user.registerUserActivity(user.id, "login");
        const requiresPasswordReset = await this.getRequiresPasswordResetByUserId(user.id);
        return { accessToken, refreshToken, session, requiresPasswordReset };
    }
    async resendMfaCode(locale, mfaToken) {
        const payload = await this.token.verifyMfaChallengeToken(mfaToken);
        const mfaMethods = await this.prisma.user_mfa.findMany({
            where: {
                user_id: payload.userId,
                verified_at: { not: null },
                type: 'email'
            },
            include: {
                user_mfa_email: true
            }
        });
        if (mfaMethods.length === 0) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('noEmailMfaFound', locale, 'No email MFA method found for this user.'));
        }
        const emailMfa = mfaMethods[0];
        if (!emailMfa.user_mfa_email || emailMfa.user_mfa_email.length === 0) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('noEmailMfaFound', locale, 'No email MFA method found for this user.'));
        }
        await this.challenge.sendMfaCodeByEmail(locale, payload.userId, emailMfa.user_mfa_email[0].email);
        return {
            success: true,
            hasEmailMfa: true
        };
    }
    async generateWebAuthnAuthenticationOptions(locale, mfaToken) {
        const { generateAuthenticationOptions } = await Promise.resolve().then(() => __importStar(require('@simplewebauthn/server')));
        const payload = await this.token.verifyMfaChallengeToken(mfaToken);
        const user = await this.prisma.user.findFirst({
            where: { id: payload.userId },
            include: {
                user_mfa: {
                    where: { type: 'webauthn', verified_at: { not: null } },
                    include: { user_mfa_webauthn: true }
                }
            }
        });
        if (!user || user.user_mfa.length === 0) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('webauthnNotConfigured', locale, 'WebAuthn not configured.'));
        }
        const rpID = new URL(process.env.FRONTEND_URL || 'http://localhost:3200').hostname;
        const allowCredentials = user.user_mfa
            .filter(mfa => mfa.user_mfa_webauthn && mfa.user_mfa_webauthn.length > 0)
            .map(mfa => ({
            id: Buffer.from(mfa.user_mfa_webauthn[0].credential_id).toString('base64url'),
        }));
        const options = await generateAuthenticationOptions({
            rpID,
            allowCredentials,
            userVerification: 'preferred',
        });
        await this.prisma.user_mfa_challenge.create({
            data: {
                user_mfa_id: user.user_mfa[0].id,
                hash: options.challenge,
                attempts: 0,
                expires_at: new Date(Date.now() + 5 * 60 * 1000),
            }
        });
        return options;
    }
    async verifyWebAuthnAuthentication(locale, mfaToken, assertionResponse, ipAddress, userAgent) {
        const { verifyAuthenticationResponse } = await Promise.resolve().then(() => __importStar(require('@simplewebauthn/server')));
        const payload = await this.token.verifyMfaChallengeToken(mfaToken);
        const userId = payload.userId;
        const challengeRecord = await this.prisma.user_mfa_challenge.findFirst({
            where: {
                user_mfa: {
                    user_id: userId,
                    type: 'webauthn',
                    verified_at: { not: null },
                },
                verified_at: null,
                expires_at: { gt: new Date() }
            },
            include: {
                user_mfa: {
                    include: { user_mfa_webauthn: true }
                }
            },
            orderBy: { created_at: 'desc' }
        });
        if (!challengeRecord || !challengeRecord.user_mfa.user_mfa_webauthn[0]) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('challengeNotFound', locale, 'Challenge not found or expired.'));
        }
        const authenticator = challengeRecord.user_mfa.user_mfa_webauthn[0];
        const rpID = new URL(process.env.FRONTEND_URL || 'http://localhost:3200').hostname;
        const expectedOrigin = process.env.FRONTEND_URL || 'http://localhost:3200';
        const verification = await verifyAuthenticationResponse({
            response: assertionResponse,
            expectedChallenge: challengeRecord.hash,
            expectedOrigin,
            expectedRPID: rpID,
            credential: {
                id: Buffer.from(authenticator.credential_id).toString('base64url'),
                publicKey: authenticator.public_key,
                counter: authenticator.sign_count,
            },
        });
        if (!verification.verified) {
            throw new common_1.BadRequestException((0, api_locale_1.getLocaleText)('verificationFailed', locale, 'Verification failed.'));
        }
        await this.prisma.user_mfa_webauthn.update({
            where: { id: authenticator.id },
            data: {
                sign_count: verification.authenticationInfo.newCounter,
                last_used_at: new Date(),
            }
        });
        await this.prisma.user_mfa_challenge.update({
            where: { id: challengeRecord.id },
            data: { verified_at: new Date() }
        });
        const { accessToken, refreshToken, session } = await this.getAuthenticationPayload(locale, userId, ipAddress, userAgent);
        const user = await this.prisma.user.findFirst({ where: { id: userId } });
        this.mail.sendTemplatedMail(locale, {
            email: payload.email,
            slug: 'auth-login-new-device',
            variables: {
                name: user.name,
                ipAddress,
                userAgent,
                location: 'Unknown',
            }
        });
        await this.user.registerUserActivity(userId, 'login');
        const requiresPasswordReset = await this.getRequiresPasswordResetByUserId(userId);
        return { accessToken, refreshToken, session, requiresPasswordReset };
    }
};
exports.AuthService = AuthService;
exports.AuthService = AuthService = __decorate([
    (0, common_1.Injectable)(),
    __param(0, (0, common_1.Inject)((0, common_1.forwardRef)(() => api_prisma_1.PrismaService))),
    __param(1, (0, common_1.Inject)((0, common_1.forwardRef)(() => mail_service_1.MailService))),
    __param(2, (0, common_1.Inject)((0, common_1.forwardRef)(() => setting_service_1.SettingService))),
    __param(3, (0, common_1.Inject)((0, common_1.forwardRef)(() => security_service_1.SecurityService))),
    __param(4, (0, common_1.Inject)((0, common_1.forwardRef)(() => token_service_1.TokenService))),
    __param(5, (0, common_1.Inject)((0, common_1.forwardRef)(() => session_service_1.SessionService))),
    __param(6, (0, common_1.Inject)((0, common_1.forwardRef)(() => user_service_1.UserService))),
    __param(7, (0, common_1.Inject)((0, common_1.forwardRef)(() => challenge_service_1.ChallengeService))),
    __metadata("design:paramtypes", [api_prisma_1.PrismaService,
        mail_service_1.MailService,
        setting_service_1.SettingService,
        security_service_1.SecurityService,
        token_service_1.TokenService,
        session_service_1.SessionService,
        user_service_1.UserService,
        challenge_service_1.ChallengeService])
], AuthService);
//# sourceMappingURL=auth.service.js.map