@marlinjai/email-mcp 1.7.0 → 1.7.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.
@@ -2,7 +2,7 @@
2
2
 
3
3
 
4
4
  // src/setup/wizard.ts
5
- import crypto3 from "node:crypto";
5
+ import crypto4 from "node:crypto";
6
6
  import { exec } from "node:child_process";
7
7
 
8
8
  // src/setup/prompts.ts
@@ -276,6 +276,7 @@ var OAuthCallbackServer = class {
276
276
  rejectCode = null;
277
277
  timeoutMs;
278
278
  timeoutHandle = null;
279
+ expectedState = null;
279
280
  constructor(timeoutMs = 12e4) {
280
281
  this.timeoutMs = timeoutMs;
281
282
  }
@@ -286,7 +287,12 @@ var OAuthCallbackServer = class {
286
287
  if (url.pathname === "/callback" || url.pathname === "/") {
287
288
  const code = url.searchParams.get("code");
288
289
  const error = url.searchParams.get("error");
289
- if (code) {
290
+ const state = url.searchParams.get("state");
291
+ if (code && this.expectedState !== null && state !== this.expectedState) {
292
+ res.writeHead(400, { "Content-Type": "text/html" });
293
+ res.end(ERROR_HTML);
294
+ this.rejectCode?.(new Error("OAuth error: state mismatch, callback did not come from the authorization request we started"));
295
+ } else if (code) {
290
296
  res.writeHead(200, { "Content-Type": "text/html" });
291
297
  res.end(SUCCESS_HTML);
292
298
  this.resolveCode?.(code);
@@ -311,7 +317,13 @@ var OAuthCallbackServer = class {
311
317
  this.server.on("error", reject);
312
318
  });
313
319
  }
314
- waitForCode() {
320
+ /**
321
+ * Resolve with the authorization code once the browser is redirected back.
322
+ * Pass the `state` that was put into the authorization URL: callbacks whose
323
+ * state does not match are rejected instead of resolved (CSRF protection).
324
+ */
325
+ waitForCode(expectedState) {
326
+ this.expectedState = expectedState ?? null;
315
327
  return new Promise((resolve, reject) => {
316
328
  this.resolveCode = (code) => {
317
329
  if (this.timeoutHandle) clearTimeout(this.timeoutHandle);
@@ -381,15 +393,17 @@ var GmailAuth = class {
381
393
  getAuthUrl(redirectUri, scopeMode) {
382
394
  const codeVerifier = crypto2.randomBytes(32).toString("base64url");
383
395
  const codeChallenge = crypto2.createHash("sha256").update(codeVerifier).digest("base64url");
396
+ const state = crypto2.randomBytes(16).toString("base64url");
384
397
  this.oauth2Client = new OAuth2Client(this.clientId, this.clientSecret, redirectUri);
385
398
  const url = this.oauth2Client.generateAuthUrl({
386
399
  access_type: "offline",
387
400
  scope: GMAIL_SCOPES[resolveScopeMode(scopeMode)],
388
401
  prompt: "consent",
389
402
  code_challenge: codeChallenge,
390
- code_challenge_method: "S256"
403
+ code_challenge_method: "S256",
404
+ state
391
405
  });
392
- return { url, codeVerifier };
406
+ return { url, codeVerifier, state };
393
407
  }
394
408
  async exchangeCode(code, codeVerifier) {
395
409
  const { tokens } = await this.oauth2Client.getToken({ code, codeVerifier });
@@ -421,6 +435,7 @@ var GmailAuth = class {
421
435
  };
422
436
 
423
437
  // src/providers/outlook/auth.ts
438
+ import crypto3 from "node:crypto";
424
439
  import fs2 from "node:fs";
425
440
  import path2 from "node:path";
426
441
  import os2 from "node:os";
@@ -471,13 +486,15 @@ var OutlookAuth = class _OutlookAuth {
471
486
  }
472
487
  async getAuthUrl(redirectUri) {
473
488
  const { verifier, challenge } = await this.cryptoProvider.generatePkceCodes();
489
+ const state = crypto3.randomBytes(16).toString("base64url");
474
490
  const url = await this.pca.getAuthCodeUrl({
475
491
  scopes: _OutlookAuth.SCOPES,
476
492
  redirectUri,
477
493
  codeChallenge: challenge,
478
- codeChallengeMethod: "S256"
494
+ codeChallengeMethod: "S256",
495
+ state
479
496
  });
480
- return { url, codeVerifier: verifier };
497
+ return { url, codeVerifier: verifier, state };
481
498
  }
482
499
  async exchangeCode(code, codeVerifier, redirectUri) {
483
500
  const result = await this.pca.acquireTokenByCode({
@@ -2887,7 +2904,7 @@ async function setupGmail(scopeModeFlag) {
2887
2904
  const port = await server.start();
2888
2905
  const redirectUri = `http://localhost:${port}/callback`;
2889
2906
  const gmailAuth = new GmailAuth(GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, redirectUri);
2890
- const { url, codeVerifier } = gmailAuth.getAuthUrl(redirectUri, scopeMode);
2907
+ const { url, codeVerifier, state } = gmailAuth.getAuthUrl(redirectUri, scopeMode);
2891
2908
  console.log("\nOpening browser for Google authorization...");
2892
2909
  console.log(`If the browser does not open, visit:
2893
2910
  ${url}
@@ -2895,7 +2912,7 @@ ${url}
2895
2912
  openBrowser(url);
2896
2913
  let code;
2897
2914
  try {
2898
- code = await server.waitForCode();
2915
+ code = await server.waitForCode(state);
2899
2916
  } catch (err) {
2900
2917
  server.shutdown();
2901
2918
  throw new Error(`OAuth flow failed: ${err.message}`);
@@ -2911,7 +2928,7 @@ ${url}
2911
2928
  if (!email) throw new Error("Could not retrieve Gmail address from profile");
2912
2929
  console.log(`Authenticated as ${email}`);
2913
2930
  const name = await promptAccountName("Gmail");
2914
- const id = crypto3.randomUUID();
2931
+ const id = crypto4.randomUUID();
2915
2932
  const creds = {
2916
2933
  id,
2917
2934
  name,
@@ -2928,7 +2945,7 @@ async function setupOutlook() {
2928
2945
  const port = await server.start();
2929
2946
  const redirectUri = `http://localhost:${port}`;
2930
2947
  const outlookAuth = new OutlookAuth(OUTLOOK_CLIENT_ID);
2931
- const { url, codeVerifier } = await outlookAuth.getAuthUrl(redirectUri);
2948
+ const { url, codeVerifier, state } = await outlookAuth.getAuthUrl(redirectUri);
2932
2949
  console.log("\nOpening browser for Microsoft authorization...");
2933
2950
  console.log(`If the browser does not open, visit:
2934
2951
  ${url}
@@ -2936,7 +2953,7 @@ ${url}
2936
2953
  openBrowser(url);
2937
2954
  let code;
2938
2955
  try {
2939
- code = await server.waitForCode();
2956
+ code = await server.waitForCode(state);
2940
2957
  } catch (err) {
2941
2958
  server.shutdown();
2942
2959
  throw new Error(`OAuth flow failed: ${err.message}`);
@@ -2949,7 +2966,7 @@ ${url}
2949
2966
  if (!email) throw new Error("Could not retrieve email from Microsoft account");
2950
2967
  console.log(`Authenticated as ${email}`);
2951
2968
  const name = await promptAccountName("Outlook");
2952
- const id = crypto3.randomUUID();
2969
+ const id = crypto4.randomUUID();
2953
2970
  const creds = {
2954
2971
  id,
2955
2972
  name,
@@ -2981,7 +2998,7 @@ async function setupICloud() {
2981
2998
  password = await askPassword("App-specific password:");
2982
2999
  }
2983
3000
  const name = await promptAccountName("iCloud");
2984
- const id = crypto3.randomUUID();
3001
+ const id = crypto4.randomUUID();
2985
3002
  const creds = {
2986
3003
  id,
2987
3004
  name,
@@ -3022,7 +3039,7 @@ async function setupIMAP() {
3022
3039
  smtpPort = parseInt(smtpPortStr, 10);
3023
3040
  }
3024
3041
  const name = await promptAccountName("IMAP");
3025
- const id = crypto3.randomUUID();
3042
+ const id = crypto4.randomUUID();
3026
3043
  const creds = {
3027
3044
  id,
3028
3045
  name,