@gooin/garmin-connect 1.6.1 → 1.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,254 +1,426 @@
1
1
  "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
+ return new (P || (P = Promise))(function (resolve, reject) {
16
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
17
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
18
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
19
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
20
+ });
21
+ };
22
+ var __generator = (this && this.__generator) || function (thisArg, body) {
23
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
24
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
25
+ function verb(n) { return function (v) { return step([n, v]); }; }
26
+ function step(op) {
27
+ if (f) throw new TypeError("Generator is already executing.");
28
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
29
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
30
+ if (y = 0, t) op = [op[0] & 2, t.value];
31
+ switch (op[0]) {
32
+ case 0: case 1: t = op; break;
33
+ case 4: _.label++; return { value: op[1], done: false };
34
+ case 5: _.label++; y = op[1]; op = [0]; continue;
35
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
36
+ default:
37
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
38
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
39
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
40
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
41
+ if (t[2]) _.ops.pop();
42
+ _.trys.pop(); continue;
43
+ }
44
+ op = body.call(thisArg, _);
45
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
46
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
+ }
48
+ };
2
49
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
50
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
51
  };
5
52
  Object.defineProperty(exports, "__esModule", { value: true });
6
53
  exports.HttpClient = void 0;
7
- const axios_1 = __importDefault(require("axios"));
8
- const form_data_1 = __importDefault(require("form-data"));
9
- const lodash_1 = __importDefault(require("lodash"));
10
- const luxon_1 = require("luxon");
11
- const oauth_1_0a_1 = __importDefault(require("oauth-1.0a"));
12
- const qs_1 = __importDefault(require("qs"));
13
- const crypto = require('crypto');
14
- const CSRF_RE = new RegExp('name="_csrf"\\s+value="(.+?)"');
15
- const TICKET_RE = new RegExp('ticket=([^"]+)"');
16
- const ACCOUNT_LOCKED_RE = new RegExp('var statuss*=s*"([^"]*)"');
17
- const USER_AGENT_CONNECTMOBILE = 'com.garmin.android.apps.connectmobile';
18
- const USER_AGENT_BROWSER = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1';
19
- const OAUTH_CONSUMER_URL = 'https://thegarth.s3.amazonaws.com/oauth_consumer.json';
54
+ var axios_1 = __importDefault(require("axios"));
55
+ var form_data_1 = __importDefault(require("form-data"));
56
+ var lodash_1 = __importDefault(require("lodash"));
57
+ var luxon_1 = require("luxon");
58
+ var oauth_1_0a_1 = __importDefault(require("oauth-1.0a"));
59
+ var qs_1 = __importDefault(require("qs"));
60
+ var crypto = require('crypto');
61
+ var CSRF_RE = new RegExp('name="_csrf"\\s+value="(.+?)"');
62
+ var TICKET_RE = new RegExp('ticket=([^"]+)"');
63
+ var ACCOUNT_LOCKED_RE = new RegExp('var statuss*=s*"([^"]*)"');
64
+ var PAGE_TITLE_RE = new RegExp('<title>([^<]*)</title>');
65
+ var USER_AGENT_CONNECTMOBILE = 'com.garmin.android.apps.connectmobile';
66
+ var USER_AGENT_BROWSER = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36';
67
+ var OAUTH_CONSUMER_URL = 'https://thegarth.s3.amazonaws.com/oauth_consumer.json';
20
68
  // refresh token
21
- let isRefreshing = false;
22
- let refreshSubscribers = [];
23
- class HttpClient {
24
- constructor(url) {
69
+ var isRefreshing = false;
70
+ var refreshSubscribers = [];
71
+ var HttpClient = /** @class */ (function () {
72
+ function HttpClient(url) {
73
+ var _this = this;
25
74
  this.url = url;
26
75
  this.client = axios_1.default.create();
27
- this.client.interceptors.response.use((response) => response, async (error) => {
28
- const originalRequest = error.config;
29
- // console.log('originalRequest:', originalRequest)
30
- // Auto Refresh token
31
- if (error.response.status === 401 && !originalRequest._retry) {
32
- if (!this.oauth2Token) {
33
- return;
34
- }
35
- if (isRefreshing) {
36
- try {
37
- const token = await new Promise((resolve) => {
38
- refreshSubscribers.push((token) => {
39
- resolve(token);
40
- });
76
+ this.client.interceptors.response.use(function (response) { return response; }, function (error) { return __awaiter(_this, void 0, void 0, function () {
77
+ var originalRequest, token, err_1;
78
+ var _this = this;
79
+ var _a;
80
+ return __generator(this, function (_b) {
81
+ switch (_b.label) {
82
+ case 0:
83
+ originalRequest = error.config;
84
+ if (!(((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) === 401 &&
85
+ !(originalRequest === null || originalRequest === void 0 ? void 0 : originalRequest._retry))) return [3 /*break*/, 6];
86
+ if (!this.oauth2Token) {
87
+ return [2 /*return*/];
88
+ }
89
+ if (!isRefreshing) return [3 /*break*/, 4];
90
+ _b.label = 1;
91
+ case 1:
92
+ _b.trys.push([1, 3, , 4]);
93
+ return [4 /*yield*/, new Promise(function (resolve) {
94
+ refreshSubscribers.push(function (token) {
95
+ resolve(token);
96
+ });
97
+ })];
98
+ case 2:
99
+ token = _b.sent();
100
+ originalRequest.headers.Authorization = "Bearer ".concat(token);
101
+ return [2 /*return*/, this.client(originalRequest)];
102
+ case 3:
103
+ err_1 = _b.sent();
104
+ console.log('err:', err_1);
105
+ return [2 /*return*/, Promise.reject(err_1)];
106
+ case 4:
107
+ originalRequest._retry = true;
108
+ isRefreshing = true;
109
+ console.log('interceptors: refreshOauth2Token start');
110
+ return [4 /*yield*/, this.refreshOauth2Token()];
111
+ case 5:
112
+ _b.sent();
113
+ console.log('interceptors: refreshOauth2Token end');
114
+ isRefreshing = false;
115
+ refreshSubscribers.forEach(function (subscriber) {
116
+ return subscriber(_this.oauth2Token.access_token);
41
117
  });
42
- originalRequest.headers.Authorization = `Bearer ${token}`;
43
- return this.client(originalRequest);
44
- }
45
- catch (err) {
46
- console.log('err:', err);
47
- return Promise.reject(err);
48
- }
118
+ refreshSubscribers = [];
119
+ originalRequest.headers.Authorization = "Bearer ".concat(this.oauth2Token.access_token);
120
+ return [2 /*return*/, this.client(originalRequest)];
121
+ case 6:
122
+ if (axios_1.default.isAxiosError(error)) {
123
+ if (error === null || error === void 0 ? void 0 : error.response)
124
+ this.handleError(error === null || error === void 0 ? void 0 : error.response);
125
+ }
126
+ throw error;
49
127
  }
50
- originalRequest._retry = true;
51
- isRefreshing = true;
52
- console.log('interceptors: refreshOauth2Token start');
53
- await this.refreshOauth2Token();
54
- console.log('interceptors: refreshOauth2Token end');
55
- isRefreshing = false;
56
- refreshSubscribers.forEach((subscriber) => subscriber(this.oauth2Token.access_token));
57
- refreshSubscribers = [];
58
- originalRequest.headers.Authorization = `Bearer ${this.oauth2Token.access_token}`;
59
- return this.client(originalRequest);
60
- }
61
- if (axios_1.default.isAxiosError(error)) {
62
- if (error === null || error === void 0 ? void 0 : error.response)
63
- this.handleError(error === null || error === void 0 ? void 0 : error.response);
64
- }
65
- throw error;
128
+ });
129
+ }); });
130
+ this.client.interceptors.request.use(function (config) { return __awaiter(_this, void 0, void 0, function () {
131
+ return __generator(this, function (_a) {
132
+ if (this.oauth2Token) {
133
+ config.headers.Authorization =
134
+ 'Bearer ' + this.oauth2Token.access_token;
135
+ }
136
+ return [2 /*return*/, config];
137
+ });
138
+ }); });
139
+ }
140
+ HttpClient.prototype.fetchOauthConsumer = function () {
141
+ return __awaiter(this, void 0, void 0, function () {
142
+ var response;
143
+ return __generator(this, function (_a) {
144
+ switch (_a.label) {
145
+ case 0: return [4 /*yield*/, axios_1.default.get(OAUTH_CONSUMER_URL)];
146
+ case 1:
147
+ response = _a.sent();
148
+ this.OAUTH_CONSUMER = {
149
+ key: response.data.consumer_key,
150
+ secret: response.data.consumer_secret
151
+ };
152
+ return [2 /*return*/];
153
+ }
154
+ });
66
155
  });
67
- this.client.interceptors.request.use(async (config) => {
68
- if (this.oauth2Token) {
69
- config.headers.Authorization =
70
- 'Bearer ' + this.oauth2Token.access_token;
71
- }
72
- return config;
156
+ };
157
+ HttpClient.prototype.checkTokenVaild = function () {
158
+ return __awaiter(this, void 0, void 0, function () {
159
+ return __generator(this, function (_a) {
160
+ switch (_a.label) {
161
+ case 0:
162
+ if (!this.oauth2Token) return [3 /*break*/, 2];
163
+ if (!(this.oauth2Token.expires_at < luxon_1.DateTime.now().toSeconds())) return [3 /*break*/, 2];
164
+ console.error('Token expired!');
165
+ return [4 /*yield*/, this.refreshOauth2Token()];
166
+ case 1:
167
+ _a.sent();
168
+ _a.label = 2;
169
+ case 2: return [2 /*return*/];
170
+ }
171
+ });
73
172
  });
74
- }
75
- async fetchOauthConsumer() {
76
- const response = await axios_1.default.get(OAUTH_CONSUMER_URL);
77
- this.OAUTH_CONSUMER = {
78
- key: response.data.consumer_key,
79
- secret: response.data.consumer_secret
80
- };
81
- }
82
- async checkTokenVaild() {
83
- if (this.oauth2Token) {
84
- if (this.oauth2Token.expires_at < luxon_1.DateTime.now().toSeconds()) {
85
- console.error('Token expired!');
86
- await this.refreshOauth2Token();
87
- }
88
- }
89
- }
90
- async get(url, config) {
91
- const response = await this.client.get(url, config);
92
- return response === null || response === void 0 ? void 0 : response.data;
93
- }
94
- async post(url, data, config) {
95
- const response = await this.client.post(url, data, config);
96
- return response === null || response === void 0 ? void 0 : response.data;
97
- }
98
- setCommonHeader(headers) {
99
- lodash_1.default.each(headers, (headerValue, key) => {
100
- this.client.defaults.headers.common[key] = headerValue;
173
+ };
174
+ HttpClient.prototype.get = function (url, config) {
175
+ return __awaiter(this, void 0, void 0, function () {
176
+ var response;
177
+ return __generator(this, function (_a) {
178
+ switch (_a.label) {
179
+ case 0: return [4 /*yield*/, this.client.get(url, config)];
180
+ case 1:
181
+ response = _a.sent();
182
+ return [2 /*return*/, response === null || response === void 0 ? void 0 : response.data];
183
+ }
184
+ });
101
185
  });
102
- }
103
- handleError(response) {
186
+ };
187
+ HttpClient.prototype.post = function (url, data, config) {
188
+ return __awaiter(this, void 0, void 0, function () {
189
+ var response;
190
+ return __generator(this, function (_a) {
191
+ switch (_a.label) {
192
+ case 0: return [4 /*yield*/, this.client.post(url, data, config)];
193
+ case 1:
194
+ response = _a.sent();
195
+ return [2 /*return*/, response === null || response === void 0 ? void 0 : response.data];
196
+ }
197
+ });
198
+ });
199
+ };
200
+ HttpClient.prototype.delete = function (url, config) {
201
+ return __awaiter(this, void 0, void 0, function () {
202
+ var response;
203
+ return __generator(this, function (_a) {
204
+ switch (_a.label) {
205
+ case 0: return [4 /*yield*/, this.client.post(url, null, __assign(__assign({}, config), { headers: __assign(__assign({}, config === null || config === void 0 ? void 0 : config.headers), { 'X-Http-Method-Override': 'DELETE' }) }))];
206
+ case 1:
207
+ response = _a.sent();
208
+ return [2 /*return*/, response === null || response === void 0 ? void 0 : response.data];
209
+ }
210
+ });
211
+ });
212
+ };
213
+ HttpClient.prototype.setCommonHeader = function (headers) {
214
+ var _this = this;
215
+ lodash_1.default.each(headers, function (headerValue, key) {
216
+ _this.client.defaults.headers.common[key] = headerValue;
217
+ });
218
+ };
219
+ HttpClient.prototype.handleError = function (response) {
104
220
  this.handleHttpError(response);
105
- }
106
- handleHttpError(response) {
107
- const { status, statusText, data } = response;
108
- const msg = `ERROR: (${status}), ${statusText}, ${JSON.stringify(data)}`;
221
+ };
222
+ HttpClient.prototype.handleHttpError = function (response) {
223
+ var status = response.status, statusText = response.statusText, data = response.data;
224
+ var msg = "ERROR: (".concat(status, "), ").concat(statusText, ", ").concat(JSON.stringify(data));
109
225
  console.error(msg);
110
226
  throw new Error(msg);
111
- }
227
+ };
112
228
  /**
113
229
  * Login to Garmin Connect
114
230
  * @param username
115
231
  * @param password
116
232
  * @returns {Promise<HttpClient>}
117
233
  */
118
- async login(username, password) {
119
- await this.fetchOauthConsumer();
120
- // Step1-3: Get ticket from page.
121
- const ticket = await this.getLoginTicket(username, password);
122
- // Step4: Oauth1
123
- const oauth1 = await this.getOauth1Token(ticket);
124
- // TODO: Handle MFA
125
- // Step 5: Oauth2
126
- await this.exchange(oauth1);
127
- return this;
128
- }
129
- async getLoginTicket(username, password) {
130
- // Step1: Set cookie
131
- const step1Params = {
132
- clientId: 'GarminConnect',
133
- locale: 'en',
134
- service: this.url.GC_MODERN
135
- };
136
- const step1Url = `${this.url.GARMIN_SSO_EMBED}?${qs_1.default.stringify(step1Params)}`;
137
- // console.log('login - step1Url:', step1Url);
138
- await this.client.get(step1Url);
139
- // Step2 Get _csrf
140
- const step2Params = {
141
- id: 'gauth-widget',
142
- embedWidget: true,
143
- locale: 'en',
144
- gauthHost: this.url.GARMIN_SSO_EMBED
145
- };
146
- const step2Url = `${this.url.SIGNIN_URL}?${qs_1.default.stringify(step2Params)}`;
147
- // console.log('login - step2Url:', step2Url);
148
- const step2Result = await this.get(step2Url);
149
- // console.log('login - step2Result:', step2Result)
150
- const csrfRegResult = CSRF_RE.exec(step2Result);
151
- if (!csrfRegResult) {
152
- throw new Error('login - csrf not found');
153
- }
154
- const csrf_token = csrfRegResult[1];
155
- // console.log('login - csrf:', csrf_token);
156
- // Step3 Get ticket
157
- const signinParams = {
158
- id: 'gauth-widget',
159
- embedWidget: true,
160
- clientId: 'GarminConnect',
161
- locale: 'en',
162
- gauthHost: this.url.GARMIN_SSO_EMBED,
163
- service: this.url.GARMIN_SSO_EMBED,
164
- source: this.url.GARMIN_SSO_EMBED,
165
- redirectAfterAccountLoginUrl: this.url.GARMIN_SSO_EMBED,
166
- redirectAfterAccountCreationUrl: this.url.GARMIN_SSO_EMBED
167
- };
168
- const step3Url = `${this.url.SIGNIN_URL}?${qs_1.default.stringify(signinParams)}`;
169
- // console.log('login - step3Url:', step3Url);
170
- const step3Form = new form_data_1.default();
171
- step3Form.append('username', username);
172
- step3Form.append('password', password);
173
- step3Form.append('embed', 'true');
174
- step3Form.append('_csrf', csrf_token);
175
- const step3Result = await this.post(step3Url, step3Form, {
176
- headers: {
177
- 'Content-Type': 'application/x-www-form-urlencoded',
178
- Dnt: 1,
179
- Origin: this.url.GARMIN_SSO_ORIGIN,
180
- Referer: this.url.SIGNIN_URL,
181
- 'User-Agent': USER_AGENT_BROWSER
182
- }
234
+ HttpClient.prototype.login = function (username, password) {
235
+ return __awaiter(this, void 0, void 0, function () {
236
+ var ticket, oauth1;
237
+ return __generator(this, function (_a) {
238
+ switch (_a.label) {
239
+ case 0: return [4 /*yield*/, this.fetchOauthConsumer()];
240
+ case 1:
241
+ _a.sent();
242
+ return [4 /*yield*/, this.getLoginTicket(username, password)];
243
+ case 2:
244
+ ticket = _a.sent();
245
+ return [4 /*yield*/, this.getOauth1Token(ticket)];
246
+ case 3:
247
+ oauth1 = _a.sent();
248
+ // TODO: Handle MFA
249
+ // Step 5: Oauth2
250
+ return [4 /*yield*/, this.exchange(oauth1)];
251
+ case 4:
252
+ // TODO: Handle MFA
253
+ // Step 5: Oauth2
254
+ _a.sent();
255
+ return [2 /*return*/, this];
256
+ }
257
+ });
183
258
  });
184
- // console.log('step3Result:', step3Result)
185
- this.handleAccountLocked(step3Result);
186
- this.handleMFA(step3Result);
187
- const ticketRegResult = TICKET_RE.exec(step3Result);
188
- if (!ticketRegResult) {
189
- throw new Error('login failed (Ticket not found or MFA), please check username and password');
190
- }
191
- const ticket = ticketRegResult[1];
192
- return ticket;
193
- }
259
+ };
260
+ HttpClient.prototype.getLoginTicket = function (username, password) {
261
+ return __awaiter(this, void 0, void 0, function () {
262
+ var step1Params, step1Url, step2Params, step2Url, step2Result, csrfRegResult, csrf_token, signinParams, step3Url, step3Form, step3Result, ticketRegResult, ticket;
263
+ return __generator(this, function (_a) {
264
+ switch (_a.label) {
265
+ case 0:
266
+ step1Params = {
267
+ clientId: 'GarminConnect',
268
+ locale: 'en',
269
+ service: this.url.GC_MODERN
270
+ };
271
+ step1Url = "".concat(this.url.GARMIN_SSO_EMBED, "?").concat(qs_1.default.stringify(step1Params));
272
+ // console.log('login - step1Url:', step1Url);
273
+ return [4 /*yield*/, this.client.get(step1Url)];
274
+ case 1:
275
+ // console.log('login - step1Url:', step1Url);
276
+ _a.sent();
277
+ step2Params = {
278
+ id: 'gauth-widget',
279
+ embedWidget: true,
280
+ locale: 'en',
281
+ gauthHost: this.url.GARMIN_SSO_EMBED
282
+ };
283
+ step2Url = "".concat(this.url.SIGNIN_URL, "?").concat(qs_1.default.stringify(step2Params));
284
+ return [4 /*yield*/, this.get(step2Url)];
285
+ case 2:
286
+ step2Result = _a.sent();
287
+ csrfRegResult = CSRF_RE.exec(step2Result);
288
+ if (!csrfRegResult) {
289
+ throw new Error('login - csrf not found');
290
+ }
291
+ csrf_token = csrfRegResult[1];
292
+ signinParams = {
293
+ id: 'gauth-widget',
294
+ embedWidget: true,
295
+ clientId: 'GarminConnect',
296
+ locale: 'en',
297
+ gauthHost: this.url.GARMIN_SSO_EMBED,
298
+ service: this.url.GARMIN_SSO_EMBED,
299
+ source: this.url.GARMIN_SSO_EMBED,
300
+ redirectAfterAccountLoginUrl: this.url.GARMIN_SSO_EMBED,
301
+ redirectAfterAccountCreationUrl: this.url.GARMIN_SSO_EMBED
302
+ };
303
+ step3Url = "".concat(this.url.SIGNIN_URL, "?").concat(qs_1.default.stringify(signinParams));
304
+ step3Form = new form_data_1.default();
305
+ step3Form.append('username', username);
306
+ step3Form.append('password', password);
307
+ step3Form.append('embed', 'true');
308
+ step3Form.append('_csrf', csrf_token);
309
+ return [4 /*yield*/, this.post(step3Url, step3Form, {
310
+ headers: {
311
+ 'Content-Type': 'application/x-www-form-urlencoded',
312
+ Dnt: 1,
313
+ Origin: this.url.GARMIN_SSO_ORIGIN,
314
+ Referer: this.url.SIGNIN_URL,
315
+ 'User-Agent': USER_AGENT_BROWSER
316
+ }
317
+ })];
318
+ case 3:
319
+ step3Result = _a.sent();
320
+ // console.log('step3Result:', step3Result)
321
+ this.handleAccountLocked(step3Result);
322
+ this.handlePageTitle(step3Result);
323
+ this.handleMFA(step3Result);
324
+ ticketRegResult = TICKET_RE.exec(step3Result);
325
+ if (!ticketRegResult) {
326
+ throw new Error('login failed (Ticket not found or MFA), please check username and password');
327
+ }
328
+ ticket = ticketRegResult[1];
329
+ return [2 /*return*/, ticket];
330
+ }
331
+ });
332
+ });
333
+ };
194
334
  // TODO: Handle MFA
195
- handleMFA(htmlStr) { }
196
- handleAccountLocked(htmlStr) {
197
- const accountLockedRegResult = ACCOUNT_LOCKED_RE.exec(htmlStr);
335
+ HttpClient.prototype.handleMFA = function (htmlStr) { };
336
+ // TODO: Handle Phone number
337
+ HttpClient.prototype.handlePageTitle = function (htmlStr) {
338
+ var pageTitileRegResult = PAGE_TITLE_RE.exec(htmlStr);
339
+ if (pageTitileRegResult) {
340
+ var title = pageTitileRegResult[1];
341
+ console.log('login page title:', title);
342
+ if (lodash_1.default.includes(title, 'Update Phone Number')) {
343
+ // current I don't know where to update it
344
+ // See: https://github.com/matin/garth/issues/19
345
+ throw new Error("login failed (Update Phone number), please update your phone number, currently I don't know where to update it");
346
+ }
347
+ }
348
+ };
349
+ HttpClient.prototype.handleAccountLocked = function (htmlStr) {
350
+ var accountLockedRegResult = ACCOUNT_LOCKED_RE.exec(htmlStr);
198
351
  if (accountLockedRegResult) {
199
- const msg = accountLockedRegResult[1];
352
+ var msg = accountLockedRegResult[1];
200
353
  console.error(msg);
201
354
  throw new Error('login failed (AccountLocked), please open connect web page to unlock your account');
202
355
  }
203
- }
204
- async refreshOauth2Token() {
205
- if (!this.OAUTH_CONSUMER) {
206
- await this.fetchOauthConsumer();
207
- }
208
- if (!this.oauth2Token || !this.oauth1Token) {
209
- throw new Error('No Oauth2Token or Oauth1Token');
210
- }
211
- const oauth1 = {
212
- oauth: this.getOauthClient(this.OAUTH_CONSUMER),
213
- token: this.oauth1Token
214
- };
215
- await this.exchange(oauth1);
216
- console.log('Oauth2 token refreshed!');
217
- }
218
- async getOauth1Token(ticket) {
219
- if (!this.OAUTH_CONSUMER) {
220
- throw new Error('No OAUTH_CONSUMER');
221
- }
222
- const params = {
223
- ticket,
224
- 'login-url': this.url.GARMIN_SSO_EMBED,
225
- 'accepts-mfa-tokens': true
226
- };
227
- const url = `${this.url.OAUTH_URL}/preauthorized?${qs_1.default.stringify(params)}`;
228
- const oauth = this.getOauthClient(this.OAUTH_CONSUMER);
229
- const step4RequestData = {
230
- url: url,
231
- method: 'GET'
232
- };
233
- const headers = oauth.toHeader(oauth.authorize(step4RequestData));
234
- // console.log('getOauth1Token - headers:', headers);
235
- const response = await this.get(url, {
236
- headers: {
237
- ...headers,
238
- 'User-Agent': USER_AGENT_CONNECTMOBILE
239
- }
356
+ };
357
+ HttpClient.prototype.refreshOauth2Token = function () {
358
+ return __awaiter(this, void 0, void 0, function () {
359
+ var oauth1;
360
+ return __generator(this, function (_a) {
361
+ switch (_a.label) {
362
+ case 0:
363
+ if (!!this.OAUTH_CONSUMER) return [3 /*break*/, 2];
364
+ return [4 /*yield*/, this.fetchOauthConsumer()];
365
+ case 1:
366
+ _a.sent();
367
+ _a.label = 2;
368
+ case 2:
369
+ if (!this.oauth2Token || !this.oauth1Token) {
370
+ throw new Error('No Oauth2Token or Oauth1Token');
371
+ }
372
+ oauth1 = {
373
+ oauth: this.getOauthClient(this.OAUTH_CONSUMER),
374
+ token: this.oauth1Token
375
+ };
376
+ return [4 /*yield*/, this.exchange(oauth1)];
377
+ case 3:
378
+ _a.sent();
379
+ console.log('Oauth2 token refreshed!');
380
+ return [2 /*return*/];
381
+ }
382
+ });
240
383
  });
241
- // console.log('getOauth1Token - response:', response);
242
- const token = qs_1.default.parse(response);
243
- // console.log('getOauth1Token - token:', token);
244
- this.oauth1Token = token;
245
- return { token, oauth };
246
- }
247
- getOauthClient(consumer) {
248
- const oauth = new oauth_1_0a_1.default({
384
+ };
385
+ HttpClient.prototype.getOauth1Token = function (ticket) {
386
+ return __awaiter(this, void 0, void 0, function () {
387
+ var params, url, oauth, step4RequestData, headers, response, token;
388
+ return __generator(this, function (_a) {
389
+ switch (_a.label) {
390
+ case 0:
391
+ if (!this.OAUTH_CONSUMER) {
392
+ throw new Error('No OAUTH_CONSUMER');
393
+ }
394
+ params = {
395
+ ticket: ticket,
396
+ 'login-url': this.url.GARMIN_SSO_EMBED,
397
+ 'accepts-mfa-tokens': true
398
+ };
399
+ url = "".concat(this.url.OAUTH_URL, "/preauthorized?").concat(qs_1.default.stringify(params));
400
+ oauth = this.getOauthClient(this.OAUTH_CONSUMER);
401
+ step4RequestData = {
402
+ url: url,
403
+ method: 'GET'
404
+ };
405
+ headers = oauth.toHeader(oauth.authorize(step4RequestData));
406
+ return [4 /*yield*/, this.get(url, {
407
+ headers: __assign(__assign({}, headers), { 'User-Agent': USER_AGENT_CONNECTMOBILE })
408
+ })];
409
+ case 1:
410
+ response = _a.sent();
411
+ token = qs_1.default.parse(response);
412
+ // console.log('getOauth1Token - token:', token);
413
+ this.oauth1Token = token;
414
+ return [2 /*return*/, { token: token, oauth: oauth }];
415
+ }
416
+ });
417
+ });
418
+ };
419
+ HttpClient.prototype.getOauthClient = function (consumer) {
420
+ var oauth = new oauth_1_0a_1.default({
249
421
  consumer: consumer,
250
422
  signature_method: 'HMAC-SHA1',
251
- hash_function(base_string, key) {
423
+ hash_function: function (base_string, key) {
252
424
  return crypto
253
425
  .createHmac('sha1', key)
254
426
  .update(base_string)
@@ -256,36 +428,44 @@ class HttpClient {
256
428
  }
257
429
  });
258
430
  return oauth;
259
- }
431
+ };
260
432
  //
261
- async exchange(oauth1) {
262
- const token = {
263
- key: oauth1.token.oauth_token,
264
- secret: oauth1.token.oauth_token_secret
265
- };
266
- // console.log('exchange - token:', token);
267
- const baseUrl = `${this.url.OAUTH_URL}/exchange/user/2.0`;
268
- const requestData = {
269
- url: baseUrl,
270
- method: 'POST',
271
- data: null
272
- };
273
- const step5AuthData = oauth1.oauth.authorize(requestData, token);
274
- // console.log('login - step5AuthData:', step5AuthData);
275
- const url = `${baseUrl}?${qs_1.default.stringify(step5AuthData)}`;
276
- // console.log('exchange - url:', url);
277
- this.oauth2Token = undefined;
278
- const response = await this.post(url, null, {
279
- headers: {
280
- 'User-Agent': USER_AGENT_CONNECTMOBILE,
281
- 'Content-Type': 'application/x-www-form-urlencoded'
282
- }
433
+ HttpClient.prototype.exchange = function (oauth1) {
434
+ return __awaiter(this, void 0, void 0, function () {
435
+ var token, baseUrl, requestData, step5AuthData, url, response;
436
+ return __generator(this, function (_a) {
437
+ switch (_a.label) {
438
+ case 0:
439
+ token = {
440
+ key: oauth1.token.oauth_token,
441
+ secret: oauth1.token.oauth_token_secret
442
+ };
443
+ baseUrl = "".concat(this.url.OAUTH_URL, "/exchange/user/2.0");
444
+ requestData = {
445
+ url: baseUrl,
446
+ method: 'POST',
447
+ data: null
448
+ };
449
+ step5AuthData = oauth1.oauth.authorize(requestData, token);
450
+ url = "".concat(baseUrl, "?").concat(qs_1.default.stringify(step5AuthData));
451
+ // console.log('exchange - url:', url);
452
+ this.oauth2Token = undefined;
453
+ return [4 /*yield*/, this.post(url, null, {
454
+ headers: {
455
+ 'User-Agent': USER_AGENT_CONNECTMOBILE,
456
+ 'Content-Type': 'application/x-www-form-urlencoded'
457
+ }
458
+ })];
459
+ case 1:
460
+ response = _a.sent();
461
+ // console.log('exchange - response:', response);
462
+ this.oauth2Token = this.setOauth2TokenExpiresAt(response);
463
+ return [2 /*return*/];
464
+ }
465
+ });
283
466
  });
284
- // console.log('exchange - response:', response);
285
- this.oauth2Token = this.setOauth2TokenExpiresAt(response);
286
- // console.log('exchange - oauth2Token:', this.oauth2Token);
287
- }
288
- setOauth2TokenExpiresAt(token) {
467
+ };
468
+ HttpClient.prototype.setOauth2TokenExpiresAt = function (token) {
289
469
  // human readable date
290
470
  token['last_update_date'] = luxon_1.DateTime.now().toLocal().toString();
291
471
  token['expires_date'] = luxon_1.DateTime.fromSeconds(luxon_1.DateTime.now().toSeconds() + token['expires_in'])
@@ -296,7 +476,8 @@ class HttpClient {
296
476
  token['refresh_token_expires_at'] =
297
477
  luxon_1.DateTime.now().toSeconds() + token['refresh_token_expires_in'];
298
478
  return token;
299
- }
300
- }
479
+ };
480
+ return HttpClient;
481
+ }());
301
482
  exports.HttpClient = HttpClient;
302
483
  //# sourceMappingURL=HttpClient.js.map