@syncello/auth 3.5.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1341,12 +1341,13 @@ var import_drizzle_orm4 = require("drizzle-orm");
1341
1341
 
1342
1342
  // src/factory.ts
1343
1343
  function createAuth(config) {
1344
- const { db, schema, getEnv, emailBranding } = config;
1344
+ const { db, schema, getEnv, emailBranding, invitations } = config;
1345
1345
  const getContext = (c) => ({
1346
1346
  db,
1347
1347
  schema,
1348
1348
  env: getEnv(c),
1349
- emailBranding
1349
+ emailBranding,
1350
+ invitations
1350
1351
  });
1351
1352
  return {
1352
1353
  db,
@@ -3324,7 +3325,10 @@ var signupRequestSchema = import_zod_openapi.z.object({
3324
3325
  name: import_zod_openapi.z.string().min(1).optional().openapi({ example: "John Doe" }),
3325
3326
  turnstileToken: import_zod_openapi.z.string().optional().openapi({ example: "XXXX.DUMMY.TOKEN.XXXX" }),
3326
3327
  // OAuth signup completion token (from /signup?oauth_token=...)
3327
- oauthToken: import_zod_openapi.z.string().optional().openapi({ example: "a1b2c3d4-..." })
3328
+ oauthToken: import_zod_openapi.z.string().optional().openapi({ example: "a1b2c3d4-..." }),
3329
+ // App invitation token (from /signup?token=...). When it validates for this
3330
+ // email, the account is created already-verified with no verification email.
3331
+ invitationToken: import_zod_openapi.z.string().optional().openapi({ example: "iIB8tDJJ..." })
3328
3332
  }).openapi("SignupRequest");
3329
3333
  var loginRequestSchema = import_zod_openapi.z.object({
3330
3334
  email: import_zod_openapi.z.string().email().openapi({ example: "user@example.com" }),
@@ -3454,8 +3458,8 @@ var signupRoute = (0, import_zod_openapi2.createRoute)({
3454
3458
  }
3455
3459
  });
3456
3460
  var signupHandler = async (c) => {
3457
- const { email, password, name, turnstileToken, oauthToken } = c.req.valid("json");
3458
- const { db: database, schema, emailBranding } = getAuthContext(c);
3461
+ const { email, password, name, turnstileToken, oauthToken, invitationToken } = c.req.valid("json");
3462
+ const { db: database, schema, emailBranding, invitations } = getAuthContext(c);
3459
3463
  const env = c.env;
3460
3464
  if (oauthToken) {
3461
3465
  const stored = await env.OAUTH_STATES.get(`oauth:signup:${oauthToken}`);
@@ -3537,40 +3541,74 @@ var signupHandler = async (c) => {
3537
3541
  return problems.badRequest(c, passwordValidation.error || "Invalid password");
3538
3542
  }
3539
3543
  const hashedPassword = await hashPassword(password, env.PASSWORD_PEPPER_V1);
3544
+ let invitedVerified = false;
3545
+ if (invitationToken && invitations) {
3546
+ const match = await invitations.verify({
3547
+ token: invitationToken,
3548
+ email: email.toLowerCase(),
3549
+ db: database,
3550
+ env
3551
+ });
3552
+ if (!match || match.email.toLowerCase() !== email.toLowerCase()) {
3553
+ return problems.badRequest(
3554
+ c,
3555
+ "This invitation is invalid, expired, or for a different email address"
3556
+ );
3557
+ }
3558
+ invitedVerified = true;
3559
+ }
3540
3560
  const [newUser] = await database.insert(schema.users).values({
3541
3561
  email: email.toLowerCase(),
3542
3562
  name: name || null,
3543
3563
  hashedPassword,
3544
3564
  pepperKid: "v1",
3545
- emailVerified: false,
3565
+ emailVerified: invitedVerified,
3546
3566
  activatedAt: /* @__PURE__ */ new Date()
3547
3567
  }).returning();
3548
- const emailToken = generateSecureToken(32);
3549
- const tokenHash = await hashToken(emailToken);
3550
- const expiresAt = Date.now() + 24 * 60 * 60 * 1e3;
3551
- await database.insert(schema.emailVerificationTokens).values({
3552
- tokenHash,
3553
- userId: newUser.id,
3554
- email: newUser.email,
3555
- expiresAt,
3556
- createdAt: Date.now()
3557
- });
3558
- const emailService = new EmailService(env, void 0, emailBranding);
3559
- const verificationUrl = `${env.APP_URL}/verify-email?token=${emailToken}`;
3560
- await emailService.sendVerificationEmail(
3561
- {
3568
+ if (!invitedVerified) {
3569
+ const emailToken = generateSecureToken(32);
3570
+ const tokenHash = await hashToken(emailToken);
3571
+ const expiresAt = Date.now() + 24 * 60 * 60 * 1e3;
3572
+ await database.insert(schema.emailVerificationTokens).values({
3573
+ tokenHash,
3574
+ userId: newUser.id,
3562
3575
  email: newUser.email,
3563
- token: emailToken,
3564
- verificationUrl,
3565
- firstName: newUser.name || void 0
3566
- },
3567
- database,
3568
- schema.users
3569
- );
3576
+ expiresAt,
3577
+ createdAt: Date.now()
3578
+ });
3579
+ const emailService = new EmailService(env, void 0, emailBranding);
3580
+ const verificationUrl = `${env.APP_URL}/verify-email?token=${emailToken}`;
3581
+ await emailService.sendVerificationEmail(
3582
+ {
3583
+ email: newUser.email,
3584
+ token: emailToken,
3585
+ verificationUrl,
3586
+ firstName: newUser.name || void 0
3587
+ },
3588
+ database,
3589
+ schema.users
3590
+ );
3591
+ }
3592
+ if (invitedVerified && invitationToken && invitations?.onAccepted) {
3593
+ try {
3594
+ await invitations.onAccepted({
3595
+ token: invitationToken,
3596
+ user: { id: newUser.id, email: newUser.email, name: newUser.name },
3597
+ db: database,
3598
+ env
3599
+ });
3600
+ } catch (err) {
3601
+ logError(err, { context: "signup_invitation_onAccepted_failed", userId: newUser.id });
3602
+ }
3603
+ }
3570
3604
  const fingerprint = await generateFingerprint(c.req.raw);
3571
3605
  const ipAddress = getClientIp(c.req.raw);
3572
3606
  const sessionId = await createSession(database, { sessions: schema.sessions }, newUser.id, fingerprint, ipAddress);
3573
- logSecurityEvent("user_signup", "low", { userId: newUser.id, email: newUser.email });
3607
+ logSecurityEvent("user_signup", "low", {
3608
+ userId: newUser.id,
3609
+ email: newUser.email,
3610
+ invited: invitedVerified
3611
+ });
3574
3612
  c.header("Set-Cookie", setSessionCookie(sessionId, c.env), { append: true });
3575
3613
  return c.json({
3576
3614
  user: {