@gooin/garmin-connect 1.6.5 → 1.6.7

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.
Files changed (63) hide show
  1. package/README.md +243 -49
  2. package/dist/common/HttpClient.d.ts +1 -0
  3. package/dist/common/HttpClient.js +271 -421
  4. package/dist/common/HttpClient.js.map +1 -1
  5. package/dist/garmin/GarminConnect copy.d.ts +76 -0
  6. package/dist/garmin/GarminConnect copy.js +397 -0
  7. package/dist/garmin/GarminConnect copy.js.map +1 -0
  8. package/dist/garmin/GarminConnect.d.ts +35 -5
  9. package/dist/garmin/GarminConnect.js +404 -322
  10. package/dist/garmin/GarminConnect.js.map +1 -1
  11. package/dist/garmin/GarminConnect_new.d.ts +76 -0
  12. package/dist/garmin/GarminConnect_new.js +397 -0
  13. package/dist/garmin/GarminConnect_new.js.map +1 -0
  14. package/dist/garmin/GarminConnect_old.d.ts +75 -0
  15. package/dist/garmin/GarminConnect_old.js +386 -0
  16. package/dist/garmin/GarminConnect_old.js.map +1 -0
  17. package/dist/garmin/UrlClass.d.ts +16 -1
  18. package/dist/garmin/UrlClass.js +114 -139
  19. package/dist/garmin/UrlClass.js.map +1 -1
  20. package/dist/garmin/Urls copy.d.ts +66 -0
  21. package/dist/garmin/Urls copy.js +109 -0
  22. package/dist/garmin/Urls copy.js.map +1 -0
  23. package/dist/garmin/common/DateUtils.d.ts +6 -0
  24. package/dist/garmin/common/DateUtils.js +38 -0
  25. package/dist/garmin/common/DateUtils.js.map +1 -0
  26. package/dist/garmin/common/HydrationUtils.d.ts +2 -0
  27. package/dist/garmin/common/HydrationUtils.js +16 -0
  28. package/dist/garmin/common/HydrationUtils.js.map +1 -0
  29. package/dist/garmin/common/WeightUtils.d.ts +1 -0
  30. package/dist/garmin/common/WeightUtils.js +9 -0
  31. package/dist/garmin/common/WeightUtils.js.map +1 -0
  32. package/dist/garmin/types/activity.d.ts +408 -0
  33. package/dist/garmin/types/activity.js +27 -0
  34. package/dist/garmin/types/activity.js.map +1 -0
  35. package/dist/garmin/types/course.d.ts +146 -0
  36. package/dist/garmin/types/course.js +3 -0
  37. package/dist/garmin/types/course.js.map +1 -0
  38. package/dist/garmin/types/golf.d.ts +67 -0
  39. package/dist/garmin/types/golf.js +3 -0
  40. package/dist/garmin/types/golf.js.map +1 -0
  41. package/dist/garmin/types/heartrate.d.ts +22 -0
  42. package/dist/garmin/types/heartrate.js +2 -0
  43. package/dist/garmin/types/heartrate.js.map +1 -0
  44. package/dist/garmin/types/hydration.d.ts +28 -0
  45. package/dist/garmin/types/hydration.js +2 -0
  46. package/dist/garmin/types/hydration.js.map +1 -0
  47. package/dist/garmin/types/index.d.ts +541 -0
  48. package/dist/garmin/types/index.js +17 -0
  49. package/dist/garmin/types/index.js.map +1 -0
  50. package/dist/garmin/types/sleep.d.ts +120 -0
  51. package/dist/garmin/types/sleep.js +3 -0
  52. package/dist/garmin/types/sleep.js.map +1 -0
  53. package/dist/garmin/types/weight.d.ts +42 -0
  54. package/dist/garmin/types/weight.js +3 -0
  55. package/dist/garmin/types/weight.js.map +1 -0
  56. package/dist/garmin/types.d.ts +107 -0
  57. package/dist/garmin/workouts/Running.js +36 -53
  58. package/dist/garmin/workouts/Running.js.map +1 -1
  59. package/dist/utils.d.ts +3 -3
  60. package/dist/utils.js +27 -36
  61. package/dist/utils.js.map +1 -1
  62. package/examples/example.js +1 -1
  63. package/package.json +1 -1
@@ -1,471 +1,322 @@
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
- };
49
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
50
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
51
4
  };
52
5
  Object.defineProperty(exports, "__esModule", { value: true });
53
6
  exports.HttpClient = void 0;
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';
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 node_crypto_1 = __importDefault(require("node: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 PAGE_TITLE_RE = new RegExp('<title>([^<]*)</title>');
18
+ const USER_AGENT_CONNECTMOBILE = 'com.garmin.android.apps.connectmobile';
19
+ const 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';
20
+ const OAUTH_CONSUMER_URL = 'https://thegarth.s3.amazonaws.com/oauth_consumer.json';
68
21
  // refresh token
69
- var isRefreshing = false;
70
- var refreshSubscribers = [];
71
- var HttpClient = /** @class */ (function () {
72
- function HttpClient(url) {
73
- var _this = this;
22
+ let isRefreshing = false;
23
+ let refreshSubscribers = [];
24
+ class HttpClient {
25
+ constructor(url) {
74
26
  this.url = url;
75
27
  this.client = axios_1.default.create();
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;
28
+ this.client.interceptors.response.use((response) => response, async (error) => {
79
29
  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);
117
- });
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;
30
+ const originalRequest = error.config;
31
+ // console.log('originalRequest:', originalRequest)
32
+ // Auto Refresh token
33
+ if (((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) === 401 &&
34
+ !(originalRequest === null || originalRequest === void 0 ? void 0 : originalRequest._retry)) {
35
+ if (!this.oauth2Token) {
36
+ return;
127
37
  }
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
- });
155
- });
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
- });
172
- });
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];
38
+ if (isRefreshing) {
39
+ try {
40
+ const token = await new Promise((resolve) => {
41
+ refreshSubscribers.push((token) => {
42
+ resolve(token);
43
+ });
44
+ });
45
+ originalRequest.headers.Authorization = `Bearer ${token}`;
46
+ return this.client(originalRequest);
47
+ }
48
+ catch (err) {
49
+ console.log('err:', err);
50
+ return Promise.reject(err);
51
+ }
183
52
  }
184
- });
53
+ originalRequest._retry = true;
54
+ isRefreshing = true;
55
+ // console.log('interceptors: refreshOauth2Token start');
56
+ await this.refreshOauth2Token();
57
+ // console.log('interceptors: refreshOauth2Token end');
58
+ isRefreshing = false;
59
+ refreshSubscribers.forEach((subscriber) => subscriber(this.oauth2Token.access_token));
60
+ refreshSubscribers = [];
61
+ originalRequest.headers.Authorization = `Bearer ${this.oauth2Token.access_token}`;
62
+ return this.client(originalRequest);
63
+ }
64
+ if (axios_1.default.isAxiosError(error)) {
65
+ if (error === null || error === void 0 ? void 0 : error.response)
66
+ this.handleError(error === null || error === void 0 ? void 0 : error.response);
67
+ }
68
+ throw error;
185
69
  });
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
- });
70
+ this.client.interceptors.request.use(async (config) => {
71
+ if (this.oauth2Token) {
72
+ config.headers.Authorization =
73
+ 'Bearer ' + this.oauth2Token.access_token;
74
+ }
75
+ return config;
198
76
  });
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
- });
77
+ }
78
+ async fetchOauthConsumer() {
79
+ const response = await axios_1.default.get(OAUTH_CONSUMER_URL);
80
+ this.OAUTH_CONSUMER = {
81
+ key: response.data.consumer_key,
82
+ secret: response.data.consumer_secret
83
+ };
84
+ }
85
+ async checkTokenVaild() {
86
+ if (this.oauth2Token) {
87
+ if (this.oauth2Token.expires_at < luxon_1.DateTime.now().toSeconds()) {
88
+ console.error('Token expired!');
89
+ await this.refreshOauth2Token();
90
+ }
91
+ }
92
+ }
93
+ async get(url, config) {
94
+ const response = await this.client.get(url, config);
95
+ return response === null || response === void 0 ? void 0 : response.data;
96
+ }
97
+ async post(url, data, config) {
98
+ const response = await this.client.post(url, data, config);
99
+ return response === null || response === void 0 ? void 0 : response.data;
100
+ }
101
+ async put(url, data, config) {
102
+ const response = await this.client.put(url, data, config);
103
+ return response === null || response === void 0 ? void 0 : response.data;
104
+ }
105
+ async delete(url, config) {
106
+ const response = await this.client.post(url, null, {
107
+ ...config,
108
+ headers: {
109
+ ...config === null || config === void 0 ? void 0 : config.headers,
110
+ 'X-Http-Method-Override': 'DELETE'
111
+ }
211
112
  });
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;
113
+ return response === null || response === void 0 ? void 0 : response.data;
114
+ }
115
+ setCommonHeader(headers) {
116
+ lodash_1.default.each(headers, (headerValue, key) => {
117
+ this.client.defaults.headers.common[key] = headerValue;
217
118
  });
218
- };
219
- HttpClient.prototype.handleError = function (response) {
119
+ }
120
+ handleError(response) {
220
121
  this.handleHttpError(response);
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));
122
+ }
123
+ handleHttpError(response) {
124
+ const { status, statusText, data } = response;
125
+ const msg = `ERROR: (${status}), ${statusText}, ${JSON.stringify(data)}`;
225
126
  console.error(msg);
226
127
  throw new Error(msg);
227
- };
128
+ }
228
129
  /**
229
130
  * Login to Garmin Connect
230
131
  * @param username
231
132
  * @param password
232
133
  * @returns {Promise<HttpClient>}
233
134
  */
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
- });
258
- });
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
- });
135
+ async login(username, password) {
136
+ await this.fetchOauthConsumer();
137
+ // Step1-3: Get ticket from page.
138
+ const ticket = await this.getLoginTicket(username, password);
139
+ // Step4: Oauth1
140
+ const oauth1 = await this.getOauth1Token(ticket);
141
+ // TODO: Handle MFA
142
+ // Step 5: Oauth2
143
+ await this.exchange(oauth1);
144
+ return this;
145
+ }
146
+ async getLoginTicket(username, password) {
147
+ // Step1: Set cookie
148
+ const step1Params = {
149
+ clientId: 'GarminConnect',
150
+ locale: 'en',
151
+ service: this.url.GC_MODERN
152
+ };
153
+ const step1Url = `${this.url.GARMIN_SSO_EMBED}?${qs_1.default.stringify(step1Params)}`;
154
+ // console.log('login - step1Url:', step1Url);
155
+ await this.client.get(step1Url);
156
+ // Step2 Get _csrf
157
+ const step2Params = {
158
+ id: 'gauth-widget',
159
+ embedWidget: true,
160
+ locale: 'en',
161
+ gauthHost: this.url.GARMIN_SSO_EMBED
162
+ };
163
+ const step2Url = `${this.url.SIGNIN_URL}?${qs_1.default.stringify(step2Params)}`;
164
+ // console.log('login - step2Url:', step2Url);
165
+ const step2Result = await this.get(step2Url);
166
+ // console.log('login - step2Result:', step2Result)
167
+ const csrfRegResult = CSRF_RE.exec(step2Result);
168
+ if (!csrfRegResult) {
169
+ throw new Error('login - csrf not found');
170
+ }
171
+ const csrf_token = csrfRegResult[1];
172
+ // console.log('login - csrf:', csrf_token);
173
+ // Step3 Get ticket
174
+ const signinParams = {
175
+ id: 'gauth-widget',
176
+ embedWidget: true,
177
+ clientId: 'GarminConnect',
178
+ locale: 'en',
179
+ gauthHost: this.url.GARMIN_SSO_EMBED,
180
+ service: this.url.GARMIN_SSO_EMBED,
181
+ source: this.url.GARMIN_SSO_EMBED,
182
+ redirectAfterAccountLoginUrl: this.url.GARMIN_SSO_EMBED,
183
+ redirectAfterAccountCreationUrl: this.url.GARMIN_SSO_EMBED
184
+ };
185
+ const step3Url = `${this.url.SIGNIN_URL}?${qs_1.default.stringify(signinParams)}`;
186
+ // console.log('login - step3Url:', step3Url);
187
+ const step3Form = new form_data_1.default();
188
+ step3Form.append('username', username);
189
+ step3Form.append('password', password);
190
+ step3Form.append('embed', 'true');
191
+ step3Form.append('_csrf', csrf_token);
192
+ const step3Result = await this.post(step3Url, step3Form, {
193
+ headers: {
194
+ 'Content-Type': 'application/x-www-form-urlencoded',
195
+ Dnt: 1,
196
+ Origin: this.url.GARMIN_SSO_ORIGIN,
197
+ Referer: this.url.SIGNIN_URL,
198
+ 'User-Agent': USER_AGENT_BROWSER
199
+ }
332
200
  });
333
- };
201
+ // console.log('step3Result:', step3Result)
202
+ this.handleAccountLocked(step3Result);
203
+ this.handlePageTitle(step3Result);
204
+ this.handleMFA(step3Result);
205
+ const ticketRegResult = TICKET_RE.exec(step3Result);
206
+ if (!ticketRegResult) {
207
+ throw new Error('login failed (Ticket not found or MFA), please check username and password');
208
+ }
209
+ const ticket = ticketRegResult[1];
210
+ return ticket;
211
+ }
334
212
  // TODO: Handle MFA
335
- HttpClient.prototype.handleMFA = function (htmlStr) { };
213
+ handleMFA(htmlStr) { }
336
214
  // TODO: Handle Phone number
337
- HttpClient.prototype.handlePageTitle = function (htmlStr) {
338
- var pageTitileRegResult = PAGE_TITLE_RE.exec(htmlStr);
215
+ handlePageTitle(htmlStr) {
216
+ const pageTitileRegResult = PAGE_TITLE_RE.exec(htmlStr);
339
217
  if (pageTitileRegResult) {
340
- var title = pageTitileRegResult[1];
218
+ const title = pageTitileRegResult[1];
341
219
  console.log('login page title:', title);
342
220
  if (lodash_1.default.includes(title, 'Update Phone Number')) {
343
221
  // current I don't know where to update it
344
222
  // 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");
223
+ throw new Error('login failed (Update Phone number), please update your phone number, See: https://github.com/matin/garth/issues/19');
346
224
  }
347
225
  }
348
- };
349
- HttpClient.prototype.handleAccountLocked = function (htmlStr) {
350
- var accountLockedRegResult = ACCOUNT_LOCKED_RE.exec(htmlStr);
226
+ }
227
+ handleAccountLocked(htmlStr) {
228
+ const accountLockedRegResult = ACCOUNT_LOCKED_RE.exec(htmlStr);
351
229
  if (accountLockedRegResult) {
352
- var msg = accountLockedRegResult[1];
230
+ const msg = accountLockedRegResult[1];
353
231
  console.error(msg);
354
232
  throw new Error('login failed (AccountLocked), please open connect web page to unlock your account');
355
233
  }
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
- });
383
- });
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
- });
234
+ }
235
+ async refreshOauth2Token() {
236
+ if (!this.OAUTH_CONSUMER) {
237
+ await this.fetchOauthConsumer();
238
+ }
239
+ if (!this.oauth2Token || !this.oauth1Token) {
240
+ throw new Error('No Oauth2Token or Oauth1Token');
241
+ }
242
+ const oauth1 = {
243
+ oauth: this.getOauthClient(this.OAUTH_CONSUMER),
244
+ token: this.oauth1Token
245
+ };
246
+ await this.exchange(oauth1);
247
+ console.log('Oauth2 token refreshed!');
248
+ }
249
+ async getOauth1Token(ticket) {
250
+ if (!this.OAUTH_CONSUMER) {
251
+ throw new Error('No OAUTH_CONSUMER');
252
+ }
253
+ const params = {
254
+ ticket,
255
+ 'login-url': this.url.GARMIN_SSO_EMBED,
256
+ 'accepts-mfa-tokens': true
257
+ };
258
+ const url = `${this.url.OAUTH_URL}/preauthorized?${qs_1.default.stringify(params)}`;
259
+ const oauth = this.getOauthClient(this.OAUTH_CONSUMER);
260
+ const step4RequestData = {
261
+ url: url,
262
+ method: 'GET'
263
+ };
264
+ const headers = oauth.toHeader(oauth.authorize(step4RequestData));
265
+ // console.log('getOauth1Token - headers:', headers);
266
+ const response = await this.get(url, {
267
+ headers: {
268
+ ...headers,
269
+ 'User-Agent': USER_AGENT_CONNECTMOBILE
270
+ }
417
271
  });
418
- };
419
- HttpClient.prototype.getOauthClient = function (consumer) {
420
- var oauth = new oauth_1_0a_1.default({
272
+ // console.log('getOauth1Token - response:', response);
273
+ const token = qs_1.default.parse(response);
274
+ // console.log('getOauth1Token - token:', token);
275
+ this.oauth1Token = token;
276
+ return { token, oauth };
277
+ }
278
+ getOauthClient(consumer) {
279
+ const oauth = new oauth_1_0a_1.default({
421
280
  consumer: consumer,
422
281
  signature_method: 'HMAC-SHA1',
423
- hash_function: function (base_string, key) {
424
- return crypto
282
+ hash_function(base_string, key) {
283
+ return node_crypto_1.default
425
284
  .createHmac('sha1', key)
426
285
  .update(base_string)
427
286
  .digest('base64');
428
287
  }
429
288
  });
430
289
  return oauth;
431
- };
290
+ }
432
291
  //
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
- });
292
+ async exchange(oauth1) {
293
+ const token = {
294
+ key: oauth1.token.oauth_token,
295
+ secret: oauth1.token.oauth_token_secret
296
+ };
297
+ // console.log('exchange - token:', token);
298
+ const baseUrl = `${this.url.OAUTH_URL}/exchange/user/2.0`;
299
+ const requestData = {
300
+ url: baseUrl,
301
+ method: 'POST',
302
+ data: null
303
+ };
304
+ const step5AuthData = oauth1.oauth.authorize(requestData, token);
305
+ // console.log('login - step5AuthData:', step5AuthData);
306
+ const url = `${baseUrl}?${qs_1.default.stringify(step5AuthData)}`;
307
+ // console.log('exchange - url:', url);
308
+ this.oauth2Token = undefined;
309
+ const response = await this.post(url, null, {
310
+ headers: {
311
+ 'User-Agent': USER_AGENT_CONNECTMOBILE,
312
+ 'Content-Type': 'application/x-www-form-urlencoded'
313
+ }
466
314
  });
467
- };
468
- HttpClient.prototype.setOauth2TokenExpiresAt = function (token) {
315
+ // console.log('exchange - response:', response);
316
+ this.oauth2Token = this.setOauth2TokenExpiresAt(response);
317
+ // console.log('exchange - oauth2Token:', this.oauth2Token);
318
+ }
319
+ setOauth2TokenExpiresAt(token) {
469
320
  // human readable date
470
321
  token['last_update_date'] = luxon_1.DateTime.now().toLocal().toString();
471
322
  token['expires_date'] = luxon_1.DateTime.fromSeconds(luxon_1.DateTime.now().toSeconds() + token['expires_in'])
@@ -476,8 +327,7 @@ var HttpClient = /** @class */ (function () {
476
327
  token['refresh_token_expires_at'] =
477
328
  luxon_1.DateTime.now().toSeconds() + token['refresh_token_expires_in'];
478
329
  return token;
479
- };
480
- return HttpClient;
481
- }());
330
+ }
331
+ }
482
332
  exports.HttpClient = HttpClient;
483
333
  //# sourceMappingURL=HttpClient.js.map