@factiii/auth 0.1.1 → 0.3.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.
@@ -5,8 +5,10 @@ var signupSchema = z.object({
5
5
  username: z.string().min(1, { message: "Username is required" }).max(30, { message: "Username must be 30 characters or less" }).regex(usernameValidationRegex, {
6
6
  message: "Username can only contain letters, numbers, and underscores"
7
7
  }),
8
- email: z.string().email({ message: "Invalid email address" }),
9
- password: z.string().min(6, { message: "Password must contain at least 6 characters" })
8
+ email: z.string().max(254, { message: "Email must be 254 characters or less" }).email({ message: "Invalid email address" }),
9
+ password: z.string().min(6, { message: "Password must contain at least 6 characters" }).max(72, { message: "Password must be 72 characters or less" }).refine((val) => val.trim().length >= 6, {
10
+ message: "Password cannot be only whitespace"
11
+ })
10
12
  });
11
13
  var loginSchema = z.object({
12
14
  username: z.string().min(1, { message: "Username or email is required" }),
@@ -26,14 +28,14 @@ var requestPasswordResetSchema = z.object({
26
28
  });
27
29
  var resetPasswordSchema = z.object({
28
30
  token: z.string().min(1, { message: "Reset token is required" }),
29
- password: z.string().min(6, { message: "Password must contain at least 6 characters" })
31
+ password: z.string().min(6, { message: "Password must contain at least 6 characters" }).max(72, { message: "Password must be 72 characters or less" })
30
32
  });
31
33
  var checkPasswordResetSchema = z.object({
32
34
  token: z.string().min(1, { message: "Reset token is required" })
33
35
  });
34
36
  var changePasswordSchema = z.object({
35
37
  currentPassword: z.string().min(1, { message: "Current password is required" }),
36
- newPassword: z.string().min(6, { message: "New password must contain at least 6 characters" })
38
+ newPassword: z.string().min(6, { message: "New password must contain at least 6 characters" }).max(72, { message: "Password must be 72 characters or less" })
37
39
  });
38
40
  var twoFaVerifySchema = z.object({
39
41
  code: z.string().min(6, { message: "Verification code is required" }),
@@ -6,7 +6,7 @@ import { z, AnyZodObject } from 'zod';
6
6
  declare const signupSchema: z.ZodObject<{
7
7
  username: z.ZodString;
8
8
  email: z.ZodString;
9
- password: z.ZodString;
9
+ password: z.ZodEffects<z.ZodString, string, string>;
10
10
  }, "strip", z.ZodTypeAny, {
11
11
  email: string;
12
12
  username: string;
@@ -6,7 +6,7 @@ import { z, AnyZodObject } from 'zod';
6
6
  declare const signupSchema: z.ZodObject<{
7
7
  username: z.ZodString;
8
8
  email: z.ZodString;
9
- password: z.ZodString;
9
+ password: z.ZodEffects<z.ZodString, string, string>;
10
10
  }, "strip", z.ZodTypeAny, {
11
11
  email: string;
12
12
  username: string;
package/dist/index.d.mts CHANGED
@@ -6,8 +6,8 @@ import { PrismaClient } from '@prisma/client';
6
6
  import * as _trpc_server from '@trpc/server';
7
7
  import * as zod from 'zod';
8
8
  import { CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
9
- import { S as SchemaExtensions, A as AuthHooks } from './hooks-CK4f4PHo.mjs';
10
- export { C as ChangePasswordInput, L as LoginInput, a as LogoutInput, O as OAuthLoginInput, R as ResetPasswordInput, b as SignupInput, T as TwoFaVerifyInput, V as VerifyEmailInput, c as biometricVerifySchema, d as changePasswordSchema, e as endAllSessionsSchema, l as loginSchema, f as logoutSchema, o as oAuthLoginSchema, g as otpLoginRequestSchema, h as otpLoginVerifySchema, r as requestPasswordResetSchema, i as resetPasswordSchema, s as signupSchema, t as twoFaResetSchema, j as twoFaSetupSchema, k as twoFaVerifySchema, v as verifyEmailSchema } from './hooks-CK4f4PHo.mjs';
9
+ import { S as SchemaExtensions, A as AuthHooks } from './hooks-B41uikq7.mjs';
10
+ export { C as ChangePasswordInput, L as LoginInput, a as LogoutInput, O as OAuthLoginInput, R as ResetPasswordInput, b as SignupInput, T as TwoFaVerifyInput, V as VerifyEmailInput, c as biometricVerifySchema, d as changePasswordSchema, e as endAllSessionsSchema, l as loginSchema, f as logoutSchema, o as oAuthLoginSchema, g as otpLoginRequestSchema, h as otpLoginVerifySchema, r as requestPasswordResetSchema, i as resetPasswordSchema, s as signupSchema, t as twoFaResetSchema, j as twoFaSetupSchema, k as twoFaVerifySchema, v as verifyEmailSchema } from './hooks-B41uikq7.mjs';
11
11
  import { SignOptions } from 'jsonwebtoken';
12
12
 
13
13
  //# sourceMappingURL=TRPCError.d.ts.map
@@ -232,6 +232,8 @@ interface TokenSettings {
232
232
  interface AuthFeatures {
233
233
  /** Enable two-factor authentication */
234
234
  twoFa?: boolean;
235
+ /** Require mobile device to enable 2FA (default: true). Set to false for testing. */
236
+ twoFaRequiresDevice?: boolean;
235
237
  /** OAuth providers configuration */
236
238
  oauth?: {
237
239
  google?: boolean;
@@ -657,19 +659,19 @@ declare function createAuthRouter<TExtensions extends SchemaExtensions = {}>(con
657
659
  input: inferParser<[TExtensions["signup"]] extends [zod.AnyZodObject] ? zod.ZodObject<{
658
660
  username: zod.ZodString;
659
661
  email: zod.ZodString;
660
- password: zod.ZodString;
662
+ password: zod.ZodEffects<zod.ZodString, string, string>;
661
663
  } & TExtensions["signup"]["shape"], "strip", zod.ZodTypeAny, zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<{
662
664
  username: zod.ZodString;
663
665
  email: zod.ZodString;
664
- password: zod.ZodString;
666
+ password: zod.ZodEffects<zod.ZodString, string, string>;
665
667
  } & TExtensions["signup"]["shape"]>, any> extends infer T_5 ? { [k_2 in keyof T_5]: T_5[k_2]; } : never, zod.baseObjectInputType<{
666
668
  username: zod.ZodString;
667
669
  email: zod.ZodString;
668
- password: zod.ZodString;
670
+ password: zod.ZodEffects<zod.ZodString, string, string>;
669
671
  } & TExtensions["signup"]["shape"]> extends infer T_6 ? { [k_3 in keyof T_6]: T_6[k_3]; } : never> : zod.ZodObject<{
670
672
  username: zod.ZodString;
671
673
  email: zod.ZodString;
672
- password: zod.ZodString;
674
+ password: zod.ZodEffects<zod.ZodString, string, string>;
673
675
  }, "strip", zod.ZodTypeAny, {
674
676
  email: string;
675
677
  username: string;
@@ -681,19 +683,19 @@ declare function createAuthRouter<TExtensions extends SchemaExtensions = {}>(con
681
683
  }>>["in"] extends infer T_7 ? T_7 extends inferParser<[TExtensions["signup"]] extends [zod.AnyZodObject] ? zod.ZodObject<{
682
684
  username: zod.ZodString;
683
685
  email: zod.ZodString;
684
- password: zod.ZodString;
686
+ password: zod.ZodEffects<zod.ZodString, string, string>;
685
687
  } & TExtensions["signup"]["shape"], "strip", zod.ZodTypeAny, zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<{
686
688
  username: zod.ZodString;
687
689
  email: zod.ZodString;
688
- password: zod.ZodString;
690
+ password: zod.ZodEffects<zod.ZodString, string, string>;
689
691
  } & TExtensions["signup"]["shape"]>, any> extends infer T_8 ? { [k_2 in keyof T_8]: T_8[k_2]; } : never, zod.baseObjectInputType<{
690
692
  username: zod.ZodString;
691
693
  email: zod.ZodString;
692
- password: zod.ZodString;
694
+ password: zod.ZodEffects<zod.ZodString, string, string>;
693
695
  } & TExtensions["signup"]["shape"]> extends infer T_9 ? { [k_3 in keyof T_9]: T_9[k_3]; } : never> : zod.ZodObject<{
694
696
  username: zod.ZodString;
695
697
  email: zod.ZodString;
696
- password: zod.ZodString;
698
+ password: zod.ZodEffects<zod.ZodString, string, string>;
697
699
  }, "strip", zod.ZodTypeAny, {
698
700
  email: string;
699
701
  username: string;
package/dist/index.d.ts CHANGED
@@ -6,8 +6,8 @@ import { PrismaClient } from '@prisma/client';
6
6
  import * as _trpc_server from '@trpc/server';
7
7
  import * as zod from 'zod';
8
8
  import { CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
9
- import { S as SchemaExtensions, A as AuthHooks } from './hooks-CK4f4PHo.js';
10
- export { C as ChangePasswordInput, L as LoginInput, a as LogoutInput, O as OAuthLoginInput, R as ResetPasswordInput, b as SignupInput, T as TwoFaVerifyInput, V as VerifyEmailInput, c as biometricVerifySchema, d as changePasswordSchema, e as endAllSessionsSchema, l as loginSchema, f as logoutSchema, o as oAuthLoginSchema, g as otpLoginRequestSchema, h as otpLoginVerifySchema, r as requestPasswordResetSchema, i as resetPasswordSchema, s as signupSchema, t as twoFaResetSchema, j as twoFaSetupSchema, k as twoFaVerifySchema, v as verifyEmailSchema } from './hooks-CK4f4PHo.js';
9
+ import { S as SchemaExtensions, A as AuthHooks } from './hooks-B41uikq7.js';
10
+ export { C as ChangePasswordInput, L as LoginInput, a as LogoutInput, O as OAuthLoginInput, R as ResetPasswordInput, b as SignupInput, T as TwoFaVerifyInput, V as VerifyEmailInput, c as biometricVerifySchema, d as changePasswordSchema, e as endAllSessionsSchema, l as loginSchema, f as logoutSchema, o as oAuthLoginSchema, g as otpLoginRequestSchema, h as otpLoginVerifySchema, r as requestPasswordResetSchema, i as resetPasswordSchema, s as signupSchema, t as twoFaResetSchema, j as twoFaSetupSchema, k as twoFaVerifySchema, v as verifyEmailSchema } from './hooks-B41uikq7.js';
11
11
  import { SignOptions } from 'jsonwebtoken';
12
12
 
13
13
  //# sourceMappingURL=TRPCError.d.ts.map
@@ -232,6 +232,8 @@ interface TokenSettings {
232
232
  interface AuthFeatures {
233
233
  /** Enable two-factor authentication */
234
234
  twoFa?: boolean;
235
+ /** Require mobile device to enable 2FA (default: true). Set to false for testing. */
236
+ twoFaRequiresDevice?: boolean;
235
237
  /** OAuth providers configuration */
236
238
  oauth?: {
237
239
  google?: boolean;
@@ -657,19 +659,19 @@ declare function createAuthRouter<TExtensions extends SchemaExtensions = {}>(con
657
659
  input: inferParser<[TExtensions["signup"]] extends [zod.AnyZodObject] ? zod.ZodObject<{
658
660
  username: zod.ZodString;
659
661
  email: zod.ZodString;
660
- password: zod.ZodString;
662
+ password: zod.ZodEffects<zod.ZodString, string, string>;
661
663
  } & TExtensions["signup"]["shape"], "strip", zod.ZodTypeAny, zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<{
662
664
  username: zod.ZodString;
663
665
  email: zod.ZodString;
664
- password: zod.ZodString;
666
+ password: zod.ZodEffects<zod.ZodString, string, string>;
665
667
  } & TExtensions["signup"]["shape"]>, any> extends infer T_5 ? { [k_2 in keyof T_5]: T_5[k_2]; } : never, zod.baseObjectInputType<{
666
668
  username: zod.ZodString;
667
669
  email: zod.ZodString;
668
- password: zod.ZodString;
670
+ password: zod.ZodEffects<zod.ZodString, string, string>;
669
671
  } & TExtensions["signup"]["shape"]> extends infer T_6 ? { [k_3 in keyof T_6]: T_6[k_3]; } : never> : zod.ZodObject<{
670
672
  username: zod.ZodString;
671
673
  email: zod.ZodString;
672
- password: zod.ZodString;
674
+ password: zod.ZodEffects<zod.ZodString, string, string>;
673
675
  }, "strip", zod.ZodTypeAny, {
674
676
  email: string;
675
677
  username: string;
@@ -681,19 +683,19 @@ declare function createAuthRouter<TExtensions extends SchemaExtensions = {}>(con
681
683
  }>>["in"] extends infer T_7 ? T_7 extends inferParser<[TExtensions["signup"]] extends [zod.AnyZodObject] ? zod.ZodObject<{
682
684
  username: zod.ZodString;
683
685
  email: zod.ZodString;
684
- password: zod.ZodString;
686
+ password: zod.ZodEffects<zod.ZodString, string, string>;
685
687
  } & TExtensions["signup"]["shape"], "strip", zod.ZodTypeAny, zod.objectUtil.addQuestionMarks<zod.baseObjectOutputType<{
686
688
  username: zod.ZodString;
687
689
  email: zod.ZodString;
688
- password: zod.ZodString;
690
+ password: zod.ZodEffects<zod.ZodString, string, string>;
689
691
  } & TExtensions["signup"]["shape"]>, any> extends infer T_8 ? { [k_2 in keyof T_8]: T_8[k_2]; } : never, zod.baseObjectInputType<{
690
692
  username: zod.ZodString;
691
693
  email: zod.ZodString;
692
- password: zod.ZodString;
694
+ password: zod.ZodEffects<zod.ZodString, string, string>;
693
695
  } & TExtensions["signup"]["shape"]> extends infer T_9 ? { [k_3 in keyof T_9]: T_9[k_3]; } : never> : zod.ZodObject<{
694
696
  username: zod.ZodString;
695
697
  email: zod.ZodString;
696
- password: zod.ZodString;
698
+ password: zod.ZodEffects<zod.ZodString, string, string>;
697
699
  }, "strip", zod.ZodTypeAny, {
698
700
  email: string;
699
701
  username: string;
package/dist/index.js CHANGED
@@ -86,22 +86,20 @@ var import_server = require("@trpc/server");
86
86
  function createNoopEmailAdapter() {
87
87
  return {
88
88
  async sendVerificationEmail(email, code) {
89
- console.log(
89
+ console.debug(
90
90
  `[NoopEmailAdapter] Would send verification email to ${email} with code ${code}`
91
91
  );
92
92
  },
93
93
  async sendPasswordResetEmail(email, token) {
94
- console.log(
94
+ console.debug(
95
95
  `[NoopEmailAdapter] Would send password reset email to ${email} with token ${token}`
96
96
  );
97
97
  },
98
98
  async sendOTPEmail(email, otp) {
99
- console.log(
100
- `[NoopEmailAdapter] Would send OTP email to ${email} with code ${otp}`
101
- );
99
+ console.debug(`[NoopEmailAdapter] Would send OTP email to ${email} with code ${otp}`);
102
100
  },
103
101
  async sendLoginNotification(email, browserName, ip) {
104
- console.log(
102
+ console.debug(
105
103
  `[NoopEmailAdapter] Would send login notification to ${email} from ${browserName} (${ip})`
106
104
  );
107
105
  }
@@ -160,6 +158,7 @@ var defaultStorageKeys = {
160
158
  };
161
159
  var defaultFeatures = {
162
160
  twoFa: true,
161
+ twoFaRequiresDevice: true,
163
162
  oauth: { google: true, apple: true },
164
163
  biometric: false,
165
164
  emailVerification: true,
@@ -307,9 +306,7 @@ function decodeToken(token) {
307
306
  }
308
307
  }
309
308
  function isJwtError(error) {
310
- return error instanceof Error && ["TokenExpiredError", "JsonWebTokenError", "NotBeforeError"].includes(
311
- error.name
312
- );
309
+ return error instanceof Error && ["TokenExpiredError", "JsonWebTokenError", "NotBeforeError"].includes(error.name);
313
310
  }
314
311
  function isTokenExpiredError(error) {
315
312
  return isJwtError(error) && error.name === "TokenExpiredError";
@@ -326,6 +323,7 @@ function createAuthGuard(config, t) {
326
323
  clearAuthCookies(ctx.res, cookieSettings, storageKeys);
327
324
  if (config.hooks?.logError) {
328
325
  try {
326
+ const cookieHeader = ctx.headers.cookie;
329
327
  const contextInfo = {
330
328
  reason: description,
331
329
  sessionId,
@@ -333,6 +331,11 @@ function createAuthGuard(config, t) {
333
331
  ip: ctx.ip,
334
332
  userAgent: ctx.headers["user-agent"],
335
333
  ...path ? { path } : {},
334
+ // Diagnostic: was Cookie header present at all, and which keys were sent?
335
+ hasCookieHeader: Boolean(cookieHeader),
336
+ cookieKeys: cookieHeader ? cookieHeader.split(";").map((c) => c.trim().split("=")[0]).filter(Boolean) : [],
337
+ origin: ctx.headers.origin ?? null,
338
+ referer: ctx.headers.referer ?? null,
336
339
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
337
340
  };
338
341
  const combinedStack = [
@@ -363,11 +366,7 @@ ${errorStack}` : null,
363
366
  select: { id: true, userId: true, socketId: true }
364
367
  });
365
368
  if (session) {
366
- await config.hooks.onSessionRevoked(
367
- session.userId,
368
- session.socketId,
369
- description
370
- );
369
+ await config.hooks.onSessionRevoked(session.userId, session.socketId, description);
371
370
  }
372
371
  }
373
372
  } catch {
@@ -436,13 +435,7 @@ ${errorStack}` : null,
436
435
  });
437
436
  }
438
437
  if (session.user.status === "BANNED") {
439
- await revokeSession(
440
- ctx,
441
- session.id,
442
- "Session revoked: User banned",
443
- void 0,
444
- path
445
- );
438
+ await revokeSession(ctx, session.id, "Session revoked: User banned", void 0, path);
446
439
  throw new import_server.TRPCError({
447
440
  message: "Unauthorized",
448
441
  code: "UNAUTHORIZED"
@@ -450,9 +443,7 @@ ${errorStack}` : null,
450
443
  }
451
444
  if (config.features?.biometric && config.hooks?.getBiometricTimeout) {
452
445
  const timeoutMs = await config.hooks.getBiometricTimeout();
453
- if (timeoutMs !== null && !["auth.refresh", "auth.verifyBiometric", "auth.logout"].includes(
454
- path
455
- )) {
446
+ if (timeoutMs !== null && !["auth.refresh", "auth.verifyBiometric", "auth.logout"].includes(path)) {
456
447
  if (!session.user.verifiedHumanAt) {
457
448
  throw new import_server.TRPCError({
458
449
  message: "Biometric verification not completed. Please verify again.",
@@ -460,9 +451,7 @@ ${errorStack}` : null,
460
451
  });
461
452
  }
462
453
  const now = /* @__PURE__ */ new Date();
463
- const verificationExpiry = new Date(
464
- session.user.verifiedHumanAt.getTime() + timeoutMs
465
- );
454
+ const verificationExpiry = new Date(session.user.verifiedHumanAt.getTime() + timeoutMs);
466
455
  if (now > verificationExpiry) {
467
456
  throw new import_server.TRPCError({
468
457
  message: "Biometric verification expired. Please verify again.",
@@ -534,13 +523,7 @@ ${errorStack}` : null,
534
523
  });
535
524
  }
536
525
  if (err instanceof import_server.TRPCError && err.code === "UNAUTHORIZED") {
537
- await revokeSession(
538
- ctx,
539
- null,
540
- "Session revoked: Unauthorized",
541
- errorStack,
542
- path
543
- );
526
+ await revokeSession(ctx, null, "Session revoked: Unauthorized", errorStack, path);
544
527
  throw new import_server.TRPCError({
545
528
  message: "Unauthorized",
546
529
  code: "UNAUTHORIZED"
@@ -552,13 +535,7 @@ ${errorStack}` : null,
552
535
  if (!meta?.authRequired) {
553
536
  return next({ ctx: { ...ctx, userId: 0 } });
554
537
  }
555
- await revokeSession(
556
- ctx,
557
- null,
558
- "Session revoked: No token sent",
559
- void 0,
560
- path
561
- );
538
+ await revokeSession(ctx, null, "Session revoked: No token sent", void 0, path);
562
539
  throw new import_server.TRPCError({ message: "Unauthorized", code: "UNAUTHORIZED" });
563
540
  }
564
541
  });
@@ -579,25 +556,19 @@ function detectBrowser(userAgent) {
579
556
  return "iOS Browser (Chrome)";
580
557
  if (/iphone|ipad|ipod/i.test(userAgent) && /fxios/i.test(userAgent))
581
558
  return "iOS Browser (Firefox)";
582
- if (/iphone|ipad|ipod/i.test(userAgent) && /edg\//i.test(userAgent))
583
- return "iOS Browser (Edge)";
559
+ if (/iphone|ipad|ipod/i.test(userAgent) && /edg\//i.test(userAgent)) return "iOS Browser (Edge)";
584
560
  if (/android/i.test(userAgent) && !/chrome|firefox|samsungbrowser|opr\/|edg\//i.test(userAgent)) {
585
561
  return "Android App";
586
562
  }
587
- if (/android/i.test(userAgent) && /chrome/i.test(userAgent))
588
- return "Android Browser (Chrome)";
589
- if (/android/i.test(userAgent) && /firefox/i.test(userAgent))
590
- return "Android Browser (Firefox)";
563
+ if (/android/i.test(userAgent) && /chrome/i.test(userAgent)) return "Android Browser (Chrome)";
564
+ if (/android/i.test(userAgent) && /firefox/i.test(userAgent)) return "Android Browser (Firefox)";
591
565
  if (/android/i.test(userAgent) && /samsungbrowser/i.test(userAgent))
592
566
  return "Android Browser (Samsung)";
593
- if (/android/i.test(userAgent) && /opr\//i.test(userAgent))
594
- return "Android Browser (Opera)";
595
- if (/android/i.test(userAgent) && /edg\//i.test(userAgent))
596
- return "Android Browser (Edge)";
567
+ if (/android/i.test(userAgent) && /opr\//i.test(userAgent)) return "Android Browser (Opera)";
568
+ if (/android/i.test(userAgent) && /edg\//i.test(userAgent)) return "Android Browser (Edge)";
597
569
  if (/chrome|chromium/i.test(userAgent)) return "Chrome";
598
570
  if (/firefox/i.test(userAgent)) return "Firefox";
599
- if (/safari/i.test(userAgent) && !/chrome|chromium|crios/i.test(userAgent))
600
- return "Safari";
571
+ if (/safari/i.test(userAgent) && !/chrome|chromium|crios/i.test(userAgent)) return "Safari";
601
572
  if (/opr\//i.test(userAgent)) return "Opera";
602
573
  if (/edg\//i.test(userAgent)) return "Edge";
603
574
  return "Unknown";
@@ -631,16 +602,10 @@ function createOAuthVerifier(keys) {
631
602
  return async function verifyOAuthToken(provider, token, extra) {
632
603
  if (provider === "GOOGLE") {
633
604
  if (!keys.google?.clientId) {
634
- throw new OAuthVerificationError(
635
- "Google OAuth configuration missing",
636
- 500
637
- );
605
+ throw new OAuthVerificationError("Google OAuth configuration missing", 500);
638
606
  }
639
607
  if (!googleClient) {
640
- throw new OAuthVerificationError(
641
- "Google OAuth client not initialized",
642
- 500
643
- );
608
+ throw new OAuthVerificationError("Google OAuth client not initialized", 500);
644
609
  }
645
610
  const audience = [keys.google.clientId];
646
611
  if (keys.google.iosClientId) {
@@ -661,10 +626,7 @@ function createOAuthVerifier(keys) {
661
626
  }
662
627
  if (provider === "APPLE") {
663
628
  if (!keys.apple?.clientId) {
664
- throw new OAuthVerificationError(
665
- "Apple OAuth configuration missing",
666
- 500
667
- );
629
+ throw new OAuthVerificationError("Apple OAuth configuration missing", 500);
668
630
  }
669
631
  const audience = [keys.apple.clientId];
670
632
  if (keys.apple.iosClientId) {
@@ -744,8 +706,10 @@ var signupSchema = import_zod.z.object({
744
706
  username: import_zod.z.string().min(1, { message: "Username is required" }).max(30, { message: "Username must be 30 characters or less" }).regex(usernameValidationRegex, {
745
707
  message: "Username can only contain letters, numbers, and underscores"
746
708
  }),
747
- email: import_zod.z.string().email({ message: "Invalid email address" }),
748
- password: import_zod.z.string().min(6, { message: "Password must contain at least 6 characters" })
709
+ email: import_zod.z.string().max(254, { message: "Email must be 254 characters or less" }).email({ message: "Invalid email address" }),
710
+ password: import_zod.z.string().min(6, { message: "Password must contain at least 6 characters" }).max(72, { message: "Password must be 72 characters or less" }).refine((val) => val.trim().length >= 6, {
711
+ message: "Password cannot be only whitespace"
712
+ })
749
713
  });
750
714
  var loginSchema = import_zod.z.object({
751
715
  username: import_zod.z.string().min(1, { message: "Username or email is required" }),
@@ -765,14 +729,14 @@ var requestPasswordResetSchema = import_zod.z.object({
765
729
  });
766
730
  var resetPasswordSchema = import_zod.z.object({
767
731
  token: import_zod.z.string().min(1, { message: "Reset token is required" }),
768
- password: import_zod.z.string().min(6, { message: "Password must contain at least 6 characters" })
732
+ password: import_zod.z.string().min(6, { message: "Password must contain at least 6 characters" }).max(72, { message: "Password must be 72 characters or less" })
769
733
  });
770
734
  var checkPasswordResetSchema = import_zod.z.object({
771
735
  token: import_zod.z.string().min(1, { message: "Reset token is required" })
772
736
  });
773
737
  var changePasswordSchema = import_zod.z.object({
774
738
  currentPassword: import_zod.z.string().min(1, { message: "Current password is required" }),
775
- newPassword: import_zod.z.string().min(6, { message: "New password must contain at least 6 characters" })
739
+ newPassword: import_zod.z.string().min(6, { message: "New password must contain at least 6 characters" }).max(72, { message: "Password must be 72 characters or less" })
776
740
  });
777
741
  var twoFaVerifySchema = import_zod.z.object({
778
742
  code: import_zod.z.string().min(6, { message: "Verification code is required" }),
@@ -1162,7 +1126,11 @@ var BaseProcedureFactory = class {
1162
1126
  });
1163
1127
  for (const session of sessionsToRevoke) {
1164
1128
  if (this.config.hooks?.onSessionRevoked) {
1165
- await this.config.hooks.onSessionRevoked(session.id, session.socketId, "End all sessions");
1129
+ await this.config.hooks.onSessionRevoked(
1130
+ session.id,
1131
+ session.socketId,
1132
+ "End all sessions"
1133
+ );
1166
1134
  }
1167
1135
  }
1168
1136
  if (!skipCurrentSession) {
@@ -1491,14 +1459,14 @@ var OAuthLoginProcedureFactory = class {
1491
1459
  }
1492
1460
  const { email, oauthId } = await this.verifyOAuthToken(provider, idToken, appleUser);
1493
1461
  if (!email) {
1494
- throw new import_server5.TRPCError({ code: "BAD_REQUEST", message: "Email not provided by OAuth provider" });
1462
+ throw new import_server5.TRPCError({
1463
+ code: "BAD_REQUEST",
1464
+ message: "Email not provided by OAuth provider"
1465
+ });
1495
1466
  }
1496
1467
  let user = await this.config.prisma.user.findFirst({
1497
1468
  where: {
1498
- OR: [
1499
- { email: { equals: email, mode: "insensitive" } },
1500
- { oauthId: { equals: oauthId } }
1501
- ]
1469
+ OR: [{ email: { equals: email, mode: "insensitive" } }, { oauthId: { equals: oauthId } }]
1502
1470
  },
1503
1471
  select: {
1504
1472
  id: true,
@@ -1648,15 +1616,17 @@ var TwoFaProcedureFactory = class {
1648
1616
  if (user.twoFaEnabled) {
1649
1617
  throw new import_server6.TRPCError({ code: "BAD_REQUEST", message: "2FA already enabled." });
1650
1618
  }
1651
- const checkSession = await this.config.prisma.session.findFirst({
1652
- where: { userId, id: sessionId },
1653
- select: { deviceId: true }
1654
- });
1655
- if (!checkSession?.deviceId) {
1656
- throw new import_server6.TRPCError({
1657
- code: "BAD_REQUEST",
1658
- message: "You must be logged in on mobile to enable 2FA."
1619
+ if (this.config.features.twoFaRequiresDevice !== false) {
1620
+ const checkSession = await this.config.prisma.session.findFirst({
1621
+ where: { userId, id: sessionId },
1622
+ select: { deviceId: true }
1659
1623
  });
1624
+ if (!checkSession?.deviceId) {
1625
+ throw new import_server6.TRPCError({
1626
+ code: "BAD_REQUEST",
1627
+ message: "You must be logged in on mobile to enable 2FA."
1628
+ });
1629
+ }
1660
1630
  }
1661
1631
  await this.config.prisma.session.updateMany({
1662
1632
  where: { userId, revokedAt: null, NOT: { id: sessionId } },
@@ -1880,15 +1850,29 @@ var TwoFaProcedureFactory = class {
1880
1850
  const { pushToken } = input;
1881
1851
  const device = await this.config.prisma.device.findFirst({
1882
1852
  where: {
1883
- ...userId !== 0 && { users: { some: { id: userId } } },
1853
+ users: { some: { id: userId } },
1884
1854
  pushToken
1885
1855
  },
1886
1856
  select: { id: true }
1887
1857
  });
1888
1858
  if (device) {
1889
- await this.config.prisma.device.delete({
1890
- where: { id: device.id }
1859
+ await this.config.prisma.session.updateMany({
1860
+ where: { userId, deviceId: device.id },
1861
+ data: { deviceId: null }
1862
+ });
1863
+ await this.config.prisma.device.update({
1864
+ where: { id: device.id },
1865
+ data: { users: { disconnect: { id: userId } } }
1891
1866
  });
1867
+ const remainingUsers = await this.config.prisma.device.findUnique({
1868
+ where: { id: device.id },
1869
+ select: { users: { select: { id: true }, take: 1 } }
1870
+ });
1871
+ if (!remainingUsers?.users.length) {
1872
+ await this.config.prisma.device.delete({
1873
+ where: { id: device.id }
1874
+ });
1875
+ }
1892
1876
  }
1893
1877
  return { deregistered: true };
1894
1878
  });
@@ -1975,10 +1959,7 @@ function getClientIp(req) {
1975
1959
  }
1976
1960
 
1977
1961
  // src/router.ts
1978
- var createContext = ({
1979
- req,
1980
- res
1981
- }) => ({
1962
+ var createContext = ({ req, res }) => ({
1982
1963
  headers: req.headers,
1983
1964
  userId: null,
1984
1965
  sessionId: null,
@@ -1991,9 +1972,7 @@ var AuthRouterFactory = class {
1991
1972
  constructor(userConfig) {
1992
1973
  this.userConfig = userConfig;
1993
1974
  this.config = createAuthConfig(this.userConfig);
1994
- this.schemas = createSchemas(
1995
- this.config.schemaExtensions
1996
- );
1975
+ this.schemas = createSchemas(this.config.schemaExtensions);
1997
1976
  this.t = createTrpcBuilder(this.config);
1998
1977
  this.authGuard = createAuthGuard(this.config, this.t);
1999
1978
  this.procedure = createBaseProcedure(this.t, this.authGuard);
@@ -2005,10 +1984,7 @@ var AuthRouterFactory = class {
2005
1984
  this.procedure,
2006
1985
  this.authProcedure
2007
1986
  );
2008
- const biometricRoutes = new BiometricProcedureFactory(
2009
- this.config,
2010
- this.authProcedure
2011
- );
1987
+ const biometricRoutes = new BiometricProcedureFactory(this.config, this.authProcedure);
2012
1988
  const emailVerificationRoutes = new EmailVerificationProcedureFactory(
2013
1989
  this.config,
2014
1990
  this.authProcedure
@@ -2017,11 +1993,7 @@ var AuthRouterFactory = class {
2017
1993
  this.config,
2018
1994
  this.procedure
2019
1995
  );
2020
- const twoFaRoutes = new TwoFaProcedureFactory(
2021
- this.config,
2022
- this.procedure,
2023
- this.authProcedure
2024
- );
1996
+ const twoFaRoutes = new TwoFaProcedureFactory(this.config, this.procedure, this.authProcedure);
2025
1997
  return this.t.router({
2026
1998
  ...baseRoutes.createBaseProcedures(this.schemas),
2027
1999
  ...oAuthLoginRoutes.createOAuthLoginProcedures(this.schemas),
package/dist/index.mjs CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  twoFaSetupSchema,
22
22
  twoFaVerifySchema,
23
23
  verifyEmailSchema
24
- } from "./chunk-2DOUP275.mjs";
24
+ } from "./chunk-PYVDWODF.mjs";
25
25
 
26
26
  // src/middleware/authGuard.ts
27
27
  import { TRPCError } from "@trpc/server";
@@ -30,22 +30,20 @@ import { TRPCError } from "@trpc/server";
30
30
  function createNoopEmailAdapter() {
31
31
  return {
32
32
  async sendVerificationEmail(email, code) {
33
- console.log(
33
+ console.debug(
34
34
  `[NoopEmailAdapter] Would send verification email to ${email} with code ${code}`
35
35
  );
36
36
  },
37
37
  async sendPasswordResetEmail(email, token) {
38
- console.log(
38
+ console.debug(
39
39
  `[NoopEmailAdapter] Would send password reset email to ${email} with token ${token}`
40
40
  );
41
41
  },
42
42
  async sendOTPEmail(email, otp) {
43
- console.log(
44
- `[NoopEmailAdapter] Would send OTP email to ${email} with code ${otp}`
45
- );
43
+ console.debug(`[NoopEmailAdapter] Would send OTP email to ${email} with code ${otp}`);
46
44
  },
47
45
  async sendLoginNotification(email, browserName, ip) {
48
- console.log(
46
+ console.debug(
49
47
  `[NoopEmailAdapter] Would send login notification to ${email} from ${browserName} (${ip})`
50
48
  );
51
49
  }
@@ -104,6 +102,7 @@ var defaultStorageKeys = {
104
102
  };
105
103
  var defaultFeatures = {
106
104
  twoFa: true,
105
+ twoFaRequiresDevice: true,
107
106
  oauth: { google: true, apple: true },
108
107
  biometric: false,
109
108
  emailVerification: true,
@@ -251,9 +250,7 @@ function decodeToken(token) {
251
250
  }
252
251
  }
253
252
  function isJwtError(error) {
254
- return error instanceof Error && ["TokenExpiredError", "JsonWebTokenError", "NotBeforeError"].includes(
255
- error.name
256
- );
253
+ return error instanceof Error && ["TokenExpiredError", "JsonWebTokenError", "NotBeforeError"].includes(error.name);
257
254
  }
258
255
  function isTokenExpiredError(error) {
259
256
  return isJwtError(error) && error.name === "TokenExpiredError";
@@ -270,6 +267,7 @@ function createAuthGuard(config, t) {
270
267
  clearAuthCookies(ctx.res, cookieSettings, storageKeys);
271
268
  if (config.hooks?.logError) {
272
269
  try {
270
+ const cookieHeader = ctx.headers.cookie;
273
271
  const contextInfo = {
274
272
  reason: description,
275
273
  sessionId,
@@ -277,6 +275,11 @@ function createAuthGuard(config, t) {
277
275
  ip: ctx.ip,
278
276
  userAgent: ctx.headers["user-agent"],
279
277
  ...path ? { path } : {},
278
+ // Diagnostic: was Cookie header present at all, and which keys were sent?
279
+ hasCookieHeader: Boolean(cookieHeader),
280
+ cookieKeys: cookieHeader ? cookieHeader.split(";").map((c) => c.trim().split("=")[0]).filter(Boolean) : [],
281
+ origin: ctx.headers.origin ?? null,
282
+ referer: ctx.headers.referer ?? null,
280
283
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
281
284
  };
282
285
  const combinedStack = [
@@ -307,11 +310,7 @@ ${errorStack}` : null,
307
310
  select: { id: true, userId: true, socketId: true }
308
311
  });
309
312
  if (session) {
310
- await config.hooks.onSessionRevoked(
311
- session.userId,
312
- session.socketId,
313
- description
314
- );
313
+ await config.hooks.onSessionRevoked(session.userId, session.socketId, description);
315
314
  }
316
315
  }
317
316
  } catch {
@@ -380,13 +379,7 @@ ${errorStack}` : null,
380
379
  });
381
380
  }
382
381
  if (session.user.status === "BANNED") {
383
- await revokeSession(
384
- ctx,
385
- session.id,
386
- "Session revoked: User banned",
387
- void 0,
388
- path
389
- );
382
+ await revokeSession(ctx, session.id, "Session revoked: User banned", void 0, path);
390
383
  throw new TRPCError({
391
384
  message: "Unauthorized",
392
385
  code: "UNAUTHORIZED"
@@ -394,9 +387,7 @@ ${errorStack}` : null,
394
387
  }
395
388
  if (config.features?.biometric && config.hooks?.getBiometricTimeout) {
396
389
  const timeoutMs = await config.hooks.getBiometricTimeout();
397
- if (timeoutMs !== null && !["auth.refresh", "auth.verifyBiometric", "auth.logout"].includes(
398
- path
399
- )) {
390
+ if (timeoutMs !== null && !["auth.refresh", "auth.verifyBiometric", "auth.logout"].includes(path)) {
400
391
  if (!session.user.verifiedHumanAt) {
401
392
  throw new TRPCError({
402
393
  message: "Biometric verification not completed. Please verify again.",
@@ -404,9 +395,7 @@ ${errorStack}` : null,
404
395
  });
405
396
  }
406
397
  const now = /* @__PURE__ */ new Date();
407
- const verificationExpiry = new Date(
408
- session.user.verifiedHumanAt.getTime() + timeoutMs
409
- );
398
+ const verificationExpiry = new Date(session.user.verifiedHumanAt.getTime() + timeoutMs);
410
399
  if (now > verificationExpiry) {
411
400
  throw new TRPCError({
412
401
  message: "Biometric verification expired. Please verify again.",
@@ -478,13 +467,7 @@ ${errorStack}` : null,
478
467
  });
479
468
  }
480
469
  if (err instanceof TRPCError && err.code === "UNAUTHORIZED") {
481
- await revokeSession(
482
- ctx,
483
- null,
484
- "Session revoked: Unauthorized",
485
- errorStack,
486
- path
487
- );
470
+ await revokeSession(ctx, null, "Session revoked: Unauthorized", errorStack, path);
488
471
  throw new TRPCError({
489
472
  message: "Unauthorized",
490
473
  code: "UNAUTHORIZED"
@@ -496,13 +479,7 @@ ${errorStack}` : null,
496
479
  if (!meta?.authRequired) {
497
480
  return next({ ctx: { ...ctx, userId: 0 } });
498
481
  }
499
- await revokeSession(
500
- ctx,
501
- null,
502
- "Session revoked: No token sent",
503
- void 0,
504
- path
505
- );
482
+ await revokeSession(ctx, null, "Session revoked: No token sent", void 0, path);
506
483
  throw new TRPCError({ message: "Unauthorized", code: "UNAUTHORIZED" });
507
484
  }
508
485
  });
@@ -523,25 +500,19 @@ function detectBrowser(userAgent) {
523
500
  return "iOS Browser (Chrome)";
524
501
  if (/iphone|ipad|ipod/i.test(userAgent) && /fxios/i.test(userAgent))
525
502
  return "iOS Browser (Firefox)";
526
- if (/iphone|ipad|ipod/i.test(userAgent) && /edg\//i.test(userAgent))
527
- return "iOS Browser (Edge)";
503
+ if (/iphone|ipad|ipod/i.test(userAgent) && /edg\//i.test(userAgent)) return "iOS Browser (Edge)";
528
504
  if (/android/i.test(userAgent) && !/chrome|firefox|samsungbrowser|opr\/|edg\//i.test(userAgent)) {
529
505
  return "Android App";
530
506
  }
531
- if (/android/i.test(userAgent) && /chrome/i.test(userAgent))
532
- return "Android Browser (Chrome)";
533
- if (/android/i.test(userAgent) && /firefox/i.test(userAgent))
534
- return "Android Browser (Firefox)";
507
+ if (/android/i.test(userAgent) && /chrome/i.test(userAgent)) return "Android Browser (Chrome)";
508
+ if (/android/i.test(userAgent) && /firefox/i.test(userAgent)) return "Android Browser (Firefox)";
535
509
  if (/android/i.test(userAgent) && /samsungbrowser/i.test(userAgent))
536
510
  return "Android Browser (Samsung)";
537
- if (/android/i.test(userAgent) && /opr\//i.test(userAgent))
538
- return "Android Browser (Opera)";
539
- if (/android/i.test(userAgent) && /edg\//i.test(userAgent))
540
- return "Android Browser (Edge)";
511
+ if (/android/i.test(userAgent) && /opr\//i.test(userAgent)) return "Android Browser (Opera)";
512
+ if (/android/i.test(userAgent) && /edg\//i.test(userAgent)) return "Android Browser (Edge)";
541
513
  if (/chrome|chromium/i.test(userAgent)) return "Chrome";
542
514
  if (/firefox/i.test(userAgent)) return "Firefox";
543
- if (/safari/i.test(userAgent) && !/chrome|chromium|crios/i.test(userAgent))
544
- return "Safari";
515
+ if (/safari/i.test(userAgent) && !/chrome|chromium|crios/i.test(userAgent)) return "Safari";
545
516
  if (/opr\//i.test(userAgent)) return "Opera";
546
517
  if (/edg\//i.test(userAgent)) return "Edge";
547
518
  return "Unknown";
@@ -575,16 +546,10 @@ function createOAuthVerifier(keys) {
575
546
  return async function verifyOAuthToken(provider, token, extra) {
576
547
  if (provider === "GOOGLE") {
577
548
  if (!keys.google?.clientId) {
578
- throw new OAuthVerificationError(
579
- "Google OAuth configuration missing",
580
- 500
581
- );
549
+ throw new OAuthVerificationError("Google OAuth configuration missing", 500);
582
550
  }
583
551
  if (!googleClient) {
584
- throw new OAuthVerificationError(
585
- "Google OAuth client not initialized",
586
- 500
587
- );
552
+ throw new OAuthVerificationError("Google OAuth client not initialized", 500);
588
553
  }
589
554
  const audience = [keys.google.clientId];
590
555
  if (keys.google.iosClientId) {
@@ -605,10 +570,7 @@ function createOAuthVerifier(keys) {
605
570
  }
606
571
  if (provider === "APPLE") {
607
572
  if (!keys.apple?.clientId) {
608
- throw new OAuthVerificationError(
609
- "Apple OAuth configuration missing",
610
- 500
611
- );
573
+ throw new OAuthVerificationError("Apple OAuth configuration missing", 500);
612
574
  }
613
575
  const audience = [keys.apple.clientId];
614
576
  if (keys.apple.iosClientId) {
@@ -1014,7 +976,11 @@ var BaseProcedureFactory = class {
1014
976
  });
1015
977
  for (const session of sessionsToRevoke) {
1016
978
  if (this.config.hooks?.onSessionRevoked) {
1017
- await this.config.hooks.onSessionRevoked(session.id, session.socketId, "End all sessions");
979
+ await this.config.hooks.onSessionRevoked(
980
+ session.id,
981
+ session.socketId,
982
+ "End all sessions"
983
+ );
1018
984
  }
1019
985
  }
1020
986
  if (!skipCurrentSession) {
@@ -1343,14 +1309,14 @@ var OAuthLoginProcedureFactory = class {
1343
1309
  }
1344
1310
  const { email, oauthId } = await this.verifyOAuthToken(provider, idToken, appleUser);
1345
1311
  if (!email) {
1346
- throw new TRPCError5({ code: "BAD_REQUEST", message: "Email not provided by OAuth provider" });
1312
+ throw new TRPCError5({
1313
+ code: "BAD_REQUEST",
1314
+ message: "Email not provided by OAuth provider"
1315
+ });
1347
1316
  }
1348
1317
  let user = await this.config.prisma.user.findFirst({
1349
1318
  where: {
1350
- OR: [
1351
- { email: { equals: email, mode: "insensitive" } },
1352
- { oauthId: { equals: oauthId } }
1353
- ]
1319
+ OR: [{ email: { equals: email, mode: "insensitive" } }, { oauthId: { equals: oauthId } }]
1354
1320
  },
1355
1321
  select: {
1356
1322
  id: true,
@@ -1500,15 +1466,17 @@ var TwoFaProcedureFactory = class {
1500
1466
  if (user.twoFaEnabled) {
1501
1467
  throw new TRPCError6({ code: "BAD_REQUEST", message: "2FA already enabled." });
1502
1468
  }
1503
- const checkSession = await this.config.prisma.session.findFirst({
1504
- where: { userId, id: sessionId },
1505
- select: { deviceId: true }
1506
- });
1507
- if (!checkSession?.deviceId) {
1508
- throw new TRPCError6({
1509
- code: "BAD_REQUEST",
1510
- message: "You must be logged in on mobile to enable 2FA."
1469
+ if (this.config.features.twoFaRequiresDevice !== false) {
1470
+ const checkSession = await this.config.prisma.session.findFirst({
1471
+ where: { userId, id: sessionId },
1472
+ select: { deviceId: true }
1511
1473
  });
1474
+ if (!checkSession?.deviceId) {
1475
+ throw new TRPCError6({
1476
+ code: "BAD_REQUEST",
1477
+ message: "You must be logged in on mobile to enable 2FA."
1478
+ });
1479
+ }
1512
1480
  }
1513
1481
  await this.config.prisma.session.updateMany({
1514
1482
  where: { userId, revokedAt: null, NOT: { id: sessionId } },
@@ -1732,15 +1700,29 @@ var TwoFaProcedureFactory = class {
1732
1700
  const { pushToken } = input;
1733
1701
  const device = await this.config.prisma.device.findFirst({
1734
1702
  where: {
1735
- ...userId !== 0 && { users: { some: { id: userId } } },
1703
+ users: { some: { id: userId } },
1736
1704
  pushToken
1737
1705
  },
1738
1706
  select: { id: true }
1739
1707
  });
1740
1708
  if (device) {
1741
- await this.config.prisma.device.delete({
1742
- where: { id: device.id }
1709
+ await this.config.prisma.session.updateMany({
1710
+ where: { userId, deviceId: device.id },
1711
+ data: { deviceId: null }
1712
+ });
1713
+ await this.config.prisma.device.update({
1714
+ where: { id: device.id },
1715
+ data: { users: { disconnect: { id: userId } } }
1743
1716
  });
1717
+ const remainingUsers = await this.config.prisma.device.findUnique({
1718
+ where: { id: device.id },
1719
+ select: { users: { select: { id: true }, take: 1 } }
1720
+ });
1721
+ if (!remainingUsers?.users.length) {
1722
+ await this.config.prisma.device.delete({
1723
+ where: { id: device.id }
1724
+ });
1725
+ }
1744
1726
  }
1745
1727
  return { deregistered: true };
1746
1728
  });
@@ -1827,10 +1809,7 @@ function getClientIp(req) {
1827
1809
  }
1828
1810
 
1829
1811
  // src/router.ts
1830
- var createContext = ({
1831
- req,
1832
- res
1833
- }) => ({
1812
+ var createContext = ({ req, res }) => ({
1834
1813
  headers: req.headers,
1835
1814
  userId: null,
1836
1815
  sessionId: null,
@@ -1843,9 +1822,7 @@ var AuthRouterFactory = class {
1843
1822
  constructor(userConfig) {
1844
1823
  this.userConfig = userConfig;
1845
1824
  this.config = createAuthConfig(this.userConfig);
1846
- this.schemas = createSchemas(
1847
- this.config.schemaExtensions
1848
- );
1825
+ this.schemas = createSchemas(this.config.schemaExtensions);
1849
1826
  this.t = createTrpcBuilder(this.config);
1850
1827
  this.authGuard = createAuthGuard(this.config, this.t);
1851
1828
  this.procedure = createBaseProcedure(this.t, this.authGuard);
@@ -1857,10 +1834,7 @@ var AuthRouterFactory = class {
1857
1834
  this.procedure,
1858
1835
  this.authProcedure
1859
1836
  );
1860
- const biometricRoutes = new BiometricProcedureFactory(
1861
- this.config,
1862
- this.authProcedure
1863
- );
1837
+ const biometricRoutes = new BiometricProcedureFactory(this.config, this.authProcedure);
1864
1838
  const emailVerificationRoutes = new EmailVerificationProcedureFactory(
1865
1839
  this.config,
1866
1840
  this.authProcedure
@@ -1869,11 +1843,7 @@ var AuthRouterFactory = class {
1869
1843
  this.config,
1870
1844
  this.procedure
1871
1845
  );
1872
- const twoFaRoutes = new TwoFaProcedureFactory(
1873
- this.config,
1874
- this.procedure,
1875
- this.authProcedure
1876
- );
1846
+ const twoFaRoutes = new TwoFaProcedureFactory(this.config, this.procedure, this.authProcedure);
1877
1847
  return this.t.router({
1878
1848
  ...baseRoutes.createBaseProcedures(this.schemas),
1879
1849
  ...oAuthLoginRoutes.createOAuthLoginProcedures(this.schemas),
@@ -1,2 +1,2 @@
1
1
  import 'zod';
2
- export { m as AuthSchemas, C as ChangePasswordInput, n as CreatedSchemas, L as LoginInput, p as LoginSchemaInput, a as LogoutInput, O as OAuthLoginInput, q as OAuthSchemaInput, R as ResetPasswordInput, b as SignupInput, u as SignupSchemaInput, T as TwoFaVerifyInput, V as VerifyEmailInput, c as biometricVerifySchema, d as changePasswordSchema, w as checkPasswordResetSchema, x as createSchemas, y as deregisterPushTokenSchema, z as disableTwofaSchema, e as endAllSessionsSchema, B as getTwofaSecretSchema, l as loginSchema, f as logoutSchema, o as oAuthLoginSchema, g as otpLoginRequestSchema, h as otpLoginVerifySchema, D as registerPushTokenSchema, r as requestPasswordResetSchema, E as resendVerificationSchema, i as resetPasswordSchema, s as signupSchema, t as twoFaResetSchema, F as twoFaResetVerifySchema, j as twoFaSetupSchema, k as twoFaVerifySchema, v as verifyEmailSchema } from './hooks-CK4f4PHo.mjs';
2
+ export { m as AuthSchemas, C as ChangePasswordInput, n as CreatedSchemas, L as LoginInput, p as LoginSchemaInput, a as LogoutInput, O as OAuthLoginInput, q as OAuthSchemaInput, R as ResetPasswordInput, b as SignupInput, u as SignupSchemaInput, T as TwoFaVerifyInput, V as VerifyEmailInput, c as biometricVerifySchema, d as changePasswordSchema, w as checkPasswordResetSchema, x as createSchemas, y as deregisterPushTokenSchema, z as disableTwofaSchema, e as endAllSessionsSchema, B as getTwofaSecretSchema, l as loginSchema, f as logoutSchema, o as oAuthLoginSchema, g as otpLoginRequestSchema, h as otpLoginVerifySchema, D as registerPushTokenSchema, r as requestPasswordResetSchema, E as resendVerificationSchema, i as resetPasswordSchema, s as signupSchema, t as twoFaResetSchema, F as twoFaResetVerifySchema, j as twoFaSetupSchema, k as twoFaVerifySchema, v as verifyEmailSchema } from './hooks-B41uikq7.mjs';
@@ -1,2 +1,2 @@
1
1
  import 'zod';
2
- export { m as AuthSchemas, C as ChangePasswordInput, n as CreatedSchemas, L as LoginInput, p as LoginSchemaInput, a as LogoutInput, O as OAuthLoginInput, q as OAuthSchemaInput, R as ResetPasswordInput, b as SignupInput, u as SignupSchemaInput, T as TwoFaVerifyInput, V as VerifyEmailInput, c as biometricVerifySchema, d as changePasswordSchema, w as checkPasswordResetSchema, x as createSchemas, y as deregisterPushTokenSchema, z as disableTwofaSchema, e as endAllSessionsSchema, B as getTwofaSecretSchema, l as loginSchema, f as logoutSchema, o as oAuthLoginSchema, g as otpLoginRequestSchema, h as otpLoginVerifySchema, D as registerPushTokenSchema, r as requestPasswordResetSchema, E as resendVerificationSchema, i as resetPasswordSchema, s as signupSchema, t as twoFaResetSchema, F as twoFaResetVerifySchema, j as twoFaSetupSchema, k as twoFaVerifySchema, v as verifyEmailSchema } from './hooks-CK4f4PHo.js';
2
+ export { m as AuthSchemas, C as ChangePasswordInput, n as CreatedSchemas, L as LoginInput, p as LoginSchemaInput, a as LogoutInput, O as OAuthLoginInput, q as OAuthSchemaInput, R as ResetPasswordInput, b as SignupInput, u as SignupSchemaInput, T as TwoFaVerifyInput, V as VerifyEmailInput, c as biometricVerifySchema, d as changePasswordSchema, w as checkPasswordResetSchema, x as createSchemas, y as deregisterPushTokenSchema, z as disableTwofaSchema, e as endAllSessionsSchema, B as getTwofaSecretSchema, l as loginSchema, f as logoutSchema, o as oAuthLoginSchema, g as otpLoginRequestSchema, h as otpLoginVerifySchema, D as registerPushTokenSchema, r as requestPasswordResetSchema, E as resendVerificationSchema, i as resetPasswordSchema, s as signupSchema, t as twoFaResetSchema, F as twoFaResetVerifySchema, j as twoFaSetupSchema, k as twoFaVerifySchema, v as verifyEmailSchema } from './hooks-B41uikq7.js';
@@ -51,8 +51,10 @@ var signupSchema = import_zod.z.object({
51
51
  username: import_zod.z.string().min(1, { message: "Username is required" }).max(30, { message: "Username must be 30 characters or less" }).regex(usernameValidationRegex, {
52
52
  message: "Username can only contain letters, numbers, and underscores"
53
53
  }),
54
- email: import_zod.z.string().email({ message: "Invalid email address" }),
55
- password: import_zod.z.string().min(6, { message: "Password must contain at least 6 characters" })
54
+ email: import_zod.z.string().max(254, { message: "Email must be 254 characters or less" }).email({ message: "Invalid email address" }),
55
+ password: import_zod.z.string().min(6, { message: "Password must contain at least 6 characters" }).max(72, { message: "Password must be 72 characters or less" }).refine((val) => val.trim().length >= 6, {
56
+ message: "Password cannot be only whitespace"
57
+ })
56
58
  });
57
59
  var loginSchema = import_zod.z.object({
58
60
  username: import_zod.z.string().min(1, { message: "Username or email is required" }),
@@ -72,14 +74,14 @@ var requestPasswordResetSchema = import_zod.z.object({
72
74
  });
73
75
  var resetPasswordSchema = import_zod.z.object({
74
76
  token: import_zod.z.string().min(1, { message: "Reset token is required" }),
75
- password: import_zod.z.string().min(6, { message: "Password must contain at least 6 characters" })
77
+ password: import_zod.z.string().min(6, { message: "Password must contain at least 6 characters" }).max(72, { message: "Password must be 72 characters or less" })
76
78
  });
77
79
  var checkPasswordResetSchema = import_zod.z.object({
78
80
  token: import_zod.z.string().min(1, { message: "Reset token is required" })
79
81
  });
80
82
  var changePasswordSchema = import_zod.z.object({
81
83
  currentPassword: import_zod.z.string().min(1, { message: "Current password is required" }),
82
- newPassword: import_zod.z.string().min(6, { message: "New password must contain at least 6 characters" })
84
+ newPassword: import_zod.z.string().min(6, { message: "New password must contain at least 6 characters" }).max(72, { message: "Password must be 72 characters or less" })
83
85
  });
84
86
  var twoFaVerifySchema = import_zod.z.object({
85
87
  code: import_zod.z.string().min(6, { message: "Verification code is required" }),
@@ -22,7 +22,7 @@ import {
22
22
  twoFaSetupSchema,
23
23
  twoFaVerifySchema,
24
24
  verifyEmailSchema
25
- } from "./chunk-2DOUP275.mjs";
25
+ } from "./chunk-PYVDWODF.mjs";
26
26
  export {
27
27
  biometricVerifySchema,
28
28
  changePasswordSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@factiii/auth",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -53,10 +53,16 @@
53
53
  "build": "tsup",
54
54
  "dev": "tsup --watch",
55
55
  "check-types": "tsc --noEmit",
56
- "lint": "eslint src --ext .ts,.tsx",
57
- "lint:fix": "eslint src --ext .ts,.tsx --fix",
56
+ "lint": "eslint src",
57
+ "lint:fix": "eslint src --fix",
58
+ "format": "prettier --check src",
59
+ "format:fix": "prettier --write src",
58
60
  "clean": "rm -rf dist node_modules/.cache .turbo",
59
- "prepublishOnly": "npm run build"
61
+ "prepublishOnly": "npm run build",
62
+ "e2e": "pnpm e2e:db:up && playwright test --reporter=list; pnpm e2e:db:down",
63
+ "e2e:ui": "pnpm e2e:db:up && playwright test --ui",
64
+ "e2e:db:up": "docker compose -f e2e/docker-compose.yml up -d --wait && prisma generate --schema=e2e/server/schema.prisma --config=e2e/server/prisma.config.ts && prisma migrate reset --schema=e2e/server/schema.prisma --config=e2e/server/prisma.config.ts --force",
65
+ "e2e:db:down": "docker compose -f e2e/docker-compose.yml down"
60
66
  },
61
67
  "dependencies": {
62
68
  "@trpc/server": "11.8.0",
@@ -83,22 +89,39 @@
83
89
  }
84
90
  },
85
91
  "devDependencies": {
92
+ "@playwright/test": "^1.58.1",
93
+ "@prisma/adapter-better-sqlite3": "^7.3.0",
94
+ "@prisma/adapter-pg": "^7.3.0",
86
95
  "@prisma/client": "^7.1.0",
96
+ "@trpc/client": "^11.8.0",
87
97
  "@trpc/server": "11.8.0",
88
98
  "@types/bcryptjs": "^2.4.6",
99
+ "@types/better-sqlite3": "^7.6.13",
89
100
  "@types/jsonwebtoken": "^9.0.9",
90
101
  "@types/node": "^20.19.0",
102
+ "@types/pg": "^8.16.0",
103
+ "@types/react": "^19.2.11",
104
+ "@types/react-dom": "^19.2.3",
91
105
  "@typescript-eslint/eslint-plugin": "^7.18.0",
92
106
  "@typescript-eslint/parser": "^7.18.0",
107
+ "@vitejs/plugin-react": "^5.1.3",
108
+ "better-sqlite3": "^12.6.2",
93
109
  "eslint": "^8.57.0",
110
+ "pg": "^8.18.0",
111
+ "prettier": "^3.8.1",
94
112
  "prisma": "^7.1.0",
113
+ "react": "^19.2.4",
114
+ "react-dom": "^19.2.4",
115
+ "react-router-dom": "^7.2.0",
95
116
  "superjson": "^1.13.3",
96
117
  "tsup": "^8.3.5",
118
+ "tsx": "^4.21.0",
97
119
  "typescript": "5.9.3",
120
+ "vite": "^7.3.1",
98
121
  "zod": "3.24.2"
99
122
  },
100
123
  "engines": {
101
124
  "node": ">=18.0.0"
102
125
  },
103
- "packageManager": "pnpm@10.26.0+sha512.3b3f6c725ebe712506c0ab1ad4133cf86b1f4b687effce62a9b38b4d72e3954242e643190fc51fa1642949c735f403debd44f5cb0edd657abe63a8b6a7e1e402"
126
+ "packageManager": "pnpm@10.26.0"
104
127
  }