UNPKG

6.18 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const get_port_1 = require("get-port");
4const http = require("http");
5const axios_1 = require("axios");
6// @ts-ignore
7const opn = require("open");
8const crypto = require("crypto");
9const cli_ux_1 = require("cli-ux");
10const base_command_1 = require("../base-command");
11const helpers_1 = require("../utils/helpers");
12const constants_1 = require("../utils/constants");
13const prompts_1 = require("../utils/prompts");
14class Login extends base_command_1.default {
15 constructor() {
16 super(...arguments);
17 this.on = (event, callback) => {
18 this._listerners[event] = [...this._listerners[event], callback];
19 };
20 this.stopServer = () => {
21 this.debug('stopping server');
22 if (this._server) {
23 this._server.close(() => {
24 this.debug('server stopped');
25 this._listerners['shutdown'].map(cb => cb());
26 });
27 }
28 cli_ux_1.default.action.stop();
29 };
30 this.startServer = async () => {
31 const port = await get_port_1.default({ port: constants_1.BEARER_LOGIN_PORT });
32 return new Promise((resolve, reject) => {
33 if (port !== constants_1.BEARER_LOGIN_PORT) {
34 this.error(`bearer login requires port ${constants_1.BEARER_LOGIN_PORT} to be available`);
35 reject();
36 }
37 this.debug('starting server');
38 const server = http
39 .createServer((request, response) => {
40 let body = '';
41 request.on('data', chunk => {
42 body += chunk;
43 });
44 request.on('end', () => {
45 try {
46 const data = JSON.parse(body);
47 this.debug(data);
48 response.setHeader('Access-Control-Allow-Origin', process.env.LOGIN_ALLOWED_ORIGIN || this.constants.DeveloperPortalUrl);
49 response.setHeader('Connection', 'close');
50 response.write('OK');
51 response.end();
52 this.stopServer();
53 this.getToken(data.code);
54 }
55 catch (e) {
56 this.debug(e);
57 }
58 });
59 })
60 .listen(constants_1.BEARER_LOGIN_PORT, () => resolve(server));
61 });
62 };
63 this.getToken = async (code) => {
64 try {
65 const { data: token } = await axios_1.default.post(`${this.constants.LoginDomain}/oauth/token`, {
66 code,
67 grant_type: 'authorization_code',
68 client_id: `${constants_1.LOGIN_CLIENT_ID}`,
69 code_verifier: `${this._verifier}`,
70 redirect_uri: this.callbackUrl
71 });
72 this.debug(token);
73 await this.bearerConfig.storeToken(token);
74 this.success('Successfully logged in!! 🐻');
75 this._listerners['success'].map(cb => cb());
76 }
77 catch (e) {
78 this.error(e);
79 }
80 };
81 }
82 async run() {
83 this._listerners = {
84 success: [],
85 error: [],
86 shutdown: []
87 };
88 this._server = await this.startServer();
89 this._verifier = base64URLEncode(crypto.randomBytes(32));
90 this._challenge = base64URLEncode(sha256(this._verifier));
91 cli_ux_1.default.action.start('Logging you in');
92 const scopes = 'offline_access email openid';
93 const audience = `cli-${constants_1.BEARER_ENV}`;
94 const params = {
95 audience,
96 scope: scopes,
97 response_type: 'code',
98 client_id: constants_1.LOGIN_CLIENT_ID,
99 code_challenge: this._challenge,
100 code_challenge_method: 'S256',
101 redirect_uri: this.callbackUrl
102 };
103 this.debug('authoriwe params %j', params);
104 const url = `${this.constants.LoginDomain}/authorize?${helpers_1.toParams(params)}`;
105 const spawned = await opn(url);
106 await Promise.race([
107 new Promise((resolve, reject) => {
108 spawned.on('close', async (code, signal) => {
109 if (code !== 0) {
110 this.stopServer();
111 this.warn(this.colors.yellow(`Unable to open a browser. If you want to retrieve a token please follow these steps\n`));
112 this.log(this.colors.bold('1/ access the url below and follow the login process:\n\n'), url);
113 this.log();
114 this.log(this.colors.bold(`2/ when you access the success page copy the token and paste it here`));
115 const token = await prompts_1.askForString('Token');
116 await this.getToken(token);
117 }
118 });
119 }),
120 Promise.all([
121 new Promise((resolve, reject) => {
122 this.on('success', resolve);
123 this.on('error', reject);
124 }),
125 new Promise((resolve, reject) => {
126 this.on('shutdown', resolve);
127 this.on('error', reject);
128 })
129 ])
130 ]);
131 }
132 get callbackUrl() {
133 return process.env.BEARER_LOGIN_CALLBACK_URL || `${this.constants.DeveloperPortalUrl}cli-login-callback`;
134 }
135}
136Login.description = 'login using Bearer credentials';
137Login.flags = Object.assign({}, base_command_1.default.flags);
138Login.args = [];
139exports.default = Login;
140function base64URLEncode(str) {
141 return str
142 .toString('base64')
143 .replace(/\+/g, '-')
144 .replace(/\//g, '_')
145 .replace(/=/g, '');
146}
147function sha256(str) {
148 return crypto
149 .createHash('sha256')
150 .update(str)
151 .digest();
152}