@tutti-os/auth-bridge 0.0.189 → 0.0.191

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/README.md CHANGED
@@ -21,12 +21,17 @@ import { createTuttiNodeAuthClient } from "@tutti-os/auth-bridge/node";
21
21
 
22
22
  const auth = createTuttiNodeAuthClient({
23
23
  authJsonPath,
24
- appCallbackUrl
24
+ appCallbackUrl,
25
+ accountHeaders: { "x-environment-lane": "example" }
25
26
  });
26
27
 
27
28
  const { session, user } = await auth.login();
28
29
  ```
29
30
 
31
+ `accountHeaders` adds host-owned headers to Account API requests. The bridge
32
+ always owns and overwrites its JSON content negotiation and Desktop session
33
+ cookie headers.
34
+
30
35
  ## Local Desktop Callback
31
36
 
32
37
  The Node bridge starts a loopback HTTP server for desktop sign-in. New desktop OAuth completions use:
package/dist/node.d.ts CHANGED
@@ -13,6 +13,7 @@ interface TuttiNodeAuthClientOptions {
13
13
  hostname?: string;
14
14
  loginIdleTimeoutMs?: number;
15
15
  loginMaxTimeoutMs?: number;
16
+ accountHeaders?: Readonly<Record<string, string>>;
16
17
  }
17
18
  interface TuttiNodeAuthClient {
18
19
  login: () => Promise<{
package/dist/node.js CHANGED
@@ -46,6 +46,7 @@ function createTuttiNodeAuthClient(options) {
46
46
  idleTimeoutMs,
47
47
  positiveMs(options.loginMaxTimeoutMs, DEFAULT_LOGIN_MAX_TIMEOUT_MS)
48
48
  );
49
+ const accountHeaders = { ...options.accountHeaders };
49
50
  async function readSession() {
50
51
  return await readAuthJson(authJsonPath);
51
52
  }
@@ -57,7 +58,11 @@ function createTuttiNodeAuthClient(options) {
57
58
  if (!session) {
58
59
  return null;
59
60
  }
60
- const user = await fetchUserInfo(accountBaseUrl, session.cookie);
61
+ const user = await fetchUserInfo(
62
+ accountBaseUrl,
63
+ session.cookie,
64
+ accountHeaders
65
+ );
61
66
  if (!user) {
62
67
  return null;
63
68
  }
@@ -86,6 +91,7 @@ function createTuttiNodeAuthClient(options) {
86
91
  hostname: options.hostname?.trim() || hostname()
87
92
  });
88
93
  const pending = {
94
+ accountHeaders,
89
95
  accountBaseUrl,
90
96
  appId,
91
97
  appCallbackUrl,
@@ -120,7 +126,12 @@ function createTuttiNodeAuthClient(options) {
120
126
  logout: async () => {
121
127
  const session = await readSession();
122
128
  if (session) {
123
- await logoutSession(accountBaseUrl, appId, session.cookie);
129
+ await logoutSession(
130
+ accountBaseUrl,
131
+ appId,
132
+ session.cookie,
133
+ accountHeaders
134
+ );
124
135
  }
125
136
  await clearSession();
126
137
  },
@@ -357,7 +368,8 @@ async function completePendingLogin(input, transferCode) {
357
368
  const sessionId = await redeemDesktopTransferCode(input, transferCode);
358
369
  const user = await fetchUserInfo(
359
370
  input.accountBaseUrl,
360
- buildSessionCookie(sessionId)
371
+ buildSessionCookie(sessionId),
372
+ input.accountHeaders
361
373
  );
362
374
  if (!user) {
363
375
  throw new Error("Failed to load user info after login");
@@ -381,16 +393,12 @@ async function waitForCompletion(input, completion) {
381
393
  }
382
394
  }
383
395
  }
384
- async function fetchUserInfo(accountBaseUrl, cookie) {
396
+ async function fetchUserInfo(accountBaseUrl, cookie, accountHeaders) {
385
397
  const response = await fetch(
386
398
  buildAccountUrl(accountBaseUrl, "/user/v1/user_info"),
387
399
  {
388
400
  method: "POST",
389
- headers: {
390
- Accept: "application/json",
391
- "Content-Type": "application/json",
392
- Cookie: cookie
393
- },
401
+ headers: buildAccountHeaders(accountHeaders, cookie),
394
402
  body: JSON.stringify({})
395
403
  }
396
404
  );
@@ -411,10 +419,7 @@ async function redeemDesktopTransferCode(input, transferCode) {
411
419
  ),
412
420
  {
413
421
  method: "POST",
414
- headers: {
415
- Accept: "application/json",
416
- "Content-Type": "application/json"
417
- },
422
+ headers: buildAccountHeaders(input.accountHeaders),
418
423
  body: JSON.stringify({
419
424
  transfer_code: transferCode,
420
425
  attempt_id: input.attemptId,
@@ -431,17 +436,13 @@ async function redeemDesktopTransferCode(input, transferCode) {
431
436
  }
432
437
  return sessionId;
433
438
  }
434
- async function logoutSession(accountBaseUrl, appId, cookie) {
439
+ async function logoutSession(accountBaseUrl, appId, cookie, accountHeaders) {
435
440
  const response = await fetch(
436
441
  buildAccountUrl(accountBaseUrl, "/auth/v1/logout-web-session"),
437
442
  {
438
443
  method: "POST",
439
- headers: {
440
- Accept: "application/json",
441
- "Content-Type": "application/json",
442
- Cookie: cookie
443
- },
444
- body: JSON.stringify({ appId })
444
+ headers: buildAccountHeaders(accountHeaders, cookie),
445
+ body: JSON.stringify({ app_id: appId })
445
446
  }
446
447
  );
447
448
  const payload = await response.json().catch(() => null);
@@ -449,6 +450,17 @@ async function logoutSession(accountBaseUrl, appId, cookie) {
449
450
  throw readEnvelopeError(response, payload);
450
451
  }
451
452
  }
453
+ function buildAccountHeaders(accountHeaders, cookie) {
454
+ const headers = new Headers(accountHeaders);
455
+ headers.set("Accept", "application/json");
456
+ headers.set("Content-Type", "application/json");
457
+ if (cookie) {
458
+ headers.set("Cookie", cookie);
459
+ } else {
460
+ headers.delete("Cookie");
461
+ }
462
+ return headers;
463
+ }
452
464
  async function findAvailablePort() {
453
465
  for (let port = AUTH_SERVER_BASE_PORT; port <= AUTH_SERVER_MAX_PORT; port += 1) {
454
466
  const available = await new Promise((resolve) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tutti-os/auth-bridge",
3
- "version": "0.0.189",
3
+ "version": "0.0.191",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",