@syncello/auth 2.5.1 → 3.2.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
@@ -32,6 +32,7 @@ var src_exports = {};
32
32
  __export(src_exports, {
33
33
  AUTH_DEFAULTS: () => AUTH_DEFAULTS,
34
34
  CHALLENGE_TTL_MS: () => CHALLENGE_TTL_MS,
35
+ CloudflareEmailAdapter: () => CloudflareEmailAdapter,
35
36
  EmailService: () => EmailService,
36
37
  MAX_CHALLENGE_ATTEMPTS: () => MAX_CHALLENGE_ATTEMPTS,
37
38
  ProblemTypes: () => ProblemTypes,
@@ -898,16 +899,16 @@ function createDeviceToken() {
898
899
  };
899
900
  }
900
901
  async function validateTrustedDevice(db, userTrustedDevicesTable, userId, tokenHash) {
901
- const { eq: eq31, and: and14, gt: gt6 } = await import("drizzle-orm");
902
+ const { eq: eq33, and: and14, gt: gt6 } = await import("drizzle-orm");
902
903
  const [device] = await db.select().from(userTrustedDevicesTable).where(
903
904
  and14(
904
- eq31(userTrustedDevicesTable.userId, userId),
905
- eq31(userTrustedDevicesTable.tokenHash, tokenHash),
905
+ eq33(userTrustedDevicesTable.userId, userId),
906
+ eq33(userTrustedDevicesTable.tokenHash, tokenHash),
906
907
  gt6(userTrustedDevicesTable.expiresAt, Date.now())
907
908
  )
908
909
  ).limit(1);
909
910
  if (device) {
910
- await db.update(userTrustedDevicesTable).set({ lastUsedAt: Date.now() }).where(eq31(userTrustedDevicesTable.id, device.id));
911
+ await db.update(userTrustedDevicesTable).set({ lastUsedAt: Date.now() }).where(eq33(userTrustedDevicesTable.id, device.id));
911
912
  return true;
912
913
  }
913
914
  return false;
@@ -1829,7 +1830,7 @@ var requireVerifiedEmail = (0, import_factory5.createMiddleware)(
1829
1830
  );
1830
1831
 
1831
1832
  // src/routes/index.ts
1832
- var import_zod_openapi37 = require("@hono/zod-openapi");
1833
+ var import_zod_openapi42 = require("@hono/zod-openapi");
1833
1834
 
1834
1835
  // src/routes/signup.ts
1835
1836
  var import_zod_openapi2 = require("@hono/zod-openapi");
@@ -2572,13 +2573,92 @@ var ResendAdapter = class {
2572
2573
  }
2573
2574
  };
2574
2575
 
2576
+ // src/lib/email/adapters/cloudflare-adapter.ts
2577
+ var CloudflareEmailAdapter = class {
2578
+ constructor(binding, options) {
2579
+ this.binding = binding;
2580
+ this.options = options;
2581
+ }
2582
+ binding;
2583
+ options;
2584
+ providerName = "cloudflare";
2585
+ async send(options) {
2586
+ if (this.options.dryRun) {
2587
+ logger_default.info("Cloudflare: Dry-run, email not sent", {
2588
+ provider: this.providerName,
2589
+ to: options.to,
2590
+ from: options.from,
2591
+ subject: options.subject
2592
+ });
2593
+ return { success: true, emailId: "dry-run" };
2594
+ }
2595
+ try {
2596
+ logger_default.info("Cloudflare: Sending email", {
2597
+ provider: this.providerName,
2598
+ to: options.to,
2599
+ subject: options.subject
2600
+ });
2601
+ const result = await this.binding.send({
2602
+ to: options.to,
2603
+ from: options.from,
2604
+ subject: options.subject,
2605
+ html: options.html,
2606
+ text: options.text,
2607
+ headers: options.tags ? Object.fromEntries(
2608
+ Object.entries(options.tags).map(([name, value]) => [`X-Tag-${name}`, value])
2609
+ ) : void 0
2610
+ });
2611
+ logger_default.info("Cloudflare: Email sent successfully", {
2612
+ provider: this.providerName,
2613
+ emailId: result.messageId,
2614
+ to: options.to
2615
+ });
2616
+ return { success: true, emailId: result.messageId };
2617
+ } catch (error) {
2618
+ const message = error instanceof Error ? error.message : "Unknown error";
2619
+ const code = error instanceof Error && "code" in error && typeof error.code === "string" ? error.code : void 0;
2620
+ const errorText = code ? `${code}: ${message}` : message;
2621
+ if (code === "E_RECIPIENT_SUPPRESSED") {
2622
+ logger_default.warn("Cloudflare: Recipient is on the suppression list", {
2623
+ provider: this.providerName,
2624
+ to: options.to
2625
+ });
2626
+ } else {
2627
+ logger_default.error("Cloudflare send error", {
2628
+ provider: this.providerName,
2629
+ error: errorText,
2630
+ to: options.to,
2631
+ subject: options.subject
2632
+ });
2633
+ }
2634
+ return { success: false, error: errorText };
2635
+ }
2636
+ }
2637
+ };
2638
+
2575
2639
  // src/lib/email/adapters/factory.ts
2576
2640
  function createEmailAdapter(env) {
2577
- logger_default.info("Creating email adapter", { provider: "resend" });
2578
- if (!env.RESEND_API_KEY) {
2579
- throw new Error("RESEND_API_KEY is required");
2641
+ const provider = env.EMAIL_PROVIDER ?? "resend";
2642
+ logger_default.info("Creating email adapter", { provider });
2643
+ switch (provider) {
2644
+ case "resend":
2645
+ if (!env.RESEND_API_KEY) {
2646
+ throw new Error("RESEND_API_KEY is required");
2647
+ }
2648
+ return new ResendAdapter(env.RESEND_API_KEY);
2649
+ case "cloudflare": {
2650
+ if (!env.EMAIL) {
2651
+ throw new Error(
2652
+ 'EMAIL send_email binding is required when EMAIL_PROVIDER=cloudflare - add [[send_email]] name = "EMAIL" to wrangler.toml'
2653
+ );
2654
+ }
2655
+ const environment = env.ENVIRONMENT?.toLowerCase();
2656
+ const dryRun = environment !== "production" && environment !== "staging";
2657
+ return new CloudflareEmailAdapter(env.EMAIL, { dryRun });
2658
+ }
2659
+ default:
2660
+ throw new Error(`Unknown EMAIL_PROVIDER: ${String(provider)}`);
2580
2661
  }
2581
- return new ResendAdapter(env.RESEND_API_KEY);
2582
2662
  }
2583
2663
 
2584
2664
  // src/lib/email/email-service.ts
@@ -2586,6 +2666,9 @@ var EmailService = class {
2586
2666
  adapter;
2587
2667
  env;
2588
2668
  get fromAddress() {
2669
+ if (this.env.EMAIL_FROM) {
2670
+ return this.env.EMAIL_FROM;
2671
+ }
2589
2672
  const appName = this.env.APP_NAME ?? "Your App";
2590
2673
  const appUrl = this.env.APP_URL ?? "";
2591
2674
  let domain = "example.com";
@@ -3207,19 +3290,19 @@ var signupRequestSchema = import_zod_openapi.z.object({
3207
3290
  var loginRequestSchema = import_zod_openapi.z.object({
3208
3291
  email: import_zod_openapi.z.string().email().openapi({ example: "user@example.com" }),
3209
3292
  password: import_zod_openapi.z.string().min(1).openapi({ example: "SecurePass123!" }),
3210
- turnstileToken: import_zod_openapi.z.string().min(1).openapi({ example: "XXXX.DUMMY.TOKEN.XXXX" })
3293
+ turnstileToken: import_zod_openapi.z.string().optional().openapi({ example: "XXXX.DUMMY.TOKEN.XXXX" })
3211
3294
  }).openapi("LoginRequest");
3212
3295
  var verifyEmailRequestSchema = import_zod_openapi.z.object({
3213
3296
  token: import_zod_openapi.z.string().min(1).openapi({ example: "verification-token-abc123" })
3214
3297
  }).openapi("VerifyEmailRequest");
3215
3298
  var forgotPasswordRequestSchema = import_zod_openapi.z.object({
3216
3299
  email: import_zod_openapi.z.string().email().openapi({ example: "user@example.com" }),
3217
- turnstileToken: import_zod_openapi.z.string().min(1).openapi({ example: "XXXX.DUMMY.TOKEN.XXXX" })
3300
+ turnstileToken: import_zod_openapi.z.string().optional().openapi({ example: "XXXX.DUMMY.TOKEN.XXXX" })
3218
3301
  }).openapi("ForgotPasswordRequest");
3219
3302
  var resetPasswordRequestSchema = import_zod_openapi.z.object({
3220
3303
  token: import_zod_openapi.z.string().min(1).openapi({ example: "reset-token-xyz789" }),
3221
3304
  password: import_zod_openapi.z.string().min(8).openapi({ example: "NewSecurePass456!" }),
3222
- turnstileToken: import_zod_openapi.z.string().min(1).openapi({ example: "XXXX.DUMMY.TOKEN.XXXX" })
3305
+ turnstileToken: import_zod_openapi.z.string().optional().openapi({ example: "XXXX.DUMMY.TOKEN.XXXX" })
3223
3306
  }).openapi("ResetPasswordRequest");
3224
3307
  var changePasswordRequestSchema = import_zod_openapi.z.object({
3225
3308
  currentPassword: import_zod_openapi.z.string().min(1).openapi({ example: "OldPassword123!" }),
@@ -3442,7 +3525,8 @@ var signupHandler = async (c) => {
3442
3525
  verificationUrl,
3443
3526
  firstName: newUser.name || void 0
3444
3527
  },
3445
- database
3528
+ database,
3529
+ schema.users
3446
3530
  );
3447
3531
  const fingerprint = await generateFingerprint(c.req.raw);
3448
3532
  const ipAddress = getClientIp(c.req.raw);
@@ -3916,7 +4000,7 @@ var forgotPasswordRoute = (0, import_zod_openapi7.createRoute)({
3916
4000
  });
3917
4001
  var forgotPasswordHandler = async (c) => {
3918
4002
  const { email, turnstileToken } = c.req.valid("json");
3919
- const database = c.get("db");
4003
+ const { db: database, schema } = getAuthContext(c);
3920
4004
  const env = c.env;
3921
4005
  const turnstileValid = await verifyTurnstileToken(
3922
4006
  turnstileToken,
@@ -3927,7 +4011,11 @@ var forgotPasswordHandler = async (c) => {
3927
4011
  if (!turnstileValid) {
3928
4012
  return problems.badRequest(c, "Invalid captcha");
3929
4013
  }
3930
- const resetResult = await createPasswordResetToken(database, email);
4014
+ const resetResult = await createPasswordResetToken(
4015
+ database,
4016
+ { users: schema.users, passwordResetTokens: schema.passwordResetTokens },
4017
+ email
4018
+ );
3931
4019
  if (!resetResult) {
3932
4020
  return c.json({ success: true });
3933
4021
  }
@@ -3939,7 +4027,8 @@ var forgotPasswordHandler = async (c) => {
3939
4027
  token: resetResult.token,
3940
4028
  resetUrl
3941
4029
  },
3942
- database
4030
+ database,
4031
+ schema.users
3943
4032
  );
3944
4033
  logSecurityEvent("password_reset_requested", "medium", { userId: resetResult.userId });
3945
4034
  return c.json({ success: true });
@@ -4081,7 +4170,7 @@ var changePasswordHandler = async (c) => {
4081
4170
  const userId = c.get("userId");
4082
4171
  if (!userId) return problems.unauthorized(c, "Not authenticated");
4083
4172
  const { currentPassword, newPassword } = c.req.valid("json");
4084
- const database = c.get("db");
4173
+ const { db: database, schema } = getAuthContext(c);
4085
4174
  const env = c.env;
4086
4175
  const passwordValidation = validatePassword(newPassword);
4087
4176
  if (!passwordValidation.valid) {
@@ -4098,7 +4187,8 @@ var changePasswordHandler = async (c) => {
4098
4187
  newPassword,
4099
4188
  currentSessionId,
4100
4189
  pepper: env.PASSWORD_PEPPER_V1,
4101
- db: database
4190
+ db: database,
4191
+ tables: { users: schema.users, sessions: schema.sessions }
4102
4192
  });
4103
4193
  if (!result.success) {
4104
4194
  return problems.badRequest(c, result.error || "Failed to change password");
@@ -4177,7 +4267,7 @@ var changeEmailHandler = async (c) => {
4177
4267
  const userId = c.get("userId");
4178
4268
  if (!userId) return problems.unauthorized(c, "Not authenticated");
4179
4269
  const { password, newEmail } = c.req.valid("json");
4180
- const database = c.get("db");
4270
+ const { db: database, schema } = getAuthContext(c);
4181
4271
  const env = c.env;
4182
4272
  const appUrl = env.APP_URL || "http://localhost:5173";
4183
4273
  const result = await requestEmailChange({
@@ -4185,7 +4275,8 @@ var changeEmailHandler = async (c) => {
4185
4275
  password,
4186
4276
  newEmail,
4187
4277
  pepper: env.PASSWORD_PEPPER_V1,
4188
- db: database
4278
+ db: database,
4279
+ tables: { users: schema.users, emailChangeTokens: schema.emailChangeTokens }
4189
4280
  });
4190
4281
  if (!result.success) {
4191
4282
  if (result.error === "Incorrect password") {
@@ -4197,7 +4288,8 @@ var changeEmailHandler = async (c) => {
4197
4288
  const confirmUrl = `${appUrl}/confirm-email-change?token=${result.confirmToken}`;
4198
4289
  const confirmResult = await emailService.sendEmailChangeConfirmation(
4199
4290
  { newEmail, confirmUrl },
4200
- database
4291
+ database,
4292
+ schema.users
4201
4293
  );
4202
4294
  if (!confirmResult.success) {
4203
4295
  return problems.badRequest(
@@ -4208,7 +4300,8 @@ var changeEmailHandler = async (c) => {
4208
4300
  const cancelUrl = `${appUrl}/cancel-email-change?token=${result.cancelToken}`;
4209
4301
  await emailService.sendEmailChangeNotification(
4210
4302
  { oldEmail: result.oldEmail, newEmail, cancelUrl },
4211
- database
4303
+ database,
4304
+ schema.users
4212
4305
  );
4213
4306
  logSecurityEvent("email_change_requested", "medium", { userId });
4214
4307
  return c.json({ success: true });
@@ -4249,8 +4342,12 @@ var confirmEmailChangeRoute = (0, import_zod_openapi13.createRoute)({
4249
4342
  });
4250
4343
  var confirmEmailChangeHandler = async (c) => {
4251
4344
  const { token } = c.req.valid("json");
4252
- const database = c.get("db");
4253
- const result = await confirmEmailChange({ token, db: database });
4345
+ const { db: database, schema } = getAuthContext(c);
4346
+ const result = await confirmEmailChange({
4347
+ token,
4348
+ db: database,
4349
+ tables: { users: schema.users, emailChangeTokens: schema.emailChangeTokens }
4350
+ });
4254
4351
  if (!result.success) {
4255
4352
  return problems.badRequest(c, result.error || "Invalid or expired token");
4256
4353
  }
@@ -4291,8 +4388,12 @@ var cancelEmailChangeRoute = (0, import_zod_openapi15.createRoute)({
4291
4388
  });
4292
4389
  var cancelEmailChangeHandler = async (c) => {
4293
4390
  const { token } = c.req.valid("json");
4294
- const database = c.get("db");
4295
- const result = await cancelEmailChange({ token, db: database });
4391
+ const { db: database, schema } = getAuthContext(c);
4392
+ const result = await cancelEmailChange({
4393
+ token,
4394
+ db: database,
4395
+ tables: { emailChangeTokens: schema.emailChangeTokens }
4396
+ });
4296
4397
  if (!result.success) {
4297
4398
  return problems.badRequest(c, result.error || "Invalid or expired token");
4298
4399
  }
@@ -4431,7 +4532,7 @@ var refreshHandler = async (c) => {
4431
4532
  if (!session) {
4432
4533
  return problems.unauthorized(c, "Invalid or expired refresh token");
4433
4534
  }
4434
- await deleteSession(db, session.id);
4535
+ await deleteSession(db, { sessions: schema.sessions }, session.id);
4435
4536
  const newSessionId = await createSession(
4436
4537
  db,
4437
4538
  { sessions: schema.sessions },
@@ -4522,7 +4623,8 @@ var resendVerificationHandler = async (c) => {
4522
4623
  verificationUrl,
4523
4624
  firstName: user.name || void 0
4524
4625
  },
4525
- db
4626
+ db,
4627
+ schema.users
4526
4628
  );
4527
4629
  logger_default.info("Verification email resent", { userId: user.id, email: user.email });
4528
4630
  return c.json({
@@ -4848,7 +4950,8 @@ var totpVerifyHandler = async (c) => {
4848
4950
  const firstName = user.name?.split(" ")[0] || null;
4849
4951
  await emailService.send2faEnabledEmail(
4850
4952
  { email: user.email, firstName, method: "totp" },
4851
- db
4953
+ db,
4954
+ schema.users
4852
4955
  );
4853
4956
  }
4854
4957
  logSecurityEvent("2fa_totp_enrolled", "high", { userId });
@@ -4905,12 +5008,12 @@ var totpDisableHandler = async (c) => {
4905
5008
  if (remaining.length === 0) {
4906
5009
  await db.delete(schema.userBackupCodes).where((0, import_drizzle_orm23.eq)(schema.userBackupCodes.userId, userId));
4907
5010
  await db.delete(schema.userTrustedDevices).where((0, import_drizzle_orm23.eq)(schema.userTrustedDevices.userId, userId));
4908
- await invalidateAllUserSessions(db, userId);
5011
+ await invalidateAllUserSessions(db, { sessions: schema.sessions, users: schema.users }, userId);
4909
5012
  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);
4910
5013
  if (user) {
4911
5014
  const emailService = new EmailService(env);
4912
5015
  const firstName = user.name?.split(" ")[0] || null;
4913
- await emailService.send2faDisabledEmail({ email: user.email, firstName }, db);
5016
+ await emailService.send2faDisabledEmail({ email: user.email, firstName }, db, schema.users);
4914
5017
  }
4915
5018
  logSecurityEvent("2fa_disabled", "critical", { userId, lastMethod: "totp" });
4916
5019
  return c.json({ success: true, sessionInvalidated: true });
@@ -4966,7 +5069,7 @@ var emailSetupHandler = async (c) => {
4966
5069
  await env.OAUTH_STATES.put(`email_2fa_setup:${userId}`, codeHash, { expirationTtl: 300 });
4967
5070
  const emailService = new EmailService(env);
4968
5071
  const firstName = user.name?.split(" ")[0] || null;
4969
- await emailService.send2faCodeEmail({ email: user.email, firstName, code }, db);
5072
+ await emailService.send2faCodeEmail({ email: user.email, firstName, code }, db, schema.users);
4970
5073
  logSecurityEvent("2fa_email_setup_initiated", "low", { userId });
4971
5074
  return c.json({ success: true });
4972
5075
  };
@@ -5047,7 +5150,8 @@ var emailVerifyHandler = async (c) => {
5047
5150
  const firstName = user.name?.split(" ")[0] || null;
5048
5151
  await emailService.send2faEnabledEmail(
5049
5152
  { email: user.email, firstName, method: "email" },
5050
- db
5153
+ db,
5154
+ schema.users
5051
5155
  );
5052
5156
  }
5053
5157
  logSecurityEvent("2fa_email_enrolled", "high", { userId });
@@ -5101,7 +5205,8 @@ var emailSendCodeHandler = async (c) => {
5101
5205
  const firstName = user.name?.split(" ")[0] || null;
5102
5206
  const emailResult = await emailService.send2faCodeEmail(
5103
5207
  { email: user.email, firstName, code },
5104
- db
5208
+ db,
5209
+ schema.users
5105
5210
  );
5106
5211
  if (!emailResult.success) {
5107
5212
  return problems.badRequest(
@@ -5163,12 +5268,12 @@ var emailDisableHandler = async (c) => {
5163
5268
  if (remaining.length === 0) {
5164
5269
  await db.delete(schema.userBackupCodes).where((0, import_drizzle_orm27.eq)(schema.userBackupCodes.userId, userId));
5165
5270
  await db.delete(schema.userTrustedDevices).where((0, import_drizzle_orm27.eq)(schema.userTrustedDevices.userId, userId));
5166
- await invalidateAllUserSessions(db, userId);
5271
+ await invalidateAllUserSessions(db, { sessions: schema.sessions, users: schema.users }, userId);
5167
5272
  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);
5168
5273
  if (user) {
5169
5274
  const emailService = new EmailService(env);
5170
5275
  const firstName = user.name?.split(" ")[0] || null;
5171
- await emailService.send2faDisabledEmail({ email: user.email, firstName }, db);
5276
+ await emailService.send2faDisabledEmail({ email: user.email, firstName }, db, schema.users);
5172
5277
  }
5173
5278
  logSecurityEvent("2fa_disabled", "critical", { userId, lastMethod: "email" });
5174
5279
  return c.json({ success: true, sessionInvalidated: true });
@@ -5489,7 +5594,7 @@ var challengeResendHandler = async (c) => {
5489
5594
  });
5490
5595
  const emailService = new EmailService(c.env);
5491
5596
  const firstName = user.name?.split(" ")[0] || null;
5492
- await emailService.send2faCodeEmail({ email: user.email, firstName, code }, db);
5597
+ await emailService.send2faCodeEmail({ email: user.email, firstName, code }, db, schema.users);
5493
5598
  logger_default.info("Email 2FA code resent", { userId: payload.userId });
5494
5599
  return c.json({ success: true });
5495
5600
  };
@@ -5498,11 +5603,8 @@ var challengeResendHandler = async (c) => {
5498
5603
  var twoFa = new import_zod_openapi36.OpenAPIHono();
5499
5604
  twoFa.use("*", csrf);
5500
5605
  twoFa.use("/status", requireAuth);
5501
- twoFa.openapi(statusRoute, statusHandler);
5502
5606
  twoFa.use("/totp/setup", requireAuth);
5503
- twoFa.openapi(totpSetupRoute, totpSetupHandler);
5504
5607
  twoFa.use("/totp/verify", requireAuth);
5505
- twoFa.openapi(totpVerifyRoute, totpVerifyHandler);
5506
5608
  twoFa.use(
5507
5609
  "/totp/disable",
5508
5610
  requireAuth,
@@ -5514,11 +5616,8 @@ twoFa.use(
5514
5616
  // 5 attempts per 15 minutes
5515
5617
  })
5516
5618
  );
5517
- twoFa.openapi(totpDisableRoute, totpDisableHandler);
5518
5619
  twoFa.use("/email/setup", requireAuth);
5519
- twoFa.openapi(emailSetupRoute, emailSetupHandler);
5520
5620
  twoFa.use("/email/verify", requireAuth);
5521
- twoFa.openapi(emailVerifyRoute, emailVerifyHandler);
5522
5621
  twoFa.use(
5523
5622
  "/email/send-code",
5524
5623
  requireAuth,
@@ -5530,7 +5629,6 @@ twoFa.use(
5530
5629
  // 3 per 5 minutes
5531
5630
  })
5532
5631
  );
5533
- twoFa.openapi(emailSendCodeRoute, emailSendCodeHandler);
5534
5632
  twoFa.use(
5535
5633
  "/email/disable",
5536
5634
  requireAuth,
@@ -5542,11 +5640,8 @@ twoFa.use(
5542
5640
  // 5 attempts per 15 minutes
5543
5641
  })
5544
5642
  );
5545
- twoFa.openapi(emailDisableRoute, emailDisableHandler);
5546
5643
  twoFa.use("/trusted-devices", requireAuth);
5547
- twoFa.openapi(trustedDevicesGetRoute, trustedDevicesGetHandler);
5548
5644
  twoFa.use("/trusted-devices/:id", requireAuth);
5549
- twoFa.openapi(trustedDevicesDeleteRoute, trustedDevicesDeleteHandler);
5550
5645
  twoFa.use(
5551
5646
  "/backup-codes/regenerate",
5552
5647
  requireAuth,
@@ -5558,7 +5653,6 @@ twoFa.use(
5558
5653
  // 3 per hour per user
5559
5654
  })
5560
5655
  );
5561
- twoFa.openapi(backupCodesRegenerateRoute, backupCodesRegenerateHandler);
5562
5656
  twoFa.use(
5563
5657
  "/challenge",
5564
5658
  rateLimit({
@@ -5569,7 +5663,6 @@ twoFa.use(
5569
5663
  // 10 per 5 min per IP
5570
5664
  })
5571
5665
  );
5572
- twoFa.openapi(challengeRoute, challengeHandler);
5573
5666
  twoFa.use(
5574
5667
  "/challenge/resend",
5575
5668
  rateLimit({
@@ -5580,40 +5673,423 @@ twoFa.use(
5580
5673
  // 3 per 5 min per IP
5581
5674
  })
5582
5675
  );
5583
- twoFa.openapi(challengeResendRoute, challengeResendHandler);
5584
- var fa_default = twoFa;
5676
+ var twoFaRoutes = twoFa.openapi(statusRoute, statusHandler).openapi(totpSetupRoute, totpSetupHandler).openapi(totpVerifyRoute, totpVerifyHandler).openapi(totpDisableRoute, totpDisableHandler).openapi(emailSetupRoute, emailSetupHandler).openapi(emailVerifyRoute, emailVerifyHandler).openapi(emailSendCodeRoute, emailSendCodeHandler).openapi(emailDisableRoute, emailDisableHandler).openapi(trustedDevicesGetRoute, trustedDevicesGetHandler).openapi(trustedDevicesDeleteRoute, trustedDevicesDeleteHandler).openapi(backupCodesRegenerateRoute, backupCodesRegenerateHandler).openapi(challengeRoute, challengeHandler).openapi(challengeResendRoute, challengeResendHandler);
5677
+ var fa_default = twoFaRoutes;
5678
+
5679
+ // src/routes/oauth/index.ts
5680
+ var import_zod_openapi41 = require("@hono/zod-openapi");
5681
+
5682
+ // src/routes/oauth/authorize.ts
5683
+ var import_zod_openapi38 = require("@hono/zod-openapi");
5684
+
5685
+ // src/routes/oauth/providers.ts
5686
+ var PROVIDERS = {
5687
+ google: {
5688
+ authorizeUrl: "https://accounts.google.com/o/oauth2/v2/auth",
5689
+ tokenUrl: "https://oauth2.googleapis.com/token",
5690
+ userinfoUrl: "https://www.googleapis.com/oauth2/v2/userinfo",
5691
+ scopes: ["email"],
5692
+ emailExtractor: (data) => data.email || null,
5693
+ userIdExtractor: (data) => data.id || null
5694
+ },
5695
+ microsoft: {
5696
+ authorizeUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
5697
+ tokenUrl: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
5698
+ userinfoUrl: "https://graph.microsoft.com/v1.0/me",
5699
+ scopes: ["openid", "email", "User.Read"],
5700
+ emailExtractor: (data) => data.mail || data.userPrincipalName || null,
5701
+ userIdExtractor: (data) => data.id || null
5702
+ }
5703
+ };
5704
+ function getProviderCredentials(provider, env) {
5705
+ if (provider === "google" && env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
5706
+ return { clientId: env.GOOGLE_CLIENT_ID, clientSecret: env.GOOGLE_CLIENT_SECRET };
5707
+ }
5708
+ if (provider === "microsoft" && env.MICROSOFT_CLIENT_ID && env.MICROSOFT_CLIENT_SECRET) {
5709
+ return { clientId: env.MICROSOFT_CLIENT_ID, clientSecret: env.MICROSOFT_CLIENT_SECRET };
5710
+ }
5711
+ return null;
5712
+ }
5713
+
5714
+ // src/routes/oauth/schemas.ts
5715
+ var import_zod_openapi37 = require("@hono/zod-openapi");
5716
+ var oauthProviderParamSchema = import_zod_openapi37.z.object({
5717
+ provider: import_zod_openapi37.z.enum(["google", "microsoft"]).openapi({ example: "google" })
5718
+ });
5719
+ var oauthAuthorizeQuerySchema = import_zod_openapi37.z.object({
5720
+ redirect: import_zod_openapi37.z.string().optional().openapi({ example: "/dashboard" }),
5721
+ invitationToken: import_zod_openapi37.z.string().optional().openapi({ example: "token123" }),
5722
+ code_challenge: import_zod_openapi37.z.string().min(43).max(128).optional().openapi({ example: "E9Mrozoa2owUednMY..." }),
5723
+ code_challenge_method: import_zod_openapi37.z.enum(["S256"]).optional().openapi({ example: "S256" }),
5724
+ redirect_uri: import_zod_openapi37.z.string().url().optional().openapi({ example: "myapp://oauth-callback" })
5725
+ }).openapi("OAuthAuthorizeQuery");
5726
+ var oauthCallbackQuerySchema = import_zod_openapi37.z.object({
5727
+ code: import_zod_openapi37.z.string().optional(),
5728
+ state: import_zod_openapi37.z.string().optional(),
5729
+ error: import_zod_openapi37.z.string().optional()
5730
+ });
5731
+ var oauthCompleteRequestSchema = import_zod_openapi37.z.object({
5732
+ code: import_zod_openapi37.z.string().uuid("Invalid code format").openapi({ example: "a1b2c3d4-..." })
5733
+ }).openapi("OAuthCompleteRequest");
5734
+ var oauthCompleteResponseSchema = import_zod_openapi37.z.object({
5735
+ message: import_zod_openapi37.z.string().openapi({ example: "Login successful" }),
5736
+ redirect: import_zod_openapi37.z.string().openapi({ example: "/" }),
5737
+ user: import_zod_openapi37.z.object({
5738
+ id: import_zod_openapi37.z.string().uuid(),
5739
+ email: import_zod_openapi37.z.string().email(),
5740
+ emailVerified: import_zod_openapi37.z.boolean()
5741
+ })
5742
+ }).openapi("OAuthCompleteResponse");
5743
+ var errorResponseSchema4 = import_zod_openapi37.z.object({
5744
+ type: import_zod_openapi37.z.string().url(),
5745
+ title: import_zod_openapi37.z.string(),
5746
+ status: import_zod_openapi37.z.number().int(),
5747
+ detail: import_zod_openapi37.z.string().optional()
5748
+ }).openapi("ErrorResponse");
5749
+
5750
+ // src/routes/oauth/authorize.ts
5751
+ var authorizeRoute = (0, import_zod_openapi38.createRoute)({
5752
+ method: "get",
5753
+ path: "/{provider}/authorize",
5754
+ tags: ["OAuth"],
5755
+ summary: "Start OAuth login flow",
5756
+ description: "Generates a CSRF state token and redirects the user to the OAuth provider.",
5757
+ request: {
5758
+ params: oauthProviderParamSchema,
5759
+ query: oauthAuthorizeQuerySchema
5760
+ },
5761
+ responses: {
5762
+ 302: { description: "Redirect to OAuth provider or error page" }
5763
+ }
5764
+ });
5765
+ var authorizeHandler = async (c) => {
5766
+ const { provider } = c.req.valid("param");
5767
+ const {
5768
+ redirect: loginRedirect,
5769
+ invitationToken,
5770
+ code_challenge,
5771
+ code_challenge_method,
5772
+ redirect_uri: mobileRedirectUri
5773
+ } = c.req.valid("query");
5774
+ const providerConfig = PROVIDERS[provider];
5775
+ if (!providerConfig) {
5776
+ return c.redirect(`${c.env.APP_URL}/login?error=oauth_invalid_provider`);
5777
+ }
5778
+ const credentials = getProviderCredentials(provider, c.env);
5779
+ if (!credentials) {
5780
+ logError(new Error(`OAuth credentials not configured for ${provider}`), {
5781
+ context: "oauth_authorize",
5782
+ provider
5783
+ });
5784
+ return c.redirect(`${c.env.APP_URL}/login?error=oauth_not_configured`);
5785
+ }
5786
+ if (code_challenge && !code_challenge_method || !code_challenge && code_challenge_method) {
5787
+ return c.redirect(`${c.env.APP_URL}/login?error=oauth_invalid_pkce`);
5788
+ }
5789
+ const state = crypto.randomUUID();
5790
+ await c.env.OAUTH_STATES.put(
5791
+ `oauth:state:${state}`,
5792
+ JSON.stringify({
5793
+ provider,
5794
+ invitationToken: invitationToken || null,
5795
+ redirect: loginRedirect || null,
5796
+ codeChallenge: code_challenge || null,
5797
+ codeChallengeMethod: code_challenge_method || null,
5798
+ mobileRedirectUri: mobileRedirectUri || null
5799
+ }),
5800
+ { expirationTtl: 600 }
5801
+ // 10 minutes
5802
+ );
5803
+ const redirectUri = new URL(`/v1/auth/oauth/${provider}/callback`, c.req.url).toString();
5804
+ const authUrl = new URL(providerConfig.authorizeUrl);
5805
+ authUrl.searchParams.set("client_id", credentials.clientId);
5806
+ authUrl.searchParams.set("redirect_uri", redirectUri);
5807
+ authUrl.searchParams.set("response_type", "code");
5808
+ if (providerConfig.scopes.length > 0) {
5809
+ authUrl.searchParams.set("scope", providerConfig.scopes.join(" "));
5810
+ }
5811
+ authUrl.searchParams.set("state", state);
5812
+ if (provider === "google") {
5813
+ authUrl.searchParams.set("prompt", "select_account");
5814
+ }
5815
+ logger_default.info("OAuth authorize initiated", { provider, hasPkce: !!code_challenge });
5816
+ return c.redirect(authUrl.toString());
5817
+ };
5818
+
5819
+ // src/routes/oauth/callback.ts
5820
+ var import_zod_openapi39 = require("@hono/zod-openapi");
5821
+ var import_drizzle_orm32 = require("drizzle-orm");
5822
+ var callbackRoute = (0, import_zod_openapi39.createRoute)({
5823
+ method: "get",
5824
+ path: "/{provider}/callback",
5825
+ tags: ["OAuth"],
5826
+ summary: "OAuth provider callback",
5827
+ description: "Handles the redirect from an OAuth provider. Validates CSRF state, exchanges the code for a token, and either starts a login or signup flow.",
5828
+ request: {
5829
+ params: oauthProviderParamSchema,
5830
+ query: oauthCallbackQuerySchema
5831
+ },
5832
+ responses: {
5833
+ 302: { description: "Redirect to app with login code or signup token" }
5834
+ }
5835
+ });
5836
+ var callbackHandler = async (c) => {
5837
+ const { provider } = c.req.valid("param");
5838
+ const { code, state, error } = c.req.valid("query");
5839
+ const appUrl = c.env.APP_URL;
5840
+ if (error) {
5841
+ logger_default.info("OAuth provider returned error", { provider, error });
5842
+ const errorType = error === "access_denied" || error === "consent_required" ? "oauth_cancelled" : "oauth_failed";
5843
+ return c.redirect(`${appUrl}/login?error=${errorType}`);
5844
+ }
5845
+ if (!code || !state) {
5846
+ logger_default.warn("Missing code or state in OAuth callback", { provider });
5847
+ return c.redirect(`${appUrl}/login?error=oauth_failed`);
5848
+ }
5849
+ const storedState = await c.env.OAUTH_STATES.get(`oauth:state:${state}`);
5850
+ if (!storedState) {
5851
+ logSecurityEvent("oauth_csrf_attempt", "high", { state, provider });
5852
+ return c.redirect(`${appUrl}/login?error=oauth_expired`);
5853
+ }
5854
+ let stateData;
5855
+ try {
5856
+ stateData = JSON.parse(storedState);
5857
+ } catch (err) {
5858
+ logError(err, { context: "oauth_state_parse", provider });
5859
+ return c.redirect(`${appUrl}/login?error=oauth_failed`);
5860
+ }
5861
+ if (stateData.provider !== provider) {
5862
+ logSecurityEvent("oauth_csrf_attempt", "high", { state, provider });
5863
+ return c.redirect(`${appUrl}/login?error=oauth_expired`);
5864
+ }
5865
+ await c.env.OAUTH_STATES.delete(`oauth:state:${state}`);
5866
+ const {
5867
+ provider: _stateProvider,
5868
+ invitationToken,
5869
+ redirect: loginRedirect,
5870
+ codeChallenge,
5871
+ codeChallengeMethod,
5872
+ mobileRedirectUri
5873
+ } = stateData;
5874
+ const codeVerifier = c.req.query("code_verifier");
5875
+ if (codeChallenge && codeChallengeMethod === "S256") {
5876
+ if (!codeVerifier) {
5877
+ logger_default.warn("OAuth PKCE: missing code_verifier", { provider });
5878
+ return c.redirect(`${appUrl}/login?error=oauth_pkce_missing`);
5879
+ }
5880
+ const pkceValid = await verifyCodeChallenge(codeVerifier, codeChallenge);
5881
+ if (!pkceValid) {
5882
+ logger_default.warn("OAuth PKCE: invalid code_verifier", { provider });
5883
+ return c.redirect(`${appUrl}/login?error=oauth_pkce_invalid`);
5884
+ }
5885
+ }
5886
+ const providerConfig = PROVIDERS[provider];
5887
+ if (!providerConfig) {
5888
+ return c.redirect(`${appUrl}/login?error=oauth_failed`);
5889
+ }
5890
+ const credentials = getProviderCredentials(provider, c.env);
5891
+ if (!credentials) {
5892
+ return c.redirect(`${appUrl}/login?error=oauth_not_configured`);
5893
+ }
5894
+ const redirectUri = new URL(`/v1/auth/oauth/${provider}/callback`, c.req.url).toString();
5895
+ try {
5896
+ const tokenResponse = await fetch(providerConfig.tokenUrl, {
5897
+ method: "POST",
5898
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
5899
+ body: new URLSearchParams({
5900
+ grant_type: "authorization_code",
5901
+ code,
5902
+ redirect_uri: redirectUri,
5903
+ client_id: credentials.clientId,
5904
+ client_secret: credentials.clientSecret
5905
+ }),
5906
+ signal: AbortSignal.timeout(1e4)
5907
+ });
5908
+ if (!tokenResponse.ok) {
5909
+ const body = await tokenResponse.text();
5910
+ logError(new Error(`OAuth token exchange failed: ${tokenResponse.status} ${body}`), {
5911
+ context: "oauth_token_exchange",
5912
+ provider
5913
+ });
5914
+ return c.redirect(`${appUrl}/login?error=oauth_failed`);
5915
+ }
5916
+ const tokenData = await tokenResponse.json();
5917
+ if (!tokenData.access_token) {
5918
+ logError(new Error("No access_token in OAuth response"), {
5919
+ context: "oauth_token_exchange",
5920
+ provider
5921
+ });
5922
+ return c.redirect(`${appUrl}/login?error=oauth_failed`);
5923
+ }
5924
+ const userinfoResponse = await fetch(providerConfig.userinfoUrl, {
5925
+ headers: { Authorization: `Bearer ${tokenData.access_token}` },
5926
+ signal: AbortSignal.timeout(1e4)
5927
+ });
5928
+ if (!userinfoResponse.ok) {
5929
+ logError(new Error(`OAuth userinfo failed: ${userinfoResponse.status}`), {
5930
+ context: "oauth_userinfo",
5931
+ provider
5932
+ });
5933
+ return c.redirect(`${appUrl}/login?error=oauth_failed`);
5934
+ }
5935
+ const userInfo = await userinfoResponse.json();
5936
+ const email = providerConfig.emailExtractor(userInfo)?.toLowerCase();
5937
+ const providerUserId = providerConfig.userIdExtractor(userInfo);
5938
+ if (!email) {
5939
+ logger_default.warn("No email returned from OAuth provider", { provider });
5940
+ return c.redirect(`${appUrl}/login?error=oauth_no_email`);
5941
+ }
5942
+ if (!providerUserId) {
5943
+ logger_default.warn("No user ID returned from OAuth provider", { provider });
5944
+ return c.redirect(`${appUrl}/login?error=oauth_failed`);
5945
+ }
5946
+ const { db, schema } = getAuthContext(c);
5947
+ const [existingUser] = await db.select({ id: schema.users.id, email: schema.users.email }).from(schema.users).where((0, import_drizzle_orm32.eq)(schema.users.email, email)).limit(1);
5948
+ if (existingUser) {
5949
+ const loginCode = crypto.randomUUID();
5950
+ await c.env.OAUTH_STATES.put(
5951
+ `oauth:login:${loginCode}`,
5952
+ JSON.stringify({
5953
+ userId: existingUser.id,
5954
+ email: existingUser.email,
5955
+ provider,
5956
+ providerUserId,
5957
+ redirect: loginRedirect,
5958
+ invitationToken
5959
+ }),
5960
+ { expirationTtl: 60 }
5961
+ );
5962
+ logSecurityEvent("oauth_login_initiated", "low", {
5963
+ userId: existingUser.id,
5964
+ email: existingUser.email,
5965
+ provider
5966
+ });
5967
+ if (mobileRedirectUri && isDeepLinkUri(mobileRedirectUri)) {
5968
+ const mobileUrl = new URL(mobileRedirectUri);
5969
+ mobileUrl.searchParams.set("code", loginCode);
5970
+ mobileUrl.searchParams.set("action", "login");
5971
+ return c.redirect(mobileUrl.toString());
5972
+ }
5973
+ return c.redirect(`${appUrl}/auth/callback?code=${loginCode}&action=login`);
5974
+ } else {
5975
+ const signupToken = crypto.randomUUID();
5976
+ await c.env.OAUTH_STATES.put(
5977
+ `oauth:signup:${signupToken}`,
5978
+ JSON.stringify({ email, provider, providerUserId, invitationToken }),
5979
+ { expirationTtl: 600 }
5980
+ );
5981
+ logger_default.info("OAuth signup initiated", { provider, email });
5982
+ if (mobileRedirectUri && isDeepLinkUri(mobileRedirectUri)) {
5983
+ const mobileUrl = new URL(mobileRedirectUri);
5984
+ mobileUrl.searchParams.set("oauth_token", signupToken);
5985
+ mobileUrl.searchParams.set("email", email);
5986
+ if (invitationToken) {
5987
+ mobileUrl.searchParams.set("invitation", invitationToken);
5988
+ }
5989
+ return c.redirect(mobileUrl.toString());
5990
+ }
5991
+ let signupUrl = `${appUrl}/signup?oauth_token=${signupToken}&email=${encodeURIComponent(email)}`;
5992
+ if (invitationToken) signupUrl += `&invitation=${invitationToken}`;
5993
+ return c.redirect(signupUrl);
5994
+ }
5995
+ } catch (err) {
5996
+ logError(err, { context: "oauth_callback", provider });
5997
+ return c.redirect(`${appUrl}/login?error=oauth_failed`);
5998
+ }
5999
+ };
6000
+
6001
+ // src/routes/oauth/complete.ts
6002
+ var import_zod_openapi40 = require("@hono/zod-openapi");
6003
+ var import_drizzle_orm33 = require("drizzle-orm");
6004
+ var completeRoute = (0, import_zod_openapi40.createRoute)({
6005
+ method: "post",
6006
+ path: "/complete",
6007
+ tags: ["OAuth"],
6008
+ summary: "Complete OAuth login",
6009
+ description: "Exchanges a one-time login code (from the OAuth callback) for a session cookie. Called by the /auth/callback frontend page.",
6010
+ request: {
6011
+ body: {
6012
+ content: { "application/json": { schema: oauthCompleteRequestSchema } },
6013
+ required: true
6014
+ }
6015
+ },
6016
+ responses: {
6017
+ 200: {
6018
+ description: "Session created successfully",
6019
+ content: { "application/json": { schema: oauthCompleteResponseSchema } }
6020
+ },
6021
+ 401: {
6022
+ description: "Invalid or expired login code",
6023
+ content: { "application/json": { schema: errorResponseSchema4 } }
6024
+ }
6025
+ }
6026
+ });
6027
+ var completeMiddleware = [
6028
+ rateLimit({
6029
+ identifier: (c) => c.req.header("cf-connecting-ip") || "unknown",
6030
+ action: "oauth_complete",
6031
+ maxAttempts: 10,
6032
+ windowMs: 9e5
6033
+ // 15 minutes
6034
+ })
6035
+ ];
6036
+ var completeHandler = async (c) => {
6037
+ const { code } = c.req.valid("json");
6038
+ const stored = await c.env.OAUTH_STATES.get(`oauth:login:${code}`);
6039
+ if (!stored) {
6040
+ return problems.unauthorized(c, "Invalid or expired login code");
6041
+ }
6042
+ await c.env.OAUTH_STATES.delete(`oauth:login:${code}`);
6043
+ const {
6044
+ userId,
6045
+ email,
6046
+ provider,
6047
+ providerUserId,
6048
+ redirect: storedRedirect
6049
+ } = JSON.parse(stored);
6050
+ const { db, schema } = getAuthContext(c);
6051
+ await db.update(schema.users).set({ emailVerified: true }).where((0, import_drizzle_orm33.eq)(schema.users.id, userId));
6052
+ await db.insert(schema.oauthAccounts).values({ userId, provider, providerUserId, email }).onConflictDoNothing();
6053
+ const fingerprint = await generateFingerprint(c.req.raw);
6054
+ const ipAddress = getClientIp(c.req.raw);
6055
+ const sessionId = await createSession(db, { sessions: schema.sessions }, userId, fingerprint, ipAddress);
6056
+ const redirect = storedRedirect?.startsWith("/") && !storedRedirect.startsWith("//") ? storedRedirect : "/";
6057
+ logger_default.info("OAuth login completed", { userId, provider, sessionId: sessionId.slice(0, 8) });
6058
+ logSecurityEvent("oauth_login_success", "low", {
6059
+ userId,
6060
+ email,
6061
+ provider,
6062
+ ip: c.req.header("cf-connecting-ip")
6063
+ });
6064
+ return c.json(
6065
+ { message: "Login successful", redirect, user: { id: userId, email, emailVerified: true } },
6066
+ 200,
6067
+ { "Set-Cookie": setSessionCookie(sessionId, c.env) }
6068
+ );
6069
+ };
6070
+
6071
+ // src/routes/oauth/index.ts
6072
+ var oauth = new import_zod_openapi41.OpenAPIHono();
6073
+ oauth.use("/complete", ...completeMiddleware);
6074
+ var oauthRoutes = oauth.openapi(authorizeRoute, authorizeHandler).openapi(callbackRoute, callbackHandler).openapi(completeRoute, completeHandler);
6075
+ var oauth_default = oauthRoutes;
5585
6076
 
5586
6077
  // src/routes/index.ts
5587
- var auth = new import_zod_openapi37.OpenAPIHono();
6078
+ var auth = new import_zod_openapi42.OpenAPIHono();
5588
6079
  auth.use("*", csrf);
5589
6080
  auth.use("/signup", ...signupMiddleware);
5590
- auth.openapi(signupRoute, signupHandler);
5591
6081
  auth.use("/login", ...loginMiddleware);
5592
- auth.openapi(loginRoute, loginHandler);
5593
- auth.openapi(logoutRoute, logoutHandler);
5594
6082
  auth.use("/me", ...meMiddleware);
5595
- auth.openapi(meRoute, meHandler);
5596
- auth.openapi(verifyEmailRoute, verifyEmailHandler);
5597
6083
  auth.use("/forgot-password", ...forgotPasswordMiddleware);
5598
- auth.openapi(forgotPasswordRoute, forgotPasswordHandler);
5599
6084
  auth.use("/reset-password", ...resetPasswordMiddleware);
5600
- auth.openapi(resetPasswordRoute, resetPasswordHandler);
5601
6085
  auth.use("/change-password", ...changePasswordMiddleware);
5602
- auth.openapi(changePasswordRoute, changePasswordHandler);
5603
6086
  auth.use("/heartbeat", ...heartbeatMiddleware);
5604
- auth.openapi(heartbeatRoute, heartbeatHandler);
5605
6087
  auth.use("/change-email", ...changeEmailMiddleware);
5606
- auth.openapi(changeEmailRoute, changeEmailHandler);
5607
- auth.openapi(confirmEmailChangeRoute, confirmEmailChangeHandler);
5608
- auth.openapi(cancelEmailChangeRoute, cancelEmailChangeHandler);
5609
6088
  auth.use("/account", ...deleteAccountMiddleware);
5610
- auth.openapi(deleteAccountRoute, deleteAccountHandler);
5611
6089
  auth.use("/refresh", ...refreshMiddleware);
5612
- auth.openapi(refreshRoute, refreshHandler);
5613
6090
  auth.use("/resend-verification", ...resendVerificationMiddleware);
5614
- auth.openapi(resendVerificationRoute, resendVerificationHandler);
5615
- auth.route("/2fa", fa_default);
5616
- var routes_default = auth;
6091
+ var authRoutes = auth.openapi(signupRoute, signupHandler).openapi(loginRoute, loginHandler).openapi(logoutRoute, logoutHandler).openapi(meRoute, meHandler).openapi(verifyEmailRoute, verifyEmailHandler).openapi(forgotPasswordRoute, forgotPasswordHandler).openapi(resetPasswordRoute, resetPasswordHandler).openapi(changePasswordRoute, changePasswordHandler).openapi(heartbeatRoute, heartbeatHandler).openapi(changeEmailRoute, changeEmailHandler).openapi(confirmEmailChangeRoute, confirmEmailChangeHandler).openapi(cancelEmailChangeRoute, cancelEmailChangeHandler).openapi(deleteAccountRoute, deleteAccountHandler).openapi(refreshRoute, refreshHandler).openapi(resendVerificationRoute, resendVerificationHandler).route("/2fa", fa_default).route("/oauth", oauth_default);
6092
+ var routes_default = authRoutes;
5617
6093
 
5618
6094
  // src/lib/email/webhook-verifier.ts
5619
6095
  var import_svix = require("svix");
@@ -5642,6 +6118,7 @@ function verifyWebhookSignature(payload, signature, secret) {
5642
6118
  0 && (module.exports = {
5643
6119
  AUTH_DEFAULTS,
5644
6120
  CHALLENGE_TTL_MS,
6121
+ CloudflareEmailAdapter,
5645
6122
  EmailService,
5646
6123
  MAX_CHALLENGE_ATTEMPTS,
5647
6124
  ProblemTypes,