@moxxy/plugin-oauth 0.0.33

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 (113) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/openai-device-flow.d.ts +34 -0
  3. package/dist/adapters/openai-device-flow.d.ts.map +1 -0
  4. package/dist/adapters/openai-device-flow.js +136 -0
  5. package/dist/adapters/openai-device-flow.js.map +1 -0
  6. package/dist/adapters/rfc8628-device-flow.d.ts +20 -0
  7. package/dist/adapters/rfc8628-device-flow.d.ts.map +1 -0
  8. package/dist/adapters/rfc8628-device-flow.js +54 -0
  9. package/dist/adapters/rfc8628-device-flow.js.map +1 -0
  10. package/dist/credential-lock.d.ts +60 -0
  11. package/dist/credential-lock.d.ts.map +1 -0
  12. package/dist/credential-lock.js +149 -0
  13. package/dist/credential-lock.js.map +1 -0
  14. package/dist/ensure-fresh.d.ts +63 -0
  15. package/dist/ensure-fresh.d.ts.map +1 -0
  16. package/dist/ensure-fresh.js +122 -0
  17. package/dist/ensure-fresh.js.map +1 -0
  18. package/dist/flow.d.ts +6 -0
  19. package/dist/flow.d.ts.map +1 -0
  20. package/dist/flow.js +5 -0
  21. package/dist/flow.js.map +1 -0
  22. package/dist/index.d.ts +43 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +82 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/oauth/browser-flow.d.ts +23 -0
  27. package/dist/oauth/browser-flow.d.ts.map +1 -0
  28. package/dist/oauth/browser-flow.js +82 -0
  29. package/dist/oauth/browser-flow.js.map +1 -0
  30. package/dist/oauth/callback-server.d.ts +10 -0
  31. package/dist/oauth/callback-server.d.ts.map +1 -0
  32. package/dist/oauth/callback-server.js +158 -0
  33. package/dist/oauth/callback-server.js.map +1 -0
  34. package/dist/oauth/device-flow-shared.d.ts +39 -0
  35. package/dist/oauth/device-flow-shared.d.ts.map +1 -0
  36. package/dist/oauth/device-flow-shared.js +101 -0
  37. package/dist/oauth/device-flow-shared.js.map +1 -0
  38. package/dist/oauth/device-flow.d.ts +29 -0
  39. package/dist/oauth/device-flow.d.ts.map +1 -0
  40. package/dist/oauth/device-flow.js +74 -0
  41. package/dist/oauth/device-flow.js.map +1 -0
  42. package/dist/oauth/poll-until.d.ts +41 -0
  43. package/dist/oauth/poll-until.d.ts.map +1 -0
  44. package/dist/oauth/poll-until.js +70 -0
  45. package/dist/oauth/poll-until.js.map +1 -0
  46. package/dist/oauth/token-exchange.d.ts +28 -0
  47. package/dist/oauth/token-exchange.d.ts.map +1 -0
  48. package/dist/oauth/token-exchange.js +142 -0
  49. package/dist/oauth/token-exchange.js.map +1 -0
  50. package/dist/oauth/types.d.ts +90 -0
  51. package/dist/oauth/types.d.ts.map +1 -0
  52. package/dist/oauth/types.js +2 -0
  53. package/dist/oauth/types.js.map +1 -0
  54. package/dist/open-browser.d.ts +32 -0
  55. package/dist/open-browser.d.ts.map +1 -0
  56. package/dist/open-browser.js +83 -0
  57. package/dist/open-browser.js.map +1 -0
  58. package/dist/pkce.d.ts +20 -0
  59. package/dist/pkce.d.ts.map +1 -0
  60. package/dist/pkce.js +34 -0
  61. package/dist/pkce.js.map +1 -0
  62. package/dist/profile.d.ts +129 -0
  63. package/dist/profile.d.ts.map +1 -0
  64. package/dist/profile.js +11 -0
  65. package/dist/profile.js.map +1 -0
  66. package/dist/run-login.d.ts +9 -0
  67. package/dist/run-login.d.ts.map +1 -0
  68. package/dist/run-login.js +93 -0
  69. package/dist/run-login.js.map +1 -0
  70. package/dist/storage.d.ts +46 -0
  71. package/dist/storage.d.ts.map +1 -0
  72. package/dist/storage.js +167 -0
  73. package/dist/storage.js.map +1 -0
  74. package/dist/tools.d.ts +8 -0
  75. package/dist/tools.d.ts.map +1 -0
  76. package/dist/tools.js +283 -0
  77. package/dist/tools.js.map +1 -0
  78. package/package.json +73 -0
  79. package/skills/google-oauth.md +313 -0
  80. package/skills/oauth-flow.md +132 -0
  81. package/src/adapters/openai-device-flow.test.ts +191 -0
  82. package/src/adapters/openai-device-flow.ts +179 -0
  83. package/src/adapters/rfc8628-device-flow.ts +75 -0
  84. package/src/credential-lock.ts +183 -0
  85. package/src/discovery.test.ts +35 -0
  86. package/src/ensure-fresh.test.ts +279 -0
  87. package/src/ensure-fresh.ts +178 -0
  88. package/src/flow.ts +9 -0
  89. package/src/index.ts +153 -0
  90. package/src/oauth/browser-flow.ts +96 -0
  91. package/src/oauth/callback-server.test.ts +90 -0
  92. package/src/oauth/callback-server.ts +208 -0
  93. package/src/oauth/device-flow-shared.test.ts +142 -0
  94. package/src/oauth/device-flow-shared.ts +129 -0
  95. package/src/oauth/device-flow.test.ts +102 -0
  96. package/src/oauth/device-flow.ts +82 -0
  97. package/src/oauth/poll-until.test.ts +63 -0
  98. package/src/oauth/poll-until.ts +100 -0
  99. package/src/oauth/token-exchange.test.ts +224 -0
  100. package/src/oauth/token-exchange.ts +169 -0
  101. package/src/oauth/types.ts +92 -0
  102. package/src/oauth.test.ts +185 -0
  103. package/src/open-browser-spawn.test.ts +69 -0
  104. package/src/open-browser.test.ts +30 -0
  105. package/src/open-browser.ts +84 -0
  106. package/src/pkce.ts +37 -0
  107. package/src/profile.test.ts +331 -0
  108. package/src/profile.ts +135 -0
  109. package/src/run-login.ts +123 -0
  110. package/src/storage.test.ts +191 -0
  111. package/src/storage.ts +208 -0
  112. package/src/tools.test.ts +239 -0
  113. package/src/tools.ts +332 -0
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Open a URL in the user's default browser. Platform-specific:
3
+ * macOS → `open <url>`
4
+ * Linux → `xdg-open <url>`
5
+ * Win32 → `cmd /c start "" "<url>"` (the URL DOUBLE-QUOTED + args passed
6
+ * verbatim — see {@link browserOpenCommand})
7
+ *
8
+ * Returns once the helper process is spawned — it does NOT wait for the
9
+ * browser itself, since the user's flow continues asynchronously via
10
+ * the OAuth callback. Failures fall back to returning the URL string
11
+ * so the caller can print it for the user to paste manually.
12
+ */
13
+ export declare function openInBrowser(url: string): Promise<void>;
14
+ /**
15
+ * Resolve the platform's "open this URL in the default browser" command.
16
+ * Exported + `platform`-parameterized so the Windows quoting can be unit-tested
17
+ * without actually spawning a browser.
18
+ *
19
+ * Windows is the tricky one: `&` (and `|`, `<`, `>`, `^`) in a URL are cmd.exe
20
+ * command separators. `cmd /c start "" http://a?x=1&y=2` truncates the URL at
21
+ * the first `&`, so an OAuth authorize URL loses redirect_uri / code_challenge /
22
+ * state and the provider rejects the request ("authorization error"). Wrapping
23
+ * the URL in double quotes makes cmd treat it as one literal token; passing the
24
+ * args verbatim (`windowsVerbatimArguments`) stops Node from re-quoting and
25
+ * undoing that. The empty `""` is the required `start` window-title placeholder.
26
+ */
27
+ export declare function browserOpenCommand(url: string, platform?: NodeJS.Platform): {
28
+ cmd: string;
29
+ args: string[];
30
+ verbatim?: boolean;
31
+ };
32
+ //# sourceMappingURL=open-browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-browser.d.ts","sourceRoot":"","sources":["../src/open-browser.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAwC9D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CAWrD"}
@@ -0,0 +1,83 @@
1
+ import { spawn } from 'node:child_process';
2
+ /**
3
+ * Open a URL in the user's default browser. Platform-specific:
4
+ * macOS → `open <url>`
5
+ * Linux → `xdg-open <url>`
6
+ * Win32 → `cmd /c start "" "<url>"` (the URL DOUBLE-QUOTED + args passed
7
+ * verbatim — see {@link browserOpenCommand})
8
+ *
9
+ * Returns once the helper process is spawned — it does NOT wait for the
10
+ * browser itself, since the user's flow continues asynchronously via
11
+ * the OAuth callback. Failures fall back to returning the URL string
12
+ * so the caller can print it for the user to paste manually.
13
+ */
14
+ export async function openInBrowser(url) {
15
+ const { cmd, args, verbatim } = browserOpenCommand(url);
16
+ await new Promise((resolve, reject) => {
17
+ const child = spawn(cmd, args, {
18
+ stdio: 'ignore',
19
+ detached: true,
20
+ // Windows only: keep cmd.exe from re-quoting our already-quoted URL,
21
+ // which would otherwise break the `&` escaping (see below).
22
+ ...(verbatim ? { windowsVerbatimArguments: true } : {}),
23
+ });
24
+ // Settle exactly once. An early spawn `error` rejects AND clears the pending
25
+ // 50ms timer (else it would fire a stray resolve later and hold a live timer
26
+ // handle on the event loop). After we've resolved, a LATE async spawn error
27
+ // must NOT be left unhandled: an 'error' event with no listener is fatal in
28
+ // Node (it re-throws as an uncaught exception). So we keep an error listener
29
+ // attached for the child's whole lifetime — it rejects before settle, and
30
+ // harmlessly swallows after.
31
+ let settled = false;
32
+ const onError = (err) => {
33
+ if (settled)
34
+ return; // post-resolve late error: swallow, never crash
35
+ settled = true;
36
+ clearTimeout(timer);
37
+ reject(err);
38
+ };
39
+ const timer = setTimeout(() => {
40
+ if (settled)
41
+ return;
42
+ settled = true;
43
+ resolve();
44
+ }, 50);
45
+ // Don't let the 50ms settle-timer keep the process alive on its own.
46
+ timer.unref?.();
47
+ // `on` (not `once`): the listener stays for the child's lifetime so any late
48
+ // 'error' is always handled (a listenerless 'error' would crash the process).
49
+ child.on('error', onError);
50
+ // `unref` so a misbehaving opener process can't keep the moxxy
51
+ // process alive past its own lifetime.
52
+ child.unref();
53
+ // Resolve after spawn — we never want to wait for the browser to
54
+ // close. A 50ms tick is enough to surface spawn-time errors.
55
+ });
56
+ }
57
+ /**
58
+ * Resolve the platform's "open this URL in the default browser" command.
59
+ * Exported + `platform`-parameterized so the Windows quoting can be unit-tested
60
+ * without actually spawning a browser.
61
+ *
62
+ * Windows is the tricky one: `&` (and `|`, `<`, `>`, `^`) in a URL are cmd.exe
63
+ * command separators. `cmd /c start "" http://a?x=1&y=2` truncates the URL at
64
+ * the first `&`, so an OAuth authorize URL loses redirect_uri / code_challenge /
65
+ * state and the provider rejects the request ("authorization error"). Wrapping
66
+ * the URL in double quotes makes cmd treat it as one literal token; passing the
67
+ * args verbatim (`windowsVerbatimArguments`) stops Node from re-quoting and
68
+ * undoing that. The empty `""` is the required `start` window-title placeholder.
69
+ */
70
+ export function browserOpenCommand(url, platform = process.platform) {
71
+ if (platform === 'darwin')
72
+ return { cmd: 'open', args: [url] };
73
+ if (platform === 'win32') {
74
+ return {
75
+ cmd: process.env.ComSpec || 'cmd.exe',
76
+ args: ['/c', 'start', '""', `"${url}"`],
77
+ verbatim: true,
78
+ };
79
+ }
80
+ // Linux + everything else assumes a Freedesktop-compliant `xdg-open`.
81
+ return { cmd: 'xdg-open', args: [url] };
82
+ }
83
+ //# sourceMappingURL=open-browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-browser.js","sourceRoot":"","sources":["../src/open-browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE3C;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAW;IAC7C,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC;IACxD,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;YAC7B,KAAK,EAAE,QAAQ;YACf,QAAQ,EAAE,IAAI;YACd,qEAAqE;YACrE,4DAA4D;YAC5D,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,wBAAwB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxD,CAAC,CAAC;QACH,6EAA6E;QAC7E,6EAA6E;QAC7E,4EAA4E;QAC5E,4EAA4E;QAC5E,6EAA6E;QAC7E,0EAA0E;QAC1E,6BAA6B;QAC7B,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,OAAO,GAAG,CAAC,GAAU,EAAQ,EAAE;YACnC,IAAI,OAAO;gBAAE,OAAO,CAAC,gDAAgD;YACrE,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,GAAG,CAAC,CAAC;QACd,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,EAAE,CAAC,CAAC;QACP,qEAAqE;QACrE,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;QAChB,6EAA6E;QAC7E,8EAA8E;QAC9E,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC3B,+DAA+D;QAC/D,uCAAuC;QACvC,KAAK,CAAC,KAAK,EAAE,CAAC;QACd,iEAAiE;QACjE,6DAA6D;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAW,EACX,WAA4B,OAAO,CAAC,QAAQ;IAE5C,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;IAC/D,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,OAAO;YACL,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,SAAS;YACrC,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,GAAG,GAAG,CAAC;YACvC,QAAQ,EAAE,IAAI;SACf,CAAC;IACJ,CAAC;IACD,sEAAsE;IACtE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;AAC1C,CAAC"}
package/dist/pkce.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Generate a PKCE code verifier per RFC 7636 §4.1.
3
+ * Spec range: 43–128 chars from the unreserved set [A-Z a-z 0-9 - . _ ~].
4
+ * We use 96 bytes of entropy → 128 base64url chars (full length).
5
+ */
6
+ export declare function generateCodeVerifier(): string;
7
+ /**
8
+ * RFC 7636 §4.2 — SHA-256(verifier) then base64url-encode. The
9
+ * challenge_method is implied: callers MUST send `code_challenge_method=S256`
10
+ * to the authorization endpoint when using this helper.
11
+ */
12
+ export declare function computeCodeChallenge(codeVerifier: string): string;
13
+ /**
14
+ * Opaque CSRF token included in the `state` query param. The callback
15
+ * server verifies that the returned state matches what we sent, so a
16
+ * cross-site forgery can't redirect the user back into our local
17
+ * server with attacker-controlled code.
18
+ */
19
+ export declare function generateState(): string;
20
+ //# sourceMappingURL=pkce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
package/dist/pkce.js ADDED
@@ -0,0 +1,34 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ /**
3
+ * Generate a PKCE code verifier per RFC 7636 §4.1.
4
+ * Spec range: 43–128 chars from the unreserved set [A-Z a-z 0-9 - . _ ~].
5
+ * We use 96 bytes of entropy → 128 base64url chars (full length).
6
+ */
7
+ export function generateCodeVerifier() {
8
+ return base64urlEncode(randomBytes(96));
9
+ }
10
+ /**
11
+ * RFC 7636 §4.2 — SHA-256(verifier) then base64url-encode. The
12
+ * challenge_method is implied: callers MUST send `code_challenge_method=S256`
13
+ * to the authorization endpoint when using this helper.
14
+ */
15
+ export function computeCodeChallenge(codeVerifier) {
16
+ return base64urlEncode(createHash('sha256').update(codeVerifier).digest());
17
+ }
18
+ /**
19
+ * Opaque CSRF token included in the `state` query param. The callback
20
+ * server verifies that the returned state matches what we sent, so a
21
+ * cross-site forgery can't redirect the user back into our local
22
+ * server with attacker-controlled code.
23
+ */
24
+ export function generateState() {
25
+ return base64urlEncode(randomBytes(24));
26
+ }
27
+ function base64urlEncode(buf) {
28
+ return buf
29
+ .toString('base64')
30
+ .replace(/\+/g, '-')
31
+ .replace(/\//g, '_')
32
+ .replace(/=+$/, '');
33
+ }
34
+ //# sourceMappingURL=pkce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;GAIG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,eAAe,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAAC,YAAoB;IACvD,OAAO,eAAe,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,eAAe,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,eAAe,CAAC,GAAW;IAClC,OAAO,GAAG;SACP,QAAQ,CAAC,QAAQ,CAAC;SAClB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Provider-framework types. A new OAuth provider plugs in by exporting an
3
+ * `OAuthProviderProfile` and (if it supports device-flow) one of the bundled
4
+ * adapters — or a custom adapter for a non-standard dialect.
5
+ *
6
+ * The host then calls `runOauthLogin(profile, ctx)` to drive the full dance
7
+ * and `ensureFreshTokens(profile, vault)` before each upstream request to
8
+ * keep the access token fresh.
9
+ */
10
+ import type { PollOutcome, PollState } from './oauth/poll-until.js';
11
+ import type { TokenSet } from './oauth/types.js';
12
+ /**
13
+ * Single source of truth for everything OAuth-related about a provider.
14
+ * Constants + extraction logic live here; the framework handles the
15
+ * actual HTTP / vault / refresh plumbing.
16
+ */
17
+ export interface OAuthProviderProfile {
18
+ /**
19
+ * Stable namespace under `oauth/<id>/*` in the vault. Lowercase,
20
+ * digits, dot/dash/underscore only — same rules as `validateProvider`.
21
+ * Example: `'openai-codex'`, `'google'`, `'github'`.
22
+ */
23
+ readonly id: string;
24
+ /** Authorization endpoint, e.g. https://accounts.google.com/o/oauth2/v2/auth */
25
+ readonly authUrl: string;
26
+ /** Token endpoint, e.g. https://oauth2.googleapis.com/token */
27
+ readonly tokenUrl: string;
28
+ readonly clientId: string;
29
+ /** Confidential clients only — loopback flows usually omit. */
30
+ readonly clientSecret?: string;
31
+ readonly scopes: ReadonlyArray<string>;
32
+ /**
33
+ * Provider-specific auth-URL params. Google wants
34
+ * `access_type=offline` + `prompt=consent` for a refresh_token; Codex
35
+ * wants `id_token_add_organizations=true` etc.
36
+ */
37
+ readonly extraAuthParams?: Readonly<Record<string, string>>;
38
+ /**
39
+ * Loopback callback config. Provider's registered redirect URI must
40
+ * match `http://localhost:<port><path>` EXACTLY.
41
+ */
42
+ readonly redirect?: {
43
+ readonly port?: number;
44
+ readonly path?: string;
45
+ };
46
+ /**
47
+ * Device-flow adapter. Omit when the provider doesn't support a
48
+ * headless flow — `runOauthLogin` will reject `headless=true` with a
49
+ * helpful message.
50
+ */
51
+ readonly deviceFlow?: DeviceFlowAdapter;
52
+ /**
53
+ * Pull a stable per-account identifier out of the token set. Codex
54
+ * extracts `chatgpt_account_id` from the id_token; Google could
55
+ * extract `email`. Returned value is shown to the user post-login and
56
+ * persisted under `extras.account_id`. Optional — providers that
57
+ * don't expose a useful identifier skip it.
58
+ */
59
+ readonly extractAccountId?: (tokens: TokenSet) => string | undefined;
60
+ /**
61
+ * Extract extra provider-specific fields to persist alongside the
62
+ * token set (e.g. `team_slug`, `org_id`). Merged with the account_id
63
+ * (if any) into the `extras` map under `oauth/<id>/extras`.
64
+ */
65
+ readonly extractExtras?: (tokens: TokenSet) => Readonly<Record<string, string>>;
66
+ /**
67
+ * Human-readable name used in prompts and success messages, e.g.
68
+ * "ChatGPT Pro/Plus", "Google Workspace". Falls back to `id` when omitted.
69
+ */
70
+ readonly displayName?: string;
71
+ }
72
+ /**
73
+ * Pluggable device-flow contract. Implementations encode a single dialect
74
+ * (RFC 8628, OpenAI's flavor, ...). The framework calls `start()` once,
75
+ * surfaces the returned prompt to the user, then modes on `poll()` via
76
+ * the shared `pollUntil` primitive.
77
+ */
78
+ export interface DeviceFlowAdapter {
79
+ start(args: DeviceFlowStartArgs): Promise<DeviceFlowInit>;
80
+ /**
81
+ * Single poll attempt against the provider's poll endpoint. May
82
+ * mutate `state.intervalMs` to honour a `slow_down` from the server.
83
+ * Resolves to `{done}` with the issued TokenSet, or `{pending: true}`
84
+ * to keep polling. Throws on fatal errors (denied / expired).
85
+ */
86
+ poll(init: DeviceFlowInit, state: PollState): Promise<PollOutcome<TokenSet>>;
87
+ }
88
+ export interface DeviceFlowStartArgs {
89
+ readonly clientId: string;
90
+ readonly clientSecret?: string;
91
+ readonly scopes: ReadonlyArray<string>;
92
+ readonly signal?: AbortSignal;
93
+ }
94
+ export interface DeviceFlowInit {
95
+ /** Short code the user enters at `verificationUri`. */
96
+ readonly userCode: string;
97
+ readonly verificationUri: string;
98
+ /** Some providers return a URL that already embeds the user_code. */
99
+ readonly verificationUriComplete?: string;
100
+ /** Initial poll interval. The framework honours `slow_down` bumps. */
101
+ readonly intervalMs: number;
102
+ /** Hard deadline (ms from now) before the device_code dies. */
103
+ readonly expiresInMs: number;
104
+ /**
105
+ * Opaque data the adapter threads from `start()` to `poll()` —
106
+ * device_code, server-assigned ids, code_verifier, etc. The framework
107
+ * never inspects this.
108
+ */
109
+ readonly providerData: unknown;
110
+ }
111
+ export interface RunOauthLoginCtx {
112
+ readonly vault: {
113
+ get(key: string): Promise<string | null>;
114
+ set(key: string, value: string, tags?: ReadonlyArray<string>): Promise<void>;
115
+ delete?(key: string): Promise<boolean>;
116
+ };
117
+ /** True ⇒ choose the device flow. False ⇒ open a browser. */
118
+ readonly headless: boolean;
119
+ /** Progress-message sink. The host wires this to its preferred renderer. */
120
+ readonly write: (chunk: string) => void;
121
+ readonly signal?: AbortSignal;
122
+ }
123
+ export interface RunOauthLoginResult {
124
+ readonly tokens: TokenSet;
125
+ /** Convenience surface for `ProviderOAuthResult.accountId`. */
126
+ readonly accountId?: string;
127
+ readonly extras: Readonly<Record<string, string>>;
128
+ }
129
+ //# sourceMappingURL=profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Provider-framework types. A new OAuth provider plugs in by exporting an
3
+ * `OAuthProviderProfile` and (if it supports device-flow) one of the bundled
4
+ * adapters — or a custom adapter for a non-standard dialect.
5
+ *
6
+ * The host then calls `runOauthLogin(profile, ctx)` to drive the full dance
7
+ * and `ensureFreshTokens(profile, vault)` before each upstream request to
8
+ * keep the access token fresh.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * High-level orchestrator. Drives the full login dance for any provider
3
+ * declared via `OAuthProviderProfile` — picks browser or device flow based
4
+ * on `ctx.headless`, surfaces the user-facing prompt, persists the result
5
+ * (token set + extras) under `oauth/<profile.id>/*`.
6
+ */
7
+ import type { OAuthProviderProfile, RunOauthLoginCtx, RunOauthLoginResult } from './profile.js';
8
+ export declare function runOauthLogin(profile: OAuthProviderProfile, ctx: RunOauthLoginCtx): Promise<RunOauthLoginResult>;
9
+ //# sourceMappingURL=run-login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-login.d.ts","sourceRoot":"","sources":["../src/run-login.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,OAAO,KAAK,EAGV,oBAAoB,EACpB,gBAAgB,EAChB,mBAAmB,EACpB,MAAM,cAAc,CAAC;AAMtB,wBAAsB,aAAa,CACjC,OAAO,EAAE,oBAAoB,EAC7B,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,mBAAmB,CAAC,CAsB9B"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * High-level orchestrator. Drives the full login dance for any provider
3
+ * declared via `OAuthProviderProfile` — picks browser or device flow based
4
+ * on `ctx.headless`, surfaces the user-facing prompt, persists the result
5
+ * (token set + extras) under `oauth/<profile.id>/*`.
6
+ */
7
+ import { MoxxyError } from '@moxxy/sdk';
8
+ import { runAuthorizationCodeFlow } from './oauth/browser-flow.js';
9
+ import { pollUntil } from './oauth/poll-until.js';
10
+ import { storeTokenSet } from './storage.js';
11
+ const DEFAULT_BROWSER_TIMEOUT_MS = 5 * 60 * 1000;
12
+ const DEFAULT_DEVICE_TIMEOUT_MS = 10 * 60 * 1000;
13
+ const DEVICE_POLL_SAFETY_MARGIN_MS = 3000;
14
+ export async function runOauthLogin(profile, ctx) {
15
+ const tokens = ctx.headless
16
+ ? await runDeviceFlow(profile, ctx)
17
+ : await runBrowserFlow(profile, ctx);
18
+ const accountId = profile.extractAccountId?.(tokens);
19
+ const customExtras = profile.extractExtras?.(tokens) ?? {};
20
+ const extras = { ...customExtras };
21
+ if (accountId)
22
+ extras.account_id = accountId;
23
+ await storeTokenSet(ctx.vault, profile.id, tokens, {
24
+ clientId: profile.clientId,
25
+ ...(profile.clientSecret ? { clientSecret: profile.clientSecret } : {}),
26
+ tokenUrl: profile.tokenUrl,
27
+ extras,
28
+ });
29
+ return {
30
+ tokens,
31
+ ...(accountId ? { accountId } : {}),
32
+ extras,
33
+ };
34
+ }
35
+ async function runBrowserFlow(profile, ctx) {
36
+ const port = profile.redirect?.port ?? 8765;
37
+ const path = profile.redirect?.path ?? '/callback';
38
+ const serviceName = profile.displayName ?? profile.id;
39
+ return runAuthorizationCodeFlow({
40
+ authUrl: profile.authUrl,
41
+ tokenUrl: profile.tokenUrl,
42
+ clientId: profile.clientId,
43
+ ...(profile.clientSecret ? { clientSecret: profile.clientSecret } : {}),
44
+ scopes: profile.scopes,
45
+ redirectPort: port,
46
+ redirectPath: path,
47
+ ...(profile.extraAuthParams ? { extraAuthParams: profile.extraAuthParams } : {}),
48
+ timeoutMs: DEFAULT_BROWSER_TIMEOUT_MS,
49
+ ...(ctx.signal ? { signal: ctx.signal } : {}),
50
+ onAuthUrl: (url) => {
51
+ ctx.write(`\nSign in to ${serviceName} to authorize moxxy\n\n` +
52
+ `If your browser doesn't open automatically, paste this URL:\n\n ${url}\n\n` +
53
+ `Waiting for callback on http://localhost:${port}${path} (5 min timeout)…\n\n`);
54
+ },
55
+ });
56
+ }
57
+ async function runDeviceFlow(profile, ctx) {
58
+ if (!profile.deviceFlow) {
59
+ throw new MoxxyError({
60
+ code: 'OAUTH_FLOW_NOT_SUPPORTED',
61
+ message: `Provider "${profile.id}" doesn't expose a headless (device-code) flow.`,
62
+ hint: 'Run this command on a host with a browser, or ask the provider plugin author to ' +
63
+ 'add a deviceFlow adapter to the profile.',
64
+ context: { provider: profile.id },
65
+ });
66
+ }
67
+ const adapter = profile.deviceFlow;
68
+ const init = await adapter.start({
69
+ clientId: profile.clientId,
70
+ ...(profile.clientSecret ? { clientSecret: profile.clientSecret } : {}),
71
+ scopes: profile.scopes,
72
+ ...(ctx.signal ? { signal: ctx.signal } : {}),
73
+ });
74
+ const serviceName = profile.displayName ?? profile.id;
75
+ ctx.write(`\nSign in to ${serviceName} (headless / device code flow)\n\n` +
76
+ ` 1. On any browser-capable device, open:\n` +
77
+ ` ${init.verificationUriComplete ?? init.verificationUri}\n\n` +
78
+ ` 2. Enter this code:\n` +
79
+ ` ${init.userCode}\n\n` +
80
+ `Polling every ${Math.round(init.intervalMs / 1000)}s ` +
81
+ `(${Math.round(init.expiresInMs / 60000)} min timeout)…\n\n`);
82
+ // Cap the polling deadline by the device_code's own expiry — once it
83
+ // dies, any further poll is wasted.
84
+ const timeoutMs = Math.min(init.expiresInMs, DEFAULT_DEVICE_TIMEOUT_MS);
85
+ return pollUntil((state) => adapter.poll(init, state), {
86
+ intervalMs: init.intervalMs + DEVICE_POLL_SAFETY_MARGIN_MS,
87
+ timeoutMs,
88
+ label: `${profile.id} device flow`,
89
+ leadingWait: true,
90
+ ...(ctx.signal ? { signal: ctx.signal } : {}),
91
+ });
92
+ }
93
+ //# sourceMappingURL=run-login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-login.js","sourceRoot":"","sources":["../src/run-login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ import type { TokenSet } from './flow.js';
2
+ /**
3
+ * Minimal vault shape this module uses. Both `@moxxy/plugin-vault`'s
4
+ * `VaultStore` and the SDK's `ProviderVault` structurally satisfy it, so
5
+ * callers can pass whichever they hold.
6
+ */
7
+ export interface OAuthVault {
8
+ get(key: string): Promise<string | null>;
9
+ set(key: string, value: string, tags?: ReadonlyArray<string>): Promise<void>;
10
+ delete?(key: string): Promise<boolean>;
11
+ }
12
+ export declare function validateProvider(name: string): void;
13
+ export interface StoreTokenSetMeta {
14
+ readonly clientId: string;
15
+ readonly clientSecret?: string;
16
+ readonly tokenUrl: string;
17
+ /**
18
+ * Provider-specific extras the framework persists alongside the token
19
+ * set. Used for fields like `account_id`, `org_id`, `team_slug` that
20
+ * don't fit `TokenSet` but the caller wants to recover later.
21
+ */
22
+ readonly extras?: Readonly<Record<string, string>>;
23
+ }
24
+ export interface StoredCreds {
25
+ readonly tokenSet: TokenSet;
26
+ readonly clientId: string;
27
+ readonly clientSecret: string | null;
28
+ readonly tokenUrl: string;
29
+ readonly extras: Readonly<Record<string, string>>;
30
+ }
31
+ export declare function storeTokenSet(vault: OAuthVault, provider: string, tokens: TokenSet, meta: StoreTokenSetMeta): Promise<void>;
32
+ export declare function readStoredCreds(vault: OAuthVault, provider: string): Promise<StoredCreds | null>;
33
+ export declare function clearStoredCreds(vault: OAuthVault, provider: string): Promise<number>;
34
+ /**
35
+ * True when the access token has expired (or is within `skewMs` of doing so).
36
+ *
37
+ * A token with no `expiresAt` is treated as non-expiring (some providers issue
38
+ * tokens without `expires_in`). A non-FINITE `expiresAt` (NaN/Infinity — corrupt
39
+ * data, an out-of-range parse) is treated as EXPIRED, never as fresh: a bare
40
+ * `>=` against NaN is always false, which would otherwise mark a token eternally
41
+ * valid and suppress every refresh. Forcing the expired branch degrades to a
42
+ * refresh (or a clear AUTH_EXPIRED if no refresh_token) instead of silently
43
+ * serving a token whose real expiry is unknown.
44
+ */
45
+ export declare function isExpired(tokens: TokenSet, skewMs?: number): boolean;
46
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAqB1C;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACxC;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMnD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACnD;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,QAAQ,EAChB,IAAI,EAAE,iBAAiB,GACtB,OAAO,CAAC,IAAI,CAAC,CAkCf;AAED,wBAAsB,eAAe,CACnC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAiD7B;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoB3F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,SAAS,GAAG,OAAO,CAIpE"}