-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathmain.js
189 lines (175 loc) · 5.36 KB
/
main.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
177
178
179
180
181
182
183
184
185
186
187
188
189
const fs = require('fs')
const path = require('path')
const http = require('http')
const {refreshTokenFlow} = require('./low-level')
const {
loadUserData,
refreshTokenFlowIfNeeded
} = require('./high-level')
const utils = require('./utils')
const MINUTES_19 = 1000 * 60 * 19
const DEFAULT_PORT = 8089
const reference = {}
let needAuthentication = true
function start(config = {autoRefresh: false, webhook: false, port: DEFAULT_PORT}) {
return new Promise(async function(mainResolve, mainReject) {
if (config.port == null) config.port = process.env.PORT || DEFAULT_PORT
const baseUrl = `http://localhost:${config.port}`
const server = createServer(config, (error, data) => {
if (error) {
return mainReject(error)
}
return mainResolve(data)
})
server.listen(config.port)
console.log('listen on port', config.port)
let result = null
if (config.webhook === false) {
result = await loadUserData(reference, `${baseUrl}/challenge`, function() {
return utils.getInput('Zugangsnummer/Username: ')
}, function() {
return utils.getInput('PIN/Password: ', true)
}, function() {
return utils.getInput('TAN: ')
})
if (config.autoRefresh) {
setInterval(triggerTokenRefresh, MINUTES_19)
}
return mainResolve(result)
} else {
result = await loadUserData()
if (result == null) {
console.log(`waiting for webhook, login via on: ${baseUrl}`)
} else {
if (config.autoRefresh) {
setInterval(triggerTokenRefresh, MINUTES_19)
}
return mainResolve(result)
}
}
})
}
function createServer(config = {autoRefresh: false, webhook: false}, callback) {
// callback only for webhook mode
if (callback == null && config.webhook === true) {
throw new Error('callback is required for webhook mode')
}
if (config.autoRefresh == null) config.autoRefresh = false
if (config.webhook == null) config.webhook = false
let refresh_token = null
let tanHandlerResolve = null
function htmlTemplate(title, body) {
return `<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta charset="UTF-8">
<title>comdirect ${title}</title>
<style>
input, button {
font-size: 1em;
}
</style>
</head>
<body>
${body}
</body>
<script>
function send() {
const value = document.querySelector('input').value
window.location.href = '/tan/' + value
}
function login() {
const username = document.querySelector('input[type=number]').value
const password = document.querySelector('input[type=password]').value
fetch('/login', {
method: 'POST',
headers: {'Content-Type':'application/json'},
body: JSON.stringify({username: username, password: password})
}).then(() => {
setTimeout(() => {
window.location.href = '/challenge'
}, 1000 * 2)
}).catch(error => {
console.error(error)
alert(error.toString())
})
}
</script>
</html>`
}
const server = http.createServer(async function(req, res) {
if (req.url === '/challenge' && needAuthentication) {
res.writeHead(200, {'Content-Type': 'text/html'})
let form = ''
if (config.webhook === true) {
form = `<br><input type="number"> <button onClick="send()">Send</button>`
}
const html = htmlTemplate('TAN challenge',
`<img src="data:image/png;base64,${reference.challenge}">${form}`
)
res.end(html)
} else if (req.url === '/' && needAuthentication) {
res.writeHead(200, {'Content-Type': 'text/html'})
res.write(htmlTemplate('Login', `
<input placeholder="Username" type="number"><br>
<input placeholder="Password" type="password"><br>
<button onClick="login()">Login</button>`))
res.end()
} else if (req.url === '/login' && needAuthentication) {
let body = ''
req.on('data', function(data) {
body += data
})
req.on('end', function() {
const {username, password} = JSON.parse(body)
loadUserData(reference, `/challenge`, function() {
return Promise.resolve(username)
}, function() {
res.writeHead(200, {'Content-Type': 'text/html'})
res.end('') // this will redirect the user to the challenge URL
return Promise.resolve(password)
}, function() {
return new Promise((resolve, reject) => {
tanHandlerResolve = resolve
})
})
.then(data => {
console.log('authentication was successful')
needAuthentication = false
if (config.autoRefresh) {
setInterval(triggerTokenRefresh, MINUTES_19)
}
callback(null, data)
})
.catch(callback)
})
} else if (req.url.indexOf('/tan') === 0 && needAuthentication) {
const [x, y, tan] = req.url.split('/')
res.writeHead(200, {'Content-Type': 'text/html'})
res.end(htmlTemplate('TAN', 'Please check the server log.'))
tanHandlerResolve(tan)
} else {
// handle everything else but only if the invocation comes from start
if (config.port != null) {
res.writeHead(404, {'Content-Type': 'text/html'})
return res.end('Not found')
}
}
})
return server
}
function triggerTokenRefresh() {
refreshTokenFlow()
.then(status => {
console.log('refresh token was updated')
}).catch(error => {
console.error(error)
console.log('Stopping server and application')
process.exit(1)
})
}
module.exports = {
start,
createServer
}