@syncello/auth 3.3.1 → 3.4.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.
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
  {
@@ -3710,7 +3717,7 @@ var resetPasswordRoute = createRoute7({
3710
3717
  });
3711
3718
  var resetPasswordHandler = async (c) => {
3712
3719
  const { token, password, turnstileToken } = c.req.valid("json");
3713
- const database = c.get("db");
3720
+ const { db: database, schema } = getAuthContext(c);
3714
3721
  const env = c.env;
3715
3722
  const turnstileValid = await verifyTurnstileToken(
3716
3723
  turnstileToken,
@@ -3725,7 +3732,18 @@ var resetPasswordHandler = async (c) => {
3725
3732
  if (!passwordValidation.valid) {
3726
3733
  return problems.badRequest(c, passwordValidation.error || "Invalid password");
3727
3734
  }
3728
- const result = await resetPassword(database, token, password, env.PASSWORD_PEPPER_V1, "v1");
3735
+ const result = await resetPassword(
3736
+ database,
3737
+ {
3738
+ users: schema.users,
3739
+ passwordResetTokens: schema.passwordResetTokens,
3740
+ sessions: schema.sessions
3741
+ },
3742
+ token,
3743
+ password,
3744
+ env.PASSWORD_PEPPER_V1,
3745
+ "v1"
3746
+ );
3729
3747
  if (!result.success) {
3730
3748
  return problems.badRequest(c, result.error || "Invalid or expired reset token");
3731
3749
  }
@@ -3907,7 +3925,7 @@ var changeEmailHandler = async (c) => {
3907
3925
  const userId = c.get("userId");
3908
3926
  if (!userId) return problems.unauthorized(c, "Not authenticated");
3909
3927
  const { password, newEmail } = c.req.valid("json");
3910
- const { db: database, schema } = getAuthContext(c);
3928
+ const { db: database, schema, emailBranding } = getAuthContext(c);
3911
3929
  const env = c.env;
3912
3930
  const appUrl = env.APP_URL || "http://localhost:5173";
3913
3931
  const result = await requestEmailChange({
@@ -3924,7 +3942,7 @@ var changeEmailHandler = async (c) => {
3924
3942
  }
3925
3943
  return problems.badRequest(c, result.error || "Failed to request email change");
3926
3944
  }
3927
- const emailService = new EmailService(env);
3945
+ const emailService = new EmailService(env, void 0, emailBranding);
3928
3946
  const confirmUrl = `${appUrl}/confirm-email-change?token=${result.confirmToken}`;
3929
3947
  const confirmResult = await emailService.sendEmailChangeConfirmation(
3930
3948
  { newEmail, confirmUrl },
@@ -4229,7 +4247,7 @@ var resendVerificationRoute = createRoute15({
4229
4247
  });
4230
4248
  var resendVerificationHandler = async (c) => {
4231
4249
  const userId = c.get("userId");
4232
- const { db, schema } = getAuthContext(c);
4250
+ const { db, schema, emailBranding } = getAuthContext(c);
4233
4251
  const env = c.env;
4234
4252
  const [user] = await db.select({
4235
4253
  id: schema.users.id,
@@ -4254,7 +4272,7 @@ var resendVerificationHandler = async (c) => {
4254
4272
  expiresAt,
4255
4273
  createdAt: Date.now()
4256
4274
  });
4257
- const emailService = new EmailService(env);
4275
+ const emailService = new EmailService(env, void 0, emailBranding);
4258
4276
  const verificationUrl = `${env.APP_URL}/verify-email?token=${emailToken}`;
4259
4277
  await emailService.sendVerificationEmail(
4260
4278
  {
@@ -4542,7 +4560,7 @@ var totpVerifyHandler = async (c) => {
4542
4560
  const userId = c.get("userId");
4543
4561
  if (!userId) return problems.unauthorized(c);
4544
4562
  const { code } = c.req.valid("json");
4545
- const { db, schema, env } = getAuthContext(c);
4563
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4546
4564
  const secret = await env.OAUTH_STATES.get(`totp_setup:${userId}`);
4547
4565
  if (!secret) {
4548
4566
  return problems.badRequest(c, "Setup expired. Please start again.");
@@ -4586,7 +4604,7 @@ var totpVerifyHandler = async (c) => {
4586
4604
  backupCodes = codes.map(formatBackupCode);
4587
4605
  }
4588
4606
  if (user) {
4589
- const emailService = new EmailService(env);
4607
+ const emailService = new EmailService(env, void 0, emailBranding);
4590
4608
  const firstName = user.name?.split(" ")[0] || null;
4591
4609
  await emailService.send2faEnabledEmail(
4592
4610
  { email: user.email, firstName, method: "totp" },
@@ -4634,7 +4652,7 @@ var totpDisableHandler = async (c) => {
4634
4652
  return problems.unauthorized(c);
4635
4653
  }
4636
4654
  const { code, method } = c.req.valid("json");
4637
- const { db, schema, env } = getAuthContext(c);
4655
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4638
4656
  const verification = await verifyAny2faCode(db, env, userId, code, method, {
4639
4657
  user2faMethods: schema.user2faMethods,
4640
4658
  userBackupCodes: schema.userBackupCodes
@@ -4651,7 +4669,7 @@ var totpDisableHandler = async (c) => {
4651
4669
  await invalidateAllUserSessions(db, { sessions: schema.sessions, users: schema.users }, userId);
4652
4670
  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
4671
  if (user) {
4654
- const emailService = new EmailService(env);
4672
+ const emailService = new EmailService(env, void 0, emailBranding);
4655
4673
  const firstName = user.name?.split(" ")[0] || null;
4656
4674
  await emailService.send2faDisabledEmail({ email: user.email, firstName }, db, schema.users);
4657
4675
  }
@@ -4697,7 +4715,7 @@ var emailSetupRoute = createRoute20({
4697
4715
  var emailSetupHandler = async (c) => {
4698
4716
  const userId = c.get("userId");
4699
4717
  if (!userId) return problems.unauthorized(c);
4700
- const { db, schema, env } = getAuthContext(c);
4718
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4701
4719
  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
4720
  if (existing.length > 0) {
4703
4721
  return problems.badRequest(c, "Email 2FA already enabled");
@@ -4707,7 +4725,7 @@ var emailSetupHandler = async (c) => {
4707
4725
  const code = generateEmailOtp();
4708
4726
  const codeHash = await hashToken(code);
4709
4727
  await env.OAUTH_STATES.put(`email_2fa_setup:${userId}`, codeHash, { expirationTtl: 300 });
4710
- const emailService = new EmailService(env);
4728
+ const emailService = new EmailService(env, void 0, emailBranding);
4711
4729
  const firstName = user.name?.split(" ")[0] || null;
4712
4730
  await emailService.send2faCodeEmail({ email: user.email, firstName, code }, db, schema.users);
4713
4731
  logSecurityEvent("2fa_email_setup_initiated", "low", { userId });
@@ -4748,7 +4766,7 @@ var emailVerifyHandler = async (c) => {
4748
4766
  const userId = c.get("userId");
4749
4767
  if (!userId) return problems.unauthorized(c);
4750
4768
  const { code } = c.req.valid("json");
4751
- const { db, schema, env } = getAuthContext(c);
4769
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4752
4770
  const storedHash = await env.OAUTH_STATES.get(`email_2fa_setup:${userId}`);
4753
4771
  if (!storedHash) {
4754
4772
  return problems.badRequest(c, "Code expired. Please request a new one.");
@@ -4786,7 +4804,7 @@ var emailVerifyHandler = async (c) => {
4786
4804
  backupCodes = codes.map(formatBackupCode);
4787
4805
  }
4788
4806
  if (user) {
4789
- const emailService = new EmailService(env);
4807
+ const emailService = new EmailService(env, void 0, emailBranding);
4790
4808
  const firstName = user.name?.split(" ")[0] || null;
4791
4809
  await emailService.send2faEnabledEmail(
4792
4810
  { email: user.email, firstName, method: "email" },
@@ -4829,7 +4847,7 @@ var emailSendCodeRoute = createRoute22({
4829
4847
  var emailSendCodeHandler = async (c) => {
4830
4848
  const userId = c.get("userId");
4831
4849
  if (!userId) return problems.unauthorized(c);
4832
- const { db, schema, env } = getAuthContext(c);
4850
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4833
4851
  const [emailMethod] = await db.select().from(schema.user2faMethods).where(and11(eq25(schema.user2faMethods.userId, userId), eq25(schema.user2faMethods.method, "email"))).limit(1);
4834
4852
  if (!emailMethod) {
4835
4853
  return problems.badRequest(c, "Email 2FA not enabled");
@@ -4841,7 +4859,7 @@ var emailSendCodeHandler = async (c) => {
4841
4859
  await env.OAUTH_STATES.put(`email_2fa_challenge:${userId}`, codeHash, {
4842
4860
  expirationTtl: 300
4843
4861
  });
4844
- const emailService = new EmailService(env);
4862
+ const emailService = new EmailService(env, void 0, emailBranding);
4845
4863
  const firstName = user.name?.split(" ")[0] || null;
4846
4864
  const emailResult = await emailService.send2faCodeEmail(
4847
4865
  { email: user.email, firstName, code },
@@ -4894,7 +4912,7 @@ var emailDisableHandler = async (c) => {
4894
4912
  return problems.unauthorized(c);
4895
4913
  }
4896
4914
  const { code, method } = c.req.valid("json");
4897
- const { db, schema, env } = getAuthContext(c);
4915
+ const { db, schema, env, emailBranding } = getAuthContext(c);
4898
4916
  const verification = await verifyAny2faCode(db, env, userId, code, method, {
4899
4917
  user2faMethods: schema.user2faMethods,
4900
4918
  userBackupCodes: schema.userBackupCodes
@@ -4911,7 +4929,7 @@ var emailDisableHandler = async (c) => {
4911
4929
  await invalidateAllUserSessions(db, { sessions: schema.sessions, users: schema.users }, userId);
4912
4930
  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
4931
  if (user) {
4914
- const emailService = new EmailService(env);
4932
+ const emailService = new EmailService(env, void 0, emailBranding);
4915
4933
  const firstName = user.name?.split(" ")[0] || null;
4916
4934
  await emailService.send2faDisabledEmail({ email: user.email, firstName }, db, schema.users);
4917
4935
  }
@@ -5208,7 +5226,7 @@ var challengeResendRoute = createRoute27({
5208
5226
  }
5209
5227
  });
5210
5228
  var challengeResendHandler = async (c) => {
5211
- const { db, schema } = getAuthContext(c);
5229
+ const { db, schema, emailBranding } = getAuthContext(c);
5212
5230
  const cookieName = getChallengeCookieName(c.env);
5213
5231
  const challengeToken = getCookie3(c, cookieName);
5214
5232
  if (!challengeToken) {
@@ -5232,7 +5250,7 @@ var challengeResendHandler = async (c) => {
5232
5250
  await c.env.OAUTH_STATES.put(`email_2fa_challenge:${payload.userId}`, codeHash, {
5233
5251
  expirationTtl: 300
5234
5252
  });
5235
- const emailService = new EmailService(c.env);
5253
+ const emailService = new EmailService(c.env, void 0, emailBranding);
5236
5254
  const firstName = user.name?.split(" ")[0] || null;
5237
5255
  await emailService.send2faCodeEmail({ email: user.email, firstName, code }, db, schema.users);
5238
5256
  logger_default.info("Email 2FA code resent", { userId: payload.userId });