@onboardbase/cli
Version:
[](https://www.npmjs.com/package/@onboardbase/cli) [](https://www.npmjs.com/package/@onboardbase/cli) [ • 9.02 kB
JavaScript
;
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;