@kaiord/garmin-connect 4.8.1 → 5.0.0
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.
- package/dist/index.js +116 -116
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -10,6 +10,45 @@ import { join, dirname } from 'path';
|
|
|
10
10
|
|
|
11
11
|
// src/adapters/auth/garmin-auth-provider.ts
|
|
12
12
|
var createCookieFetch = () => fetchCookie(globalThis.fetch);
|
|
13
|
+
var makeRequest = (url, init, refresh, fetchFn) => {
|
|
14
|
+
if (!refresh.state.oauth2Token) {
|
|
15
|
+
throw createServiceApiError("Token unavailable after refresh", 401);
|
|
16
|
+
}
|
|
17
|
+
return fetchFn(url, {
|
|
18
|
+
...init,
|
|
19
|
+
headers: {
|
|
20
|
+
...init?.headers,
|
|
21
|
+
Authorization: `Bearer ${refresh.state.oauth2Token.access_token}`
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
var authFetch = async (url, init, refresh, fetchFn) => {
|
|
26
|
+
if (!refresh.state.oauth2Token) {
|
|
27
|
+
throw createServiceApiError("Not authenticated", 401);
|
|
28
|
+
}
|
|
29
|
+
if (refresh.state.oauth2Token.expires_at < Math.floor(Date.now() / 1e3)) {
|
|
30
|
+
await refresh.ensureFreshToken();
|
|
31
|
+
}
|
|
32
|
+
const res = await makeRequest(url, init, refresh, fetchFn);
|
|
33
|
+
if (res.status === 401) {
|
|
34
|
+
await refresh.ensureFreshToken();
|
|
35
|
+
const retry = await makeRequest(url, init, refresh, fetchFn);
|
|
36
|
+
if (!retry.ok) {
|
|
37
|
+
throw createServiceApiError(
|
|
38
|
+
`API request failed after token refresh: ${retry.statusText}`,
|
|
39
|
+
retry.status
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return retry;
|
|
43
|
+
}
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
throw createServiceApiError(
|
|
46
|
+
`API request failed: ${res.statusText}`,
|
|
47
|
+
res.status
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
return res;
|
|
51
|
+
};
|
|
13
52
|
|
|
14
53
|
// src/adapters/http/urls.ts
|
|
15
54
|
var GARMIN_SSO_ORIGIN = "https://sso.garmin.com";
|
|
@@ -213,6 +252,25 @@ var garminSso = async (username, password, logger, fetchFn) => {
|
|
|
213
252
|
};
|
|
214
253
|
|
|
215
254
|
// src/adapters/http/token-refresh.ts
|
|
255
|
+
var notifyAll = (subs, token) => {
|
|
256
|
+
subs.forEach((s) => s.resolve(token));
|
|
257
|
+
};
|
|
258
|
+
var rejectAll = (subs, error) => {
|
|
259
|
+
subs.forEach((s) => s.reject(error));
|
|
260
|
+
};
|
|
261
|
+
var getOrFetchConsumer = async (state, fetchFn) => {
|
|
262
|
+
if (state.consumer) return state.consumer;
|
|
263
|
+
state.consumer = await fetchOAuthConsumer(fetchFn);
|
|
264
|
+
return state.consumer;
|
|
265
|
+
};
|
|
266
|
+
var doRefreshToken = async (state, fetchFn, logger) => {
|
|
267
|
+
if (!state.oauth1Token || !state.oauth2Token) {
|
|
268
|
+
throw createServiceApiError("No tokens available for refresh", 401);
|
|
269
|
+
}
|
|
270
|
+
const cons = await getOrFetchConsumer(state, fetchFn);
|
|
271
|
+
state.oauth2Token = await exchangeOAuth2(state.oauth1Token, cons, fetchFn);
|
|
272
|
+
logger.info("OAuth2 token refreshed");
|
|
273
|
+
};
|
|
216
274
|
var createTokenRefreshManager = (fetchFn, logger) => {
|
|
217
275
|
const state = {
|
|
218
276
|
oauth1Token: void 0,
|
|
@@ -221,49 +279,24 @@ var createTokenRefreshManager = (fetchFn, logger) => {
|
|
|
221
279
|
};
|
|
222
280
|
let isRefreshing = false;
|
|
223
281
|
let subscribers = [];
|
|
224
|
-
const getConsumer = async () => {
|
|
225
|
-
if (state.consumer) return state.consumer;
|
|
226
|
-
state.consumer = await fetchOAuthConsumer(fetchFn);
|
|
227
|
-
return state.consumer;
|
|
228
|
-
};
|
|
229
|
-
const refreshToken = async () => {
|
|
230
|
-
if (!state.oauth1Token || !state.oauth2Token) {
|
|
231
|
-
throw createServiceApiError("No tokens available for refresh", 401);
|
|
232
|
-
}
|
|
233
|
-
const cons = await getConsumer();
|
|
234
|
-
state.oauth2Token = await exchangeOAuth2(state.oauth1Token, cons, fetchFn);
|
|
235
|
-
logger.info("OAuth2 token refreshed");
|
|
236
|
-
};
|
|
237
|
-
const waitForRefresh = () => new Promise((resolve, reject) => {
|
|
238
|
-
subscribers.push({ resolve, reject });
|
|
239
|
-
});
|
|
240
|
-
const notifySubscribers = () => {
|
|
241
|
-
if (!state.oauth2Token) return;
|
|
242
|
-
const token = state.oauth2Token.access_token;
|
|
243
|
-
subscribers.forEach((s) => {
|
|
244
|
-
s.resolve(token);
|
|
245
|
-
});
|
|
246
|
-
subscribers = [];
|
|
247
|
-
};
|
|
248
|
-
const rejectSubscribers = (error) => {
|
|
249
|
-
subscribers.forEach((s) => {
|
|
250
|
-
s.reject(error);
|
|
251
|
-
});
|
|
252
|
-
subscribers = [];
|
|
253
|
-
};
|
|
254
282
|
return {
|
|
255
283
|
state,
|
|
256
284
|
ensureFreshToken: async () => {
|
|
257
285
|
if (isRefreshing) {
|
|
258
|
-
await
|
|
286
|
+
await new Promise((resolve, reject) => {
|
|
287
|
+
subscribers.push({ resolve, reject });
|
|
288
|
+
});
|
|
259
289
|
return;
|
|
260
290
|
}
|
|
261
291
|
isRefreshing = true;
|
|
262
292
|
try {
|
|
263
|
-
await
|
|
264
|
-
|
|
293
|
+
await doRefreshToken(state, fetchFn, logger);
|
|
294
|
+
if (state.oauth2Token)
|
|
295
|
+
notifyAll(subscribers, state.oauth2Token.access_token);
|
|
296
|
+
subscribers = [];
|
|
265
297
|
} catch (error) {
|
|
266
|
-
|
|
298
|
+
rejectAll(subscribers, error);
|
|
299
|
+
subscribers = [];
|
|
267
300
|
throw error;
|
|
268
301
|
} finally {
|
|
269
302
|
isRefreshing = false;
|
|
@@ -275,52 +308,14 @@ var createTokenRefreshManager = (fetchFn, logger) => {
|
|
|
275
308
|
// src/adapters/http/garmin-http-client.ts
|
|
276
309
|
var createGarminHttpClient = (logger, fetchFn = globalThis.fetch) => {
|
|
277
310
|
const refresh = createTokenRefreshManager(fetchFn, logger);
|
|
278
|
-
const
|
|
279
|
-
if (!refresh.state.oauth2Token) {
|
|
280
|
-
throw createServiceApiError("Token unavailable after refresh", 401);
|
|
281
|
-
}
|
|
282
|
-
return fetchFn(url, {
|
|
283
|
-
...init,
|
|
284
|
-
headers: {
|
|
285
|
-
...init?.headers,
|
|
286
|
-
Authorization: `Bearer ${refresh.state.oauth2Token.access_token}`
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
};
|
|
290
|
-
const authFetch = async (url, init) => {
|
|
291
|
-
if (!refresh.state.oauth2Token) {
|
|
292
|
-
throw createServiceApiError("Not authenticated", 401);
|
|
293
|
-
}
|
|
294
|
-
if (refresh.state.oauth2Token.expires_at < Math.floor(Date.now() / 1e3)) {
|
|
295
|
-
await refresh.ensureFreshToken();
|
|
296
|
-
}
|
|
297
|
-
const res = await makeRequest(url, init);
|
|
298
|
-
if (res.status === 401) {
|
|
299
|
-
await refresh.ensureFreshToken();
|
|
300
|
-
const retry = await makeRequest(url, init);
|
|
301
|
-
if (!retry.ok) {
|
|
302
|
-
throw createServiceApiError(
|
|
303
|
-
`API request failed after token refresh: ${retry.statusText}`,
|
|
304
|
-
retry.status
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
return retry;
|
|
308
|
-
}
|
|
309
|
-
if (!res.ok) {
|
|
310
|
-
throw createServiceApiError(
|
|
311
|
-
`API request failed: ${res.statusText}`,
|
|
312
|
-
res.status
|
|
313
|
-
);
|
|
314
|
-
}
|
|
315
|
-
return res;
|
|
316
|
-
};
|
|
311
|
+
const fetch = (url, init) => authFetch(url, init, refresh, fetchFn);
|
|
317
312
|
return {
|
|
318
313
|
get: async (url) => {
|
|
319
|
-
const res = await
|
|
314
|
+
const res = await fetch(url);
|
|
320
315
|
return await res.json();
|
|
321
316
|
},
|
|
322
317
|
post: async (url, body) => {
|
|
323
|
-
const res = await
|
|
318
|
+
const res = await fetch(url, {
|
|
324
319
|
method: "POST",
|
|
325
320
|
headers: { "Content-Type": "application/json" },
|
|
326
321
|
body: body !== null ? JSON.stringify(body) : void 0
|
|
@@ -328,7 +323,7 @@ var createGarminHttpClient = (logger, fetchFn = globalThis.fetch) => {
|
|
|
328
323
|
return await res.json();
|
|
329
324
|
},
|
|
330
325
|
del: async (url) => {
|
|
331
|
-
const res = await
|
|
326
|
+
const res = await fetch(url, {
|
|
332
327
|
method: "POST",
|
|
333
328
|
headers: { "X-Http-Method-Override": "DELETE" }
|
|
334
329
|
});
|
|
@@ -365,51 +360,56 @@ var garminTokensSchema = z.object({
|
|
|
365
360
|
});
|
|
366
361
|
|
|
367
362
|
// src/adapters/auth/garmin-auth-provider.ts
|
|
363
|
+
var buildAuthProvider = (ctx) => ({
|
|
364
|
+
login: async (username, password) => {
|
|
365
|
+
const result = await garminSso(username, password, ctx.logger, ctx.fetchFn);
|
|
366
|
+
ctx.state.oauth1 = result.oauth1;
|
|
367
|
+
ctx.httpClient.setTokens(result.oauth1, result.oauth2);
|
|
368
|
+
if (ctx.tokenStore) {
|
|
369
|
+
await ctx.tokenStore.save({
|
|
370
|
+
oauth1: result.oauth1,
|
|
371
|
+
oauth2: result.oauth2
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
is_authenticated: () => {
|
|
376
|
+
const token = ctx.httpClient.getOAuth2Token();
|
|
377
|
+
if (!token) return false;
|
|
378
|
+
return token.expires_at > Math.floor(Date.now() / 1e3);
|
|
379
|
+
},
|
|
380
|
+
export_tokens: async () => {
|
|
381
|
+
const token = ctx.httpClient.getOAuth2Token();
|
|
382
|
+
if (!token || !ctx.state.oauth1) {
|
|
383
|
+
throw createServiceAuthError("No tokens to export");
|
|
384
|
+
}
|
|
385
|
+
return { oauth1: ctx.state.oauth1, oauth2: token };
|
|
386
|
+
},
|
|
387
|
+
restore_tokens: async (data) => {
|
|
388
|
+
const parsed = garminTokensSchema.parse(data);
|
|
389
|
+
ctx.state.oauth1 = parsed.oauth1;
|
|
390
|
+
ctx.httpClient.setTokens(parsed.oauth1, parsed.oauth2);
|
|
391
|
+
ctx.logger.info("Tokens restored from stored session");
|
|
392
|
+
},
|
|
393
|
+
logout: async () => {
|
|
394
|
+
ctx.state.oauth1 = void 0;
|
|
395
|
+
ctx.httpClient.clearTokens();
|
|
396
|
+
if (ctx.tokenStore) await ctx.tokenStore.clear();
|
|
397
|
+
ctx.logger.info("Logged out from Garmin Connect");
|
|
398
|
+
}
|
|
399
|
+
});
|
|
368
400
|
var createGarminAuthProvider = (options) => {
|
|
369
401
|
const logger = options?.logger ?? createConsoleLogger();
|
|
370
402
|
const tokenStore = options?.tokenStore;
|
|
371
403
|
const fetchFn = options?.fetchFn ?? createCookieFetch();
|
|
372
404
|
const httpClient = createGarminHttpClient(logger, fetchFn);
|
|
373
|
-
|
|
374
|
-
const auth = {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
oauth1: result.oauth1,
|
|
382
|
-
oauth2: result.oauth2
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
},
|
|
386
|
-
is_authenticated: () => {
|
|
387
|
-
const token = httpClient.getOAuth2Token();
|
|
388
|
-
if (!token) return false;
|
|
389
|
-
return token.expires_at > Math.floor(Date.now() / 1e3);
|
|
390
|
-
},
|
|
391
|
-
export_tokens: async () => {
|
|
392
|
-
const token = httpClient.getOAuth2Token();
|
|
393
|
-
if (!token || !currentOAuth1) {
|
|
394
|
-
throw createServiceAuthError("No tokens to export");
|
|
395
|
-
}
|
|
396
|
-
return { oauth1: currentOAuth1, oauth2: token };
|
|
397
|
-
},
|
|
398
|
-
restore_tokens: async (data) => {
|
|
399
|
-
const parsed = garminTokensSchema.parse(data);
|
|
400
|
-
currentOAuth1 = parsed.oauth1;
|
|
401
|
-
httpClient.setTokens(parsed.oauth1, parsed.oauth2);
|
|
402
|
-
logger.info("Tokens restored from stored session");
|
|
403
|
-
},
|
|
404
|
-
logout: async () => {
|
|
405
|
-
currentOAuth1 = void 0;
|
|
406
|
-
httpClient.clearTokens();
|
|
407
|
-
if (tokenStore) {
|
|
408
|
-
await tokenStore.clear();
|
|
409
|
-
}
|
|
410
|
-
logger.info("Logged out from Garmin Connect");
|
|
411
|
-
}
|
|
412
|
-
};
|
|
405
|
+
const state = { oauth1: void 0 };
|
|
406
|
+
const auth = buildAuthProvider({
|
|
407
|
+
httpClient,
|
|
408
|
+
state,
|
|
409
|
+
tokenStore,
|
|
410
|
+
logger,
|
|
411
|
+
fetchFn
|
|
412
|
+
});
|
|
413
413
|
return { auth, httpClient };
|
|
414
414
|
};
|
|
415
415
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/adapters/http/cookie-fetch.ts","../src/adapters/http/urls.ts","../src/adapters/http/oauth-consumer.ts","../src/adapters/http/sso-validators.ts","../src/adapters/http/sso-login.ts","../src/adapters/http/oauth-signer.ts","../src/adapters/http/sso-oauth.ts","../src/adapters/http/garmin-sso.ts","../src/adapters/http/token-refresh.ts","../src/adapters/http/garmin-http-client.ts","../src/adapters/schemas/garmin-token.schema.ts","../src/adapters/auth/garmin-auth-provider.ts","../src/adapters/mappers/workout-summary.mapper.ts","../src/adapters/schemas/workout-response.schema.ts","../src/adapters/client/garmin-workout-service.ts","../src/adapters/client/garmin-connect-client.ts","../src/adapters/token-store/file-token-store.ts","../src/adapters/token-store/memory-token-store.ts"],"names":["createServiceAuthError","createServiceApiError","z","createConsoleLogger"],"mappings":"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","file":"index.js","sourcesContent":["import fetchCookie from \"fetch-cookie\";\n\nexport const createCookieFetch = (): typeof globalThis.fetch =>\n fetchCookie(globalThis.fetch) as typeof globalThis.fetch;\n","export const GARMIN_SSO_ORIGIN = \"https://sso.garmin.com\";\nexport const GARMIN_SSO_EMBED = \"https://sso.garmin.com/sso/embed\";\nexport const SIGNIN_URL = \"https://sso.garmin.com/sso/signin\";\nexport const GC_MODERN = \"https://connect.garmin.com/modern\";\nexport const API_BASE = \"https://connectapi.garmin.com\";\nexport const OAUTH_URL = `${API_BASE}/oauth-service/oauth`;\nexport const WORKOUT_URL = `${API_BASE}/workout-service`;\n/** OAuth consumer credentials hosted by the garth project (third-party). */\nexport const OAUTH_CONSUMER_URL =\n \"https://thegarth.s3.amazonaws.com/oauth_consumer.json\";\n\nexport const USER_AGENT_MOBILE = \"com.garmin.android.apps.connectmobile\";\nexport const USER_AGENT_BROWSER =\n \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36\";\n","import { createServiceAuthError } from \"@kaiord/core\";\nimport { OAUTH_CONSUMER_URL } from \"./urls\";\nimport type { FetchFn, OAuthConsumer } from \"./types\";\n\nexport const fetchOAuthConsumer = async (\n fetchFn: FetchFn\n): Promise<OAuthConsumer> => {\n const res = await fetchFn(OAUTH_CONSUMER_URL);\n if (!res.ok) {\n throw createServiceAuthError(\n `Failed to fetch OAuth consumer: ${res.status} ${res.statusText}`\n );\n }\n const data = (await res.json()) as {\n consumer_key: string;\n consumer_secret: string;\n };\n return { key: data.consumer_key, secret: data.consumer_secret };\n};\n","import { createServiceAuthError } from \"@kaiord/core\";\nimport type { Logger } from \"@kaiord/core\";\n\nconst ACCOUNT_LOCKED_RE = /var status\\s*=\\s*\"([^\"]*)\"/;\nconst PAGE_TITLE_RE = /<title>([^<]*)<\\/title>/;\n\nexport const checkAccountLocked = (html: string): void => {\n const match = ACCOUNT_LOCKED_RE.exec(html);\n if (match && match[1] === \"ACCOUNT_LOCKED\") {\n throw createServiceAuthError(\n `Account locked: ${match[1]}. Unlock via Garmin Connect web.`\n );\n }\n};\n\nexport const checkPageTitle = (html: string, logger: Logger): void => {\n const match = PAGE_TITLE_RE.exec(html);\n if (match?.[1]?.includes(\"Update Phone Number\")) {\n throw createServiceAuthError(\"Login failed: phone number update required.\");\n }\n if (match) {\n logger.debug(\"Login page title\", { title: match[1] });\n }\n};\n","import { createServiceAuthError } from \"@kaiord/core\";\nimport { checkAccountLocked, checkPageTitle } from \"./sso-validators\";\nimport {\n GARMIN_SSO_EMBED,\n GC_MODERN,\n GARMIN_SSO_ORIGIN,\n SIGNIN_URL,\n USER_AGENT_BROWSER,\n} from \"./urls\";\nimport type { FetchFn } from \"./types\";\nimport type { Logger } from \"@kaiord/core\";\n\nconst CSRF_RE = /name=\"_csrf\"\\s+value=\"(.+?)\"/;\nconst TICKET_RE = /ticket=([^\"]+)\"/;\n\nconst fetchCsrfToken = async (fetchFn: FetchFn): Promise<string> => {\n const signinParams = new URLSearchParams({\n id: \"gauth-widget\",\n embedWidget: \"true\",\n locale: \"en\",\n gauthHost: GARMIN_SSO_EMBED,\n });\n const csrfRes = await fetchFn(`${SIGNIN_URL}?${signinParams}`);\n if (!csrfRes.ok) {\n throw createServiceAuthError(\n `SSO login page returned ${csrfRes.status}: ${csrfRes.statusText}`\n );\n }\n const csrfHtml = await csrfRes.text();\n const csrfMatch = CSRF_RE.exec(csrfHtml);\n if (!csrfMatch) {\n throw createServiceAuthError(\"CSRF token not found on login page\");\n }\n return csrfMatch[1];\n};\n\nconst submitLogin = async (\n username: string,\n password: string,\n csrf: string,\n fetchFn: FetchFn\n): Promise<string> => {\n const loginParams = new URLSearchParams({\n id: \"gauth-widget\",\n embedWidget: \"true\",\n clientId: \"GarminConnect\",\n locale: \"en\",\n gauthHost: GARMIN_SSO_EMBED,\n service: GARMIN_SSO_EMBED,\n source: GARMIN_SSO_EMBED,\n redirectAfterAccountLoginUrl: GARMIN_SSO_EMBED,\n redirectAfterAccountCreationUrl: GARMIN_SSO_EMBED,\n });\n const body = new URLSearchParams({\n username,\n password,\n embed: \"true\",\n _csrf: csrf,\n });\n const loginRes = await fetchFn(`${SIGNIN_URL}?${loginParams}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Dnt: \"1\",\n Origin: GARMIN_SSO_ORIGIN,\n Referer: SIGNIN_URL,\n \"User-Agent\": USER_AGENT_BROWSER,\n },\n body: body.toString(),\n });\n return loginRes.text();\n};\n\nexport const getLoginTicket = async (\n username: string,\n password: string,\n fetchFn: FetchFn,\n logger: Logger\n): Promise<string> => {\n const embedParams = new URLSearchParams({\n clientId: \"GarminConnect\",\n locale: \"en\",\n service: GC_MODERN,\n });\n await fetchFn(`${GARMIN_SSO_EMBED}?${embedParams}`);\n\n const csrf = await fetchCsrfToken(fetchFn);\n const loginHtml = await submitLogin(username, password, csrf, fetchFn);\n\n checkAccountLocked(loginHtml);\n checkPageTitle(loginHtml, logger);\n\n const ticketMatch = TICKET_RE.exec(loginHtml);\n if (!ticketMatch) {\n throw createServiceAuthError(\n \"Login failed: ticket not found. Check username and password.\"\n );\n }\n return ticketMatch[1];\n};\n","import { createHmac } from \"node:crypto\";\nimport OAuth from \"oauth-1.0a\";\nimport type { OAuthConsumer } from \"./types\";\n\nexport type { OAuthConsumer } from \"./types\";\nexport type OAuthToken = { key: string; secret: string };\n\nexport type OAuthSigner = {\n toHeader: (\n request: { url: string; method: string },\n token?: OAuthToken\n ) => Record<string, string>;\n};\n\nexport const createOAuthSigner = (consumer: OAuthConsumer): OAuthSigner => {\n const oauth = new OAuth({\n consumer,\n signature_method: \"HMAC-SHA1\",\n hash_function(baseString: string, key: string) {\n return createHmac(\"sha1\", key).update(baseString).digest(\"base64\");\n },\n });\n\n return {\n toHeader: (request, token) => {\n const authorized = token\n ? oauth.authorize(request, token)\n : oauth.authorize(request);\n const header = oauth.toHeader(authorized);\n return Object.fromEntries(Object.entries(header));\n },\n };\n};\n","import { createServiceAuthError } from \"@kaiord/core\";\nimport { createOAuthSigner } from \"./oauth-signer\";\nimport { GARMIN_SSO_EMBED, OAUTH_URL, USER_AGENT_MOBILE } from \"./urls\";\nimport type { FetchFn, OAuthConsumer } from \"./types\";\nimport type { OAuth1Token, OAuth2Token } from \"./types\";\n\nexport const getOAuth1Token = async (\n ticket: string,\n consumer: OAuthConsumer,\n fetchFn: FetchFn\n): Promise<OAuth1Token> => {\n const signer = createOAuthSigner(consumer);\n const params = new URLSearchParams({\n ticket,\n \"login-url\": GARMIN_SSO_EMBED,\n \"accepts-mfa-tokens\": \"true\",\n });\n const url = `${OAUTH_URL}/preauthorized?${params}`;\n const headers = signer.toHeader({ url, method: \"GET\" });\n\n const res = await fetchFn(url, {\n headers: { ...headers, \"User-Agent\": USER_AGENT_MOBILE },\n });\n if (!res.ok) {\n throw createServiceAuthError(\n `OAuth1 token request failed: ${res.status} ${res.statusText}`\n );\n }\n const text = await res.text();\n const parsed = new URLSearchParams(text);\n\n const oauthToken = parsed.get(\"oauth_token\");\n const oauthTokenSecret = parsed.get(\"oauth_token_secret\");\n if (!oauthToken || !oauthTokenSecret) {\n throw createServiceAuthError(\"OAuth1 token exchange failed\");\n }\n\n return { oauth_token: oauthToken, oauth_token_secret: oauthTokenSecret };\n};\n\nexport const exchangeOAuth2 = async (\n oauth1: OAuth1Token,\n consumer: OAuthConsumer,\n fetchFn: FetchFn\n): Promise<OAuth2Token> => {\n const signer = createOAuthSigner(consumer);\n const baseUrl = `${OAUTH_URL}/exchange/user/2.0`;\n const token = {\n key: oauth1.oauth_token,\n secret: oauth1.oauth_token_secret,\n };\n const authHeader = signer.toHeader({ url: baseUrl, method: \"POST\" }, token);\n\n const res = await fetchFn(baseUrl, {\n method: \"POST\",\n headers: {\n ...authHeader,\n \"User-Agent\": USER_AGENT_MOBILE,\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n });\n if (!res.ok) {\n throw createServiceAuthError(\n `OAuth2 exchange failed: ${res.status} ${res.statusText}`\n );\n }\n const data = (await res.json()) as OAuth2Token;\n\n return {\n ...data,\n expires_at: Math.floor(Date.now() / 1000) + data.expires_in,\n };\n};\n","import { fetchOAuthConsumer } from \"./oauth-consumer\";\nimport { getLoginTicket } from \"./sso-login\";\nimport { getOAuth1Token, exchangeOAuth2 as exchange } from \"./sso-oauth\";\nimport type { FetchFn, OAuth1Token, OAuth2Token } from \"./types\";\nimport type { Logger } from \"@kaiord/core\";\n\nexport type { OAuth1Token, OAuth2Token } from \"./types\";\nexport type SsoResult = { oauth1: OAuth1Token; oauth2: OAuth2Token };\n\n/**\n * Garmin Connect SSO login flow.\n * The fetchFn must be cookie-aware (persist cookies across requests).\n * Use fetch-cookie or similar wrapper for Node.js environments.\n */\nexport const garminSso = async (\n username: string,\n password: string,\n logger: Logger,\n fetchFn: FetchFn\n): Promise<SsoResult> => {\n logger.info(\"Starting Garmin Connect SSO login\");\n\n const consumer = await fetchOAuthConsumer(fetchFn);\n const ticket = await getLoginTicket(username, password, fetchFn, logger);\n logger.debug(\"SSO ticket obtained\");\n\n const oauth1 = await getOAuth1Token(ticket, consumer, fetchFn);\n logger.debug(\"OAuth1 token obtained\");\n\n const oauth2 = await exchange(oauth1, consumer, fetchFn);\n logger.info(\"Garmin Connect SSO login successful\");\n\n return { oauth1, oauth2 };\n};\n\nexport { exchangeOAuth2 } from \"./sso-oauth\";\n","import { createServiceApiError } from \"@kaiord/core\";\nimport { exchangeOAuth2 } from \"./garmin-sso\";\nimport { fetchOAuthConsumer } from \"./oauth-consumer\";\nimport type { FetchFn, OAuthConsumer, OAuth1Token, OAuth2Token } from \"./types\";\nimport type { Logger } from \"@kaiord/core\";\n\ntype RefreshSubscriber = {\n resolve: (token: string) => void;\n reject: (error: unknown) => void;\n};\n\nexport type TokenState = {\n oauth1Token: OAuth1Token | undefined;\n oauth2Token: OAuth2Token | undefined;\n consumer: OAuthConsumer | undefined;\n};\n\nexport type TokenRefreshManager = {\n ensureFreshToken: () => Promise<void>;\n state: TokenState;\n};\n\nexport const createTokenRefreshManager = (\n fetchFn: FetchFn,\n logger: Logger\n): TokenRefreshManager => {\n const state: TokenState = {\n oauth1Token: undefined,\n oauth2Token: undefined,\n consumer: undefined,\n };\n let isRefreshing = false;\n let subscribers: RefreshSubscriber[] = [];\n\n const getConsumer = async (): Promise<OAuthConsumer> => {\n if (state.consumer) return state.consumer;\n state.consumer = await fetchOAuthConsumer(fetchFn);\n return state.consumer;\n };\n\n const refreshToken = async (): Promise<void> => {\n if (!state.oauth1Token || !state.oauth2Token) {\n throw createServiceApiError(\"No tokens available for refresh\", 401);\n }\n const cons = await getConsumer();\n state.oauth2Token = await exchangeOAuth2(state.oauth1Token, cons, fetchFn);\n logger.info(\"OAuth2 token refreshed\");\n };\n\n const waitForRefresh = (): Promise<string> =>\n new Promise((resolve, reject) => {\n subscribers.push({ resolve, reject });\n });\n\n const notifySubscribers = (): void => {\n if (!state.oauth2Token) return;\n const token = state.oauth2Token.access_token;\n subscribers.forEach((s) => {\n s.resolve(token);\n });\n subscribers = [];\n };\n\n const rejectSubscribers = (error: unknown): void => {\n subscribers.forEach((s) => {\n s.reject(error);\n });\n subscribers = [];\n };\n\n return {\n state,\n ensureFreshToken: async (): Promise<void> => {\n if (isRefreshing) {\n await waitForRefresh();\n return;\n }\n isRefreshing = true;\n try {\n await refreshToken();\n notifySubscribers();\n } catch (error) {\n rejectSubscribers(error);\n throw error;\n } finally {\n isRefreshing = false;\n }\n },\n };\n};\n","import { createServiceApiError } from \"@kaiord/core\";\nimport { createTokenRefreshManager } from \"./token-refresh\";\nimport type {\n FetchFn,\n GarminHttpClient,\n OAuth1Token,\n OAuth2Token,\n} from \"./types\";\nimport type { Logger } from \"@kaiord/core\";\n\nexport type { GarminHttpClient } from \"./types\";\n\nexport const createGarminHttpClient = (\n logger: Logger,\n fetchFn: FetchFn = globalThis.fetch\n): GarminHttpClient => {\n const refresh = createTokenRefreshManager(fetchFn, logger);\n\n const makeRequest = (url: string, init?: RequestInit): Promise<Response> => {\n if (!refresh.state.oauth2Token) {\n throw createServiceApiError(\"Token unavailable after refresh\", 401);\n }\n return fetchFn(url, {\n ...init,\n headers: {\n ...init?.headers,\n Authorization: `Bearer ${refresh.state.oauth2Token.access_token}`,\n },\n });\n };\n\n const authFetch = async (\n url: string,\n init?: RequestInit\n ): Promise<Response> => {\n if (!refresh.state.oauth2Token) {\n throw createServiceApiError(\"Not authenticated\", 401);\n }\n if (refresh.state.oauth2Token.expires_at < Math.floor(Date.now() / 1000)) {\n await refresh.ensureFreshToken();\n }\n\n const res = await makeRequest(url, init);\n\n if (res.status === 401) {\n await refresh.ensureFreshToken();\n const retry = await makeRequest(url, init);\n if (!retry.ok) {\n throw createServiceApiError(\n `API request failed after token refresh: ${retry.statusText}`,\n retry.status\n );\n }\n return retry;\n }\n if (!res.ok) {\n throw createServiceApiError(\n `API request failed: ${res.statusText}`,\n res.status\n );\n }\n return res;\n };\n\n return {\n get: async <T>(url: string): Promise<T> => {\n const res = await authFetch(url);\n return (await res.json()) as T;\n },\n post: async <T>(url: string, body: unknown): Promise<T> => {\n const res = await authFetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: body !== null ? JSON.stringify(body) : undefined,\n });\n return (await res.json()) as T;\n },\n del: async <T>(url: string): Promise<T> => {\n const res = await authFetch(url, {\n method: \"POST\",\n headers: { \"X-Http-Method-Override\": \"DELETE\" },\n });\n const text = await res.text();\n return (text ? JSON.parse(text) : undefined) as T;\n },\n setTokens: (o1: OAuth1Token, o2: OAuth2Token): void => {\n refresh.state.oauth1Token = o1;\n refresh.state.oauth2Token = o2;\n },\n clearTokens: (): void => {\n refresh.state.oauth1Token = undefined;\n refresh.state.oauth2Token = undefined;\n refresh.state.consumer = undefined;\n },\n getOAuth2Token: () => refresh.state.oauth2Token,\n };\n};\n","import { z } from \"zod\";\n\nexport const oauth1TokenSchema = z.object({\n oauth_token: z.string(),\n oauth_token_secret: z.string(),\n});\n\nexport const oauth2TokenSchema = z.object({\n access_token: z.string(),\n refresh_token: z.string(),\n token_type: z.string(),\n expires_in: z.number(),\n refresh_token_expires_in: z.number(),\n expires_at: z.number(),\n});\n\nexport const garminTokensSchema = z.object({\n oauth1: oauth1TokenSchema,\n oauth2: oauth2TokenSchema,\n});\n","import { createConsoleLogger, createServiceAuthError } from \"@kaiord/core\";\nimport { createCookieFetch } from \"../http/cookie-fetch\";\nimport { createGarminHttpClient } from \"../http/garmin-http-client\";\nimport { garminSso } from \"../http/garmin-sso\";\nimport { garminTokensSchema } from \"../schemas/garmin-token.schema\";\nimport type { GarminHttpClient } from \"../http/garmin-http-client\";\nimport type { AuthProvider, Logger, TokenStore } from \"@kaiord/core\";\n\nexport type GarminAuthProviderOptions = {\n logger?: Logger;\n tokenStore?: TokenStore;\n fetchFn?: typeof globalThis.fetch;\n};\n\nexport type GarminAuthProviderResult = {\n auth: AuthProvider;\n httpClient: GarminHttpClient;\n};\n\nexport const createGarminAuthProvider = (\n options?: GarminAuthProviderOptions\n): GarminAuthProviderResult => {\n const logger = options?.logger ?? createConsoleLogger();\n const tokenStore = options?.tokenStore;\n const fetchFn = options?.fetchFn ?? createCookieFetch();\n const httpClient = createGarminHttpClient(logger, fetchFn);\n let currentOAuth1:\n | { oauth_token: string; oauth_token_secret: string }\n | undefined;\n\n const auth: AuthProvider = {\n login: async (username, password) => {\n const result = await garminSso(username, password, logger, fetchFn);\n currentOAuth1 = result.oauth1;\n httpClient.setTokens(result.oauth1, result.oauth2);\n if (tokenStore) {\n await tokenStore.save({\n oauth1: result.oauth1,\n oauth2: result.oauth2,\n });\n }\n },\n\n is_authenticated: () => {\n const token = httpClient.getOAuth2Token();\n if (!token) return false;\n return token.expires_at > Math.floor(Date.now() / 1000);\n },\n\n export_tokens: async () => {\n const token = httpClient.getOAuth2Token();\n if (!token || !currentOAuth1) {\n throw createServiceAuthError(\"No tokens to export\");\n }\n return { oauth1: currentOAuth1, oauth2: token };\n },\n\n restore_tokens: async (data) => {\n const parsed = garminTokensSchema.parse(data);\n currentOAuth1 = parsed.oauth1;\n httpClient.setTokens(parsed.oauth1, parsed.oauth2);\n logger.info(\"Tokens restored from stored session\");\n },\n\n logout: async () => {\n currentOAuth1 = undefined;\n httpClient.clearTokens();\n if (tokenStore) {\n await tokenStore.clear();\n }\n logger.info(\"Logged out from Garmin Connect\");\n },\n };\n\n return { auth, httpClient };\n};\n","import type { WorkoutSummary } from \"@kaiord/core\";\n\nexport const mapToWorkoutSummary = (garminWorkout: {\n workoutId?: number | string;\n workoutName?: string;\n sportType?: { sportTypeKey?: string };\n createdDate?: number | string;\n updatedDate?: number | string;\n}): WorkoutSummary => ({\n id: String(garminWorkout.workoutId ?? \"\"),\n name: garminWorkout.workoutName ?? \"Unnamed\",\n sport: garminWorkout.sportType?.sportTypeKey ?? \"unknown\",\n created_at: garminWorkout.createdDate\n ? new Date(garminWorkout.createdDate).toISOString()\n : \"\",\n updated_at: garminWorkout.updatedDate\n ? new Date(garminWorkout.updatedDate).toISOString()\n : \"\",\n});\n","import { z } from \"zod\";\n\n/** GET /workout-service/workouts?start=N&limit=N - List workout summaries */\nexport const garminWorkoutSummarySchema = z.object({\n workoutId: z.number().or(z.string()),\n workoutName: z.string().optional(),\n sportType: z.object({ sportTypeKey: z.string().optional() }).optional(),\n createdDate: z.number().or(z.string()).optional(),\n updatedDate: z.number().or(z.string()).optional(),\n});\n\n/** POST /workout-service/workout - Push a workout (returns created workout) */\nexport const garminPushResponseSchema = z.object({\n workoutId: z.number().or(z.string()),\n workoutName: z.string().optional(),\n});\n","import {\n createConsoleLogger,\n toText,\n createServiceApiError,\n} from \"@kaiord/core\";\nimport { createGarminWriter } from \"@kaiord/garmin\";\nimport { WORKOUT_URL } from \"../http/urls\";\nimport { mapToWorkoutSummary } from \"../mappers/workout-summary.mapper\";\nimport {\n garminPushResponseSchema,\n garminWorkoutSummarySchema,\n} from \"../schemas/workout-response.schema\";\nimport type { GarminHttpClient } from \"../http/garmin-http-client\";\nimport type {\n KRD,\n ListOptions,\n Logger,\n PushResult,\n WorkoutService,\n WorkoutSummary,\n} from \"@kaiord/core\";\n\nexport type GarminWorkoutClient = Pick<WorkoutService, \"push\" | \"list\">;\n\nconst pushWorkout = async (\n krd: KRD,\n httpClient: GarminHttpClient,\n garminWriter: ReturnType<typeof createGarminWriter>,\n log: Logger\n): Promise<PushResult> => {\n try {\n log.info(\"Pushing workout to Garmin Connect\");\n const gcnJson = await toText(krd, garminWriter, log);\n const payload = JSON.parse(gcnJson) as Record<string, unknown>;\n\n const raw = await httpClient.post<unknown>(\n `${WORKOUT_URL}/workout`,\n payload\n );\n const result = garminPushResponseSchema.parse(raw);\n\n return {\n id: String(result.workoutId),\n name: result.workoutName ?? \"Workout\",\n url: `https://connect.garmin.com/modern/workout/${result.workoutId}`,\n };\n } catch (error) {\n throw createServiceApiError(\"Failed to push workout\", undefined, error);\n }\n};\n\nconst listWorkouts = async (\n httpClient: GarminHttpClient,\n log: Logger,\n options?: ListOptions\n): Promise<WorkoutSummary[]> => {\n try {\n log.info(\"Listing workouts from Garmin Connect\");\n const start = options?.offset ?? 0;\n const limit = options?.limit ?? 20;\n const params = new URLSearchParams({\n start: String(start),\n limit: String(limit),\n });\n\n const raw = await httpClient.get<unknown>(\n `${WORKOUT_URL}/workouts?${params}`\n );\n const workouts = garminWorkoutSummarySchema.array().parse(raw);\n return workouts.map(mapToWorkoutSummary);\n } catch (error) {\n throw createServiceApiError(\"Failed to list workouts\", undefined, error);\n }\n};\n\nexport const createGarminWorkoutService = (\n httpClient: GarminHttpClient,\n logger?: Logger\n): GarminWorkoutClient => {\n const log = logger ?? createConsoleLogger();\n const garminWriter = createGarminWriter(log);\n\n return {\n push: (krd) => pushWorkout(krd, httpClient, garminWriter, log),\n list: (opts) => listWorkouts(httpClient, log, opts),\n };\n};\n","import { createGarminWorkoutService } from \"./garmin-workout-service\";\nimport { createGarminAuthProvider } from \"../auth/garmin-auth-provider\";\nimport type { GarminAuthProviderOptions } from \"../auth/garmin-auth-provider\";\n\nexport type GarminConnectClientOptions = GarminAuthProviderOptions;\n\nexport const createGarminConnectClient = (\n options?: GarminConnectClientOptions\n) => {\n const { auth, httpClient } = createGarminAuthProvider(options);\n const service = createGarminWorkoutService(httpClient, options?.logger);\n return { auth, service };\n};\n","import { readFile, writeFile, mkdir, unlink } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { TokenData, TokenStore } from \"@kaiord/core\";\n\nconst DEFAULT_PATH = join(homedir(), \".kaiord\", \"garmin-tokens.json\");\n\nexport const createFileTokenStore = (\n filePath: string = DEFAULT_PATH\n): TokenStore => ({\n save: async (tokens: TokenData) => {\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, JSON.stringify(tokens, null, 2), {\n encoding: \"utf-8\",\n mode: 0o600,\n });\n },\n\n load: async (): Promise<TokenData | null> => {\n try {\n const content = await readFile(filePath, \"utf-8\");\n return JSON.parse(content) as TokenData;\n } catch {\n return null;\n }\n },\n\n clear: async () => {\n try {\n await unlink(filePath);\n } catch {\n // File may not exist\n }\n },\n});\n","import type { TokenData, TokenStore } from \"@kaiord/core\";\n\nexport const createMemoryTokenStore = (): TokenStore => {\n let stored: TokenData | null = null;\n\n return {\n save: async (tokens: TokenData) => {\n stored = tokens;\n },\n load: async () => stored,\n clear: async () => {\n stored = null;\n },\n };\n};\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/adapters/http/cookie-fetch.ts","../src/adapters/http/garmin-auth-fetch.ts","../src/adapters/http/urls.ts","../src/adapters/http/oauth-consumer.ts","../src/adapters/http/sso-validators.ts","../src/adapters/http/sso-login.ts","../src/adapters/http/oauth-signer.ts","../src/adapters/http/sso-oauth.ts","../src/adapters/http/garmin-sso.ts","../src/adapters/http/token-refresh.ts","../src/adapters/http/garmin-http-client.ts","../src/adapters/schemas/garmin-token.schema.ts","../src/adapters/auth/garmin-auth-provider.ts","../src/adapters/mappers/workout-summary.mapper.ts","../src/adapters/schemas/workout-response.schema.ts","../src/adapters/client/garmin-workout-service.ts","../src/adapters/client/garmin-connect-client.ts","../src/adapters/token-store/file-token-store.ts","../src/adapters/token-store/memory-token-store.ts"],"names":["createServiceAuthError","createServiceApiError","z","createConsoleLogger"],"mappings":"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","file":"index.js","sourcesContent":["import fetchCookie from \"fetch-cookie\";\n\nexport const createCookieFetch = (): typeof globalThis.fetch =>\n fetchCookie(globalThis.fetch) as typeof globalThis.fetch;\n","import { createServiceApiError } from \"@kaiord/core\";\nimport type { TokenRefreshManager } from \"./token-refresh\";\nimport type { FetchFn } from \"./types\";\n\nconst makeRequest = (\n url: string,\n init: RequestInit | undefined,\n refresh: TokenRefreshManager,\n fetchFn: FetchFn\n): Promise<Response> => {\n if (!refresh.state.oauth2Token) {\n throw createServiceApiError(\"Token unavailable after refresh\", 401);\n }\n return fetchFn(url, {\n ...init,\n headers: {\n ...init?.headers,\n Authorization: `Bearer ${refresh.state.oauth2Token.access_token}`,\n },\n });\n};\n\nexport const authFetch = async (\n url: string,\n init: RequestInit | undefined,\n refresh: TokenRefreshManager,\n fetchFn: FetchFn\n): Promise<Response> => {\n if (!refresh.state.oauth2Token) {\n throw createServiceApiError(\"Not authenticated\", 401);\n }\n if (refresh.state.oauth2Token.expires_at < Math.floor(Date.now() / 1000)) {\n await refresh.ensureFreshToken();\n }\n const res = await makeRequest(url, init, refresh, fetchFn);\n if (res.status === 401) {\n await refresh.ensureFreshToken();\n const retry = await makeRequest(url, init, refresh, fetchFn);\n if (!retry.ok) {\n throw createServiceApiError(\n `API request failed after token refresh: ${retry.statusText}`,\n retry.status\n );\n }\n return retry;\n }\n if (!res.ok) {\n throw createServiceApiError(\n `API request failed: ${res.statusText}`,\n res.status\n );\n }\n return res;\n};\n","export const GARMIN_SSO_ORIGIN = \"https://sso.garmin.com\";\nexport const GARMIN_SSO_EMBED = \"https://sso.garmin.com/sso/embed\";\nexport const SIGNIN_URL = \"https://sso.garmin.com/sso/signin\";\nexport const GC_MODERN = \"https://connect.garmin.com/modern\";\nexport const API_BASE = \"https://connectapi.garmin.com\";\nexport const OAUTH_URL = `${API_BASE}/oauth-service/oauth`;\nexport const WORKOUT_URL = `${API_BASE}/workout-service`;\n/** OAuth consumer credentials hosted by the garth project (third-party). */\nexport const OAUTH_CONSUMER_URL =\n \"https://thegarth.s3.amazonaws.com/oauth_consumer.json\";\n\nexport const USER_AGENT_MOBILE = \"com.garmin.android.apps.connectmobile\";\nexport const USER_AGENT_BROWSER =\n \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36\";\n","import { createServiceAuthError } from \"@kaiord/core\";\nimport { OAUTH_CONSUMER_URL } from \"./urls\";\nimport type { FetchFn, OAuthConsumer } from \"./types\";\n\nexport const fetchOAuthConsumer = async (\n fetchFn: FetchFn\n): Promise<OAuthConsumer> => {\n const res = await fetchFn(OAUTH_CONSUMER_URL);\n if (!res.ok) {\n throw createServiceAuthError(\n `Failed to fetch OAuth consumer: ${res.status} ${res.statusText}`\n );\n }\n const data = (await res.json()) as {\n consumer_key: string;\n consumer_secret: string;\n };\n return { key: data.consumer_key, secret: data.consumer_secret };\n};\n","import { createServiceAuthError } from \"@kaiord/core\";\nimport type { Logger } from \"@kaiord/core\";\n\nconst ACCOUNT_LOCKED_RE = /var status\\s*=\\s*\"([^\"]*)\"/;\nconst PAGE_TITLE_RE = /<title>([^<]*)<\\/title>/;\n\nexport const checkAccountLocked = (html: string): void => {\n const match = ACCOUNT_LOCKED_RE.exec(html);\n if (match && match[1] === \"ACCOUNT_LOCKED\") {\n throw createServiceAuthError(\n `Account locked: ${match[1]}. Unlock via Garmin Connect web.`\n );\n }\n};\n\nexport const checkPageTitle = (html: string, logger: Logger): void => {\n const match = PAGE_TITLE_RE.exec(html);\n if (match?.[1]?.includes(\"Update Phone Number\")) {\n throw createServiceAuthError(\"Login failed: phone number update required.\");\n }\n if (match) {\n logger.debug(\"Login page title\", { title: match[1] });\n }\n};\n","import { createServiceAuthError } from \"@kaiord/core\";\nimport { checkAccountLocked, checkPageTitle } from \"./sso-validators\";\nimport {\n GARMIN_SSO_EMBED,\n GC_MODERN,\n GARMIN_SSO_ORIGIN,\n SIGNIN_URL,\n USER_AGENT_BROWSER,\n} from \"./urls\";\nimport type { FetchFn } from \"./types\";\nimport type { Logger } from \"@kaiord/core\";\n\nconst CSRF_RE = /name=\"_csrf\"\\s+value=\"(.+?)\"/;\nconst TICKET_RE = /ticket=([^\"]+)\"/;\n\nconst fetchCsrfToken = async (fetchFn: FetchFn): Promise<string> => {\n const signinParams = new URLSearchParams({\n id: \"gauth-widget\",\n embedWidget: \"true\",\n locale: \"en\",\n gauthHost: GARMIN_SSO_EMBED,\n });\n const csrfRes = await fetchFn(`${SIGNIN_URL}?${signinParams}`);\n if (!csrfRes.ok) {\n throw createServiceAuthError(\n `SSO login page returned ${csrfRes.status}: ${csrfRes.statusText}`\n );\n }\n const csrfHtml = await csrfRes.text();\n const csrfMatch = CSRF_RE.exec(csrfHtml);\n if (!csrfMatch) {\n throw createServiceAuthError(\"CSRF token not found on login page\");\n }\n return csrfMatch[1];\n};\n\nconst submitLogin = async (\n username: string,\n password: string,\n csrf: string,\n fetchFn: FetchFn\n): Promise<string> => {\n const loginParams = new URLSearchParams({\n id: \"gauth-widget\",\n embedWidget: \"true\",\n clientId: \"GarminConnect\",\n locale: \"en\",\n gauthHost: GARMIN_SSO_EMBED,\n service: GARMIN_SSO_EMBED,\n source: GARMIN_SSO_EMBED,\n redirectAfterAccountLoginUrl: GARMIN_SSO_EMBED,\n redirectAfterAccountCreationUrl: GARMIN_SSO_EMBED,\n });\n const body = new URLSearchParams({\n username,\n password,\n embed: \"true\",\n _csrf: csrf,\n });\n const loginRes = await fetchFn(`${SIGNIN_URL}?${loginParams}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n Dnt: \"1\",\n Origin: GARMIN_SSO_ORIGIN,\n Referer: SIGNIN_URL,\n \"User-Agent\": USER_AGENT_BROWSER,\n },\n body: body.toString(),\n });\n return loginRes.text();\n};\n\nexport const getLoginTicket = async (\n username: string,\n password: string,\n fetchFn: FetchFn,\n logger: Logger\n): Promise<string> => {\n const embedParams = new URLSearchParams({\n clientId: \"GarminConnect\",\n locale: \"en\",\n service: GC_MODERN,\n });\n await fetchFn(`${GARMIN_SSO_EMBED}?${embedParams}`);\n\n const csrf = await fetchCsrfToken(fetchFn);\n const loginHtml = await submitLogin(username, password, csrf, fetchFn);\n\n checkAccountLocked(loginHtml);\n checkPageTitle(loginHtml, logger);\n\n const ticketMatch = TICKET_RE.exec(loginHtml);\n if (!ticketMatch) {\n throw createServiceAuthError(\n \"Login failed: ticket not found. Check username and password.\"\n );\n }\n return ticketMatch[1];\n};\n","import { createHmac } from \"node:crypto\";\nimport OAuth from \"oauth-1.0a\";\nimport type { OAuthConsumer } from \"./types\";\n\nexport type { OAuthConsumer } from \"./types\";\nexport type OAuthToken = { key: string; secret: string };\n\nexport type OAuthSigner = {\n toHeader: (\n request: { url: string; method: string },\n token?: OAuthToken\n ) => Record<string, string>;\n};\n\nexport const createOAuthSigner = (consumer: OAuthConsumer): OAuthSigner => {\n const oauth = new OAuth({\n consumer,\n signature_method: \"HMAC-SHA1\",\n hash_function(baseString: string, key: string) {\n return createHmac(\"sha1\", key).update(baseString).digest(\"base64\");\n },\n });\n\n return {\n toHeader: (request, token) => {\n const authorized = token\n ? oauth.authorize(request, token)\n : oauth.authorize(request);\n const header = oauth.toHeader(authorized);\n return Object.fromEntries(Object.entries(header));\n },\n };\n};\n","import { createServiceAuthError } from \"@kaiord/core\";\nimport { createOAuthSigner } from \"./oauth-signer\";\nimport { GARMIN_SSO_EMBED, OAUTH_URL, USER_AGENT_MOBILE } from \"./urls\";\nimport type { FetchFn, OAuthConsumer } from \"./types\";\nimport type { OAuth1Token, OAuth2Token } from \"./types\";\n\nexport const getOAuth1Token = async (\n ticket: string,\n consumer: OAuthConsumer,\n fetchFn: FetchFn\n): Promise<OAuth1Token> => {\n const signer = createOAuthSigner(consumer);\n const params = new URLSearchParams({\n ticket,\n \"login-url\": GARMIN_SSO_EMBED,\n \"accepts-mfa-tokens\": \"true\",\n });\n const url = `${OAUTH_URL}/preauthorized?${params}`;\n const headers = signer.toHeader({ url, method: \"GET\" });\n\n const res = await fetchFn(url, {\n headers: { ...headers, \"User-Agent\": USER_AGENT_MOBILE },\n });\n if (!res.ok) {\n throw createServiceAuthError(\n `OAuth1 token request failed: ${res.status} ${res.statusText}`\n );\n }\n const text = await res.text();\n const parsed = new URLSearchParams(text);\n\n const oauthToken = parsed.get(\"oauth_token\");\n const oauthTokenSecret = parsed.get(\"oauth_token_secret\");\n if (!oauthToken || !oauthTokenSecret) {\n throw createServiceAuthError(\"OAuth1 token exchange failed\");\n }\n\n return { oauth_token: oauthToken, oauth_token_secret: oauthTokenSecret };\n};\n\nexport const exchangeOAuth2 = async (\n oauth1: OAuth1Token,\n consumer: OAuthConsumer,\n fetchFn: FetchFn\n): Promise<OAuth2Token> => {\n const signer = createOAuthSigner(consumer);\n const baseUrl = `${OAUTH_URL}/exchange/user/2.0`;\n const token = {\n key: oauth1.oauth_token,\n secret: oauth1.oauth_token_secret,\n };\n const authHeader = signer.toHeader({ url: baseUrl, method: \"POST\" }, token);\n\n const res = await fetchFn(baseUrl, {\n method: \"POST\",\n headers: {\n ...authHeader,\n \"User-Agent\": USER_AGENT_MOBILE,\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n });\n if (!res.ok) {\n throw createServiceAuthError(\n `OAuth2 exchange failed: ${res.status} ${res.statusText}`\n );\n }\n const data = (await res.json()) as OAuth2Token;\n\n return {\n ...data,\n expires_at: Math.floor(Date.now() / 1000) + data.expires_in,\n };\n};\n","import { fetchOAuthConsumer } from \"./oauth-consumer\";\nimport { getLoginTicket } from \"./sso-login\";\nimport { getOAuth1Token, exchangeOAuth2 as exchange } from \"./sso-oauth\";\nimport type { FetchFn, OAuth1Token, OAuth2Token } from \"./types\";\nimport type { Logger } from \"@kaiord/core\";\n\nexport type { OAuth1Token, OAuth2Token } from \"./types\";\nexport type SsoResult = { oauth1: OAuth1Token; oauth2: OAuth2Token };\n\n/**\n * Garmin Connect SSO login flow.\n * The fetchFn must be cookie-aware (persist cookies across requests).\n * Use fetch-cookie or similar wrapper for Node.js environments.\n */\nexport const garminSso = async (\n username: string,\n password: string,\n logger: Logger,\n fetchFn: FetchFn\n): Promise<SsoResult> => {\n logger.info(\"Starting Garmin Connect SSO login\");\n\n const consumer = await fetchOAuthConsumer(fetchFn);\n const ticket = await getLoginTicket(username, password, fetchFn, logger);\n logger.debug(\"SSO ticket obtained\");\n\n const oauth1 = await getOAuth1Token(ticket, consumer, fetchFn);\n logger.debug(\"OAuth1 token obtained\");\n\n const oauth2 = await exchange(oauth1, consumer, fetchFn);\n logger.info(\"Garmin Connect SSO login successful\");\n\n return { oauth1, oauth2 };\n};\n\nexport { exchangeOAuth2 } from \"./sso-oauth\";\n","import { createServiceApiError } from \"@kaiord/core\";\nimport { exchangeOAuth2 } from \"./garmin-sso\";\nimport { fetchOAuthConsumer } from \"./oauth-consumer\";\nimport type { FetchFn, OAuthConsumer, OAuth1Token, OAuth2Token } from \"./types\";\nimport type { Logger } from \"@kaiord/core\";\n\ntype RefreshSubscriber = {\n resolve: (token: string) => void;\n reject: (error: unknown) => void;\n};\n\nexport type TokenState = {\n oauth1Token: OAuth1Token | undefined;\n oauth2Token: OAuth2Token | undefined;\n consumer: OAuthConsumer | undefined;\n};\n\nexport type TokenRefreshManager = {\n ensureFreshToken: () => Promise<void>;\n state: TokenState;\n};\n\nconst notifyAll = (subs: RefreshSubscriber[], token: string): void => {\n subs.forEach((s) => s.resolve(token));\n};\n\nconst rejectAll = (subs: RefreshSubscriber[], error: unknown): void => {\n subs.forEach((s) => s.reject(error));\n};\n\nconst getOrFetchConsumer = async (\n state: TokenState,\n fetchFn: FetchFn\n): Promise<OAuthConsumer> => {\n if (state.consumer) return state.consumer;\n state.consumer = await fetchOAuthConsumer(fetchFn);\n return state.consumer;\n};\n\nconst doRefreshToken = async (\n state: TokenState,\n fetchFn: FetchFn,\n logger: Logger\n): Promise<void> => {\n if (!state.oauth1Token || !state.oauth2Token) {\n throw createServiceApiError(\"No tokens available for refresh\", 401);\n }\n const cons = await getOrFetchConsumer(state, fetchFn);\n state.oauth2Token = await exchangeOAuth2(state.oauth1Token, cons, fetchFn);\n logger.info(\"OAuth2 token refreshed\");\n};\n\nexport const createTokenRefreshManager = (\n fetchFn: FetchFn,\n logger: Logger\n): TokenRefreshManager => {\n const state: TokenState = {\n oauth1Token: undefined,\n oauth2Token: undefined,\n consumer: undefined,\n };\n let isRefreshing = false;\n let subscribers: RefreshSubscriber[] = [];\n\n return {\n state,\n ensureFreshToken: async (): Promise<void> => {\n if (isRefreshing) {\n await new Promise<string>((resolve, reject) => {\n subscribers.push({ resolve, reject });\n });\n return;\n }\n isRefreshing = true;\n try {\n await doRefreshToken(state, fetchFn, logger);\n if (state.oauth2Token)\n notifyAll(subscribers, state.oauth2Token.access_token);\n subscribers = [];\n } catch (error) {\n rejectAll(subscribers, error);\n subscribers = [];\n throw error;\n } finally {\n isRefreshing = false;\n }\n },\n };\n};\n","import { authFetch } from \"./garmin-auth-fetch\";\nimport { createTokenRefreshManager } from \"./token-refresh\";\nimport type {\n FetchFn,\n GarminHttpClient,\n OAuth1Token,\n OAuth2Token,\n} from \"./types\";\nimport type { Logger } from \"@kaiord/core\";\n\nexport type { GarminHttpClient } from \"./types\";\n\nexport const createGarminHttpClient = (\n logger: Logger,\n fetchFn: FetchFn = globalThis.fetch\n): GarminHttpClient => {\n const refresh = createTokenRefreshManager(fetchFn, logger);\n const fetch = (url: string, init?: RequestInit) =>\n authFetch(url, init, refresh, fetchFn);\n\n return {\n get: async <T>(url: string): Promise<T> => {\n const res = await fetch(url);\n return (await res.json()) as T;\n },\n post: async <T>(url: string, body: unknown): Promise<T> => {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: body !== null ? JSON.stringify(body) : undefined,\n });\n return (await res.json()) as T;\n },\n del: async <T>(url: string): Promise<T> => {\n const res = await fetch(url, {\n method: \"POST\",\n headers: { \"X-Http-Method-Override\": \"DELETE\" },\n });\n const text = await res.text();\n return (text ? JSON.parse(text) : undefined) as T;\n },\n setTokens: (o1: OAuth1Token, o2: OAuth2Token): void => {\n refresh.state.oauth1Token = o1;\n refresh.state.oauth2Token = o2;\n },\n clearTokens: (): void => {\n refresh.state.oauth1Token = undefined;\n refresh.state.oauth2Token = undefined;\n refresh.state.consumer = undefined;\n },\n getOAuth2Token: () => refresh.state.oauth2Token,\n };\n};\n","import { z } from \"zod\";\n\nexport const oauth1TokenSchema = z.object({\n oauth_token: z.string(),\n oauth_token_secret: z.string(),\n});\n\nexport const oauth2TokenSchema = z.object({\n access_token: z.string(),\n refresh_token: z.string(),\n token_type: z.string(),\n expires_in: z.number(),\n refresh_token_expires_in: z.number(),\n expires_at: z.number(),\n});\n\nexport const garminTokensSchema = z.object({\n oauth1: oauth1TokenSchema,\n oauth2: oauth2TokenSchema,\n});\n","import { createConsoleLogger, createServiceAuthError } from \"@kaiord/core\";\nimport { createCookieFetch } from \"../http/cookie-fetch\";\nimport { createGarminHttpClient } from \"../http/garmin-http-client\";\nimport { garminSso } from \"../http/garmin-sso\";\nimport { garminTokensSchema } from \"../schemas/garmin-token.schema\";\nimport type { GarminHttpClient } from \"../http/garmin-http-client\";\nimport type { AuthProvider, Logger, TokenStore } from \"@kaiord/core\";\n\nexport type GarminAuthProviderOptions = {\n logger?: Logger;\n tokenStore?: TokenStore;\n fetchFn?: typeof globalThis.fetch;\n};\n\nexport type GarminAuthProviderResult = {\n auth: AuthProvider;\n httpClient: GarminHttpClient;\n};\n\ntype AuthState = {\n oauth1: { oauth_token: string; oauth_token_secret: string } | undefined;\n};\n\ntype AuthContext = {\n httpClient: GarminHttpClient;\n state: AuthState;\n tokenStore: TokenStore | undefined;\n logger: Logger;\n fetchFn: typeof globalThis.fetch;\n};\n\nconst buildAuthProvider = (ctx: AuthContext): AuthProvider => ({\n login: async (username, password) => {\n const result = await garminSso(username, password, ctx.logger, ctx.fetchFn);\n ctx.state.oauth1 = result.oauth1;\n ctx.httpClient.setTokens(result.oauth1, result.oauth2);\n if (ctx.tokenStore) {\n await ctx.tokenStore.save({\n oauth1: result.oauth1,\n oauth2: result.oauth2,\n });\n }\n },\n is_authenticated: () => {\n const token = ctx.httpClient.getOAuth2Token();\n if (!token) return false;\n return token.expires_at > Math.floor(Date.now() / 1000);\n },\n export_tokens: async () => {\n const token = ctx.httpClient.getOAuth2Token();\n if (!token || !ctx.state.oauth1) {\n throw createServiceAuthError(\"No tokens to export\");\n }\n return { oauth1: ctx.state.oauth1, oauth2: token };\n },\n restore_tokens: async (data) => {\n const parsed = garminTokensSchema.parse(data);\n ctx.state.oauth1 = parsed.oauth1;\n ctx.httpClient.setTokens(parsed.oauth1, parsed.oauth2);\n ctx.logger.info(\"Tokens restored from stored session\");\n },\n logout: async () => {\n ctx.state.oauth1 = undefined;\n ctx.httpClient.clearTokens();\n if (ctx.tokenStore) await ctx.tokenStore.clear();\n ctx.logger.info(\"Logged out from Garmin Connect\");\n },\n});\n\nexport const createGarminAuthProvider = (\n options?: GarminAuthProviderOptions\n): GarminAuthProviderResult => {\n const logger = options?.logger ?? createConsoleLogger();\n const tokenStore = options?.tokenStore;\n const fetchFn = options?.fetchFn ?? createCookieFetch();\n const httpClient = createGarminHttpClient(logger, fetchFn);\n const state: AuthState = { oauth1: undefined };\n const auth = buildAuthProvider({\n httpClient,\n state,\n tokenStore,\n logger,\n fetchFn,\n });\n return { auth, httpClient };\n};\n","import type { WorkoutSummary } from \"@kaiord/core\";\n\nexport const mapToWorkoutSummary = (garminWorkout: {\n workoutId?: number | string;\n workoutName?: string;\n sportType?: { sportTypeKey?: string };\n createdDate?: number | string;\n updatedDate?: number | string;\n}): WorkoutSummary => ({\n id: String(garminWorkout.workoutId ?? \"\"),\n name: garminWorkout.workoutName ?? \"Unnamed\",\n sport: garminWorkout.sportType?.sportTypeKey ?? \"unknown\",\n created_at: garminWorkout.createdDate\n ? new Date(garminWorkout.createdDate).toISOString()\n : \"\",\n updated_at: garminWorkout.updatedDate\n ? new Date(garminWorkout.updatedDate).toISOString()\n : \"\",\n});\n","import { z } from \"zod\";\n\n/** GET /workout-service/workouts?start=N&limit=N - List workout summaries */\nexport const garminWorkoutSummarySchema = z.object({\n workoutId: z.number().or(z.string()),\n workoutName: z.string().optional(),\n sportType: z.object({ sportTypeKey: z.string().optional() }).optional(),\n createdDate: z.number().or(z.string()).optional(),\n updatedDate: z.number().or(z.string()).optional(),\n});\n\n/** POST /workout-service/workout - Push a workout (returns created workout) */\nexport const garminPushResponseSchema = z.object({\n workoutId: z.number().or(z.string()),\n workoutName: z.string().optional(),\n});\n","import {\n createConsoleLogger,\n toText,\n createServiceApiError,\n} from \"@kaiord/core\";\nimport { createGarminWriter } from \"@kaiord/garmin\";\nimport { WORKOUT_URL } from \"../http/urls\";\nimport { mapToWorkoutSummary } from \"../mappers/workout-summary.mapper\";\nimport {\n garminPushResponseSchema,\n garminWorkoutSummarySchema,\n} from \"../schemas/workout-response.schema\";\nimport type { GarminHttpClient } from \"../http/garmin-http-client\";\nimport type {\n KRD,\n ListOptions,\n Logger,\n PushResult,\n WorkoutService,\n WorkoutSummary,\n} from \"@kaiord/core\";\n\nexport type GarminWorkoutClient = Pick<WorkoutService, \"push\" | \"list\">;\n\nconst pushWorkout = async (\n krd: KRD,\n httpClient: GarminHttpClient,\n garminWriter: ReturnType<typeof createGarminWriter>,\n log: Logger\n): Promise<PushResult> => {\n try {\n log.info(\"Pushing workout to Garmin Connect\");\n const gcnJson = await toText(krd, garminWriter, log);\n const payload = JSON.parse(gcnJson) as Record<string, unknown>;\n\n const raw = await httpClient.post<unknown>(\n `${WORKOUT_URL}/workout`,\n payload\n );\n const result = garminPushResponseSchema.parse(raw);\n\n return {\n id: String(result.workoutId),\n name: result.workoutName ?? \"Workout\",\n url: `https://connect.garmin.com/modern/workout/${result.workoutId}`,\n };\n } catch (error) {\n throw createServiceApiError(\"Failed to push workout\", undefined, error);\n }\n};\n\nconst listWorkouts = async (\n httpClient: GarminHttpClient,\n log: Logger,\n options?: ListOptions\n): Promise<WorkoutSummary[]> => {\n try {\n log.info(\"Listing workouts from Garmin Connect\");\n const start = options?.offset ?? 0;\n const limit = options?.limit ?? 20;\n const params = new URLSearchParams({\n start: String(start),\n limit: String(limit),\n });\n\n const raw = await httpClient.get<unknown>(\n `${WORKOUT_URL}/workouts?${params}`\n );\n const workouts = garminWorkoutSummarySchema.array().parse(raw);\n return workouts.map(mapToWorkoutSummary);\n } catch (error) {\n throw createServiceApiError(\"Failed to list workouts\", undefined, error);\n }\n};\n\nexport const createGarminWorkoutService = (\n httpClient: GarminHttpClient,\n logger?: Logger\n): GarminWorkoutClient => {\n const log = logger ?? createConsoleLogger();\n const garminWriter = createGarminWriter(log);\n\n return {\n push: (krd) => pushWorkout(krd, httpClient, garminWriter, log),\n list: (opts) => listWorkouts(httpClient, log, opts),\n };\n};\n","import { createGarminWorkoutService } from \"./garmin-workout-service\";\nimport { createGarminAuthProvider } from \"../auth/garmin-auth-provider\";\nimport type { GarminAuthProviderOptions } from \"../auth/garmin-auth-provider\";\n\nexport type GarminConnectClientOptions = GarminAuthProviderOptions;\n\nexport const createGarminConnectClient = (\n options?: GarminConnectClientOptions\n) => {\n const { auth, httpClient } = createGarminAuthProvider(options);\n const service = createGarminWorkoutService(httpClient, options?.logger);\n return { auth, service };\n};\n","import { readFile, writeFile, mkdir, unlink } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { TokenData, TokenStore } from \"@kaiord/core\";\n\nconst DEFAULT_PATH = join(homedir(), \".kaiord\", \"garmin-tokens.json\");\n\nexport const createFileTokenStore = (\n filePath: string = DEFAULT_PATH\n): TokenStore => ({\n save: async (tokens: TokenData) => {\n await mkdir(dirname(filePath), { recursive: true });\n await writeFile(filePath, JSON.stringify(tokens, null, 2), {\n encoding: \"utf-8\",\n mode: 0o600,\n });\n },\n\n load: async (): Promise<TokenData | null> => {\n try {\n const content = await readFile(filePath, \"utf-8\");\n return JSON.parse(content) as TokenData;\n } catch {\n return null;\n }\n },\n\n clear: async () => {\n try {\n await unlink(filePath);\n } catch {\n // File may not exist\n }\n },\n});\n","import type { TokenData, TokenStore } from \"@kaiord/core\";\n\nexport const createMemoryTokenStore = (): TokenStore => {\n let stored: TokenData | null = null;\n\n return {\n save: async (tokens: TokenData) => {\n stored = tokens;\n },\n load: async () => stored,\n clear: async () => {\n stored = null;\n },\n };\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaiord/garmin-connect",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Garmin Connect API client for the Kaiord health & fitness data framework",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"fetch-cookie": "^3.2.0",
|
|
20
20
|
"oauth-1.0a": "^2.2.6",
|
|
21
21
|
"zod": "^4.0.0",
|
|
22
|
-
"@kaiord/
|
|
23
|
-
"@kaiord/
|
|
22
|
+
"@kaiord/garmin": "^5.0.0",
|
|
23
|
+
"@kaiord/core": "^4.9.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/node": "^25.0.0",
|