forked from nightscout/minimed-connect-to-nightscout
-
Notifications
You must be signed in to change notification settings - Fork 0
/
carelink.js
245 lines (205 loc) · 8.13 KB
/
carelink.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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/* jshint node: true */
"use strict";
var _ = require('lodash'),
axios = require('axios').default,
axiosCookieJarSupport = require('axios-cookiejar-support').default,
tough = require('tough-cookie'),
qs = require('qs');
var logger = require('./logger');
var CARELINK_EU = process.env['MMCONNECT_SERVER'] === 'EU';
var DEFAULT_MAX_RETRY_DURATION = module.exports.defaultMaxRetryDuration = 512;
var carelinkServerAddress = CARELINK_EU ? "carelink.minimed.eu" : "carelink.minimed.com";
var CARELINKEU_LOGIN_URL = 'https://' + carelinkServerAddress + '/patient/sso/login?country=gb&lang=en';
var CARELINKEU_REFRESH_TOKEN_URL = 'https://' + carelinkServerAddress + '/patient/sso/reauth';
var CARELINKEU_JSON_BASE_URL = 'https://' + carelinkServerAddress + '/patient/connect/data?cpSerialNumber=NONE&msgType=last24hours&requestTime=';
var CARELINKEU_TOKEN_COOKIE = 'auth_tmp_token';
var CARELINKEU_TOKENEXPIRE_COOKIE = 'c_token_valid_to';
var CARELINK_SECURITY_URL = 'https://' + carelinkServerAddress + '/patient/j_security_check';
var CARELINK_AFTER_LOGIN_URL = 'https://' + carelinkServerAddress + '/patient/main/login.do';
var CARELINK_JSON_BASE_URL = 'https://' + carelinkServerAddress + '/patient/connect/ConnectViewerServlet?cpSerialNumber=NONE&msgType=last24hours&requestTime=';
var CARELINK_LOGIN_COOKIE = '_WL_AUTHCOOKIE_JSESSIONID';
var carelinkJsonUrlNow = function () {
return (CARELINK_EU ? CARELINKEU_JSON_BASE_URL : CARELINK_JSON_BASE_URL) + Date.now();
};
var Client = exports.Client = function (options) {
if (!(this instanceof Client)) {
return new Client(arguments[0]);
}
axiosCookieJarSupport(axios);
var cookieJar = new tough.CookieJar();
axios.defaults.jar = new tough.CookieJar();
axios.defaults.maxRedirects = 0;
axios.defaults.withCredentials = true;
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
if (error.response && error.response.status >= 200 && error.response.status < 400) {
return error.response;
} else {
// Do something with response error
return Promise.reject(error);
}
});
if (options.maxRetryDuration === undefined) {
options.maxRetryDuration = DEFAULT_MAX_RETRY_DURATION;
}
function getCookies() {
let cookies = [];
axios.defaults.jar.store.getAllCookies(function (err, cookieArray) {
if (err)
cookies = [];
cookies = cookieArray;
});
return cookies.filter(c => c.domain === carelinkServerAddress);
}
function haveCookie(cookieName) {
return _.some(getCookies(), {key: cookieName});
}
function getCookie(cookieName) {
return _.find(getCookies(), {key: cookieName});
}
async function doLogin() {
return await axios.post(
CARELINK_SECURITY_URL,
qs.stringify({
j_username: options.username,
j_password: options.password,
j_character_encoding: "UTF-8"
}));
}
async function doFetchCookie() {
return await axios.get(CARELINK_AFTER_LOGIN_URL);
}
async function doLoginEu1() {
return await axios.get(CARELINKEU_LOGIN_URL);
}
async function doLoginEu2(response) {
return await axios.get(response.headers.location);
}
async function doLoginEu3(response) {
let uri = new URL(response.headers.location);
let uriParam = uri.searchParams;
let url = `${uri.origin}${uri.pathname}?locale=${uriParam.get('locale')}&countrycode=${uriParam.get('countrycode')}`;
response = await axios.post(url, qs.stringify({
sessionID: uriParam.get('sessionID'),
sessionData: uriParam.get('sessionData'),
locale: "en",
action: "login",
username: options.username,
password: options.password,
actionButton: "Log in",
}));
if (_.get(response, 'data', '').includes(uri.pathname))
throw new Error('Carelink invalid username or password');
return response;
}
async function doLoginEu4(response) {
let regex = /(<form action=")(.*)" method="POST"/gm;
let url = (regex.exec(response.data) || [])[2] || '';
// Session data is changed, need to get it from the html body form
regex = /(<input type="hidden" name="sessionID" value=")(.*)"/gm;
let sessionId = (regex.exec(response.data) || [])[2] || '';
regex = /(<input type="hidden" name="sessionData" value=")(.*)"/gm;
let sessionData = (regex.exec(response.data)[2] || []) || '';
return await axios.post(url, qs.stringify({
action: "consent",
sessionID: sessionId,
sessionData: sessionData,
response_type: "code",
response_mode: "query",
}), {
maxRedirects: 0,
});
}
async function doLoginEu5(response) {
return await axios.get(response.headers.location, {maxRedirects: 0});
}
async function refreshTokenEu() {
return await axios.post(
CARELINKEU_REFRESH_TOKEN_URL,
{},
{
headers: {
Authorization: "Bearer " + _.get(getCookie(CARELINKEU_TOKEN_COOKIE), 'value', ''),
},
},
).catch(async function (error) {
if (error.response && error.response.status === 401) {
// Login again
await checkLogin();
} else {
throw error;
}
});
}
async function getConnectData() {
var url = carelinkJsonUrlNow();
logger.log('GET ' + url);
var config = {
headers: {},
};
if (CARELINK_EU) {
config.headers.Authorization = "Bearer " + _.get(getCookie(CARELINKEU_TOKEN_COOKIE), 'value', '');
}
return await axios.get(url, config);
}
async function checkLogin() {
if (CARELINK_EU) {
// EU - SSO method
if (haveCookie(CARELINKEU_TOKEN_COOKIE) || haveCookie(CARELINKEU_TOKENEXPIRE_COOKIE)) {
let expire = new Date(Date.parse(_.get(getCookie(CARELINKEU_TOKENEXPIRE_COOKIE), 'value')));
// Refresh token if expires in 10 minutes
if (expire < new Date(Date.now() + 10 * 1000 * 60))
await refreshTokenEu();
} else {
logger.log('Logging in to CareLink');
let response = await doLoginEu1();
response = await doLoginEu2(response);
response = await doLoginEu3(response);
response = await doLoginEu4(response);
await doLoginEu5(response);
}
} else {
// US - Cookie method
if (!haveCookie(CARELINK_LOGIN_COOKIE)) {
logger.log('Logging in to CareLink');
let response = await doLogin()
await doFetchCookie(response)
}
}
}
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function fetch(callback) {
try {
let maxRetry = 3;
for (let i = 1; i <= maxRetry; i++) {
await checkLogin();
try {
let response = await getConnectData();
callback(null, response.data);
return;
} catch (e1) {
if (i === maxRetry)
throw e1;
if (e1.response && e1.response.status === 401) {
// reauth
cookieJar.removeAllCookiesSync();
}
let timeout = retryDurationOnAttempt(i);
await sleep(1000 * timeout);
}
}
throw new Error('Failed to download Carelink data');
} catch (e) {
callback(`${e.toString()}\nstack: ${e.stack}`, null);
}
}
return {
fetch: fetch
};
};