UNPKG

6.47 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const express = require("express");
4const crypto = require("crypto");
5const axios_1 = require("axios");
6const inquirer = require("inquirer");
7const startLocalServer_1 = require("../actions/startLocalServer");
8// TODO: use esModuleInterop config
9// @ts-ignore
10const cli_ux_1 = require("cli-ux");
11// @ts-ignore
12const opn = require("open");
13const base_1 = require("./base");
14const helpers_1 = require("../utils/helpers");
15const constants_1 = require("../utils/constants");
16const prompts_1 = require("../utils/prompts");
17class LoginAction extends base_1.default {
18 constructor() {
19 super(...arguments);
20 this._listerners = { success: [], error: [] };
21 this.on = (event, callback) => {
22 this._listerners[event] = [...this._listerners[event], callback];
23 };
24 this.stopServer = () => new Promise((resolve, _reject) => {
25 if (this._server) {
26 this.logger.debug('stopping server');
27 this._server.destroy(() => {
28 this.logger.debug('server stopped');
29 resolve();
30 });
31 }
32 else {
33 resolve();
34 }
35 });
36 this.startServer = async () => {
37 this.on('error', this.stopServer);
38 try {
39 const app = express();
40 app.use((_req, res, next) => {
41 res.setHeader('Connection', 'close');
42 next();
43 });
44 app.get('/login/callback', (req, res, next) => {
45 const code = req.query.code || '';
46 try {
47 res.send(constants_1.SUCCESS_LOGIN_PAGE).end();
48 Promise.all([this.getToken(code), this.stopServer()]).then(() => {
49 this.logger.debug('calling success listeners');
50 this._listerners.success.map(cb => cb());
51 });
52 }
53 catch (e) {
54 this.logger.debug(e);
55 this.logger.error('Error while fetching token');
56 this._listerners.error.map(cb => cb());
57 }
58 });
59 this.logger.debug('starting server');
60 return await startLocalServer_1.startServer(constants_1.BEARER_LOGIN_PORT, app);
61 }
62 catch (e) {
63 if (e instanceof startLocalServer_1.UnavailablePort) {
64 this.logger.error('bearer login cannot start');
65 }
66 throw e;
67 }
68 };
69 this.getToken = async (code) => {
70 try {
71 const { data: token } = await axios_1.default.post(`${this.logger.constants.LoginDomain}/oauth/token`, {
72 code,
73 grant_type: 'authorization_code',
74 client_id: `${constants_1.LOGIN_CLIENT_ID}`,
75 code_verifier: `${this._verifier}`,
76 redirect_uri: this.callbackUrl
77 });
78 this.logger.debug('saving token: %j', token);
79 await this.logger.bearerConfig.storeToken(token);
80 }
81 catch (e) {
82 this.logger.error(e);
83 }
84 };
85 }
86 async run() {
87 this._server = await this.startServer();
88 this._verifier = base64URLEncode(crypto.randomBytes(32));
89 this._challenge = base64URLEncode(sha256(this._verifier));
90 cli_ux_1.default.action.start('Logging you in');
91 const scopes = 'offline_access email openid';
92 const audience = `cli-${constants_1.BEARER_ENV}`;
93 const params = {
94 audience,
95 scope: scopes,
96 response_type: 'code',
97 client_id: constants_1.LOGIN_CLIENT_ID,
98 code_challenge: this._challenge,
99 code_challenge_method: 'S256',
100 redirect_uri: this.callbackUrl
101 };
102 this.logger.debug('authorize params %j', params);
103 const url = `${this.logger.constants.LoginDomain}/authorize?${helpers_1.toParams(params)}`;
104 const spawned = await opn(url);
105 await Promise.race([
106 new Promise((resolve, reject) => {
107 spawned.on('close', async (code, signal) => {
108 if (code !== 0) {
109 this.logger.warn(this.logger.colors.yellow(`Unable to open a browser. If you want to retrieve a token please follow these steps\n`));
110 this.logger.log(this.logger.colors.bold('1/ access the url below and follow the login process:\n\n'), url);
111 this.logger.log();
112 this.logger.log(this.logger.colors.bold(`2/ when you access the success page copy the token and paste it here`));
113 const token = await prompts_1.askForString('Token');
114 await this.getToken(token);
115 resolve();
116 }
117 });
118 }),
119 new Promise((resolve, reject) => {
120 this.on('error', reject);
121 this.on('success', resolve);
122 })
123 ]);
124 this.logger.success('Successfully logged in!! 🐻');
125 cli_ux_1.default.action.stop();
126 }
127 get callbackUrl() {
128 return process.env.BEARER_LOGIN_CALLBACK_URL || `${this.logger.constants.DeveloperPortalUrl}cli/login-callback`;
129 }
130}
131const loginFlow = base_1.createExport(LoginAction);
132exports.default = loginFlow;
133async function promptToLogin(command) {
134 command.log(command.colors.bold('⚠️ It looks like you are not logged in'));
135 const { shouldLogin } = await inquirer.prompt([
136 {
137 message: 'Would you like to login?',
138 name: 'shouldLogin',
139 type: 'list',
140 choices: [{ name: 'Yes', value: true }, { name: 'No', value: false }]
141 }
142 ]);
143 if (shouldLogin) {
144 await loginFlow(command);
145 }
146 else {
147 command.exit(0);
148 }
149}
150exports.promptToLogin = promptToLogin;
151function base64URLEncode(str) {
152 return str
153 .toString('base64')
154 .replace(/\+/g, '-')
155 .replace(/\//g, '_')
156 .replace(/=/g, '');
157}
158function sha256(str) {
159 return crypto
160 .createHash('sha256')
161 .update(str)
162 .digest();
163}