@syncello/auth 3.3.1 → 3.4.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.cjs CHANGED
@@ -1341,11 +1341,12 @@ var import_drizzle_orm4 = require("drizzle-orm");
1341
1341
 
1342
1342
  // src/factory.ts
1343
1343
  function createAuth(config) {
1344
- const { db, schema, getEnv } = config;
1344
+ const { db, schema, getEnv, emailBranding } = config;
1345
1345
  const getContext = (c) => ({
1346
1346
  db,
1347
1347
  schema,
1348
- env: getEnv(c)
1348
+ env: getEnv(c),
1349
+ emailBranding
1349
1350
  });
1350
1351
  return {
1351
1352
  db,
@@ -1927,11 +1928,15 @@ function generateEmailTemplate(options) {
1927
1928
  appName = "App",
1928
1929
  buttonBgColor = "#00fe9a",
1929
1930
  buttonTextColor = "#000004",
1930
- buttonFontWeight = 500
1931
+ buttonFontWeight = 500,
1932
+ buttonRadius = "6px",
1933
+ logoUrl,
1934
+ footerColor = "#1e1b4b"
1931
1935
  } = options;
1932
1936
  const safeButtonUrl = buttonUrl ? sanitizeEmailUrl(buttonUrl, appUrl) : "";
1933
1937
  const safeAppUrl = appUrl ? sanitizeEmailUrl(appUrl, appUrl) : "";
1934
1938
  const safeMarketingUrl = marketingUrl ? sanitizeEmailUrl(marketingUrl, appUrl) : "";
1939
+ const safeLogoUrl = logoUrl ? sanitizeEmailUrl(logoUrl, appUrl) : `${safeAppUrl.replace(/\/$/, "")}/email-header-image.png`;
1935
1940
  return `
1936
1941
  <!DOCTYPE html>
1937
1942
  <html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" lang="en">
@@ -2071,7 +2076,7 @@ function generateEmailTemplate(options) {
2071
2076
  <td class="pad" style="padding-left:40px;padding-right:40px;width:100%;">
2072
2077
  <div class="alignment" align="center">
2073
2078
  <div class="fullWidth" style="max-width: 50%;">
2074
- <img src="${safeAppUrl}/email-header-image.png" style="display: block; height: auto; border: 0; width: 100%;" alt="${appName}" title="${appName}" height="auto">
2079
+ <img src="${safeLogoUrl}" style="display: block; height: auto; border: 0; width: 100%;" alt="${appName}" title="${appName}" height="auto">
2075
2080
  </div>
2076
2081
  </div>
2077
2082
  </td>
@@ -2128,7 +2133,7 @@ function generateEmailTemplate(options) {
2128
2133
  <v:textbox inset="0px,0px,0px,0px">
2129
2134
  <center dir="false" style="color:${buttonTextColor};font-family:'Montserrat',Arial,sans-serif;font-size:16px;font-weight:${buttonFontWeight};">
2130
2135
  <![endif]-->
2131
- <a href="${safeButtonUrl}" style="background-color: ${buttonBgColor}; border: 0px solid transparent; border-radius: 6px; color: ${buttonTextColor}; display: inline-block; font-family: 'Montserrat', Arial, sans-serif; font-size: 16px; font-weight: ${buttonFontWeight}; mso-border-alt: none; padding: 10px 20px; text-align: center; text-decoration: none; text-transform: capitalize; word-break: keep-all;">
2136
+ <a href="${safeButtonUrl}" style="background-color: ${buttonBgColor}; border: 0px solid transparent; border-radius: ${buttonRadius}; color: ${buttonTextColor}; display: inline-block; font-family: 'Montserrat', Arial, sans-serif; font-size: 16px; font-weight: ${buttonFontWeight}; mso-border-alt: none; padding: 10px 20px; text-align: center; text-decoration: none; text-transform: capitalize; word-break: keep-all;">
2132
2137
  ${buttonText}
2133
2138
  </a>
2134
2139
  <!--[if mso]>
@@ -2219,7 +2224,7 @@ function generateEmailTemplate(options) {
2219
2224
  <tbody>
2220
2225
  <tr>
2221
2226
  <td>
2222
- <table class="row-content stack" align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: #1e1b4b; color: #000000; max-width: 640px; width: 100%; margin: 0 auto;">
2227
+ <table class="row-content stack" align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; background-color: ${footerColor}; color: #000000; max-width: 640px; width: 100%; margin: 0 auto;">
2223
2228
  <tbody>
2224
2229
  <tr>
2225
2230
  <td class="column column-1" width="100%" style="mso-table-lspace: 0pt; mso-table-rspace: 0pt; font-weight: 400; text-align: left; vertical-align: top; padding: 20px;">
@@ -2278,7 +2283,7 @@ function extractAppUrl(url) {
2278
2283
  return "https://your-domain.com";
2279
2284
  }
2280
2285
  }
2281
- function generateVerificationEmail(data, appName = "Your App", configuredAppUrl = "") {
2286
+ function generateVerificationEmail(data, appName = "Your App", configuredAppUrl = "", branding) {
2282
2287
  const { email, verificationUrl, firstName } = data;
2283
2288
  const appUrl = configuredAppUrl;
2284
2289
  const safeUrl = sanitizeEmailUrl(verificationUrl, appUrl);
@@ -2288,12 +2293,10 @@ function generateVerificationEmail(data, appName = "Your App", configuredAppUrl
2288
2293
  body: `Please verify that your email address is <strong>${escapeHtml(email)}</strong>, and that you entered it when signing up for ${escapeHtml(appName)}.`,
2289
2294
  buttonText: "Verify Email",
2290
2295
  buttonUrl: safeUrl,
2291
- buttonBgColor: "#00fe9a",
2292
- buttonTextColor: "#000004",
2293
- buttonFontWeight: 500,
2294
2296
  welcomeMessage: safeFirstName ? `Hi ${safeFirstName}, Welcome to ${escapeHtml(appName)}!` : void 0,
2295
2297
  footerNote: "",
2296
- appUrl
2298
+ appUrl,
2299
+ ...branding
2297
2300
  });
2298
2301
  return {
2299
2302
  to: email,
@@ -2313,7 +2316,7 @@ If you didn't create an account with ${appName}, you can safely ignore this emai
2313
2316
  }
2314
2317
  };
2315
2318
  }
2316
- function generatePasswordResetEmail(data, appName = "Your App", configuredAppUrl = "") {
2319
+ function generatePasswordResetEmail(data, appName = "Your App", configuredAppUrl = "", branding) {
2317
2320
  const { email, resetUrl } = data;
2318
2321
  const appUrl = configuredAppUrl;
2319
2322
  const safeUrl = sanitizeEmailUrl(resetUrl, appUrl);
@@ -2322,15 +2325,13 @@ function generatePasswordResetEmail(data, appName = "Your App", configuredAppUrl
2322
2325
  body: "We received a request to reset your password. Click the button below to create a new password.",
2323
2326
  buttonText: "Reset Password",
2324
2327
  buttonUrl: safeUrl,
2325
- buttonBgColor: "#00fe9a",
2326
- buttonTextColor: "#000004",
2327
- buttonFontWeight: 500,
2328
2328
  securityWarning: `
2329
2329
  <strong>Link expires in 1 hour.</strong><br>
2330
2330
  For security, this link can only be used once.
2331
2331
  `,
2332
2332
  footerNote: "If you didn't request this change, no action is needed. Your password will remain unchanged.",
2333
- appUrl
2333
+ appUrl,
2334
+ ...branding
2334
2335
  });
2335
2336
  return {
2336
2337
  to: email,
@@ -2350,7 +2351,7 @@ If you didn't request a password reset, you can safely ignore this email.`,
2350
2351
  }
2351
2352
  };
2352
2353
  }
2353
- function generateEmailChangeConfirmation(data, appName = "Your App", configuredAppUrl = "") {
2354
+ function generateEmailChangeConfirmation(data, appName = "Your App", configuredAppUrl = "", branding) {
2354
2355
  const { newEmail, confirmUrl } = data;
2355
2356
  const appUrl = configuredAppUrl;
2356
2357
  const safeUrl = sanitizeEmailUrl(confirmUrl, appUrl);
@@ -2359,15 +2360,13 @@ function generateEmailChangeConfirmation(data, appName = "Your App", configuredA
2359
2360
  body: `We received a request to change your ${escapeHtml(appName)} account email to this address. Click the button below to confirm this change.`,
2360
2361
  buttonText: "Confirm Email Change",
2361
2362
  buttonUrl: safeUrl,
2362
- buttonBgColor: "#00fe9a",
2363
- buttonTextColor: "#000004",
2364
- buttonFontWeight: 500,
2365
2363
  securityWarning: `
2366
2364
  <strong>This link expires in 24 hours.</strong><br>
2367
2365
  For security, this link can only be used once.
2368
2366
  `,
2369
2367
  footerNote: "If you didn't request this change, you can safely ignore this email.",
2370
- appUrl
2368
+ appUrl,
2369
+ ...branding
2371
2370
  });
2372
2371
  return {
2373
2372
  to: newEmail,
@@ -2389,7 +2388,7 @@ If you didn't request this change, you can safely ignore this email.`,
2389
2388
  }
2390
2389
  };
2391
2390
  }
2392
- function generateEmailChangeNotification(data, appName = "Your App", configuredAppUrl = "") {
2391
+ function generateEmailChangeNotification(data, appName = "Your App", configuredAppUrl = "", branding) {
2393
2392
  const { oldEmail, newEmail, cancelUrl } = data;
2394
2393
  const appUrl = configuredAppUrl;
2395
2394
  const safeUrl = sanitizeEmailUrl(cancelUrl, appUrl);
@@ -2399,15 +2398,13 @@ function generateEmailChangeNotification(data, appName = "Your App", configuredA
2399
2398
  body: `Someone requested to change your ${escapeHtml(appName)} account email to <strong>${safeNewEmail}</strong>.`,
2400
2399
  buttonText: "This Wasn't Me - Cancel Change",
2401
2400
  buttonUrl: safeUrl,
2402
- buttonBgColor: "#00fe9a",
2403
- buttonTextColor: "#000004",
2404
- buttonFontWeight: 500,
2405
2401
  securityWarning: `
2406
2402
  If you made this request, no action is needed.<br>
2407
2403
  Complete the change by clicking the link sent to your new email address.
2408
2404
  `,
2409
2405
  footerNote: "If you didn't request this change, click the button above to cancel it.",
2410
- appUrl
2406
+ appUrl,
2407
+ ...branding
2411
2408
  });
2412
2409
  return {
2413
2410
  to: oldEmail,
@@ -2427,7 +2424,7 @@ ${safeUrl}`,
2427
2424
  }
2428
2425
  };
2429
2426
  }
2430
- function generate2faCodeEmail(data, appName = "Your App", appUrl = "") {
2427
+ function generate2faCodeEmail(data, appName = "Your App", appUrl = "", branding) {
2431
2428
  const { email, firstName, code } = data;
2432
2429
  const safeFirstName = firstName ? escapeHtml(firstName) : "there";
2433
2430
  const html = generateEmailTemplate({
@@ -2442,7 +2439,8 @@ function generate2faCodeEmail(data, appName = "Your App", appUrl = "") {
2442
2439
  buttonText: "",
2443
2440
  buttonUrl: "",
2444
2441
  footerNote: "",
2445
- appUrl
2442
+ appUrl,
2443
+ ...branding
2446
2444
  });
2447
2445
  return {
2448
2446
  to: email,
@@ -2462,7 +2460,7 @@ If you didn't request this code, you can safely ignore this email.
2462
2460
  tags: { type: "2fa_code" }
2463
2461
  };
2464
2462
  }
2465
- function generate2faEnabledEmail(data, appName = "Your App", appUrl = "") {
2463
+ function generate2faEnabledEmail(data, appName = "Your App", appUrl = "", branding) {
2466
2464
  const { email, firstName, method } = data;
2467
2465
  const safeFirstName = firstName ? escapeHtml(firstName) : "there";
2468
2466
  const methodName = method === "totp" ? "an authenticator app" : "email codes";
@@ -2476,7 +2474,8 @@ function generate2faEnabledEmail(data, appName = "Your App", appUrl = "") {
2476
2474
  buttonText: "",
2477
2475
  buttonUrl: "",
2478
2476
  footerNote: "",
2479
- appUrl
2477
+ appUrl,
2478
+ ...branding
2480
2479
  });
2481
2480
  return {
2482
2481
  to: email,
@@ -2492,7 +2491,7 @@ If you didn't make this change, please contact support immediately and reset you
2492
2491
  tags: { type: "2fa_enabled" }
2493
2492
  };
2494
2493
  }
2495
- function generate2faDisabledEmail(data, appName = "Your App", appUrl = "") {
2494
+ function generate2faDisabledEmail(data, appName = "Your App", appUrl = "", branding) {
2496
2495
  const { email, firstName } = data;
2497
2496
  const safeFirstName = firstName ? escapeHtml(firstName) : "there";
2498
2497
  const html = generateEmailTemplate({
@@ -2504,10 +2503,9 @@ function generate2faDisabledEmail(data, appName = "Your App", appUrl = "") {
2504
2503
  `,
2505
2504
  buttonText: "Reset Password",
2506
2505
  buttonUrl: appUrl ? `${appUrl}/forgot-password` : "",
2507
- buttonBgColor: "#ef4444",
2508
- buttonTextColor: "#ffffff",
2509
2506
  footerNote: "",
2510
- appUrl
2507
+ appUrl,
2508
+ ...branding
2511
2509
  });
2512
2510
  return {
2513
2511
  to: email,
@@ -2697,6 +2695,7 @@ function createEmailAdapter(env) {
2697
2695
  var EmailService = class {
2698
2696
  adapter;
2699
2697
  env;
2698
+ branding;
2700
2699
  get fromAddress() {
2701
2700
  if (this.env.EMAIL_FROM) {
2702
2701
  return this.env.EMAIL_FROM;
@@ -2710,9 +2709,10 @@ var EmailService = class {
2710
2709
  }
2711
2710
  return `${appName} <hello@${domain}>`;
2712
2711
  }
2713
- constructor(env, adapter) {
2712
+ constructor(env, adapter, branding) {
2714
2713
  this.env = env;
2715
2714
  this.adapter = adapter ?? createEmailAdapter(env);
2715
+ this.branding = branding;
2716
2716
  }
2717
2717
  /**
2718
2718
  * Check if email address has bounced or complained
@@ -2764,7 +2764,8 @@ var EmailService = class {
2764
2764
  const template = generateVerificationEmail(
2765
2765
  data,
2766
2766
  this.env.APP_NAME ?? "Your App",
2767
- this.env.APP_URL ?? ""
2767
+ this.env.APP_URL ?? "",
2768
+ this.branding
2768
2769
  );
2769
2770
  const recipient = this.getRecipientEmail(data.email, "verification");
2770
2771
  logger_default.info("Sending verification email", {
@@ -2824,7 +2825,8 @@ var EmailService = class {
2824
2825
  const template = generatePasswordResetEmail(
2825
2826
  data,
2826
2827
  this.env.APP_NAME ?? "Your App",
2827
- this.env.APP_URL ?? ""
2828
+ this.env.APP_URL ?? "",
2829
+ this.branding
2828
2830
  );
2829
2831
  const recipient = this.getRecipientEmail(data.email, "password_reset");
2830
2832
  logger_default.info("Sending password reset email", {
@@ -2884,7 +2886,8 @@ var EmailService = class {
2884
2886
  const template = generateEmailChangeConfirmation(
2885
2887
  data,
2886
2888
  this.env.APP_NAME ?? "Your App",
2887
- this.env.APP_URL ?? ""
2889
+ this.env.APP_URL ?? "",
2890
+ this.branding
2888
2891
  );
2889
2892
  const recipient = this.getRecipientEmail(data.newEmail, "email_change_confirmation");
2890
2893
  logger_default.info("Sending email change confirmation", {
@@ -2944,7 +2947,8 @@ var EmailService = class {
2944
2947
  const template = generateEmailChangeNotification(
2945
2948
  data,
2946
2949
  this.env.APP_NAME ?? "Your App",
2947
- this.env.APP_URL ?? ""
2950
+ this.env.APP_URL ?? "",
2951
+ this.branding
2948
2952
  );
2949
2953
  const recipient = this.getRecipientEmail(data.oldEmail, "email_change_notification");
2950
2954
  logger_default.info("Sending email change notification", {
@@ -3001,7 +3005,8 @@ var EmailService = class {
3001
3005
  const template = generate2faCodeEmail(
3002
3006
  data,
3003
3007
  this.env.APP_NAME ?? "Your App",
3004
- this.env.APP_URL ?? ""
3008
+ this.env.APP_URL ?? "",
3009
+ this.branding
3005
3010
  );
3006
3011
  const recipient = this.getRecipientEmail(data.email, "2fa_code");
3007
3012
  logger_default.info("Sending 2FA code email", {
@@ -3058,7 +3063,8 @@ var EmailService = class {
3058
3063
  const template = generate2faEnabledEmail(
3059
3064
  data,
3060
3065
  this.env.APP_NAME ?? "Your App",
3061
- this.env.APP_URL ?? ""
3066
+ this.env.APP_URL ?? "",
3067
+ this.branding
3062
3068
  );
3063
3069
  const recipient = this.getRecipientEmail(data.email, "2fa_enabled");
3064
3070
  logger_default.info("Sending 2FA enabled email", {
@@ -3116,7 +3122,8 @@ var EmailService = class {
3116
3122
  const template = generate2faDisabledEmail(
3117
3123
  data,
3118
3124
  this.env.APP_NAME ?? "Your App",
3119
- this.env.APP_URL ?? ""
3125
+ this.env.APP_URL ?? "",
3126
+ this.branding
3120
3127
  );
3121
3128
  const recipient = this.getRecipientEmail(data.email, "2fa_disabled");
3122
3129
  logger_default.info("Sending 2FA disabled email", {
@@ -3448,7 +3455,7 @@ var signupRoute = (0, import_zod_openapi2.createRoute)({
3448
3455
  });
3449
3456
  var signupHandler = async (c) => {
3450
3457
  const { email, password, name, turnstileToken, oauthToken } = c.req.valid("json");
3451
- const { db: database, schema } = getAuthContext(c);
3458
+ const { db: database, schema, emailBranding } = getAuthContext(c);
3452
3459
  const env = c.env;
3453
3460
  if (oauthToken) {
3454
3461
  const stored = await env.OAUTH_STATES.get(`oauth:signup:${oauthToken}`);
@@ -3548,7 +3555,7 @@ var signupHandler = async (c) => {
3548
3555
  expiresAt,
3549
3556
  createdAt: Date.now()
3550
3557
  });
3551
- const emailService = new EmailService(env);
3558
+ const emailService = new EmailService(env, void 0, emailBranding);
3552
3559
  const verificationUrl = `${env.APP_URL}/verify-email?token=${emailToken}`;
3553
3560
  await emailService.sendVerificationEmail(
3554
3561
  {
@@ -4032,7 +4039,7 @@ var forgotPasswordRoute = (0, import_zod_openapi7.createRoute)({
4032
4039
  });
4033
4040
  var forgotPasswordHandler = async (c) => {
4034
4041
  const { email, turnstileToken } = c.req.valid("json");
4035
- const { db: database, schema } = getAuthContext(c);
4042
+ const { db: database, schema, emailBranding } = getAuthContext(c);
4036
4043
  const env = c.env;
4037
4044
  const turnstileValid = await verifyTurnstileToken(
4038
4045
  turnstileToken,
@@ -4051,7 +4058,7 @@ var forgotPasswordHandler = async (c) => {
4051
4058
  if (!resetResult) {
4052
4059
  return c.json({ success: true });
4053
4060
  }
4054
- const emailService = new EmailService(env);
4061
+ const emailService = new EmailService(env, void 0, emailBranding);
4055
4062
  const resetUrl = `${env.APP_URL}/reset-password?token=${resetResult.token}`;
4056
4063
  await emailService.sendPasswordResetEmail(
4057
4064
  {
@@ -4299,7 +4306,7 @@ var changeEmailHandler = async (c) => {
4299
4306
  const userId = c.get("userId");
4300
4307
  if (!userId) return problems.unauthorized(c, "Not authenticated");
4301
4308
  const { password, newEmail } = c.req.valid("json");
4302
- const { db: database, schema } = getAuthContext(c);
4309
+ const { db: database, schema, emailBranding } = getAuthContext(c);
4303
4310
  const env = c.env;
4304
4311
  const appUrl = env.APP_URL || "http://localhost:5173";
4305
4312
  const result = await requestEmailChange({
@@ -4316,7 +4323,7 @@ var changeEmailHandler = async (c) => {
4316
4323
  }
4317
4324
  return problems.badRequest(c, result.error || "Failed to request email change");
4318
4325
  }
4319
- const emailService = new EmailService(env);
4326
+ const emailService = new EmailService(env, void 0, emailBranding);
4320
4327
  const confirmUrl = `${appUrl}/confirm-email-change?token=${result.confirmToken}`;
4321
4328
  const confirmResult = await emailService.sendEmailChangeConfirmation(
4322
4329
  { newEmail, confirmUrl },
@@ -4621,7 +4628,7 @@ var resendVerificationRoute = (0, import_zod_openapi21.createRoute)({
4621
4628
  });
4622
4629
  var resendVerificationHandler = async (c) => {
4623
4630
  const userId = c.get("userId");
4624
- const { db, schema } = getAuthContext(c);
4631
+ const { db, schema, emailBranding } = getAuthContext(c);
4625
4632
  const env = c.env;
4626
4633
  const [user] = await db.select({
4627
4634
  id: schema.users.id,
@@ -4646,7 +4653,7 @@ var resendVerificationHandler = async (c) => {
4646
4653
  expiresAt,
4647
4654
  createdAt: Date.now()
4648
4655
  });
4649
- const emailService = new EmailService(env);
4656
+ const emailService = new EmailService(env, void 0, emailBranding);
4650
4657
  const verificationUrl = `${env.APP_URL}/verify-email?token=${emailToken}`;
4651
4658
  await emailService.sendVerificationEmail(
4652
4659
  {
@@ -4934,7 +4941,7 @@ var totpVerifyHandler = async (c) => {
4934
4941
  const userId = c.get("userId");
4935
4942
  if (!userId) return problems.unauthorized(c);
4936
4943
  const { code } = c.req.valid("json");
4937
- const { db, schema, env } = getAuthContext(c);
4944
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4938
4945
  const secret = await env.OAUTH_STATES.get(`totp_setup:${userId}`);
4939
4946
  if (!secret) {
4940
4947
  return problems.badRequest(c, "Setup expired. Please start again.");
@@ -4978,7 +4985,7 @@ var totpVerifyHandler = async (c) => {
4978
4985
  backupCodes = codes.map(formatBackupCode);
4979
4986
  }
4980
4987
  if (user) {
4981
- const emailService = new EmailService(env);
4988
+ const emailService = new EmailService(env, void 0, emailBranding);
4982
4989
  const firstName = user.name?.split(" ")[0] || null;
4983
4990
  await emailService.send2faEnabledEmail(
4984
4991
  { email: user.email, firstName, method: "totp" },
@@ -5026,7 +5033,7 @@ var totpDisableHandler = async (c) => {
5026
5033
  return problems.unauthorized(c);
5027
5034
  }
5028
5035
  const { code, method } = c.req.valid("json");
5029
- const { db, schema, env } = getAuthContext(c);
5036
+ const { db, schema, env, emailBranding } = getAuthContext(c);
5030
5037
  const verification = await verifyAny2faCode(db, env, userId, code, method, {
5031
5038
  user2faMethods: schema.user2faMethods,
5032
5039
  userBackupCodes: schema.userBackupCodes
@@ -5043,7 +5050,7 @@ var totpDisableHandler = async (c) => {
5043
5050
  await invalidateAllUserSessions(db, { sessions: schema.sessions, users: schema.users }, userId);
5044
5051
  const [user] = await db.select({ email: schema.users.email, name: schema.users.name }).from(schema.users).where((0, import_drizzle_orm23.eq)(schema.users.id, userId)).limit(1);
5045
5052
  if (user) {
5046
- const emailService = new EmailService(env);
5053
+ const emailService = new EmailService(env, void 0, emailBranding);
5047
5054
  const firstName = user.name?.split(" ")[0] || null;
5048
5055
  await emailService.send2faDisabledEmail({ email: user.email, firstName }, db, schema.users);
5049
5056
  }
@@ -5089,7 +5096,7 @@ var emailSetupRoute = (0, import_zod_openapi28.createRoute)({
5089
5096
  var emailSetupHandler = async (c) => {
5090
5097
  const userId = c.get("userId");
5091
5098
  if (!userId) return problems.unauthorized(c);
5092
- const { db, schema, env } = getAuthContext(c);
5099
+ const { db, schema, env, emailBranding } = getAuthContext(c);
5093
5100
  const existing = await db.select({ id: schema.user2faMethods.id }).from(schema.user2faMethods).where((0, import_drizzle_orm24.and)((0, import_drizzle_orm24.eq)(schema.user2faMethods.userId, userId), (0, import_drizzle_orm24.eq)(schema.user2faMethods.method, "email"))).limit(1);
5094
5101
  if (existing.length > 0) {
5095
5102
  return problems.badRequest(c, "Email 2FA already enabled");
@@ -5099,7 +5106,7 @@ var emailSetupHandler = async (c) => {
5099
5106
  const code = generateEmailOtp();
5100
5107
  const codeHash = await hashToken(code);
5101
5108
  await env.OAUTH_STATES.put(`email_2fa_setup:${userId}`, codeHash, { expirationTtl: 300 });
5102
- const emailService = new EmailService(env);
5109
+ const emailService = new EmailService(env, void 0, emailBranding);
5103
5110
  const firstName = user.name?.split(" ")[0] || null;
5104
5111
  await emailService.send2faCodeEmail({ email: user.email, firstName, code }, db, schema.users);
5105
5112
  logSecurityEvent("2fa_email_setup_initiated", "low", { userId });
@@ -5140,7 +5147,7 @@ var emailVerifyHandler = async (c) => {
5140
5147
  const userId = c.get("userId");
5141
5148
  if (!userId) return problems.unauthorized(c);
5142
5149
  const { code } = c.req.valid("json");
5143
- const { db, schema, env } = getAuthContext(c);
5150
+ const { db, schema, env, emailBranding } = getAuthContext(c);
5144
5151
  const storedHash = await env.OAUTH_STATES.get(`email_2fa_setup:${userId}`);
5145
5152
  if (!storedHash) {
5146
5153
  return problems.badRequest(c, "Code expired. Please request a new one.");
@@ -5178,7 +5185,7 @@ var emailVerifyHandler = async (c) => {
5178
5185
  backupCodes = codes.map(formatBackupCode);
5179
5186
  }
5180
5187
  if (user) {
5181
- const emailService = new EmailService(env);
5188
+ const emailService = new EmailService(env, void 0, emailBranding);
5182
5189
  const firstName = user.name?.split(" ")[0] || null;
5183
5190
  await emailService.send2faEnabledEmail(
5184
5191
  { email: user.email, firstName, method: "email" },
@@ -5221,7 +5228,7 @@ var emailSendCodeRoute = (0, import_zod_openapi30.createRoute)({
5221
5228
  var emailSendCodeHandler = async (c) => {
5222
5229
  const userId = c.get("userId");
5223
5230
  if (!userId) return problems.unauthorized(c);
5224
- const { db, schema, env } = getAuthContext(c);
5231
+ const { db, schema, env, emailBranding } = getAuthContext(c);
5225
5232
  const [emailMethod] = await db.select().from(schema.user2faMethods).where((0, import_drizzle_orm26.and)((0, import_drizzle_orm26.eq)(schema.user2faMethods.userId, userId), (0, import_drizzle_orm26.eq)(schema.user2faMethods.method, "email"))).limit(1);
5226
5233
  if (!emailMethod) {
5227
5234
  return problems.badRequest(c, "Email 2FA not enabled");
@@ -5233,7 +5240,7 @@ var emailSendCodeHandler = async (c) => {
5233
5240
  await env.OAUTH_STATES.put(`email_2fa_challenge:${userId}`, codeHash, {
5234
5241
  expirationTtl: 300
5235
5242
  });
5236
- const emailService = new EmailService(env);
5243
+ const emailService = new EmailService(env, void 0, emailBranding);
5237
5244
  const firstName = user.name?.split(" ")[0] || null;
5238
5245
  const emailResult = await emailService.send2faCodeEmail(
5239
5246
  { email: user.email, firstName, code },
@@ -5286,7 +5293,7 @@ var emailDisableHandler = async (c) => {
5286
5293
  return problems.unauthorized(c);
5287
5294
  }
5288
5295
  const { code, method } = c.req.valid("json");
5289
- const { db, schema, env } = getAuthContext(c);
5296
+ const { db, schema, env, emailBranding } = getAuthContext(c);
5290
5297
  const verification = await verifyAny2faCode(db, env, userId, code, method, {
5291
5298
  user2faMethods: schema.user2faMethods,
5292
5299
  userBackupCodes: schema.userBackupCodes
@@ -5303,7 +5310,7 @@ var emailDisableHandler = async (c) => {
5303
5310
  await invalidateAllUserSessions(db, { sessions: schema.sessions, users: schema.users }, userId);
5304
5311
  const [user] = await db.select({ email: schema.users.email, name: schema.users.name }).from(schema.users).where((0, import_drizzle_orm27.eq)(schema.users.id, userId)).limit(1);
5305
5312
  if (user) {
5306
- const emailService = new EmailService(env);
5313
+ const emailService = new EmailService(env, void 0, emailBranding);
5307
5314
  const firstName = user.name?.split(" ")[0] || null;
5308
5315
  await emailService.send2faDisabledEmail({ email: user.email, firstName }, db, schema.users);
5309
5316
  }
@@ -5600,7 +5607,7 @@ var challengeResendRoute = (0, import_zod_openapi35.createRoute)({
5600
5607
  }
5601
5608
  });
5602
5609
  var challengeResendHandler = async (c) => {
5603
- const { db, schema } = getAuthContext(c);
5610
+ const { db, schema, emailBranding } = getAuthContext(c);
5604
5611
  const cookieName = getChallengeCookieName(c.env);
5605
5612
  const challengeToken = (0, import_cookie4.getCookie)(c, cookieName);
5606
5613
  if (!challengeToken) {
@@ -5624,7 +5631,7 @@ var challengeResendHandler = async (c) => {
5624
5631
  await c.env.OAUTH_STATES.put(`email_2fa_challenge:${payload.userId}`, codeHash, {
5625
5632
  expirationTtl: 300
5626
5633
  });
5627
- const emailService = new EmailService(c.env);
5634
+ const emailService = new EmailService(c.env, void 0, emailBranding);
5628
5635
  const firstName = user.name?.split(" ")[0] || null;
5629
5636
  await emailService.send2faCodeEmail({ email: user.email, firstName, code }, db, schema.users);
5630
5637
  logger_default.info("Email 2FA code resent", { userId: payload.userId });