-
Notifications
You must be signed in to change notification settings - Fork 5
/
high-level.js
145 lines (132 loc) · 3.84 KB
/
high-level.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
const fs = require('fs')
const CREDENTIALS_FILE_PATH = process.env.CREDENTIALS_FILE_PATH || '.credentials'
const {
oAuthInit,
getSessionStatus,
validateSesssionTAN,
activateSesssionTAN,
oAuthSecondaryFlow,
refreshTokenFlow,
getAccountInfo,
_setStorageHandler
} = require('./low-level')
const utils = require('./utils')
let memory = {}
const persistence = process.env.PERSISTENCE
const DEBUG = process.env.DEBUG || false
if (persistence) {
console.log('using', CREDENTIALS_FILE_PATH)
}
function load() {
if (persistence) {
try {
return JSON.parse(fs.readFileSync(CREDENTIALS_FILE_PATH, 'utf8'))
} catch (error) {
console.log('creating empty file:', CREDENTIALS_FILE_PATH)
fs.writeFileSync(CREDENTIALS_FILE_PATH, '{}', 'utf8')
return {}
}
}
return memory
}
function save(object) {
const credentials = load()
const patched = Object.assign({}, credentials, object)
if (persistence) {
fs.writeFileSync(CREDENTIALS_FILE_PATH, JSON.stringify(patched, null, 2), 'utf8')
} else {
memory = patched
}
}
_setStorageHandler({load: load, save: save})
async function loadUserData(reference, challengeUrl, username, password, tan) {
const data = await getValidCredentials(reference, challengeUrl, username, password, tan)
if (data == null) {
return null // for webhook
}
if (data.accountId == null) {
const accountId = await getAccountInfo()
save({accountId})
return load()
}
return data
}
// refresh access token if needed
async function refreshTokenFlowIfNeeded() {
try {
await getAccountInfo()
return {status: 'still valid'}
} catch (error) {
// ignore
}
try {
await refreshTokenFlow()
return {status: 'updated'}
} catch (error) {
throw error
}
}
async function getValidCredentials(reference, challengeUrl, username, password, tan) {
const data = load()
try {
// try access token
const accountId = await getAccountInfo()
if (accountId != null) {
// everything is loaded and still valid
save({accountId})
return load()
}
} catch (error) {
// ignore (access token has expired)
}
try {
if (data.refresh_token != null) {
const response = await refreshTokenFlow()
return load()
}
// first run, continue with runOAuthFlow()
} catch (error) {
// ignore (refresh token has expired)
}
// initial/very first run will do this
return await runOAuthFlow(reference, challengeUrl, username, password, tan)
}
async function runOAuthFlow(reference, challengeUrl, username, password, tan) {
if (username == null || password == null || tan == null) {
return null // for webhook
}
const _username = await username()
const _password = await password()
const oAuthResponse = await oAuthInit(_username, _password)
const sessionId = utils.guid()
const requestId = utils.requestId()
save({sessionId, requestId})
const sessionStatus = await getSessionStatus()
const sessionUUID = sessionStatus[0].identifier
const validationResponse = await validateSesssionTAN(sessionUUID)
if (validationResponse.headers['x-once-authentication-info'] != null) {
const header = JSON.parse(validationResponse.headers['x-once-authentication-info'])
if (DEBUG) console.log('x-once-authentication-info', header)
if (header.id != null && header.typ == 'P_TAN' && header.challenge != null) {
reference.challenge = header.challenge
console.log(`Please solve the photoTAN with your browser: ${challengeUrl}`)
const _tan = await tan()
const activatedSession = await activateSesssionTAN(_tan, header.id, sessionStatus[0].identifier)
if (activatedSession.sessionTanActive === true) {
const finalRespone = await oAuthSecondaryFlow()
return Object.assign({}, load(), {kdnr: finalRespone.kdnr} )
} else {
throw new Error('photoTAN was not successful')
}
}
}
throw new Error('photoTAN not possible')
}
module.exports = {
loadUserData,
getValidCredentials,
refreshTokenFlow,
refreshTokenFlowIfNeeded,
load,
save
}