@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.js CHANGED
@@ -949,11 +949,12 @@ import { eq as eq4, and as and2, gt as gt2 } from "drizzle-orm";
949
949
 
950
950
  // src/factory.ts
951
951
  function createAuth(config) {
952
- const { db, schema, getEnv } = config;
952
+ const { db, schema, getEnv, emailBranding } = config;
953
953
  const getContext = (c) => ({
954
954
  db,
955
955
  schema,
956
- env: getEnv(c)
956
+ env: getEnv(c),
957
+ emailBranding
957
958
  });
958
959
  return {
959
960
  db,
@@ -1535,11 +1536,15 @@ function generateEmailTemplate(options) {
1535
1536
  appName = "App",
1536
1537
  buttonBgColor = "#00fe9a",
1537
1538
  buttonTextColor = "#000004",
1538
- buttonFontWeight = 500
1539
+ buttonFontWeight = 500,
1540
+ buttonRadius = "6px",
1541
+ logoUrl,
1542
+ footerColor = "#1e1b4b"
1539
1543
  } = options;
1540
1544
  const safeButtonUrl = buttonUrl ? sanitizeEmailUrl(buttonUrl, appUrl) : "";
1541
1545
  const safeAppUrl = appUrl ? sanitizeEmailUrl(appUrl, appUrl) : "";
1542
1546
  const safeMarketingUrl = marketingUrl ? sanitizeEmailUrl(marketingUrl, appUrl) : "";
1547
+ const safeLogoUrl = logoUrl ? sanitizeEmailUrl(logoUrl, appUrl) : `${safeAppUrl.replace(/\/$/, "")}/email-header-image.png`;
1543
1548
  return `
1544
1549
  <!DOCTYPE html>
1545
1550
  <html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" lang="en">
@@ -1679,7 +1684,7 @@ function generateEmailTemplate(options) {
1679
1684
  <td class="pad" style="padding-left:40px;padding-right:40px;width:100%;">
1680
1685
  <div class="alignment" align="center">
1681
1686
  <div class="fullWidth" style="max-width: 50%;">
1682
- <img src="${safeAppUrl}/email-header-image.png" style="display: block; height: auto; border: 0; width: 100%;" alt="${appName}" title="${appName}" height="auto">
1687
+ <img src="${safeLogoUrl}" style="display: block; height: auto; border: 0; width: 100%;" alt="${appName}" title="${appName}" height="auto">
1683
1688
  </div>
1684
1689
  </div>
1685
1690
  </td>
@@ -1736,7 +1741,7 @@ function generateEmailTemplate(options) {
1736
1741
  <v:textbox inset="0px,0px,0px,0px">
1737
1742
  <center dir="false" style="color:${buttonTextColor};font-family:'Montserrat',Arial,sans-serif;font-size:16px;font-weight:${buttonFontWeight};">
1738
1743
  <![endif]-->
1739
- <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;">
1744
+ <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;">
1740
1745
  ${buttonText}
1741
1746
  </a>
1742
1747
  <!--[if mso]>
@@ -1827,7 +1832,7 @@ function generateEmailTemplate(options) {
1827
1832
  <tbody>
1828
1833
  <tr>
1829
1834
  <td>
1830
- <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;">
1835
+ <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;">
1831
1836
  <tbody>
1832
1837
  <tr>
1833
1838
  <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;">
@@ -1886,7 +1891,7 @@ function extractAppUrl(url) {
1886
1891
  return "https://your-domain.com";
1887
1892
  }
1888
1893
  }
1889
- function generateVerificationEmail(data, appName = "Your App", configuredAppUrl = "") {
1894
+ function generateVerificationEmail(data, appName = "Your App", configuredAppUrl = "", branding) {
1890
1895
  const { email, verificationUrl, firstName } = data;
1891
1896
  const appUrl = configuredAppUrl;
1892
1897
  const safeUrl = sanitizeEmailUrl(verificationUrl, appUrl);
@@ -1896,12 +1901,10 @@ function generateVerificationEmail(data, appName = "Your App", configuredAppUrl
1896
1901
  body: `Please verify that your email address is <strong>${escapeHtml(email)}</strong>, and that you entered it when signing up for ${escapeHtml(appName)}.`,
1897
1902
  buttonText: "Verify Email",
1898
1903
  buttonUrl: safeUrl,
1899
- buttonBgColor: "#00fe9a",
1900
- buttonTextColor: "#000004",
1901
- buttonFontWeight: 500,
1902
1904
  welcomeMessage: safeFirstName ? `Hi ${safeFirstName}, Welcome to ${escapeHtml(appName)}!` : void 0,
1903
1905
  footerNote: "",
1904
- appUrl
1906
+ appUrl,
1907
+ ...branding
1905
1908
  });
1906
1909
  return {
1907
1910
  to: email,
@@ -1921,7 +1924,7 @@ If you didn't create an account with ${appName}, you can safely ignore this emai
1921
1924
  }
1922
1925
  };
1923
1926
  }
1924
- function generatePasswordResetEmail(data, appName = "Your App", configuredAppUrl = "") {
1927
+ function generatePasswordResetEmail(data, appName = "Your App", configuredAppUrl = "", branding) {
1925
1928
  const { email, resetUrl } = data;
1926
1929
  const appUrl = configuredAppUrl;
1927
1930
  const safeUrl = sanitizeEmailUrl(resetUrl, appUrl);
@@ -1930,15 +1933,13 @@ function generatePasswordResetEmail(data, appName = "Your App", configuredAppUrl
1930
1933
  body: "We received a request to reset your password. Click the button below to create a new password.",
1931
1934
  buttonText: "Reset Password",
1932
1935
  buttonUrl: safeUrl,
1933
- buttonBgColor: "#00fe9a",
1934
- buttonTextColor: "#000004",
1935
- buttonFontWeight: 500,
1936
1936
  securityWarning: `
1937
1937
  <strong>Link expires in 1 hour.</strong><br>
1938
1938
  For security, this link can only be used once.
1939
1939
  `,
1940
1940
  footerNote: "If you didn't request this change, no action is needed. Your password will remain unchanged.",
1941
- appUrl
1941
+ appUrl,
1942
+ ...branding
1942
1943
  });
1943
1944
  return {
1944
1945
  to: email,
@@ -1958,7 +1959,7 @@ If you didn't request a password reset, you can safely ignore this email.`,
1958
1959
  }
1959
1960
  };
1960
1961
  }
1961
- function generateEmailChangeConfirmation(data, appName = "Your App", configuredAppUrl = "") {
1962
+ function generateEmailChangeConfirmation(data, appName = "Your App", configuredAppUrl = "", branding) {
1962
1963
  const { newEmail, confirmUrl } = data;
1963
1964
  const appUrl = configuredAppUrl;
1964
1965
  const safeUrl = sanitizeEmailUrl(confirmUrl, appUrl);
@@ -1967,15 +1968,13 @@ function generateEmailChangeConfirmation(data, appName = "Your App", configuredA
1967
1968
  body: `We received a request to change your ${escapeHtml(appName)} account email to this address. Click the button below to confirm this change.`,
1968
1969
  buttonText: "Confirm Email Change",
1969
1970
  buttonUrl: safeUrl,
1970
- buttonBgColor: "#00fe9a",
1971
- buttonTextColor: "#000004",
1972
- buttonFontWeight: 500,
1973
1971
  securityWarning: `
1974
1972
  <strong>This link expires in 24 hours.</strong><br>
1975
1973
  For security, this link can only be used once.
1976
1974
  `,
1977
1975
  footerNote: "If you didn't request this change, you can safely ignore this email.",
1978
- appUrl
1976
+ appUrl,
1977
+ ...branding
1979
1978
  });
1980
1979
  return {
1981
1980
  to: newEmail,
@@ -1997,7 +1996,7 @@ If you didn't request this change, you can safely ignore this email.`,
1997
1996
  }
1998
1997
  };
1999
1998
  }
2000
- function generateEmailChangeNotification(data, appName = "Your App", configuredAppUrl = "") {
1999
+ function generateEmailChangeNotification(data, appName = "Your App", configuredAppUrl = "", branding) {
2001
2000
  const { oldEmail, newEmail, cancelUrl } = data;
2002
2001
  const appUrl = configuredAppUrl;
2003
2002
  const safeUrl = sanitizeEmailUrl(cancelUrl, appUrl);
@@ -2007,15 +2006,13 @@ function generateEmailChangeNotification(data, appName = "Your App", configuredA
2007
2006
  body: `Someone requested to change your ${escapeHtml(appName)} account email to <strong>${safeNewEmail}</strong>.`,
2008
2007
  buttonText: "This Wasn't Me - Cancel Change",
2009
2008
  buttonUrl: safeUrl,
2010
- buttonBgColor: "#00fe9a",
2011
- buttonTextColor: "#000004",
2012
- buttonFontWeight: 500,
2013
2009
  securityWarning: `
2014
2010
  If you made this request, no action is needed.<br>
2015
2011
  Complete the change by clicking the link sent to your new email address.
2016
2012
  `,
2017
2013
  footerNote: "If you didn't request this change, click the button above to cancel it.",
2018
- appUrl
2014
+ appUrl,
2015
+ ...branding
2019
2016
  });
2020
2017
  return {
2021
2018
  to: oldEmail,
@@ -2035,7 +2032,7 @@ ${safeUrl}`,
2035
2032
  }
2036
2033
  };
2037
2034
  }
2038
- function generate2faCodeEmail(data, appName = "Your App", appUrl = "") {
2035
+ function generate2faCodeEmail(data, appName = "Your App", appUrl = "", branding) {
2039
2036
  const { email, firstName, code } = data;
2040
2037
  const safeFirstName = firstName ? escapeHtml(firstName) : "there";
2041
2038
  const html = generateEmailTemplate({
@@ -2050,7 +2047,8 @@ function generate2faCodeEmail(data, appName = "Your App", appUrl = "") {
2050
2047
  buttonText: "",
2051
2048
  buttonUrl: "",
2052
2049
  footerNote: "",
2053
- appUrl
2050
+ appUrl,
2051
+ ...branding
2054
2052
  });
2055
2053
  return {
2056
2054
  to: email,
@@ -2070,7 +2068,7 @@ If you didn't request this code, you can safely ignore this email.
2070
2068
  tags: { type: "2fa_code" }
2071
2069
  };
2072
2070
  }
2073
- function generate2faEnabledEmail(data, appName = "Your App", appUrl = "") {
2071
+ function generate2faEnabledEmail(data, appName = "Your App", appUrl = "", branding) {
2074
2072
  const { email, firstName, method } = data;
2075
2073
  const safeFirstName = firstName ? escapeHtml(firstName) : "there";
2076
2074
  const methodName = method === "totp" ? "an authenticator app" : "email codes";
@@ -2084,7 +2082,8 @@ function generate2faEnabledEmail(data, appName = "Your App", appUrl = "") {
2084
2082
  buttonText: "",
2085
2083
  buttonUrl: "",
2086
2084
  footerNote: "",
2087
- appUrl
2085
+ appUrl,
2086
+ ...branding
2088
2087
  });
2089
2088
  return {
2090
2089
  to: email,
@@ -2100,7 +2099,7 @@ If you didn't make this change, please contact support immediately and reset you
2100
2099
  tags: { type: "2fa_enabled" }
2101
2100
  };
2102
2101
  }
2103
- function generate2faDisabledEmail(data, appName = "Your App", appUrl = "") {
2102
+ function generate2faDisabledEmail(data, appName = "Your App", appUrl = "", branding) {
2104
2103
  const { email, firstName } = data;
2105
2104
  const safeFirstName = firstName ? escapeHtml(firstName) : "there";
2106
2105
  const html = generateEmailTemplate({
@@ -2112,10 +2111,9 @@ function generate2faDisabledEmail(data, appName = "Your App", appUrl = "") {
2112
2111
  `,
2113
2112
  buttonText: "Reset Password",
2114
2113
  buttonUrl: appUrl ? `${appUrl}/forgot-password` : "",
2115
- buttonBgColor: "#ef4444",
2116
- buttonTextColor: "#ffffff",
2117
2114
  footerNote: "",
2118
- appUrl
2115
+ appUrl,
2116
+ ...branding
2119
2117
  });
2120
2118
  return {
2121
2119
  to: email,
@@ -2305,6 +2303,7 @@ function createEmailAdapter(env) {
2305
2303
  var EmailService = class {
2306
2304
  adapter;
2307
2305
  env;
2306
+ branding;
2308
2307
  get fromAddress() {
2309
2308
  if (this.env.EMAIL_FROM) {
2310
2309
  return this.env.EMAIL_FROM;
@@ -2318,9 +2317,10 @@ var EmailService = class {
2318
2317
  }
2319
2318
  return `${appName} <hello@${domain}>`;
2320
2319
  }
2321
- constructor(env, adapter) {
2320
+ constructor(env, adapter, branding) {
2322
2321
  this.env = env;
2323
2322
  this.adapter = adapter ?? createEmailAdapter(env);
2323
+ this.branding = branding;
2324
2324
  }
2325
2325
  /**
2326
2326
  * Check if email address has bounced or complained
@@ -2372,7 +2372,8 @@ var EmailService = class {
2372
2372
  const template = generateVerificationEmail(
2373
2373
  data,
2374
2374
  this.env.APP_NAME ?? "Your App",
2375
- this.env.APP_URL ?? ""
2375
+ this.env.APP_URL ?? "",
2376
+ this.branding
2376
2377
  );
2377
2378
  const recipient = this.getRecipientEmail(data.email, "verification");
2378
2379
  logger_default.info("Sending verification email", {
@@ -2432,7 +2433,8 @@ var EmailService = class {
2432
2433
  const template = generatePasswordResetEmail(
2433
2434
  data,
2434
2435
  this.env.APP_NAME ?? "Your App",
2435
- this.env.APP_URL ?? ""
2436
+ this.env.APP_URL ?? "",
2437
+ this.branding
2436
2438
  );
2437
2439
  const recipient = this.getRecipientEmail(data.email, "password_reset");
2438
2440
  logger_default.info("Sending password reset email", {
@@ -2492,7 +2494,8 @@ var EmailService = class {
2492
2494
  const template = generateEmailChangeConfirmation(
2493
2495
  data,
2494
2496
  this.env.APP_NAME ?? "Your App",
2495
- this.env.APP_URL ?? ""
2497
+ this.env.APP_URL ?? "",
2498
+ this.branding
2496
2499
  );
2497
2500
  const recipient = this.getRecipientEmail(data.newEmail, "email_change_confirmation");
2498
2501
  logger_default.info("Sending email change confirmation", {
@@ -2552,7 +2555,8 @@ var EmailService = class {
2552
2555
  const template = generateEmailChangeNotification(
2553
2556
  data,
2554
2557
  this.env.APP_NAME ?? "Your App",
2555
- this.env.APP_URL ?? ""
2558
+ this.env.APP_URL ?? "",
2559
+ this.branding
2556
2560
  );
2557
2561
  const recipient = this.getRecipientEmail(data.oldEmail, "email_change_notification");
2558
2562
  logger_default.info("Sending email change notification", {
@@ -2609,7 +2613,8 @@ var EmailService = class {
2609
2613
  const template = generate2faCodeEmail(
2610
2614
  data,
2611
2615
  this.env.APP_NAME ?? "Your App",
2612
- this.env.APP_URL ?? ""
2616
+ this.env.APP_URL ?? "",
2617
+ this.branding
2613
2618
  );
2614
2619
  const recipient = this.getRecipientEmail(data.email, "2fa_code");
2615
2620
  logger_default.info("Sending 2FA code email", {
@@ -2666,7 +2671,8 @@ var EmailService = class {
2666
2671
  const template = generate2faEnabledEmail(
2667
2672
  data,
2668
2673
  this.env.APP_NAME ?? "Your App",
2669
- this.env.APP_URL ?? ""
2674
+ this.env.APP_URL ?? "",
2675
+ this.branding
2670
2676
  );
2671
2677
  const recipient = this.getRecipientEmail(data.email, "2fa_enabled");
2672
2678
  logger_default.info("Sending 2FA enabled email", {
@@ -2724,7 +2730,8 @@ var EmailService = class {
2724
2730
  const template = generate2faDisabledEmail(
2725
2731
  data,
2726
2732
  this.env.APP_NAME ?? "Your App",
2727
- this.env.APP_URL ?? ""
2733
+ this.env.APP_URL ?? "",
2734
+ this.branding
2728
2735
  );
2729
2736
  const recipient = this.getRecipientEmail(data.email, "2fa_disabled");
2730
2737
  logger_default.info("Sending 2FA disabled email", {
@@ -3056,7 +3063,7 @@ var signupRoute = createRoute({
3056
3063
  });
3057
3064
  var signupHandler = async (c) => {
3058
3065
  const { email, password, name, turnstileToken, oauthToken } = c.req.valid("json");
3059
- const { db: database, schema } = getAuthContext(c);
3066
+ const { db: database, schema, emailBranding } = getAuthContext(c);
3060
3067
  const env = c.env;
3061
3068
  if (oauthToken) {
3062
3069
  const stored = await env.OAUTH_STATES.get(`oauth:signup:${oauthToken}`);
@@ -3156,7 +3163,7 @@ var signupHandler = async (c) => {
3156
3163
  expiresAt,
3157
3164
  createdAt: Date.now()
3158
3165
  });
3159
- const emailService = new EmailService(env);
3166
+ const emailService = new EmailService(env, void 0, emailBranding);
3160
3167
  const verificationUrl = `${env.APP_URL}/verify-email?token=${emailToken}`;
3161
3168
  await emailService.sendVerificationEmail(
3162
3169
  {
@@ -3640,7 +3647,7 @@ var forgotPasswordRoute = createRoute6({
3640
3647
  });
3641
3648
  var forgotPasswordHandler = async (c) => {
3642
3649
  const { email, turnstileToken } = c.req.valid("json");
3643
- const { db: database, schema } = getAuthContext(c);
3650
+ const { db: database, schema, emailBranding } = getAuthContext(c);
3644
3651
  const env = c.env;
3645
3652
  const turnstileValid = await verifyTurnstileToken(
3646
3653
  turnstileToken,
@@ -3659,7 +3666,7 @@ var forgotPasswordHandler = async (c) => {
3659
3666
  if (!resetResult) {
3660
3667
  return c.json({ success: true });
3661
3668
  }
3662
- const emailService = new EmailService(env);
3669
+ const emailService = new EmailService(env, void 0, emailBranding);
3663
3670
  const resetUrl = `${env.APP_URL}/reset-password?token=${resetResult.token}`;
3664
3671
  await emailService.sendPasswordResetEmail(
3665
3672
  {
@@ -3907,7 +3914,7 @@ var changeEmailHandler = async (c) => {
3907
3914
  const userId = c.get("userId");
3908
3915
  if (!userId) return problems.unauthorized(c, "Not authenticated");
3909
3916
  const { password, newEmail } = c.req.valid("json");
3910
- const { db: database, schema } = getAuthContext(c);
3917
+ const { db: database, schema, emailBranding } = getAuthContext(c);
3911
3918
  const env = c.env;
3912
3919
  const appUrl = env.APP_URL || "http://localhost:5173";
3913
3920
  const result = await requestEmailChange({
@@ -3924,7 +3931,7 @@ var changeEmailHandler = async (c) => {
3924
3931
  }
3925
3932
  return problems.badRequest(c, result.error || "Failed to request email change");
3926
3933
  }
3927
- const emailService = new EmailService(env);
3934
+ const emailService = new EmailService(env, void 0, emailBranding);
3928
3935
  const confirmUrl = `${appUrl}/confirm-email-change?token=${result.confirmToken}`;
3929
3936
  const confirmResult = await emailService.sendEmailChangeConfirmation(
3930
3937
  { newEmail, confirmUrl },
@@ -4229,7 +4236,7 @@ var resendVerificationRoute = createRoute15({
4229
4236
  });
4230
4237
  var resendVerificationHandler = async (c) => {
4231
4238
  const userId = c.get("userId");
4232
- const { db, schema } = getAuthContext(c);
4239
+ const { db, schema, emailBranding } = getAuthContext(c);
4233
4240
  const env = c.env;
4234
4241
  const [user] = await db.select({
4235
4242
  id: schema.users.id,
@@ -4254,7 +4261,7 @@ var resendVerificationHandler = async (c) => {
4254
4261
  expiresAt,
4255
4262
  createdAt: Date.now()
4256
4263
  });
4257
- const emailService = new EmailService(env);
4264
+ const emailService = new EmailService(env, void 0, emailBranding);
4258
4265
  const verificationUrl = `${env.APP_URL}/verify-email?token=${emailToken}`;
4259
4266
  await emailService.sendVerificationEmail(
4260
4267
  {
@@ -4542,7 +4549,7 @@ var totpVerifyHandler = async (c) => {
4542
4549
  const userId = c.get("userId");
4543
4550
  if (!userId) return problems.unauthorized(c);
4544
4551
  const { code } = c.req.valid("json");
4545
- const { db, schema, env } = getAuthContext(c);
4552
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4546
4553
  const secret = await env.OAUTH_STATES.get(`totp_setup:${userId}`);
4547
4554
  if (!secret) {
4548
4555
  return problems.badRequest(c, "Setup expired. Please start again.");
@@ -4586,7 +4593,7 @@ var totpVerifyHandler = async (c) => {
4586
4593
  backupCodes = codes.map(formatBackupCode);
4587
4594
  }
4588
4595
  if (user) {
4589
- const emailService = new EmailService(env);
4596
+ const emailService = new EmailService(env, void 0, emailBranding);
4590
4597
  const firstName = user.name?.split(" ")[0] || null;
4591
4598
  await emailService.send2faEnabledEmail(
4592
4599
  { email: user.email, firstName, method: "totp" },
@@ -4634,7 +4641,7 @@ var totpDisableHandler = async (c) => {
4634
4641
  return problems.unauthorized(c);
4635
4642
  }
4636
4643
  const { code, method } = c.req.valid("json");
4637
- const { db, schema, env } = getAuthContext(c);
4644
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4638
4645
  const verification = await verifyAny2faCode(db, env, userId, code, method, {
4639
4646
  user2faMethods: schema.user2faMethods,
4640
4647
  userBackupCodes: schema.userBackupCodes
@@ -4651,7 +4658,7 @@ var totpDisableHandler = async (c) => {
4651
4658
  await invalidateAllUserSessions(db, { sessions: schema.sessions, users: schema.users }, userId);
4652
4659
  const [user] = await db.select({ email: schema.users.email, name: schema.users.name }).from(schema.users).where(eq22(schema.users.id, userId)).limit(1);
4653
4660
  if (user) {
4654
- const emailService = new EmailService(env);
4661
+ const emailService = new EmailService(env, void 0, emailBranding);
4655
4662
  const firstName = user.name?.split(" ")[0] || null;
4656
4663
  await emailService.send2faDisabledEmail({ email: user.email, firstName }, db, schema.users);
4657
4664
  }
@@ -4697,7 +4704,7 @@ var emailSetupRoute = createRoute20({
4697
4704
  var emailSetupHandler = async (c) => {
4698
4705
  const userId = c.get("userId");
4699
4706
  if (!userId) return problems.unauthorized(c);
4700
- const { db, schema, env } = getAuthContext(c);
4707
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4701
4708
  const existing = await db.select({ id: schema.user2faMethods.id }).from(schema.user2faMethods).where(and10(eq23(schema.user2faMethods.userId, userId), eq23(schema.user2faMethods.method, "email"))).limit(1);
4702
4709
  if (existing.length > 0) {
4703
4710
  return problems.badRequest(c, "Email 2FA already enabled");
@@ -4707,7 +4714,7 @@ var emailSetupHandler = async (c) => {
4707
4714
  const code = generateEmailOtp();
4708
4715
  const codeHash = await hashToken(code);
4709
4716
  await env.OAUTH_STATES.put(`email_2fa_setup:${userId}`, codeHash, { expirationTtl: 300 });
4710
- const emailService = new EmailService(env);
4717
+ const emailService = new EmailService(env, void 0, emailBranding);
4711
4718
  const firstName = user.name?.split(" ")[0] || null;
4712
4719
  await emailService.send2faCodeEmail({ email: user.email, firstName, code }, db, schema.users);
4713
4720
  logSecurityEvent("2fa_email_setup_initiated", "low", { userId });
@@ -4748,7 +4755,7 @@ var emailVerifyHandler = async (c) => {
4748
4755
  const userId = c.get("userId");
4749
4756
  if (!userId) return problems.unauthorized(c);
4750
4757
  const { code } = c.req.valid("json");
4751
- const { db, schema, env } = getAuthContext(c);
4758
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4752
4759
  const storedHash = await env.OAUTH_STATES.get(`email_2fa_setup:${userId}`);
4753
4760
  if (!storedHash) {
4754
4761
  return problems.badRequest(c, "Code expired. Please request a new one.");
@@ -4786,7 +4793,7 @@ var emailVerifyHandler = async (c) => {
4786
4793
  backupCodes = codes.map(formatBackupCode);
4787
4794
  }
4788
4795
  if (user) {
4789
- const emailService = new EmailService(env);
4796
+ const emailService = new EmailService(env, void 0, emailBranding);
4790
4797
  const firstName = user.name?.split(" ")[0] || null;
4791
4798
  await emailService.send2faEnabledEmail(
4792
4799
  { email: user.email, firstName, method: "email" },
@@ -4829,7 +4836,7 @@ var emailSendCodeRoute = createRoute22({
4829
4836
  var emailSendCodeHandler = async (c) => {
4830
4837
  const userId = c.get("userId");
4831
4838
  if (!userId) return problems.unauthorized(c);
4832
- const { db, schema, env } = getAuthContext(c);
4839
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4833
4840
  const [emailMethod] = await db.select().from(schema.user2faMethods).where(and11(eq25(schema.user2faMethods.userId, userId), eq25(schema.user2faMethods.method, "email"))).limit(1);
4834
4841
  if (!emailMethod) {
4835
4842
  return problems.badRequest(c, "Email 2FA not enabled");
@@ -4841,7 +4848,7 @@ var emailSendCodeHandler = async (c) => {
4841
4848
  await env.OAUTH_STATES.put(`email_2fa_challenge:${userId}`, codeHash, {
4842
4849
  expirationTtl: 300
4843
4850
  });
4844
- const emailService = new EmailService(env);
4851
+ const emailService = new EmailService(env, void 0, emailBranding);
4845
4852
  const firstName = user.name?.split(" ")[0] || null;
4846
4853
  const emailResult = await emailService.send2faCodeEmail(
4847
4854
  { email: user.email, firstName, code },
@@ -4894,7 +4901,7 @@ var emailDisableHandler = async (c) => {
4894
4901
  return problems.unauthorized(c);
4895
4902
  }
4896
4903
  const { code, method } = c.req.valid("json");
4897
- const { db, schema, env } = getAuthContext(c);
4904
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4898
4905
  const verification = await verifyAny2faCode(db, env, userId, code, method, {
4899
4906
  user2faMethods: schema.user2faMethods,
4900
4907
  userBackupCodes: schema.userBackupCodes
@@ -4911,7 +4918,7 @@ var emailDisableHandler = async (c) => {
4911
4918
  await invalidateAllUserSessions(db, { sessions: schema.sessions, users: schema.users }, userId);
4912
4919
  const [user] = await db.select({ email: schema.users.email, name: schema.users.name }).from(schema.users).where(eq26(schema.users.id, userId)).limit(1);
4913
4920
  if (user) {
4914
- const emailService = new EmailService(env);
4921
+ const emailService = new EmailService(env, void 0, emailBranding);
4915
4922
  const firstName = user.name?.split(" ")[0] || null;
4916
4923
  await emailService.send2faDisabledEmail({ email: user.email, firstName }, db, schema.users);
4917
4924
  }
@@ -5208,7 +5215,7 @@ var challengeResendRoute = createRoute27({
5208
5215
  }
5209
5216
  });
5210
5217
  var challengeResendHandler = async (c) => {
5211
- const { db, schema } = getAuthContext(c);
5218
+ const { db, schema, emailBranding } = getAuthContext(c);
5212
5219
  const cookieName = getChallengeCookieName(c.env);
5213
5220
  const challengeToken = getCookie3(c, cookieName);
5214
5221
  if (!challengeToken) {
@@ -5232,7 +5239,7 @@ var challengeResendHandler = async (c) => {
5232
5239
  await c.env.OAUTH_STATES.put(`email_2fa_challenge:${payload.userId}`, codeHash, {
5233
5240
  expirationTtl: 300
5234
5241
  });
5235
- const emailService = new EmailService(c.env);
5242
+ const emailService = new EmailService(c.env, void 0, emailBranding);
5236
5243
  const firstName = user.name?.split(" ")[0] || null;
5237
5244
  await emailService.send2faCodeEmail({ email: user.email, firstName, code }, db, schema.users);
5238
5245
  logger_default.info("Email 2FA code resent", { userId: payload.userId });