UNPKG

quip-cli

Version:

A Command Line Interface for the Quip Live Apps platform

136 lines (135 loc) 5.77 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.login = void 0; const tslib_1 = require("tslib"); const command_1 = require("@oclif/command"); const fs_1 = tslib_1.__importDefault(require("fs")); const http_1 = tslib_1.__importDefault(require("http")); const open_1 = tslib_1.__importDefault(require("open")); const path_1 = tslib_1.__importDefault(require("path")); const querystring_1 = tslib_1.__importDefault(require("querystring")); const url_1 = tslib_1.__importDefault(require("url")); const auth_1 = require("../lib/auth"); const config_1 = require("../lib/config"); const pkce_challenge_1 = tslib_1.__importDefault(require("pkce-challenge")); const cli_api_1 = require("../lib/cli-api"); const print_1 = require("../lib/print"); let server_; const waitForLogin = (hostname, port, ready) => { const pagePromise = fs_1.default.promises.readFile(path_1.default.join(__dirname, "../..", "templates", "logged-in.html"), "utf-8"); return new Promise((resolve) => { server_ = http_1.default.createServer(async (req, res) => { const urlInfo = url_1.default.parse(req.url || ""); const query = querystring_1.default.parse(urlInfo.query || ""); resolve(query); if (query.next) { res.statusCode = 302; res.setHeader('Location', query.next); res.end(); } else { res.statusCode = 200; res.setHeader("Content-Type", "text/html"); res.end(await pagePromise); } server_ === null || server_ === void 0 ? void 0 : server_.close(); }); server_.listen(port, hostname, ready); }); }; const DEFAULT_HOSTNAME = "127.0.0.1"; const DEFAULT_PORT = 9898; exports.login = async ({ site, transparent = false, hostname = DEFAULT_HOSTNAME, port = DEFAULT_PORT, config = config_1.defaultConfigPath(), }) => { const { code_challenge, code_verifier } = pkce_challenge_1.default(43); const state = cli_api_1.getStateString(); const redirectURL = `http://${hostname}:${port}`; let loginURL = `https://${site}/cli/login?client_id=quip-cli&response_type=code&redirect_uri=${encodeURIComponent(redirectURL)}&state=${state}&code_challenge=${code_challenge}&code_challenge_method=S256`; if (transparent) { loginURL += "&transparent=true"; } else { print_1.println(`opening login URL in your browser. Log in to Quip there.\n${loginURL}`); } let currentWindow; const responseParams = await waitForLogin(hostname, port, async () => { currentWindow = await open_1.default(loginURL); }); currentWindow === null || currentWindow === void 0 ? void 0 : currentWindow.emit("close"); if (responseParams.cancelled) { throw new Error("Login cancelled."); } else if (responseParams.state !== state) { throw new Error("API returned invalid state."); } else if (!responseParams.code || responseParams.error) { throw new Error(`Login Failed: ${responseParams.error || `no code returned, got ${JSON.stringify(responseParams, null, 2)}`}`); } const tokenResponse = await cli_api_1.callAPI(site, "token", "post", { client_id: "quip-cli", grant_type: "authorization_code", redirect_uri: encodeURIComponent(redirectURL), code_verifier: code_verifier, code: responseParams.code, }); const accessToken = tokenResponse.accessToken || tokenResponse.access_token; if (!accessToken || tokenResponse.error) { throw new Error(`Failed to acquire access token: ${tokenResponse.error} - response: ${JSON.stringify(tokenResponse, null, 2)}`); } await config_1.writeSiteConfig(config, site, { accessToken }); }; class Login extends command_1.Command { async catch(error) { server_ === null || server_ === void 0 ? void 0 : server_.close(); throw error; } async run() { const { flags } = this.parse(Login); const { site, force, hostname, port, config } = flags; if (!force && (await auth_1.isLoggedIn(config, site))) { let alt = ""; if (site === config_1.DEFAULT_SITE) { alt = " or --site to log in to a different site"; } this.log(`You're already logged in to ${site}. Pass --force to log in again${alt}.`); return; } try { await exports.login({ site, hostname, port, config }); this.log("Successfully logged in."); } catch (e) { this.error(e); } } } exports.default = Login; Login.description = "Logs in to Quip and stores credentials in the .quiprc file"; Login.flags = { help: command_1.flags.help({ char: "h" }), force: command_1.flags.boolean({ char: "f", description: "forces a re-login even if a user is currently logged in", }), site: command_1.flags.string({ char: "s", description: "use a specific quip site rather than the standard quip.com login", default: config_1.DEFAULT_SITE, }), port: command_1.flags.integer({ hidden: true, description: "Use a custom port for the OAuth redirect server (defaults to 9898)", default: DEFAULT_PORT, }), hostname: command_1.flags.string({ hidden: true, description: "Use a custom hostname for the OAuth redirect server (defaults to 127.0.0.1)", default: DEFAULT_HOSTNAME, }), config: command_1.flags.string({ hidden: true, description: "Use a custom config file (default ~/.quiprc)", default: () => config_1.defaultConfigPath(), }), }; Login.args = [];