"use strict";
/*
 * server component for the TimeLimit App
 * Copyright (C) 2019 - 2023 Jonas Lochmann
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.signInByMailCode = exports.sendLoginCode = void 0;
const http_errors_1 = require("http-errors");
const mail_1 = require("../../util/mail");
const random_words_1 = require("../../util/random-words");
const ratelimit_authmail_1 = require("../../util/ratelimit-authmail");
const token_1 = require("../../util/token");
const index_1 = require("./index");
const sendLoginCode = async ({ mail, deviceAuthToken, locale, database }) => {
    let deviceName = null;
    if (deviceAuthToken !== undefined) {
        const info = await database.transaction(async (transaction) => {
            const deviceEntryUnsafe = await database.device.findOne({
                where: { deviceAuthToken },
                attributes: ['familyId', 'name'],
                transaction
            });
            if (!deviceEntryUnsafe) {
                throw new http_errors_1.Unauthorized();
            }
            const deviceEntry = {
                familyId: deviceEntryUnsafe.familyId,
                name: deviceEntryUnsafe.name
            };
            const userEntryCounter = await database.user.count({
                where: {
                    familyId: deviceEntry.familyId,
                    mail
                },
                transaction
            });
            if (userEntryCounter === 1) {
                return { deviceName: deviceEntry.name };
            }
            else {
                // do not show the device name if it is from another family
                // otherwise third parties could chose a part of the content of the mail
                return { deviceName: null };
            }
        });
        deviceName = info.deviceName;
    }
    try {
        await (0, ratelimit_authmail_1.checkMailSendLimit)(mail);
    }
    catch (ex) {
        throw new http_errors_1.TooManyRequests();
    }
    const mailLoginToken = (0, token_1.generateAuthToken)();
    const code = (0, random_words_1.randomWords)(3);
    await (0, mail_1.sendAuthenticationMail)({
        receiver: mail,
        code,
        locale,
        deviceName
    });
    await database.transaction(async (transaction) => {
        await database.mailLoginToken.create({
            mailLoginToken,
            receivedCode: code,
            mail,
            createdAt: Date.now().toString(10),
            remainingAttempts: 3,
            locale
        }, { transaction });
    });
    return {
        mailLoginToken
    };
};
exports.sendLoginCode = sendLoginCode;
// 403 Forbidden = receivedCode is invalid
// 410 Gone = mailLoginToken is invalid or expired
const signInByMailCode = async ({ mailLoginToken, receivedCode, database }) => {
    const result = await database.transaction(async (transaction) => {
        const entry = await database.mailLoginToken.findOne({
            where: {
                mailLoginToken
            },
            transaction
        });
        if ((!entry) || entry.remainingAttempts === 0) {
            throw new http_errors_1.Gone();
        }
        if (!(0, random_words_1.areWordSequencesEqual)(entry.receivedCode, receivedCode)) {
            entry.remainingAttempts--;
            await entry.save({ transaction });
            if (entry.remainingAttempts === 0) {
                return () => { throw new http_errors_1.Gone(); };
            }
            else {
                return () => { throw new http_errors_1.Forbidden(); };
            }
        }
        const counter = await database.mailLoginToken.destroy({
            where: {
                mailLoginToken
            },
            transaction
        });
        if (counter !== 1) {
            throw new http_errors_1.Gone();
        }
        const mailAuthToken = await (0, index_1.createAuthTokenByMailAddress)({
            mail: entry.mail,
            locale: entry.locale,
            database,
            transaction
        });
        return () => ({ mailAuthToken });
    });
    return result();
};
exports.signInByMailCode = signInByMailCode;
//# sourceMappingURL=login-by-mail.js.map