"use strict";
/*
 * @adonisjs/auth
 *
 * (c) AdonisJS
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.OATClient = void 0;
const luxon_1 = require("luxon");
const crypto_1 = require("crypto");
const utils_1 = require("@poppinss/utils");
const helpers_1 = require("@poppinss/utils/build/helpers");
const ProviderToken_1 = require("../../Tokens/ProviderToken");
/**
 * OAT client to login a user during tests using the
 * opaque tokens guard
 */
class OATClient {
    constructor(name, config, provider, tokenProvider) {
        this.name = name;
        this.config = config;
        this.provider = provider;
        this.tokenProvider = tokenProvider;
        /**
         * Length of the raw token. The hash length will vary
         */
        this.tokenLength = 60;
        /**
         * Token type for the persistance store
         */
        this.tokenType = this.config.tokenProvider.type || 'opaque_token';
    }
    /**
     * Returns the provider user instance from the regular user details. Raises
     * exception when id is missing
     */
    async getUserForLogin(user, identifierKey) {
        const providerUser = await this.provider.getUserFor(user);
        /**
         * Ensure id exists on the user
         */
        const id = providerUser.getId();
        if (!id) {
            throw new utils_1.Exception(`Cannot login user. Value of "${identifierKey}" is not defined`);
        }
        return providerUser;
    }
    /**
     * Converts value to a sha256 hash
     */
    generateHash(token) {
        return (0, crypto_1.createHash)('sha256').update(token).digest('hex');
    }
    /**
     * Converts expiry duration to an absolute date/time value
     */
    getExpiresAtDate(expiresIn) {
        if (!expiresIn) {
            return;
        }
        const milliseconds = typeof expiresIn === 'string' ? helpers_1.string.toMs(expiresIn) : expiresIn;
        return luxon_1.DateTime.local().plus({ milliseconds });
    }
    /**
     * Generates a new token + hash for the persistance
     */
    generateTokenForPersistance(expiresIn) {
        const token = helpers_1.string.generateRandom(this.tokenLength);
        return {
            token,
            hash: this.generateHash(token),
            expiresAt: this.getExpiresAtDate(expiresIn),
        };
    }
    /**
     * Returns the request data to mark user as logged in
     */
    async login(user, options) {
        /**
         * Normalize options with defaults
         */
        const { expiresIn, name, ...meta } = Object.assign({
            name: 'Opaque Access Token',
        }, options);
        /**
         * Since the login method is not exposed to the end user, we cannot expect
         * them to instantiate and pass an instance of provider user, so we
         * create one manually.
         */
        const providerUser = await this.getUserForLogin(user, this.config.provider.identifierKey);
        /**
         * "getUserForLogin" raises exception when id is missing, so we can
         * safely assume it is defined
         */
        const id = providerUser.getId();
        const token = this.generateTokenForPersistance(expiresIn);
        /**
         * Persist token to the database. Make sure that we are always
         * passing the hash to the storage driver
         */
        const providerToken = new ProviderToken_1.ProviderToken(name, token.hash, id, this.tokenType);
        providerToken.expiresAt = token.expiresAt;
        providerToken.meta = meta;
        this.tokenId = await this.tokenProvider.write(providerToken);
        return {
            headers: {
                Authorization: `Bearer ${helpers_1.base64.urlEncode(this.tokenId)}.${token.token}`,
            },
        };
    }
    /**
     * Logout user
     */
    async logout() {
        if (this.tokenId) {
            await this.tokenProvider.destroy(this.tokenId, this.tokenType);
        }
    }
}
exports.OATClient = OATClient;
