"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.callback = exports.begin = void 0;
const tslib_1 = require("tslib");
const isbot_1 = require("isbot");
const common_1 = require("../../clients/common");
const processed_query_1 = tslib_1.__importDefault(require("../../utils/processed-query"));
const ShopifyErrors = tslib_1.__importStar(require("../../error"));
const hmac_validator_1 = require("../../utils/hmac-validator");
const shop_validator_1 = require("../../utils/shop-validator");
const http_1 = require("../../../runtime/http");
const logger_1 = require("../../logger");
const types_1 = require("../../clients/types");
const fetch_request_1 = require("../../utils/fetch-request");
const types_2 = require("./types");
const nonce_1 = require("./nonce");
const safe_compare_1 = require("./safe-compare");
const create_session_1 = require("./create-session");
const logForBot = ({ request, log, func }) => {
    log.debug(`Possible bot request to auth ${func}: `, {
        userAgent: request.headers['User-Agent'],
    });
};
function begin(config) {
    return async ({ shop, callbackPath, isOnline, ...adapterArgs }) => {
        throwIfCustomStoreApp(config.isCustomStoreApp, 'Cannot perform OAuth for private apps');
        const log = (0, logger_1.logger)(config);
        log.info('Beginning OAuth', { shop, isOnline, callbackPath });
        const request = await (0, http_1.abstractConvertRequest)(adapterArgs);
        const response = await (0, http_1.abstractConvertIncomingResponse)(adapterArgs);
        let userAgent = request.headers['User-Agent'];
        if (Array.isArray(userAgent)) {
            userAgent = userAgent[0];
        }
        if ((0, isbot_1.isbot)(userAgent)) {
            logForBot({ request, log, func: 'begin' });
            response.statusCode = 410;
            return (0, http_1.abstractConvertResponse)(response, adapterArgs);
        }
        const cookies = new http_1.Cookies(request, response, {
            keys: [config.apiSecretKey],
            secure: true,
        });
        const state = (0, nonce_1.nonce)();
        await cookies.setAndSign(types_2.STATE_COOKIE_NAME, state, {
            expires: new Date(Date.now() + 60000),
            sameSite: 'lax',
            secure: true,
            path: callbackPath,
        });
        const query = {
            client_id: config.apiKey,
            scope: config.scopes.toString(),
            redirect_uri: `${config.hostScheme}://${config.hostName}${callbackPath}`,
            state,
            'grant_options[]': isOnline ? 'per-user' : '',
        };
        const processedQuery = new processed_query_1.default();
        processedQuery.putAll(query);
        const cleanShop = (0, shop_validator_1.sanitizeShop)(config)(shop, true);
        const redirectUrl = `https://${cleanShop}/admin/oauth/authorize${processedQuery.stringify()}`;
        response.statusCode = 302;
        response.statusText = 'Found';
        response.headers = {
            ...response.headers,
            ...cookies.response.headers,
            Location: redirectUrl,
        };
        log.debug(`OAuth started, redirecting to ${redirectUrl}`, { shop, isOnline });
        return (0, http_1.abstractConvertResponse)(response, adapterArgs);
    };
}
exports.begin = begin;
function callback(config) {
    return async function callback({ ...adapterArgs }) {
        throwIfCustomStoreApp(config.isCustomStoreApp, 'Cannot perform OAuth for private apps');
        const log = (0, logger_1.logger)(config);
        const request = await (0, http_1.abstractConvertRequest)(adapterArgs);
        const query = new URL(request.url, `${config.hostScheme}://${config.hostName}`).searchParams;
        const shop = query.get('shop');
        const response = {};
        let userAgent = request.headers['User-Agent'];
        if (Array.isArray(userAgent)) {
            userAgent = userAgent[0];
        }
        if ((0, isbot_1.isbot)(userAgent)) {
            logForBot({ request, log, func: 'callback' });
            throw new ShopifyErrors.BotActivityDetected('Invalid OAuth callback initiated by bot');
        }
        log.info('Completing OAuth', { shop });
        const cookies = new http_1.Cookies(request, response, {
            keys: [config.apiSecretKey],
            secure: true,
        });
        const stateFromCookie = await cookies.getAndVerify(types_2.STATE_COOKIE_NAME);
        cookies.deleteCookie(types_2.STATE_COOKIE_NAME);
        if (!stateFromCookie) {
            log.error('Could not find OAuth cookie', { shop });
            throw new ShopifyErrors.CookieNotFound(`Cannot complete OAuth process. Could not find an OAuth cookie for shop url: ${shop}`);
        }
        const authQuery = Object.fromEntries(query.entries());
        if (!(await validQuery({ config, query: authQuery, stateFromCookie }))) {
            log.error('Invalid OAuth callback', { shop, stateFromCookie });
            throw new ShopifyErrors.InvalidOAuthError('Invalid OAuth callback.');
        }
        log.debug('OAuth request is valid, requesting access token', { shop });
        const body = {
            client_id: config.apiKey,
            client_secret: config.apiSecretKey,
            code: query.get('code'),
        };
        const cleanShop = (0, shop_validator_1.sanitizeShop)(config)(query.get('shop'), true);
        const postResponse = await (0, fetch_request_1.fetchRequestFactory)(config)(`https://${cleanShop}/admin/oauth/access_token`, {
            method: 'POST',
            body: JSON.stringify(body),
            headers: {
                'Content-Type': types_1.DataType.JSON,
                Accept: types_1.DataType.JSON,
            },
        });
        if (!postResponse.ok) {
            (0, common_1.throwFailedRequest)(await postResponse.json(), false, postResponse);
        }
        const session = (0, create_session_1.createSession)({
            accessTokenResponse: await postResponse.json(),
            shop: cleanShop,
            state: stateFromCookie,
            config,
        });
        if (!config.isEmbeddedApp) {
            await cookies.setAndSign(types_2.SESSION_COOKIE_NAME, session.id, {
                expires: session.expires,
                sameSite: 'lax',
                secure: true,
                path: '/',
            });
        }
        return {
            headers: (await (0, http_1.abstractConvertHeaders)(cookies.response.headers, adapterArgs)),
            session,
        };
    };
}
exports.callback = callback;
async function validQuery({ config, query, stateFromCookie, }) {
    return ((await (0, hmac_validator_1.validateHmac)(config)(query)) &&
        (0, safe_compare_1.safeCompare)(query.state, stateFromCookie));
}
function throwIfCustomStoreApp(isCustomStoreApp, message) {
    if (isCustomStoreApp) {
        throw new ShopifyErrors.PrivateAppError(message);
    }
}
//# sourceMappingURL=oauth.js.map