UNPKG

@onboardbase/cli

Version:

[![Version](https://img.shields.io/npm/v/@onboardbase/cli.svg)](https://www.npmjs.com/package/@onboardbase/cli) [![Downloads/week](https://img.shields.io/npm/dw/@onboardbase/cli.svg)](https://www.npmjs.com/package/@onboardbase/cli) [![License](https://img

207 lines (206 loc) 9.02 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseAuthService = void 0; const chalk = require("chalk"); const os = require("os"); const open = require("open"); const base_service_1 = require("../../common/base.service"); const config_1 = require("../../configuration/config"); const utils_1 = require("../../utils"); const inquirer = require("inquirer"); const defaults_1 = require("../../common/defaults"); const errors_1 = require("../../common/errors"); class BaseAuthService extends base_service_1.BaseService { constructor(configManager) { super(configManager); } /** * Logs in the user. * * @param flags - The login flags. * @param shouldCloseProcess - Indicates whether the process should be closed after login. * @param additionalProcesses - Additional processes to be handled after login. */ async login({ flags, shouldCloseProcess, additionalProcesses, }) { var _a; const { pollingCode, authCode } = await this._getAuthAndPollingCode(); let newConfig = { scope: "/", token: undefined, }; if (this.getConfigForCurrentScope("token")) { console.log("You are already logged in."); } newConfig.scope = (_a = flags.scope) !== null && _a !== void 0 ? _a : (await this._getLoginScope(flags)); const authUrl = this._getAuthUrl(authCode); await this._handleOpeningUrlAndWaitingForAuthToComplete(authUrl, authCode); let authTokenResponse = await this.httpInstance.getAuthToken(pollingCode); if (authTokenResponse === null || authTokenResponse === void 0 ? void 0 : authTokenResponse.error) { authTokenResponse = await this._handleAuthentication({ pollingCode, newConfig, flags, shouldCloseProcess, additionalProcesses, }); } return authTokenResponse; } async _getLoginScope(flags) { // assume a global token let scope = "/"; const globalToken = this.configManager.getFromRootConfig("token"); if (!globalToken) return scope; const options = await this._askWhereToScopeLogin(); if (!this._isLoginScopedToProjectPath(options)) { if (!flags.overwrite && globalToken) { this._informUserToForceOverwriteThenExit(); } } if (this._isLoginScopedToProjectPath(options)) { scope = process.cwd(); } return scope; } /** * Handles the process of opening the authentication URL in the default browser. * The method also waits for the authorization to complete (by polling at an interval). * * @param authUrl The authentication URL to be opened. * @param authCode The authorization code. */ async _handleOpeningUrlAndWaitingForAuthToComplete(authUrl, authCode) { const { browserOption } = await this._askToOpenAuthUrlInBrowser(); if (browserOption === "Yes") { await open(authUrl); console.log("Complete authorization at " + authUrl); } else { console.log("Complete authorization at " + authUrl); console.log("Your auth code is:\n%s", chalk.green(authCode)); } console.log(""); console.log("Waiting..."); } async _askToOpenAuthUrlInBrowser() { return await inquirer.prompt([ { type: "list", name: "browserOption", message: "Open the authorization page in your browser?", choices: ["Yes", "No"], }, ]); } _isLoginScopedToProjectPath(options) { return options.config.startsWith("Scope login"); } _informUserToForceOverwriteThenExit() { const message = `This scope is already authorized from a previous login. You can pass the ${chalk.greenBright.bold("--overwrite")} flag to overwrite this directory.`; throw new errors_1.BadInputError(message); } async _getAuthAndPollingCode() { const hostname = os.hostname(); const hostARCH = os.arch(); const fingerprint = await (0, utils_1.getMachineID)(); const hostOS = os.platform(); const { pollingCode, authCode } = await this.httpInstance.generateAuthCode(fingerprint, hostOS, hostname, hostARCH); return { pollingCode, authCode }; } _getAuthUrl(authCode) { const dashboardHost = this.getConfigForCurrentScope("dashboard-host") || defaults_1.DEFAULT_DASHBOARD_HOST; const authUrl = dashboardHost.concat(`/auth/cli?authCode=${authCode}`); return authUrl; } async _askWhereToScopeLogin() { return await inquirer.prompt([ { type: "list", name: "config", message: "Would you like to scope your new login to the current directory, or overwrite the existing global login?", choices: [ `Scope login to current directory (${process.cwd()})`, `Overwrite global login`, ], }, ]); } _handleAuthentication(input) { return new Promise((resolve, reject) => { const pollingInterval = 4000; const pollingTimeout = 300000; const intervalHandler = setInterval(async () => { const res = await this._handlePollingAtInterval(Object.assign({ intervalHandler, intervalTimeout }, input)); if (!(res === null || res === void 0 ? void 0 : res.error)) resolve(res); }, pollingInterval); const intervalTimeout = setTimeout(() => { this._handleTimeoutExceeded({ intervalHandler, intervalTimeout }); reject("Timeout Error"); }, pollingTimeout); }); } async _handlePollingAtInterval(input) { const { pollingCode } = input; const authTokenResponse = await this.httpInstance.getAuthToken(pollingCode); if (!(authTokenResponse === null || authTokenResponse === void 0 ? void 0 : authTokenResponse.error)) { await this._handleSuccessfulAuth({ authTokenResponse, flags: input.flags, newConfig: input.newConfig, intervalHandler: input.intervalHandler, intervalTimeout: input.intervalTimeout, shouldCloseProcess: input.shouldCloseProcess, additionalProcesses: input.additionalProcesses, }); } return authTokenResponse; } _handleTimeoutExceeded(input) { const { intervalHandler, intervalTimeout } = input; clearInterval(intervalHandler); clearTimeout(intervalTimeout); const message = chalk.bold.red("Authentication Timeout exceeded..."); throw new errors_1.BadNetworkError(message); } async _handleSuccessfulAuth(data) { const { intervalHandler, intervalTimeout, authTokenResponse, newConfig, flags, shouldCloseProcess, additionalProcesses, } = data; clearInterval(intervalHandler); clearTimeout(intervalTimeout); const { token, user } = authTokenResponse === null || authTokenResponse === void 0 ? void 0 : authTokenResponse.verifyAuthCode; const currentApiHost = this.getConfigForCurrentScope("api-host"); const currentDashboardHost = this.getConfigForCurrentScope("dashboard-host"); this.configManager.setDefaultGlobalConfig({ scope: newConfig.scope, content: { token, password: flags.password || "", requirePassword: Boolean(flags.password), "api-host": currentApiHost || defaults_1.DEFAULT_API_GRAPHQL_ENDPOINT, "dashboard-host": currentDashboardHost || defaults_1.DEFAULT_DASHBOARD_HOST, requirePasswordForCurrentSession: Boolean(flags.password), merged: false, "auth-result": { user }, projectConfigLastModified: 0, }, }); await config_1.default.updateGlobalConfig(Object.assign(newConfig, { token, dashboardHost: defaults_1.DEFAULT_DASHBOARD_HOST, apiHost: defaults_1.DEFAULT_API_GRAPHQL_ENDPOINT, requirePassword: Boolean(flags.password), password: flags.password, requirePasswordForCurrentSession: Boolean(flags.password), })); console.log(`${chalk.green("√")} ${chalk.bold(chalk.gray("Authentication successful"))}`); if (shouldCloseProcess) { process.exit(0); } else if (!shouldCloseProcess && additionalProcesses) { await additionalProcesses.nextFunc(additionalProcesses.executor); } } } exports.BaseAuthService = BaseAuthService;