@provos/ironcurtain 0.8.0 → 0.9.1

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 (216) hide show
  1. package/README.md +11 -10
  2. package/dist/auth/auth-command.d.ts +15 -0
  3. package/dist/auth/auth-command.js +424 -0
  4. package/dist/auth/auth-command.js.map +1 -0
  5. package/dist/auth/oauth-flow.d.ts +46 -0
  6. package/dist/auth/oauth-flow.js +279 -0
  7. package/dist/auth/oauth-flow.js.map +1 -0
  8. package/dist/auth/oauth-provider.d.ts +55 -0
  9. package/dist/auth/oauth-provider.js +73 -0
  10. package/dist/auth/oauth-provider.js.map +1 -0
  11. package/dist/auth/oauth-registry.d.ts +34 -0
  12. package/dist/auth/oauth-registry.js +69 -0
  13. package/dist/auth/oauth-registry.js.map +1 -0
  14. package/dist/auth/oauth-token-provider.d.ts +52 -0
  15. package/dist/auth/oauth-token-provider.js +159 -0
  16. package/dist/auth/oauth-token-provider.js.map +1 -0
  17. package/dist/auth/oauth-token-store.d.ts +30 -0
  18. package/dist/auth/oauth-token-store.js +93 -0
  19. package/dist/auth/oauth-token-store.js.map +1 -0
  20. package/dist/auth/providers/google-scopes.d.ts +35 -0
  21. package/dist/auth/providers/google-scopes.js +183 -0
  22. package/dist/auth/providers/google-scopes.js.map +1 -0
  23. package/dist/auth/providers/google.d.ts +2 -0
  24. package/dist/auth/providers/google.js +28 -0
  25. package/dist/auth/providers/google.js.map +1 -0
  26. package/dist/cli.js +6 -0
  27. package/dist/cli.js.map +1 -1
  28. package/dist/config/config-command.js +39 -1
  29. package/dist/config/config-command.js.map +1 -1
  30. package/dist/config/constitution-readonly.md +14 -11
  31. package/dist/config/constitution-user-base.md +15 -0
  32. package/dist/config/generated/test-scenarios.json +1 -1
  33. package/dist/config/generated/tool-annotations.json +10617 -296
  34. package/dist/config/generated-readonly/compiled-policy.json +787 -244
  35. package/dist/config/index.d.ts +2 -2
  36. package/dist/config/index.js +8 -0
  37. package/dist/config/index.js.map +1 -1
  38. package/dist/config/mcp-servers.json +23 -0
  39. package/dist/config/paths.d.ts +15 -0
  40. package/dist/config/paths.js +35 -0
  41. package/dist/config/paths.js.map +1 -1
  42. package/dist/config/types.d.ts +8 -0
  43. package/dist/config/user-config.d.ts +2 -0
  44. package/dist/config/user-config.js +2 -0
  45. package/dist/config/user-config.js.map +1 -1
  46. package/dist/cron/headless-transport.d.ts +4 -0
  47. package/dist/cron/headless-transport.js +1 -1
  48. package/dist/cron/headless-transport.js.map +1 -1
  49. package/dist/daemon/ironcurtain-daemon.js +6 -1
  50. package/dist/daemon/ironcurtain-daemon.js.map +1 -1
  51. package/dist/docker/docker-agent-session.d.ts +3 -1
  52. package/dist/docker/docker-agent-session.js +49 -28
  53. package/dist/docker/docker-agent-session.js.map +1 -1
  54. package/dist/docker/docker-infrastructure.js +39 -11
  55. package/dist/docker/docker-infrastructure.js.map +1 -1
  56. package/dist/docker/docker-manager.d.ts +2 -0
  57. package/dist/docker/docker-manager.js +9 -1
  58. package/dist/docker/docker-manager.js.map +1 -1
  59. package/dist/docker/mitm-proxy.d.ts +50 -0
  60. package/dist/docker/mitm-proxy.js +220 -31
  61. package/dist/docker/mitm-proxy.js.map +1 -1
  62. package/dist/docker/oauth-credentials.d.ts +45 -6
  63. package/dist/docker/oauth-credentials.js +151 -24
  64. package/dist/docker/oauth-credentials.js.map +1 -1
  65. package/dist/docker/oauth-token-manager.d.ts +15 -16
  66. package/dist/docker/oauth-token-manager.js +23 -17
  67. package/dist/docker/oauth-token-manager.js.map +1 -1
  68. package/dist/docker/platform.d.ts +6 -6
  69. package/dist/docker/platform.js +8 -6
  70. package/dist/docker/platform.js.map +1 -1
  71. package/dist/docker/proxy-tools.d.ts +80 -0
  72. package/dist/docker/proxy-tools.js +248 -0
  73. package/dist/docker/proxy-tools.js.map +1 -0
  74. package/dist/docker/pty-session.js +48 -29
  75. package/dist/docker/pty-session.js.map +1 -1
  76. package/dist/docker/pty-types.d.ts +4 -0
  77. package/dist/docker/pty-types.js.map +1 -1
  78. package/dist/escalation/escalation-watcher.d.ts +3 -1
  79. package/dist/escalation/escalation-watcher.js +6 -2
  80. package/dist/escalation/escalation-watcher.js.map +1 -1
  81. package/dist/escalation/listener-command.js +24 -10
  82. package/dist/escalation/listener-command.js.map +1 -1
  83. package/dist/escalation/listener-lock.d.ts +5 -0
  84. package/dist/escalation/listener-lock.js +14 -0
  85. package/dist/escalation/listener-lock.js.map +1 -1
  86. package/dist/index.js +6 -1
  87. package/dist/index.js.map +1 -1
  88. package/dist/memory/auto-save.d.ts +25 -0
  89. package/dist/memory/auto-save.js +89 -0
  90. package/dist/memory/auto-save.js.map +1 -0
  91. package/dist/mux/mux-app.d.ts +4 -0
  92. package/dist/mux/mux-app.js +186 -21
  93. package/dist/mux/mux-app.js.map +1 -1
  94. package/dist/mux/mux-command.d.ts +2 -2
  95. package/dist/mux/mux-command.js +14 -22
  96. package/dist/mux/mux-command.js.map +1 -1
  97. package/dist/mux/mux-escalation-manager.d.ts +9 -3
  98. package/dist/mux/mux-escalation-manager.js +69 -19
  99. package/dist/mux/mux-escalation-manager.js.map +1 -1
  100. package/dist/mux/mux-input-handler.d.ts +33 -1
  101. package/dist/mux/mux-input-handler.js +112 -29
  102. package/dist/mux/mux-input-handler.js.map +1 -1
  103. package/dist/mux/mux-renderer.d.ts +17 -1
  104. package/dist/mux/mux-renderer.js +288 -7
  105. package/dist/mux/mux-renderer.js.map +1 -1
  106. package/dist/mux/pty-bridge.d.ts +4 -0
  107. package/dist/mux/pty-bridge.js +13 -1
  108. package/dist/mux/pty-bridge.js.map +1 -1
  109. package/dist/mux/types.d.ts +24 -1
  110. package/dist/mux/types.js +10 -1
  111. package/dist/mux/types.js.map +1 -1
  112. package/dist/persona/persona-command.js +0 -5
  113. package/dist/persona/persona-command.js.map +1 -1
  114. package/dist/pipeline/annotate.js +1 -7
  115. package/dist/pipeline/annotate.js.map +1 -1
  116. package/dist/pipeline/compile.d.ts +5 -2
  117. package/dist/pipeline/compile.js +23 -11
  118. package/dist/pipeline/compile.js.map +1 -1
  119. package/dist/pipeline/constitution-compiler.d.ts +302 -3
  120. package/dist/pipeline/constitution-compiler.js +285 -23
  121. package/dist/pipeline/constitution-compiler.js.map +1 -1
  122. package/dist/pipeline/generate-with-repair.d.ts +2 -2
  123. package/dist/pipeline/generate-with-repair.js +2 -2
  124. package/dist/pipeline/handwritten-scenarios.d.ts +20 -7
  125. package/dist/pipeline/handwritten-scenarios.js +20 -41
  126. package/dist/pipeline/handwritten-scenarios.js.map +1 -1
  127. package/dist/pipeline/list-resolver.d.ts +18 -20
  128. package/dist/pipeline/list-resolver.js +28 -36
  129. package/dist/pipeline/list-resolver.js.map +1 -1
  130. package/dist/pipeline/pipeline-runner.d.ts +102 -13
  131. package/dist/pipeline/pipeline-runner.js +497 -237
  132. package/dist/pipeline/pipeline-runner.js.map +1 -1
  133. package/dist/pipeline/pipeline-shared.d.ts +7 -9
  134. package/dist/pipeline/pipeline-shared.js +8 -18
  135. package/dist/pipeline/pipeline-shared.js.map +1 -1
  136. package/dist/pipeline/policy-verifier.d.ts +32 -5
  137. package/dist/pipeline/policy-verifier.js +137 -8
  138. package/dist/pipeline/policy-verifier.js.map +1 -1
  139. package/dist/pipeline/proxy-mcp-connections.d.ts +25 -0
  140. package/dist/pipeline/proxy-mcp-connections.js +121 -0
  141. package/dist/pipeline/proxy-mcp-connections.js.map +1 -0
  142. package/dist/pipeline/refresh-lists.js +21 -13
  143. package/dist/pipeline/refresh-lists.js.map +1 -1
  144. package/dist/pipeline/scenario-generator.d.ts +29 -49
  145. package/dist/pipeline/scenario-generator.js +180 -143
  146. package/dist/pipeline/scenario-generator.js.map +1 -1
  147. package/dist/pipeline/scenario-schema-validator.d.ts +51 -0
  148. package/dist/pipeline/scenario-schema-validator.js +132 -0
  149. package/dist/pipeline/scenario-schema-validator.js.map +1 -0
  150. package/dist/pipeline/tool-annotator.d.ts +9 -8
  151. package/dist/pipeline/tool-annotator.js +48 -95
  152. package/dist/pipeline/tool-annotator.js.map +1 -1
  153. package/dist/pipeline/types.d.ts +31 -24
  154. package/dist/sandbox/index.js +20 -0
  155. package/dist/sandbox/index.js.map +1 -1
  156. package/dist/servers/fetch-server.js +10 -5
  157. package/dist/servers/fetch-server.js.map +1 -1
  158. package/dist/session/agent-session.d.ts +6 -19
  159. package/dist/session/agent-session.js +18 -69
  160. package/dist/session/agent-session.js.map +1 -1
  161. package/dist/session/base-transport.d.ts +17 -1
  162. package/dist/session/base-transport.js +25 -1
  163. package/dist/session/base-transport.js.map +1 -1
  164. package/dist/session/cli-transport.d.ts +5 -0
  165. package/dist/session/cli-transport.js +48 -25
  166. package/dist/session/cli-transport.js.map +1 -1
  167. package/dist/session/preflight.js +15 -16
  168. package/dist/session/preflight.js.map +1 -1
  169. package/dist/session/prompts.js +0 -1
  170. package/dist/session/prompts.js.map +1 -1
  171. package/dist/session/session-manager.d.ts +6 -0
  172. package/dist/session/session-manager.js +12 -0
  173. package/dist/session/session-manager.js.map +1 -1
  174. package/dist/session/types.d.ts +6 -1
  175. package/dist/session/types.js.map +1 -1
  176. package/dist/signal/signal-bot-daemon.js +24 -8
  177. package/dist/signal/signal-bot-daemon.js.map +1 -1
  178. package/dist/signal/signal-transport.d.ts +5 -2
  179. package/dist/signal/signal-transport.js +3 -3
  180. package/dist/signal/signal-transport.js.map +1 -1
  181. package/dist/trusted-process/approval-whitelist.d.ts +117 -0
  182. package/dist/trusted-process/approval-whitelist.js +237 -0
  183. package/dist/trusted-process/approval-whitelist.js.map +1 -0
  184. package/dist/trusted-process/gworkspace-credentials.d.ts +40 -0
  185. package/dist/trusted-process/gworkspace-credentials.js +45 -0
  186. package/dist/trusted-process/gworkspace-credentials.js.map +1 -0
  187. package/dist/trusted-process/index.d.ts +11 -1
  188. package/dist/trusted-process/index.js +63 -27
  189. package/dist/trusted-process/index.js.map +1 -1
  190. package/dist/trusted-process/mcp-proxy-server.d.ts +14 -0
  191. package/dist/trusted-process/mcp-proxy-server.js +325 -64
  192. package/dist/trusted-process/mcp-proxy-server.js.map +1 -1
  193. package/dist/trusted-process/policy-engine.d.ts +8 -2
  194. package/dist/trusted-process/policy-engine.js +18 -22
  195. package/dist/trusted-process/policy-engine.js.map +1 -1
  196. package/dist/trusted-process/policy-types.d.ts +17 -0
  197. package/dist/trusted-process/sandbox-integration.d.ts +23 -0
  198. package/dist/trusted-process/sandbox-integration.js +115 -3
  199. package/dist/trusted-process/sandbox-integration.js.map +1 -1
  200. package/dist/trusted-process/token-file-refresher.d.ts +69 -0
  201. package/dist/trusted-process/token-file-refresher.js +120 -0
  202. package/dist/trusted-process/token-file-refresher.js.map +1 -0
  203. package/dist/types/argument-roles.d.ts +7 -1
  204. package/dist/types/argument-roles.js +50 -0
  205. package/dist/types/argument-roles.js.map +1 -1
  206. package/dist/types/audit.d.ts +11 -0
  207. package/docker/Dockerfile.claude-code +2 -2
  208. package/docker/entrypoint-claude-code.sh +15 -5
  209. package/package.json +3 -2
  210. package/src/config/constitution-user-base.md +15 -0
  211. package/src/config/generated/test-scenarios.json +1 -1
  212. package/src/config/generated/tool-annotations.json +10617 -296
  213. package/src/config/mcp-servers.json +23 -0
  214. package/dist/pipeline/mcp-connections.d.ts +0 -16
  215. package/dist/pipeline/mcp-connections.js +0 -61
  216. package/dist/pipeline/mcp-connections.js.map +0 -1
@@ -0,0 +1,279 @@
1
+ /**
2
+ * OAuth 2.0 authorization code flow with PKCE.
3
+ *
4
+ * Runs the complete browser-based flow: generates PKCE challenge,
5
+ * starts an ephemeral callback server, opens the browser, waits for
6
+ * the authorization code, and exchanges it for tokens.
7
+ */
8
+ import { createHash, randomBytes } from 'node:crypto';
9
+ import { createServer } from 'node:http';
10
+ import { execFile } from 'node:child_process';
11
+ import { URL } from 'node:url';
12
+ export class OAuthFlowError extends Error {
13
+ constructor(message, options) {
14
+ super(message, options);
15
+ this.name = 'OAuthFlowError';
16
+ }
17
+ }
18
+ // ---------------------------------------------------------------------------
19
+ // PKCE
20
+ // ---------------------------------------------------------------------------
21
+ const CODE_VERIFIER_LENGTH = 64;
22
+ /** Generates a cryptographically random code verifier (43-128 chars, base64url). */
23
+ export function generateCodeVerifier() {
24
+ return randomBytes(CODE_VERIFIER_LENGTH).toString('base64url');
25
+ }
26
+ /** Computes the S256 code challenge from a code verifier. */
27
+ export function computeCodeChallenge(verifier) {
28
+ const hash = createHash('sha256').update(verifier).digest();
29
+ return hash.toString('base64url');
30
+ }
31
+ // ---------------------------------------------------------------------------
32
+ // State parameter (CSRF nonce)
33
+ // ---------------------------------------------------------------------------
34
+ function generateState() {
35
+ return randomBytes(32).toString('base64url');
36
+ }
37
+ // ---------------------------------------------------------------------------
38
+ // Browser open
39
+ // ---------------------------------------------------------------------------
40
+ function openBrowser(url) {
41
+ const cmd = process.platform === 'darwin' ? 'open' : 'xdg-open';
42
+ execFile(cmd, [url], (err) => {
43
+ // Best-effort: if browser fails to open, user can copy the URL from terminal
44
+ if (err) {
45
+ console.error(`Could not open browser automatically. Please visit:\n${url}`);
46
+ }
47
+ });
48
+ }
49
+ function startCallbackServer(callbackPath, expectedState) {
50
+ let resolveResult;
51
+ const result = new Promise((resolve) => {
52
+ resolveResult = resolve;
53
+ });
54
+ const server = createServer((req, res) => {
55
+ const requestUrl = new URL(req.url ?? '/', `http://127.0.0.1`);
56
+ if (requestUrl.pathname !== callbackPath) {
57
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
58
+ res.end('Not found');
59
+ return;
60
+ }
61
+ const error = requestUrl.searchParams.get('error');
62
+ if (error) {
63
+ const description = requestUrl.searchParams.get('error_description') ?? error;
64
+ respondWithHtml(res, 'Authorization Failed', `Authorization failed: ${description}`);
65
+ resolveResult({ ok: false, error: `Authorization failed: ${description}` });
66
+ return;
67
+ }
68
+ const state = requestUrl.searchParams.get('state');
69
+ if (state !== expectedState) {
70
+ respondWithHtml(res, 'Invalid State', 'State parameter mismatch. Possible CSRF attack.');
71
+ resolveResult({ ok: false, error: 'State parameter mismatch' });
72
+ return;
73
+ }
74
+ const code = requestUrl.searchParams.get('code');
75
+ if (!code) {
76
+ respondWithHtml(res, 'Missing Code', 'No authorization code received.');
77
+ resolveResult({ ok: false, error: 'No authorization code in callback' });
78
+ return;
79
+ }
80
+ respondWithHtml(res, 'Authorization Successful', 'Authorization successful! You can close this tab.');
81
+ resolveResult({ ok: true, code });
82
+ });
83
+ server.listen(0, '127.0.0.1');
84
+ const getPort = () => {
85
+ const address = server.address();
86
+ if (typeof address === 'object' && address !== null) {
87
+ return address.port;
88
+ }
89
+ throw new OAuthFlowError('Failed to get callback server port');
90
+ };
91
+ return { server, result, getPort };
92
+ }
93
+ function escapeHtml(text) {
94
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
95
+ }
96
+ function respondWithHtml(res, title, message) {
97
+ const safeTitle = escapeHtml(title);
98
+ const safeMessage = escapeHtml(message);
99
+ const html = `<!DOCTYPE html>
100
+ <html><head><title>${safeTitle}</title></head>
101
+ <body><h1>${safeTitle}</h1><p>${safeMessage}</p></body></html>`;
102
+ res.writeHead(200, { 'Content-Type': 'text/html', Connection: 'close' });
103
+ res.end(html);
104
+ }
105
+ async function exchangeCodeForTokens(provider, clientCredentials, code, redirectUri, codeVerifier) {
106
+ const body = new URLSearchParams({
107
+ grant_type: 'authorization_code',
108
+ code,
109
+ redirect_uri: redirectUri,
110
+ client_id: clientCredentials.clientId,
111
+ client_secret: clientCredentials.clientSecret,
112
+ code_verifier: codeVerifier,
113
+ });
114
+ const EXCHANGE_TIMEOUT_MS = 30_000;
115
+ let response;
116
+ try {
117
+ response = await fetchWithTimeout(provider.tokenUrl, {
118
+ method: 'POST',
119
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
120
+ body: body.toString(),
121
+ }, EXCHANGE_TIMEOUT_MS);
122
+ }
123
+ catch (err) {
124
+ if (err instanceof Error && err.name === 'TimeoutError') {
125
+ throw new OAuthFlowError('Token exchange timed out', { cause: err });
126
+ }
127
+ throw err;
128
+ }
129
+ if (!response.ok) {
130
+ const text = await response.text();
131
+ throw new OAuthFlowError(`Token exchange failed (${response.status}): ${text}`);
132
+ }
133
+ let data;
134
+ try {
135
+ data = (await safeResponseJson(response));
136
+ }
137
+ catch (err) {
138
+ throw new OAuthFlowError('Token exchange returned non-JSON response', { cause: err });
139
+ }
140
+ if (!data.access_token) {
141
+ throw new OAuthFlowError('Token response missing access_token');
142
+ }
143
+ if (!data.refresh_token) {
144
+ throw new OAuthFlowError('Token response missing refresh_token. ' +
145
+ 'This usually means the provider did not issue a refresh token. ' +
146
+ 'For Google, ensure prompt=consent and access_type=offline are set.');
147
+ }
148
+ if (!data.expires_in || data.expires_in <= 0) {
149
+ throw new OAuthFlowError('Token response missing or invalid expires_in');
150
+ }
151
+ const expiresAt = Date.now() + data.expires_in * 1000;
152
+ const grantedScopes = data.scope ? data.scope.split(' ') : [];
153
+ const token = {
154
+ accessToken: data.access_token,
155
+ refreshToken: data.refresh_token,
156
+ expiresAt,
157
+ scopes: grantedScopes,
158
+ };
159
+ return { token, grantedScopes };
160
+ }
161
+ // ---------------------------------------------------------------------------
162
+ // Authorization URL
163
+ // ---------------------------------------------------------------------------
164
+ function buildAuthorizationUrl(provider, clientId, redirectUri, state, codeChallenge, scopes) {
165
+ const url = new URL(provider.authorizationUrl);
166
+ url.searchParams.set('response_type', 'code');
167
+ url.searchParams.set('client_id', clientId);
168
+ url.searchParams.set('redirect_uri', redirectUri);
169
+ url.searchParams.set('state', state);
170
+ url.searchParams.set('scope', scopes.join(' '));
171
+ if (provider.usePkce) {
172
+ url.searchParams.set('code_challenge', codeChallenge);
173
+ url.searchParams.set('code_challenge_method', 'S256');
174
+ }
175
+ // Apply provider-specific extra params (e.g. access_type=offline for Google)
176
+ if (provider.extraAuthParams) {
177
+ for (const [key, value] of Object.entries(provider.extraAuthParams)) {
178
+ url.searchParams.set(key, value);
179
+ }
180
+ }
181
+ return url.toString();
182
+ }
183
+ // ---------------------------------------------------------------------------
184
+ // Main flow
185
+ // ---------------------------------------------------------------------------
186
+ const DEFAULT_TIMEOUT_MS = 120_000;
187
+ /**
188
+ * Runs the OAuth 2.0 authorization code flow with PKCE.
189
+ *
190
+ * 1. Generates PKCE code verifier and challenge
191
+ * 2. Starts an ephemeral HTTP server on 127.0.0.1:0 (OS-assigned port)
192
+ * 3. Opens the authorization URL in the user's default browser
193
+ * 4. Waits for the callback with the authorization code
194
+ * 5. Exchanges the code for tokens using client_id + client_secret
195
+ * 6. Stops the callback server
196
+ */
197
+ export async function runOAuthFlow(provider, clientCredentials, scopes, timeoutMs, options) {
198
+ const effectiveScopes = scopes ?? provider.defaultScopes;
199
+ const timeout = timeoutMs ?? DEFAULT_TIMEOUT_MS;
200
+ // Step 1: PKCE
201
+ const codeVerifier = generateCodeVerifier();
202
+ const codeChallenge = computeCodeChallenge(codeVerifier);
203
+ // Step 2: State nonce
204
+ const state = generateState();
205
+ // Step 3: Start callback server
206
+ const { server, result, getPort } = startCallbackServer(provider.callbackPath, state);
207
+ try {
208
+ // Wait for the server to be listening before reading the port
209
+ await new Promise((resolve) => {
210
+ if (server.listening) {
211
+ resolve();
212
+ }
213
+ else {
214
+ server.once('listening', resolve);
215
+ }
216
+ });
217
+ const port = getPort();
218
+ const redirectUri = `http://127.0.0.1:${port}${provider.callbackPath}`;
219
+ // Step 4: Build authorization URL and open browser
220
+ const authUrl = buildAuthorizationUrl(provider, clientCredentials.clientId, redirectUri, state, codeChallenge, effectiveScopes);
221
+ const opener = options?.openUrl ?? openBrowser;
222
+ opener(authUrl);
223
+ // Step 5: Wait for callback (with timeout)
224
+ const outcome = await withTimeout(result, timeout);
225
+ if (!outcome.ok) {
226
+ throw new OAuthFlowError(outcome.error);
227
+ }
228
+ // Step 6: Exchange code for tokens
229
+ return await exchangeCodeForTokens(provider, clientCredentials, outcome.code, redirectUri, codeVerifier);
230
+ }
231
+ finally {
232
+ // Step 7: Stop server and force-close any lingering connections
233
+ server.close();
234
+ server.closeAllConnections();
235
+ }
236
+ }
237
+ // ---------------------------------------------------------------------------
238
+ // Timeout helpers
239
+ // ---------------------------------------------------------------------------
240
+ function withTimeout(promise, ms) {
241
+ return new Promise((resolve, reject) => {
242
+ const timer = setTimeout(() => {
243
+ reject(new OAuthFlowError(`OAuth flow timed out after ${ms}ms`));
244
+ }, ms);
245
+ promise.then((value) => {
246
+ clearTimeout(timer);
247
+ resolve(value);
248
+ }, (err) => {
249
+ clearTimeout(timer);
250
+ reject(err instanceof Error ? err : new Error(String(err)));
251
+ });
252
+ });
253
+ }
254
+ /**
255
+ * Wraps fetch() with an AbortSignal-based timeout.
256
+ * Callers are responsible for catching the AbortError and rethrowing as
257
+ * the appropriate domain error (OAuthFlowError, OAuthTokenExpiredError, etc.).
258
+ */
259
+ export async function fetchWithTimeout(url, init, timeoutMs) {
260
+ const response = await fetch(url, {
261
+ ...init,
262
+ signal: AbortSignal.timeout(timeoutMs),
263
+ });
264
+ return response;
265
+ }
266
+ /**
267
+ * Safely parses a Response body as JSON.
268
+ * Returns the parsed value, or throws with a descriptive message on failure.
269
+ */
270
+ export async function safeResponseJson(response) {
271
+ const text = await response.text();
272
+ try {
273
+ return JSON.parse(text);
274
+ }
275
+ catch (err) {
276
+ throw new SyntaxError(`Expected JSON response but got: ${text.slice(0, 200)}`, { cause: err });
277
+ }
278
+ }
279
+ //# sourceMappingURL=oauth-flow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-flow.js","sourceRoot":"","sources":["../../src/auth/oauth-flow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Core OAuth provider types and credential loading.
3
+ *
4
+ * Defines the shape of an OAuth provider configuration and provides
5
+ * utilities for loading client credentials from disk.
6
+ */
7
+ /** Expand this union as new providers are registered in oauth-registry.ts. */
8
+ export type OAuthProviderId = 'google';
9
+ export interface OAuthProviderConfig {
10
+ readonly id: OAuthProviderId;
11
+ readonly displayName: string;
12
+ readonly authorizationUrl: string;
13
+ readonly tokenUrl: string;
14
+ readonly defaultScopes: readonly string[];
15
+ readonly callbackPath: string;
16
+ readonly usePkce: boolean;
17
+ readonly serverNames: readonly string[];
18
+ readonly tokenEnvVar: string;
19
+ readonly refreshTokenEnvVar: string;
20
+ readonly clientIdEnvVar: string;
21
+ readonly clientSecretEnvVar: string;
22
+ readonly credentialsFilename: string;
23
+ readonly additionalEnvVars?: Readonly<Record<string, (token: StoredOAuthToken) => string>>;
24
+ readonly postAuthUrl?: string;
25
+ readonly revocationUrl?: string;
26
+ /** Extra query parameters for the authorization URL (e.g. access_type, prompt). */
27
+ readonly extraAuthParams?: Readonly<Record<string, string>>;
28
+ /** Interactive scope picker shown when no --scopes flag and stdin is a TTY. */
29
+ readonly scopePicker?: (existingScopes: readonly string[]) => Promise<readonly string[] | symbol>;
30
+ /** Resolves short scope names (e.g. "gmail.send") to full scope URLs for --scopes flag. */
31
+ readonly resolveShortScopes?: (shortNames: readonly string[]) => readonly string[];
32
+ /** Provider-specific guidance shown when non-default scopes are requested. */
33
+ readonly nonDefaultScopeWarning?: string;
34
+ }
35
+ export interface OAuthClientCredentials {
36
+ readonly clientId: string;
37
+ readonly clientSecret: string;
38
+ }
39
+ export interface StoredOAuthToken {
40
+ readonly accessToken: string;
41
+ readonly refreshToken: string;
42
+ readonly expiresAt: number;
43
+ readonly scopes: readonly string[];
44
+ }
45
+ /**
46
+ * Loads OAuth client credentials from the provider's credentials file.
47
+ *
48
+ * Reads from ~/.ironcurtain/oauth/{provider.credentialsFilename}.
49
+ * Supports the Google "installed" Desktop app JSON format:
50
+ * { "installed": { "client_id": "...", "client_secret": "..." } }
51
+ *
52
+ * Returns null if the file does not exist.
53
+ * Throws if the file exists but contains invalid or incomplete data.
54
+ */
55
+ export declare function loadClientCredentials(provider: OAuthProviderConfig): OAuthClientCredentials | null;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Core OAuth provider types and credential loading.
3
+ *
4
+ * Defines the shape of an OAuth provider configuration and provides
5
+ * utilities for loading client credentials from disk.
6
+ */
7
+ import { existsSync, readFileSync } from 'node:fs';
8
+ import { resolve } from 'node:path';
9
+ import { getOAuthDir } from '../config/paths.js';
10
+ // ---------------------------------------------------------------------------
11
+ // Credential loading
12
+ // ---------------------------------------------------------------------------
13
+ /**
14
+ * Loads OAuth client credentials from the provider's credentials file.
15
+ *
16
+ * Reads from ~/.ironcurtain/oauth/{provider.credentialsFilename}.
17
+ * Supports the Google "installed" Desktop app JSON format:
18
+ * { "installed": { "client_id": "...", "client_secret": "..." } }
19
+ *
20
+ * Returns null if the file does not exist.
21
+ * Throws if the file exists but contains invalid or incomplete data.
22
+ */
23
+ export function loadClientCredentials(provider) {
24
+ const credentialsPath = resolve(getOAuthDir(), provider.credentialsFilename);
25
+ if (!existsSync(credentialsPath)) {
26
+ return null;
27
+ }
28
+ const raw = readFileSync(credentialsPath, 'utf-8');
29
+ const parsed = parseCredentialsJson(raw, credentialsPath);
30
+ return extractClientCredentials(parsed, credentialsPath);
31
+ }
32
+ /**
33
+ * Parses the raw JSON string from a credentials file.
34
+ * Throws a descriptive error on invalid JSON.
35
+ */
36
+ function parseCredentialsJson(raw, filePath) {
37
+ try {
38
+ return JSON.parse(raw);
39
+ }
40
+ catch (err) {
41
+ const message = err instanceof Error ? err.message : String(err);
42
+ throw new Error(`Invalid JSON in ${filePath}: ${message}`, { cause: err });
43
+ }
44
+ }
45
+ /**
46
+ * Extracts client_id and client_secret from the parsed credentials object.
47
+ *
48
+ * Supports two formats:
49
+ * 1. Desktop app (Google "installed"): { "installed": { "client_id": "...", "client_secret": "..." } }
50
+ * 2. Flat format: { "client_id": "...", "client_secret": "..." }
51
+ */
52
+ function extractClientCredentials(parsed, filePath) {
53
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
54
+ throw new Error(`Invalid credentials format in ${filePath}: expected a JSON object`);
55
+ }
56
+ const obj = parsed;
57
+ // Check for "installed" wrapper (Google Desktop app format)
58
+ const source = isPlainObject(obj['installed']) ? obj['installed'] : obj;
59
+ const clientId = source['client_id'];
60
+ const clientSecret = source['client_secret'];
61
+ if (typeof clientId !== 'string' || clientId.length === 0) {
62
+ throw new Error(`Missing or empty client_id in ${filePath}`);
63
+ }
64
+ if (typeof clientSecret !== 'string' || clientSecret.length === 0) {
65
+ throw new Error(`Missing or empty client_secret in ${filePath}`);
66
+ }
67
+ return { clientId, clientSecret };
68
+ }
69
+ /** Type guard for non-null, non-array objects. */
70
+ function isPlainObject(value) {
71
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
72
+ }
73
+ //# sourceMappingURL=oauth-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-provider.js","sourceRoot":"","sources":["../../src/auth/oauth-provider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAgDjD,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAA6B;IACjE,MAAM,eAAe,GAAG,OAAO,CAAC,WAAW,EAAE,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IAE7E,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,GAAG,GAAG,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,oBAAoB,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IAC1D,OAAO,wBAAwB,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,SAAS,oBAAoB,CAAC,GAAW,EAAE,QAAgB;IACzD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,mBAAmB,QAAQ,KAAK,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAS,wBAAwB,CAAC,MAAe,EAAE,QAAgB;IACjE,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,KAAK,CAAC,iCAAiC,QAAQ,0BAA0B,CAAC,CAAC;IACvF,CAAC;IAED,MAAM,GAAG,GAAG,MAAiC,CAAC;IAE9C,4DAA4D;IAC5D,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAExE,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IACrC,MAAM,YAAY,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC;IAE7C,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,iCAAiC,QAAQ,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,IAAI,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CAAC,qCAAqC,QAAQ,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;AACpC,CAAC;AAED,kDAAkD;AAClD,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * OAuth provider registry.
3
+ *
4
+ * Central lookup for all registered OAuth providers. Provides access
5
+ * by provider ID or by MCP server name.
6
+ */
7
+ import type { OAuthProviderConfig, OAuthProviderId } from './oauth-provider.js';
8
+ /**
9
+ * Returns the provider config for a given provider ID.
10
+ * Throws if the provider ID is not registered.
11
+ */
12
+ export declare function getOAuthProvider(id: OAuthProviderId): OAuthProviderConfig;
13
+ /**
14
+ * Returns all registered OAuth providers.
15
+ */
16
+ export declare function getAllOAuthProviders(): readonly OAuthProviderConfig[];
17
+ /**
18
+ * Returns the provider config associated with a given MCP server name,
19
+ * or undefined if no provider is registered for that server.
20
+ */
21
+ export declare function getProviderForServer(serverName: string): OAuthProviderConfig | undefined;
22
+ /**
23
+ * Returns true if the given string is a registered provider ID.
24
+ */
25
+ export declare function isValidProviderId(id: string): id is OAuthProviderId;
26
+ /**
27
+ * Resolves a provider ID string to its config, or writes an error and exits
28
+ * if the ID is not registered. Shared by setup-command and auth-command.
29
+ */
30
+ export declare function resolveProviderOrExit(id: string): OAuthProviderConfig;
31
+ /**
32
+ * Prints registered provider IDs and display names to stdout.
33
+ */
34
+ export declare function printAvailableProviders(): void;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * OAuth provider registry.
3
+ *
4
+ * Central lookup for all registered OAuth providers. Provides access
5
+ * by provider ID or by MCP server name.
6
+ */
7
+ import { googleOAuthProvider } from './providers/google.js';
8
+ // ---------------------------------------------------------------------------
9
+ // Registry
10
+ // ---------------------------------------------------------------------------
11
+ const providers = [googleOAuthProvider];
12
+ const providerById = new Map(providers.map((p) => [p.id, p]));
13
+ const providerByServerName = new Map(providers.flatMap((p) => p.serverNames.map((name) => [name, p])));
14
+ // ---------------------------------------------------------------------------
15
+ // Public API
16
+ // ---------------------------------------------------------------------------
17
+ /**
18
+ * Returns the provider config for a given provider ID.
19
+ * Throws if the provider ID is not registered.
20
+ */
21
+ export function getOAuthProvider(id) {
22
+ const provider = providerById.get(id);
23
+ if (!provider) {
24
+ throw new Error(`Unknown OAuth provider: ${id}`);
25
+ }
26
+ return provider;
27
+ }
28
+ /**
29
+ * Returns all registered OAuth providers.
30
+ */
31
+ export function getAllOAuthProviders() {
32
+ return providers;
33
+ }
34
+ /**
35
+ * Returns the provider config associated with a given MCP server name,
36
+ * or undefined if no provider is registered for that server.
37
+ */
38
+ export function getProviderForServer(serverName) {
39
+ return providerByServerName.get(serverName);
40
+ }
41
+ /**
42
+ * Returns true if the given string is a registered provider ID.
43
+ */
44
+ export function isValidProviderId(id) {
45
+ return providerById.has(id);
46
+ }
47
+ /**
48
+ * Resolves a provider ID string to its config, or writes an error and exits
49
+ * if the ID is not registered. Shared by setup-command and auth-command.
50
+ */
51
+ export function resolveProviderOrExit(id) {
52
+ if (!isValidProviderId(id)) {
53
+ process.stdout.write(`Unknown provider: ${id}\n\n`);
54
+ printAvailableProviders();
55
+ process.exit(1);
56
+ }
57
+ return getOAuthProvider(id);
58
+ }
59
+ /**
60
+ * Prints registered provider IDs and display names to stdout.
61
+ */
62
+ export function printAvailableProviders() {
63
+ process.stdout.write('Available providers:\n');
64
+ for (const p of providers) {
65
+ process.stdout.write(` ${p.id} ${p.displayName}\n`);
66
+ }
67
+ process.stdout.write('\n');
68
+ }
69
+ //# sourceMappingURL=oauth-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-registry.js","sourceRoot":"","sources":["../../src/auth/oauth-registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAE5D,8EAA8E;AAC9E,WAAW;AACX,8EAA8E;AAE9E,MAAM,SAAS,GAAmC,CAAC,mBAAmB,CAAC,CAAC;AAExE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAuC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAEpG,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAClC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAU,CAAC,CAAC,CAC1E,CAAC;AAEF,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAAmB;IAClD,MAAM,QAAQ,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,IAAI,KAAK,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAAkB;IACrD,OAAO,oBAAoB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,EAAU;IAC1C,OAAO,YAAY,CAAC,GAAG,CAAC,EAAqB,CAAC,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,EAAU;IAC9C,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;QACpD,uBAAuB,EAAE,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,gBAAgB,CAAC,EAAE,CAAC,CAAC;AAC9B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB;IACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAC/C,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Runtime OAuth token provider with transparent refresh.
3
+ *
4
+ * Provides valid access tokens for a specific OAuth provider, handling
5
+ * token expiry detection and refresh with deduplication. Follows the
6
+ * re-read-before-refresh pattern from OAuthTokenManager to coordinate
7
+ * with other processes that may refresh concurrently.
8
+ */
9
+ import type { OAuthProviderConfig, OAuthClientCredentials } from './oauth-provider.js';
10
+ export declare class OAuthTokenExpiredError extends Error {
11
+ readonly providerId: string;
12
+ constructor(providerId: string, message?: string, options?: ErrorOptions);
13
+ }
14
+ /**
15
+ * Provides valid access tokens for a specific OAuth provider at runtime.
16
+ * Handles transparent refresh with deduplication (single in-flight refresh).
17
+ */
18
+ export declare class OAuthTokenProvider {
19
+ private readonly provider;
20
+ private readonly clientCredentials;
21
+ private inflightRefresh;
22
+ constructor(provider: OAuthProviderConfig, clientCredentials: OAuthClientCredentials);
23
+ /**
24
+ * Returns a valid access token, refreshing if near expiry.
25
+ * Uses the re-read-before-refresh pattern: checks if another process
26
+ * already refreshed the token before attempting our own refresh.
27
+ */
28
+ getValidAccessToken(): Promise<string>;
29
+ /**
30
+ * Forces a token refresh regardless of current expiry.
31
+ * Used by the TokenFileRefresher which has a more aggressive threshold
32
+ * (10 min) than the normal getValidAccessToken (5 min).
33
+ */
34
+ forceRefresh(): Promise<string>;
35
+ /** Returns true if a token file exists for this provider. */
36
+ isAuthorized(): boolean;
37
+ /** Returns the scopes granted during the last authorization flow. */
38
+ getGrantedScopes(): readonly string[];
39
+ /**
40
+ * Core refresh with deduplication. Concurrent callers share a single
41
+ * in-flight refresh promise.
42
+ */
43
+ private doRefresh;
44
+ /**
45
+ * Re-read-before-refresh pattern: another process may have already
46
+ * refreshed the token since our initial check.
47
+ *
48
+ * When force=true, skips the early return so the token is always
49
+ * refreshed via the provider's token endpoint.
50
+ */
51
+ private executeRefresh;
52
+ }