1 |
|
2 | const Octokit = require('@octokit/rest')
|
3 | const inquirer = require('inquirer')
|
4 | const querystring = require('querystring')
|
5 | const get = require('lodash.get')
|
6 | const open = require('open')
|
7 | const http = require('http')
|
8 | const getPort = require('get-port')
|
9 |
|
10 | module.exports = getGitHubToken
|
11 |
|
12 | async function getGitHubToken(opts) {
|
13 | console.log('')
|
14 |
|
15 | opts = Object.assign(
|
16 | {
|
17 | userAgent: 'Netlify-cli-octokit',
|
18 | note: 'Netlify-cli-gh-auth',
|
19 | scopes: []
|
20 | },
|
21 | opts
|
22 | )
|
23 |
|
24 | async function promptForOTP() {
|
25 | const { otp } = await inquirer.prompt([
|
26 | {
|
27 | type: 'input',
|
28 | name: 'otp',
|
29 | message: 'Your GitHub OTP/2FA Code:',
|
30 | filter: input => input.trim()
|
31 | }
|
32 | ])
|
33 | return otp
|
34 | }
|
35 |
|
36 | const authChoiceNetlify = 'Authorize with GitHub through app.netlify.com'
|
37 | const authChoiceManual = 'Enter your GitHub credentials manually'
|
38 | const authChoices = [authChoiceNetlify, authChoiceManual]
|
39 |
|
40 | const { initChoice } = await inquirer.prompt([
|
41 | {
|
42 | type: 'list',
|
43 | name: 'initChoice',
|
44 | message:
|
45 | 'Netlify CLI needs access to your GitHub account to configure Webhooks and Deploy Keys. ' +
|
46 | 'What would you like to do?',
|
47 | choices: authChoices
|
48 | }
|
49 | ])
|
50 |
|
51 | if (initChoice === authChoiceNetlify) {
|
52 | const port = await getPort({ port: 3000 })
|
53 | let deferredResolve
|
54 | let deferredReject
|
55 | const deferredPromise = new Promise(function(resolve, reject) {
|
56 | deferredResolve = resolve
|
57 | deferredReject = reject
|
58 | })
|
59 |
|
60 | const server = http.createServer(function(req, res) {
|
61 | const parameters = querystring.parse(req.url.slice(req.url.indexOf('?') + 1))
|
62 | if (parameters.token) {
|
63 | deferredResolve(parameters)
|
64 | res.end(
|
65 | "<html><head><script>if(history.replaceState){history.replaceState({},'','/')}</script><style>html{font-family:sans-serif;background:#0e1e25}body{overflow:hidden;position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;height:100vh;width:100vw;}h3{margin:0}.card{position:relative;display:flex;flex-direction:column;width:75%;max-width:364px;padding:24px;background:white;color:rgb(14,30,37);border-radius:8px;box-shadow:0 2px 4px 0 rgba(14,30,37,.16);}</style></head>" +
|
66 | "<body><div class=card><h3>Logged In</h3><p>You're now logged into Netlify CLI with your " +
|
67 | parameters.provider +
|
68 | ' credentials. Please close this window.</p></div>'
|
69 | )
|
70 | server.close()
|
71 | return
|
72 | }
|
73 | res.end('BAD PARAMETERS')
|
74 | server.close()
|
75 | deferredReject(new Error('Got invalid parameters for CLI login'))
|
76 | })
|
77 |
|
78 | await new Promise(function(resolve, reject) {
|
79 | server.on('error', reject)
|
80 | server.listen(port, resolve)
|
81 | })
|
82 |
|
83 | const webUI = process.env.NETLIFY_WEB_UI || 'https://app.netlify.com'
|
84 | const url =
|
85 | webUI +
|
86 | '/cli?' +
|
87 | querystring.encode({
|
88 | host: 'http://localhost:' + port,
|
89 | provider: 'github'
|
90 | })
|
91 |
|
92 | try {
|
93 | await open(url)
|
94 | } catch (err) {
|
95 | console.log(
|
96 | 'Netlify CLI could not open the browser for you.' + ' Please visit this URL in a browser on this device: ' + url
|
97 | )
|
98 | }
|
99 |
|
100 | return await deferredPromise
|
101 | } else {
|
102 | const { username, password } = await inquirer.prompt([
|
103 | {
|
104 | type: 'input',
|
105 | name: 'username',
|
106 | message: 'Your GitHub username:',
|
107 | filter: input => input.trim()
|
108 | },
|
109 | {
|
110 | type: 'password',
|
111 | name: 'password',
|
112 | message: 'Your GitHub password:',
|
113 | mask: '*',
|
114 | filter: input => input.trim()
|
115 | }
|
116 | ])
|
117 |
|
118 |
|
119 | const octokit = new Octokit({
|
120 | auth: {
|
121 | username,
|
122 | password,
|
123 | async on2fa() {
|
124 | return promptForOTP()
|
125 | }
|
126 | }
|
127 | })
|
128 |
|
129 | let response = await octokit.oauthAuthorizations.createAuthorization({
|
130 | note: opts.note + ' (' + new Date().toJSON() + ')',
|
131 | note_url: 'https://cli.netlify.com/',
|
132 | scopes: opts.scopes,
|
133 | headers: {
|
134 | 'User-Agent': opts.userAgent
|
135 | }
|
136 | })
|
137 |
|
138 | if (get(response, 'data.token')) {
|
139 | return { user: username, token: get(response, 'data.token') }
|
140 | } else {
|
141 | const error = new Error('Github authentication failed')
|
142 | error.response = response
|
143 | throw error
|
144 | }
|
145 | }
|
146 | }
|