-
Notifications
You must be signed in to change notification settings - Fork 31
/
ghauth.js
176 lines (131 loc) · 4.36 KB
/
ghauth.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
const read = require('read')
, hyperquest = require('hyperquest')
, bl = require('bl')
, path = require('path')
, fs = require('fs')
, mkdirp = require('mkdirp')
, xtend = require('xtend')
, appCfg = require('application-config')
const defaultUA = 'Magic Node.js application that does magic things with ghauth'
, defaultScopes = []
, defaultNote = 'Node.js command-line app with ghauth'
, defaultAuthUrl = 'https://api.github.com/authorizations'
, defaultPromptName = 'GitHub'
function createAuth (options, callback) {
var reqOptions = {
headers : {
'X-GitHub-OTP' : options.otp || null
, 'User-Agent' : options.userAgent || defaultUA
, 'Content-type' : 'application/json'
}
, method : 'post'
, auth : options.user + ':' + options.pass
}
, authUrl = options.authUrl || defaultAuthUrl
, currentDate = new Date().toJSON()
, req = hyperquest(authUrl, reqOptions)
req.pipe(bl(afterCreateAuthResponse))
function afterCreateAuthResponse (err, data) {
if (err)
return callback(err)
data = JSON.parse(data.toString())
if (data.message) {
var error = new Error(data.message)
error.data = data
return callback(error)
}
if (!data.token)
return callback(new Error('No token from GitHub!'))
callback(null, data.token)
}
req.end(JSON.stringify({
scopes : options.scopes || defaultScopes
, note : (options.note || defaultNote) + ' (' + currentDate + ')'
}))
}
function prompt (options, callback) {
var promptName = options.promptName || defaultPromptName
, usernamePrompt = 'Your ' + promptName + ' username:'
, passwordPrompt = 'Your ' + promptName + ' password:'
, user
, pass
read({ prompt: usernamePrompt }, afterUsernameRead)
function afterUsernameRead (err, _user) {
if (err)
return callback(err)
if (_user === '')
return callback()
user = _user
read({ prompt: passwordPrompt, silent: true, replace: '\u2714' }, afterPasswordRead)
}
function afterPasswordRead (err, _pass) {
if (err)
return callback(err)
pass = _pass
// Check for 2FA. This triggers an SMS if needed
var reqOptions = {
headers : {
'User-Agent' : options.userAgent || defaultUA
}
, method : 'post'
, auth : user + ':' + pass
}
, authUrl = options.authUrl || defaultAuthUrl
hyperquest(authUrl, reqOptions, after2FaResponse).end();
}
function after2FaResponse (err, response) {
if (err)
return callback(err)
var otp = response.headers['x-github-otp']
if (!otp || otp.indexOf('required') < 0)
return callback(null, { user: user, pass: pass, otp: null })
read({ prompt: 'Your GitHub OTP/2FA Code (optional):' }, afterOtpRead)
}
function afterOtpRead (err, otp) {
if (err)
return callback(err)
callback(null, { user: user, pass: pass, otp: otp })
}
}
function auth (options, callback) {
if (typeof options != 'object')
throw new TypeError('ghauth requires an options argument')
if (typeof callback != 'function')
throw new TypeError('ghauth requires a callback argument')
var config
if (!options.noSave) {
if (typeof options.configName != 'string')
throw new TypeError('ghauth requires an options.configName property')
config = appCfg(options.configName)
config.read(afterConfigRead)
} else {
prompt(options, afterPrompt)
}
function afterConfigRead (err, authData) {
if (err)
return callback(err)
if (authData && authData.user && authData.token)
return callback(null, authData)
prompt(options, afterPrompt)
}
function afterPrompt (err, data) {
if (err)
return callback(err)
createAuth(xtend(options, data), afterCreateAuth)
function afterCreateAuth (err, token) {
if (err)
return callback(err)
var tokenData = { user: data.user, token: token }
if (options.noSave)
return callback(null, tokenData)
process.umask(0o077);
config.write(tokenData, afterWrite)
function afterWrite (err) {
if (err)
return callback(err)
callback(null, tokenData)
}
}
}
}
module.exports = auth