@factiii/auth 0.5.3 → 0.5.5

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.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ __require,
2
3
  biometricVerifySchema,
3
4
  changePasswordSchema,
4
5
  checkPasswordResetSchema,
@@ -17,11 +18,266 @@ import {
17
18
  twoFaResetVerifySchema,
18
19
  twoFaVerifySchema,
19
20
  verifyEmailSchema
20
- } from "./chunk-EHI4P63M.mjs";
21
+ } from "./chunk-KUYH4DBN.mjs";
21
22
 
22
23
  // src/middleware/authGuard.ts
23
24
  import { TRPCError } from "@trpc/server";
24
25
 
26
+ // src/adapters/prismaAdapter.ts
27
+ function createPrismaAdapter(prisma) {
28
+ const db = prisma;
29
+ return {
30
+ user: {
31
+ async findByEmailInsensitive(email) {
32
+ return db.user.findFirst({
33
+ where: { email: { equals: email, mode: "insensitive" } }
34
+ });
35
+ },
36
+ async findByUsernameInsensitive(username) {
37
+ return db.user.findFirst({
38
+ where: { username: { equals: username, mode: "insensitive" } }
39
+ });
40
+ },
41
+ async findByEmailOrUsernameInsensitive(identifier) {
42
+ return db.user.findFirst({
43
+ where: {
44
+ OR: [
45
+ { email: { equals: identifier, mode: "insensitive" } },
46
+ { username: { equals: identifier, mode: "insensitive" } }
47
+ ]
48
+ }
49
+ });
50
+ },
51
+ async findByEmailOrOAuthId(email, oauthId) {
52
+ return db.user.findFirst({
53
+ where: {
54
+ OR: [
55
+ { email: { equals: email, mode: "insensitive" } },
56
+ { oauthId: { equals: oauthId } }
57
+ ]
58
+ }
59
+ });
60
+ },
61
+ async findById(id) {
62
+ return db.user.findUnique({ where: { id } });
63
+ },
64
+ async findActiveById(id) {
65
+ return db.user.findUnique({
66
+ where: { id, status: "ACTIVE" }
67
+ });
68
+ },
69
+ async create(data) {
70
+ return db.user.create({ data });
71
+ },
72
+ async update(id, data) {
73
+ return db.user.update({ where: { id }, data });
74
+ }
75
+ },
76
+ session: {
77
+ async findById(id) {
78
+ const session = await db.session.findUnique({
79
+ where: { id },
80
+ select: {
81
+ id: true,
82
+ userId: true,
83
+ socketId: true,
84
+ twoFaSecret: true,
85
+ browserName: true,
86
+ issuedAt: true,
87
+ lastUsed: true,
88
+ revokedAt: true,
89
+ deviceId: true,
90
+ user: { select: { status: true, verifiedHumanAt: true } }
91
+ }
92
+ });
93
+ return session;
94
+ },
95
+ async create(data) {
96
+ return db.session.create({ data });
97
+ },
98
+ async update(id, data) {
99
+ return db.session.update({ where: { id }, data });
100
+ },
101
+ async updateLastUsed(id) {
102
+ const session = await db.session.update({
103
+ where: { id },
104
+ data: { lastUsed: /* @__PURE__ */ new Date() },
105
+ select: {
106
+ id: true,
107
+ userId: true,
108
+ socketId: true,
109
+ twoFaSecret: true,
110
+ browserName: true,
111
+ issuedAt: true,
112
+ lastUsed: true,
113
+ revokedAt: true,
114
+ deviceId: true,
115
+ user: { select: { verifiedHumanAt: true } }
116
+ }
117
+ });
118
+ return session;
119
+ },
120
+ async revoke(id) {
121
+ await db.session.update({
122
+ where: { id },
123
+ data: { revokedAt: /* @__PURE__ */ new Date() }
124
+ });
125
+ },
126
+ async findActiveByUserId(userId, excludeSessionId) {
127
+ return db.session.findMany({
128
+ where: {
129
+ userId,
130
+ revokedAt: null,
131
+ ...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
132
+ },
133
+ select: { id: true, socketId: true, userId: true }
134
+ });
135
+ },
136
+ async revokeAllByUserId(userId, excludeSessionId) {
137
+ await db.session.updateMany({
138
+ where: {
139
+ userId,
140
+ revokedAt: null,
141
+ ...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
142
+ },
143
+ data: { revokedAt: /* @__PURE__ */ new Date() }
144
+ });
145
+ },
146
+ async findTwoFaSecretsByUserId(userId) {
147
+ return db.session.findMany({
148
+ where: { userId, twoFaSecret: { not: null } },
149
+ select: { twoFaSecret: true }
150
+ });
151
+ },
152
+ async clearTwoFaSecrets(userId, excludeSessionId) {
153
+ await db.session.updateMany({
154
+ where: {
155
+ userId,
156
+ ...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
157
+ },
158
+ data: { twoFaSecret: null }
159
+ });
160
+ },
161
+ async findByIdWithDevice(id, userId) {
162
+ const session = await db.session.findUnique({
163
+ where: { id, userId },
164
+ select: {
165
+ twoFaSecret: true,
166
+ deviceId: true,
167
+ device: { select: { pushToken: true } }
168
+ }
169
+ });
170
+ return session;
171
+ },
172
+ async revokeByDevicePushToken(userId, pushToken, excludeSessionId) {
173
+ await db.session.updateMany({
174
+ where: {
175
+ userId,
176
+ id: { not: excludeSessionId },
177
+ revokedAt: null,
178
+ device: { pushToken }
179
+ },
180
+ data: { revokedAt: /* @__PURE__ */ new Date() }
181
+ });
182
+ },
183
+ async clearDeviceId(userId, deviceId) {
184
+ await db.session.updateMany({
185
+ where: { userId, deviceId },
186
+ data: { deviceId: null }
187
+ });
188
+ }
189
+ },
190
+ otp: {
191
+ async findValidByUserAndCode(userId, code) {
192
+ return db.oTP.findFirst({
193
+ where: { userId, code, expiresAt: { gte: /* @__PURE__ */ new Date() } }
194
+ });
195
+ },
196
+ async create(data) {
197
+ return db.oTP.create({ data });
198
+ },
199
+ async delete(id) {
200
+ await db.oTP.delete({ where: { id } });
201
+ }
202
+ },
203
+ passwordReset: {
204
+ async findById(id) {
205
+ return db.passwordReset.findUnique({
206
+ where: { id },
207
+ select: { id: true, createdAt: true, userId: true }
208
+ });
209
+ },
210
+ async create(userId) {
211
+ return db.passwordReset.create({
212
+ data: { userId }
213
+ });
214
+ },
215
+ async delete(id) {
216
+ await db.passwordReset.delete({ where: { id } });
217
+ },
218
+ async deleteAllByUserId(userId) {
219
+ await db.passwordReset.deleteMany({ where: { userId } });
220
+ }
221
+ },
222
+ device: {
223
+ async findByTokenSessionAndUser(pushToken, sessionId, userId) {
224
+ return db.device.findFirst({
225
+ where: {
226
+ pushToken,
227
+ sessions: { some: { id: sessionId } },
228
+ users: { some: { id: userId } }
229
+ },
230
+ select: { id: true }
231
+ });
232
+ },
233
+ async upsertByPushToken(pushToken, sessionId, userId) {
234
+ await db.device.upsert({
235
+ where: { pushToken },
236
+ create: {
237
+ pushToken,
238
+ sessions: { connect: { id: sessionId } },
239
+ users: { connect: { id: userId } }
240
+ },
241
+ update: {
242
+ sessions: { connect: { id: sessionId } },
243
+ users: { connect: { id: userId } }
244
+ }
245
+ });
246
+ },
247
+ async findByUserAndToken(userId, pushToken) {
248
+ return db.device.findFirst({
249
+ where: { users: { some: { id: userId } }, pushToken },
250
+ select: { id: true }
251
+ });
252
+ },
253
+ async disconnectUser(deviceId, userId) {
254
+ await db.device.update({
255
+ where: { id: deviceId },
256
+ data: { users: { disconnect: { id: userId } } }
257
+ });
258
+ },
259
+ async hasRemainingUsers(deviceId) {
260
+ const result = await db.device.findUnique({
261
+ where: { id: deviceId },
262
+ select: { users: { select: { id: true }, take: 1 } }
263
+ });
264
+ return (result?.users.length ?? 0) > 0;
265
+ },
266
+ async delete(id) {
267
+ await db.device.delete({ where: { id } });
268
+ }
269
+ },
270
+ admin: {
271
+ async findByUserId(userId) {
272
+ return db.admin.findFirst({
273
+ where: { userId },
274
+ select: { ip: true }
275
+ });
276
+ }
277
+ }
278
+ };
279
+ }
280
+
25
281
  // src/adapters/email.ts
26
282
  function createNoopEmailAdapter() {
27
283
  return {
@@ -75,6 +331,294 @@ function createConsoleEmailAdapter() {
75
331
  };
76
332
  }
77
333
 
334
+ // src/adapters/drizzleAdapter.ts
335
+ function createDrizzleAdapter(db, tables) {
336
+ const {
337
+ eq,
338
+ and,
339
+ or,
340
+ isNull,
341
+ isNotNull,
342
+ gte,
343
+ ne,
344
+ sql
345
+ } = __require("drizzle-orm");
346
+ const { users, sessions, otps, passwordResets, devices, admins } = tables;
347
+ return {
348
+ user: {
349
+ async findByEmailInsensitive(email) {
350
+ const rows = await db.select().from(users).where(sql`lower(${users.email}) = lower(${email})`).limit(1);
351
+ return rows[0] ?? null;
352
+ },
353
+ async findByUsernameInsensitive(username) {
354
+ const rows = await db.select().from(users).where(sql`lower(${users.username}) = lower(${username})`).limit(1);
355
+ return rows[0] ?? null;
356
+ },
357
+ async findByEmailOrUsernameInsensitive(identifier) {
358
+ const rows = await db.select().from(users).where(
359
+ or(
360
+ sql`lower(${users.email}) = lower(${identifier})`,
361
+ sql`lower(${users.username}) = lower(${identifier})`
362
+ )
363
+ ).limit(1);
364
+ return rows[0] ?? null;
365
+ },
366
+ async findByEmailOrOAuthId(email, oauthId) {
367
+ const rows = await db.select().from(users).where(
368
+ or(
369
+ sql`lower(${users.email}) = lower(${email})`,
370
+ eq(users.oauthId, oauthId)
371
+ )
372
+ ).limit(1);
373
+ return rows[0] ?? null;
374
+ },
375
+ async findById(id) {
376
+ const rows = await db.select().from(users).where(eq(users.id, id)).limit(1);
377
+ return rows[0] ?? null;
378
+ },
379
+ async findActiveById(id) {
380
+ const rows = await db.select().from(users).where(and(eq(users.id, id), eq(users.status, "ACTIVE"))).limit(1);
381
+ return rows[0] ?? null;
382
+ },
383
+ async create(data) {
384
+ const rows = await db.insert(users).values(data).returning();
385
+ return rows[0];
386
+ },
387
+ async update(id, data) {
388
+ const rows = await db.update(users).set(data).where(eq(users.id, id)).returning();
389
+ return rows[0];
390
+ }
391
+ },
392
+ session: {
393
+ async findById(id) {
394
+ const rows = await db.select({
395
+ id: sessions.id,
396
+ userId: sessions.userId,
397
+ socketId: sessions.socketId,
398
+ twoFaSecret: sessions.twoFaSecret,
399
+ browserName: sessions.browserName,
400
+ issuedAt: sessions.issuedAt,
401
+ lastUsed: sessions.lastUsed,
402
+ revokedAt: sessions.revokedAt,
403
+ deviceId: sessions.deviceId,
404
+ user: {
405
+ status: users.status,
406
+ verifiedHumanAt: users.verifiedHumanAt
407
+ }
408
+ }).from(sessions).innerJoin(users, eq(sessions.userId, users.id)).where(eq(sessions.id, id)).limit(1);
409
+ return rows[0] ?? null;
410
+ },
411
+ async create(data) {
412
+ const rows = await db.insert(sessions).values(data).returning();
413
+ return rows[0];
414
+ },
415
+ async update(id, data) {
416
+ const rows = await db.update(sessions).set(data).where(eq(sessions.id, id)).returning();
417
+ return rows[0];
418
+ },
419
+ async updateLastUsed(id) {
420
+ await db.update(sessions).set({ lastUsed: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id));
421
+ const rows = await db.select({
422
+ id: sessions.id,
423
+ userId: sessions.userId,
424
+ socketId: sessions.socketId,
425
+ twoFaSecret: sessions.twoFaSecret,
426
+ browserName: sessions.browserName,
427
+ issuedAt: sessions.issuedAt,
428
+ lastUsed: sessions.lastUsed,
429
+ revokedAt: sessions.revokedAt,
430
+ deviceId: sessions.deviceId,
431
+ user: {
432
+ verifiedHumanAt: users.verifiedHumanAt
433
+ }
434
+ }).from(sessions).innerJoin(users, eq(sessions.userId, users.id)).where(eq(sessions.id, id)).limit(1);
435
+ return rows[0];
436
+ },
437
+ async revoke(id) {
438
+ await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id));
439
+ },
440
+ async findActiveByUserId(userId, excludeSessionId) {
441
+ const conditions = [eq(sessions.userId, userId), isNull(sessions.revokedAt)];
442
+ if (excludeSessionId !== void 0) {
443
+ conditions.push(ne(sessions.id, excludeSessionId));
444
+ }
445
+ const activeRows = await db.select({
446
+ id: sessions.id,
447
+ socketId: sessions.socketId,
448
+ userId: sessions.userId
449
+ }).from(sessions).where(and(...conditions));
450
+ return activeRows;
451
+ },
452
+ async revokeAllByUserId(userId, excludeSessionId) {
453
+ const conditions = [eq(sessions.userId, userId), isNull(sessions.revokedAt)];
454
+ if (excludeSessionId !== void 0) {
455
+ conditions.push(ne(sessions.id, excludeSessionId));
456
+ }
457
+ await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(and(...conditions));
458
+ },
459
+ async findTwoFaSecretsByUserId(userId) {
460
+ const secretRows = await db.select({ twoFaSecret: sessions.twoFaSecret }).from(sessions).where(and(eq(sessions.userId, userId), isNotNull(sessions.twoFaSecret)));
461
+ return secretRows;
462
+ },
463
+ async clearTwoFaSecrets(userId, excludeSessionId) {
464
+ const conditions = [eq(sessions.userId, userId)];
465
+ if (excludeSessionId !== void 0) {
466
+ conditions.push(ne(sessions.id, excludeSessionId));
467
+ }
468
+ await db.update(sessions).set({ twoFaSecret: null }).where(and(...conditions));
469
+ },
470
+ async findByIdWithDevice(id, userId) {
471
+ const rows = await db.select({
472
+ twoFaSecret: sessions.twoFaSecret,
473
+ deviceId: sessions.deviceId,
474
+ device: {
475
+ pushToken: devices.pushToken
476
+ }
477
+ }).from(sessions).leftJoin(devices, eq(sessions.deviceId, devices.id)).where(and(eq(sessions.id, id), eq(sessions.userId, userId))).limit(1);
478
+ if (!rows[0]) return null;
479
+ const row = rows[0];
480
+ const device = row.device;
481
+ return {
482
+ twoFaSecret: row.twoFaSecret,
483
+ deviceId: row.deviceId,
484
+ device: device?.pushToken ? { pushToken: device.pushToken } : null
485
+ };
486
+ },
487
+ async revokeByDevicePushToken(userId, pushToken, excludeSessionId) {
488
+ const deviceRows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
489
+ if (!deviceRows[0]) return;
490
+ await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(
491
+ and(
492
+ eq(sessions.userId, userId),
493
+ ne(sessions.id, excludeSessionId),
494
+ isNull(sessions.revokedAt),
495
+ eq(sessions.deviceId, deviceRows[0].id)
496
+ )
497
+ );
498
+ },
499
+ async clearDeviceId(userId, deviceId) {
500
+ await db.update(sessions).set({ deviceId: null }).where(and(eq(sessions.userId, userId), eq(sessions.deviceId, deviceId)));
501
+ }
502
+ },
503
+ otp: {
504
+ async findValidByUserAndCode(userId, code) {
505
+ const rows = await db.select().from(otps).where(
506
+ and(eq(otps.userId, userId), eq(otps.code, code), gte(otps.expiresAt, /* @__PURE__ */ new Date()))
507
+ ).limit(1);
508
+ return rows[0] ?? null;
509
+ },
510
+ async create(data) {
511
+ const rows = await db.insert(otps).values(data).returning();
512
+ return rows[0];
513
+ },
514
+ async delete(id) {
515
+ await db.delete(otps).where(eq(otps.id, id));
516
+ }
517
+ },
518
+ passwordReset: {
519
+ async findById(id) {
520
+ const rows = await db.select({
521
+ id: passwordResets.id,
522
+ createdAt: passwordResets.createdAt,
523
+ userId: passwordResets.userId
524
+ }).from(passwordResets).where(eq(passwordResets.id, id)).limit(1);
525
+ return rows[0] ?? null;
526
+ },
527
+ async create(userId) {
528
+ const rows = await db.insert(passwordResets).values({ userId }).returning();
529
+ return rows[0];
530
+ },
531
+ async delete(id) {
532
+ await db.delete(passwordResets).where(eq(passwordResets.id, id));
533
+ },
534
+ async deleteAllByUserId(userId) {
535
+ await db.delete(passwordResets).where(eq(passwordResets.userId, userId));
536
+ }
537
+ },
538
+ device: {
539
+ async findByTokenSessionAndUser(pushToken, sessionId, userId) {
540
+ const rows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
541
+ if (!rows[0]) return null;
542
+ if (tables.devicesToSessions && tables.devicesToUsers) {
543
+ const sessionLink = await db.select().from(tables.devicesToSessions).where(
544
+ and(
545
+ eq(tables.devicesToSessions.deviceId, rows[0].id),
546
+ eq(tables.devicesToSessions.sessionId, sessionId)
547
+ )
548
+ ).limit(1);
549
+ const userLink = await db.select().from(tables.devicesToUsers).where(
550
+ and(
551
+ eq(tables.devicesToUsers.deviceId, rows[0].id),
552
+ eq(tables.devicesToUsers.userId, userId)
553
+ )
554
+ ).limit(1);
555
+ if (!sessionLink[0] || !userLink[0]) return null;
556
+ }
557
+ return { id: rows[0].id };
558
+ },
559
+ async upsertByPushToken(pushToken, sessionId, userId) {
560
+ const existing = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
561
+ let deviceId;
562
+ if (existing[0]) {
563
+ deviceId = existing[0].id;
564
+ } else {
565
+ const insertedRows = await db.insert(devices).values({ pushToken }).returning({ id: devices.id });
566
+ deviceId = insertedRows[0].id;
567
+ }
568
+ if (tables.devicesToSessions) {
569
+ await db.insert(tables.devicesToSessions).values({ deviceId, sessionId }).onConflictDoNothing();
570
+ }
571
+ if (tables.devicesToUsers) {
572
+ await db.insert(tables.devicesToUsers).values({ deviceId, userId }).onConflictDoNothing();
573
+ }
574
+ await db.update(sessions).set({ deviceId }).where(eq(sessions.id, sessionId));
575
+ },
576
+ async findByUserAndToken(userId, pushToken) {
577
+ if (tables.devicesToUsers) {
578
+ const joinRows = await db.select({ id: devices.id }).from(devices).innerJoin(
579
+ tables.devicesToUsers,
580
+ eq(devices.id, tables.devicesToUsers.deviceId)
581
+ ).where(
582
+ and(
583
+ eq(devices.pushToken, pushToken),
584
+ eq(tables.devicesToUsers.userId, userId)
585
+ )
586
+ ).limit(1);
587
+ return joinRows[0] ? { id: joinRows[0].id } : null;
588
+ }
589
+ const rows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
590
+ return rows[0] ? { id: rows[0].id } : null;
591
+ },
592
+ async disconnectUser(deviceId, userId) {
593
+ if (tables.devicesToUsers) {
594
+ await db.delete(tables.devicesToUsers).where(
595
+ and(
596
+ eq(tables.devicesToUsers.deviceId, deviceId),
597
+ eq(tables.devicesToUsers.userId, userId)
598
+ )
599
+ );
600
+ }
601
+ },
602
+ async hasRemainingUsers(deviceId) {
603
+ if (tables.devicesToUsers) {
604
+ const remainingRows = await db.select({ userId: tables.devicesToUsers.userId }).from(tables.devicesToUsers).where(eq(tables.devicesToUsers.deviceId, deviceId)).limit(1);
605
+ return remainingRows.length > 0;
606
+ }
607
+ return false;
608
+ },
609
+ async delete(id) {
610
+ await db.delete(devices).where(eq(devices.id, id));
611
+ }
612
+ },
613
+ admin: {
614
+ async findByUserId(userId) {
615
+ const rows = await db.select({ ip: admins.ip }).from(admins).where(eq(admins.userId, userId)).limit(1);
616
+ return rows[0] ?? null;
617
+ }
618
+ }
619
+ };
620
+ }
621
+
78
622
  // src/utilities/config.ts
79
623
  var defaultTokenSettings = {
80
624
  jwtExpiry: 30 * 24 * 60 * 60,
@@ -105,9 +649,16 @@ var defaultFeatures = {
105
649
  otpLogin: true
106
650
  };
107
651
  function createAuthConfig(config) {
652
+ if (!config.database && !config.prisma) {
653
+ throw new Error(
654
+ "@factiii/auth: Provide either a `database` adapter or a `prisma` client in config."
655
+ );
656
+ }
657
+ const database = config.database ?? createPrismaAdapter(config.prisma);
108
658
  const emailService = config.emailService ?? createNoopEmailAdapter();
109
659
  return {
110
660
  ...config,
661
+ database,
111
662
  features: { ...defaultFeatures, ...config.features },
112
663
  tokenSettings: { ...defaultTokenSettings, ...config.tokenSettings },
113
664
  cookieSettings: { ...defaultCookieSettings, ...config.cookieSettings },
@@ -225,6 +776,7 @@ function isTokenInvalidError(error) {
225
776
  function createAuthGuard(config, t) {
226
777
  const storageKeys = config.storageKeys ?? defaultStorageKeys;
227
778
  const cookieSettings = { ...defaultCookieSettings, ...config.cookieSettings };
779
+ const database = config.database ?? createPrismaAdapter(config.prisma);
228
780
  const revokeSession = async (ctx, sessionId, description, errorStack, path) => {
229
781
  clearAuthCookie(ctx.res, cookieSettings, storageKeys);
230
782
  if (config.hooks?.logError) {
@@ -261,15 +813,9 @@ ${errorStack}` : null,
261
813
  }
262
814
  if (sessionId) {
263
815
  try {
264
- await config.prisma.session.update({
265
- where: { id: sessionId },
266
- data: { revokedAt: /* @__PURE__ */ new Date() }
267
- });
816
+ await database.session.revoke(sessionId);
268
817
  if (config.hooks?.onSessionRevoked) {
269
- const session = await config.prisma.session.findUnique({
270
- where: { id: sessionId },
271
- select: { id: true, userId: true, socketId: true }
272
- });
818
+ const session = await database.session.findById(sessionId);
273
819
  if (session) {
274
820
  await config.hooks.onSessionRevoked(session.userId, session.socketId, description);
275
821
  }
@@ -294,23 +840,7 @@ ${errorStack}` : null,
294
840
  secret: config.secrets.jwt,
295
841
  ignoreExpiration: meta?.ignoreExpiration ?? false
296
842
  });
297
- const session = await config.prisma.session.findUnique({
298
- where: {
299
- id: decodedToken.id
300
- },
301
- select: {
302
- userId: true,
303
- user: {
304
- select: {
305
- status: true,
306
- verifiedHumanAt: true
307
- }
308
- },
309
- revokedAt: true,
310
- socketId: true,
311
- id: true
312
- }
313
- });
843
+ const session = await database.session.findById(decodedToken.id);
314
844
  if (!session) {
315
845
  await revokeSession(
316
846
  ctx,
@@ -364,10 +894,7 @@ ${errorStack}` : null,
364
894
  });
365
895
  }
366
896
  if (meta?.adminRequired) {
367
- const admin = await config.prisma.admin.findFirst({
368
- where: { userId: session.userId },
369
- select: { ip: true }
370
- });
897
+ const admin = await database.admin.findByUserId(session.userId);
371
898
  if (!admin || admin.ip !== ctx.ip) {
372
899
  await revokeSession(
373
900
  ctx,
@@ -557,6 +1084,36 @@ function validatePasswordStrength(password, minLength = 6) {
557
1084
  return { valid: true };
558
1085
  }
559
1086
 
1087
+ // src/utilities/session.ts
1088
+ async function createSessionWithToken(config, params) {
1089
+ const { userId, browserName, socketId, deviceId, extraSessionData } = params;
1090
+ const session = await config.database.session.create({
1091
+ userId,
1092
+ browserName,
1093
+ socketId,
1094
+ ...deviceId != null ? { deviceId } : {},
1095
+ ...extraSessionData
1096
+ });
1097
+ const user = await config.database.user.findById(userId);
1098
+ const accessToken = createAuthToken(
1099
+ {
1100
+ id: session.id,
1101
+ userId: session.userId,
1102
+ verifiedHumanAt: user?.verifiedHumanAt ?? null
1103
+ },
1104
+ {
1105
+ secret: config.secrets.jwt,
1106
+ expiresIn: config.tokenSettings.jwtExpiry
1107
+ }
1108
+ );
1109
+ return { accessToken, sessionId: session.id };
1110
+ }
1111
+ async function createSessionWithTokenAndCookie(config, params, res) {
1112
+ const result = await createSessionWithToken(config, params);
1113
+ setAuthCookie(res, result.accessToken, config.cookieSettings, config.storageKeys);
1114
+ return result;
1115
+ }
1116
+
560
1117
  // src/utilities/totp.ts
561
1118
  import crypto from "crypto";
562
1119
  import { TOTP } from "totp-generator";
@@ -622,19 +1179,14 @@ var BaseProcedureFactory = class {
622
1179
  if (this.config.hooks?.beforeRegister) {
623
1180
  await this.config.hooks.beforeRegister(typedInput);
624
1181
  }
625
- const usernameCheck = await this.config.prisma.user.findFirst({
626
- where: { username: { equals: username, mode: "insensitive" } }
627
- });
1182
+ const usernameCheck = await this.config.database.user.findByUsernameInsensitive(username);
628
1183
  if (usernameCheck) {
629
1184
  throw new TRPCError2({
630
1185
  code: "CONFLICT",
631
1186
  message: "An account already exists with that username."
632
1187
  });
633
1188
  }
634
- const emailCheck = await this.config.prisma.user.findFirst({
635
- where: { email: { equals: email, mode: "insensitive" } },
636
- select: { id: true }
637
- });
1189
+ const emailCheck = await this.config.database.user.findByEmailInsensitive(email);
638
1190
  if (emailCheck) {
639
1191
  throw new TRPCError2({
640
1192
  code: "CONFLICT",
@@ -642,30 +1194,25 @@ var BaseProcedureFactory = class {
642
1194
  });
643
1195
  }
644
1196
  const hashedPassword = await hashPassword(password);
645
- const user = await this.config.prisma.user.create({
646
- data: {
647
- username,
648
- email,
649
- password: hashedPassword,
650
- status: "ACTIVE",
651
- tag: this.config.features.biometric ? "BOT" : "HUMAN",
652
- twoFaEnabled: false,
653
- emailVerificationStatus: "UNVERIFIED",
654
- verifiedHumanAt: null
655
- }
1197
+ const user = await this.config.database.user.create({
1198
+ username,
1199
+ email,
1200
+ password: hashedPassword,
1201
+ status: "ACTIVE",
1202
+ tag: this.config.features.biometric ? "BOT" : "HUMAN",
1203
+ twoFaEnabled: false,
1204
+ emailVerificationStatus: "UNVERIFIED",
1205
+ verifiedHumanAt: null
656
1206
  });
657
1207
  if (this.config.hooks?.onUserCreated) {
658
1208
  await this.config.hooks.onUserCreated(user.id, typedInput);
659
1209
  }
660
1210
  const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
661
- const session = await this.config.prisma.session.create({
662
- data: {
663
- userId: user.id,
664
- browserName: detectBrowser(userAgent),
665
- socketId: null,
666
- ...extraSessionData
667
- },
668
- select: { id: true, userId: true }
1211
+ const session = await this.config.database.session.create({
1212
+ userId: user.id,
1213
+ browserName: detectBrowser(userAgent),
1214
+ socketId: null,
1215
+ ...extraSessionData
669
1216
  });
670
1217
  if (this.config.hooks?.onSessionCreated) {
671
1218
  await this.config.hooks.onSessionCreated(session.id, typedInput);
@@ -703,24 +1250,7 @@ var BaseProcedureFactory = class {
703
1250
  if (this.config.hooks?.beforeLogin) {
704
1251
  await this.config.hooks.beforeLogin(typedInput);
705
1252
  }
706
- const user = await this.config.prisma.user.findFirst({
707
- where: {
708
- OR: [
709
- { email: { equals: username, mode: "insensitive" } },
710
- { username: { equals: username, mode: "insensitive" } }
711
- ]
712
- },
713
- select: {
714
- id: true,
715
- status: true,
716
- password: true,
717
- twoFaEnabled: true,
718
- email: true,
719
- username: true,
720
- oauthProvider: true,
721
- verifiedHumanAt: true
722
- }
723
- });
1253
+ const user = await this.config.database.user.findByEmailOrUsernameInsensitive(username);
724
1254
  if (!user) {
725
1255
  throw new TRPCError2({
726
1256
  code: "FORBIDDEN",
@@ -761,10 +1291,7 @@ var BaseProcedureFactory = class {
761
1291
  };
762
1292
  }
763
1293
  let validCode = false;
764
- const secrets = await this.config.prisma.session.findMany({
765
- where: { userId: user.id, twoFaSecret: { not: null } },
766
- select: { twoFaSecret: true }
767
- });
1294
+ const secrets = await this.config.database.session.findTwoFaSecretsByUserId(user.id);
768
1295
  for (const s of secrets) {
769
1296
  if (s.twoFaSecret && await verifyTotp(code, cleanBase32String(s.twoFaSecret))) {
770
1297
  validCode = true;
@@ -772,14 +1299,13 @@ var BaseProcedureFactory = class {
772
1299
  }
773
1300
  }
774
1301
  if (!validCode) {
775
- const checkOTP = await this.config.prisma.oTP.findFirst({
776
- where: { userId: user.id, code: Number(code), expiresAt: { gte: /* @__PURE__ */ new Date() } }
777
- });
1302
+ const checkOTP = await this.config.database.otp.findValidByUserAndCode(
1303
+ user.id,
1304
+ Number(code)
1305
+ );
778
1306
  if (checkOTP) {
779
1307
  validCode = true;
780
- await this.config.prisma.oTP.delete({
781
- where: { id: checkOTP.id }
782
- });
1308
+ await this.config.database.otp.delete(checkOTP.id);
783
1309
  }
784
1310
  }
785
1311
  if (!validCode) {
@@ -790,24 +1316,11 @@ var BaseProcedureFactory = class {
790
1316
  }
791
1317
  }
792
1318
  const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
793
- const session = await this.config.prisma.session.create({
794
- data: {
795
- userId: user.id,
796
- browserName: detectBrowser(userAgent),
797
- socketId: null,
798
- ...extraSessionData
799
- },
800
- select: {
801
- id: true,
802
- userId: true,
803
- socketId: true,
804
- browserName: true,
805
- issuedAt: true,
806
- lastUsed: true,
807
- revokedAt: true,
808
- deviceId: true,
809
- twoFaSecret: true
810
- }
1319
+ const session = await this.config.database.session.create({
1320
+ userId: user.id,
1321
+ browserName: detectBrowser(userAgent),
1322
+ socketId: null,
1323
+ ...extraSessionData
811
1324
  });
812
1325
  if (this.config.hooks?.onUserLogin) {
813
1326
  await this.config.hooks.onUserLogin(user.id, session.id);
@@ -838,15 +1351,9 @@ var BaseProcedureFactory = class {
838
1351
  return this.authProcedure.meta({ ignoreExpiration: true }).mutation(async ({ ctx }) => {
839
1352
  const { userId, sessionId } = ctx;
840
1353
  if (sessionId) {
841
- await this.config.prisma.session.update({
842
- where: { id: sessionId },
843
- data: { revokedAt: /* @__PURE__ */ new Date() }
844
- });
1354
+ await this.config.database.session.revoke(sessionId);
845
1355
  if (userId) {
846
- await this.config.prisma.user.update({
847
- where: { id: userId },
848
- data: { isActive: false }
849
- });
1356
+ await this.config.database.user.update(userId, { isActive: false });
850
1357
  }
851
1358
  if (this.config.hooks?.afterLogout) {
852
1359
  await this.config.hooks.afterLogout(userId, sessionId, ctx.socketId);
@@ -858,15 +1365,7 @@ var BaseProcedureFactory = class {
858
1365
  }
859
1366
  refresh() {
860
1367
  return this.authProcedure.query(async ({ ctx }) => {
861
- const session = await this.config.prisma.session.update({
862
- where: { id: ctx.sessionId },
863
- data: { lastUsed: /* @__PURE__ */ new Date() },
864
- select: {
865
- id: true,
866
- userId: true,
867
- user: { select: { verifiedHumanAt: true } }
868
- }
869
- });
1368
+ const session = await this.config.database.session.updateLastUsed(ctx.sessionId);
870
1369
  if (this.config.hooks?.onRefresh) {
871
1370
  this.config.hooks.onRefresh(session.userId).catch(() => {
872
1371
  });
@@ -891,22 +1390,14 @@ var BaseProcedureFactory = class {
891
1390
  return this.authProcedure.input(endAllSessionsSchema).mutation(async ({ ctx, input }) => {
892
1391
  const { skipCurrentSession } = input;
893
1392
  const { userId, sessionId } = ctx;
894
- const sessionsToRevoke = await this.config.prisma.session.findMany({
895
- where: {
896
- userId,
897
- revokedAt: null,
898
- ...skipCurrentSession ? { NOT: { id: sessionId } } : {}
899
- },
900
- select: { socketId: true, id: true, userId: true }
901
- });
902
- await this.config.prisma.session.updateMany({
903
- where: {
904
- userId,
905
- revokedAt: null,
906
- ...skipCurrentSession ? { NOT: { id: sessionId } } : {}
907
- },
908
- data: { revokedAt: /* @__PURE__ */ new Date() }
909
- });
1393
+ const sessionsToRevoke = await this.config.database.session.findActiveByUserId(
1394
+ userId,
1395
+ skipCurrentSession ? sessionId : void 0
1396
+ );
1397
+ await this.config.database.session.revokeAllByUserId(
1398
+ userId,
1399
+ skipCurrentSession ? sessionId : void 0
1400
+ );
910
1401
  for (const session of sessionsToRevoke) {
911
1402
  if (this.config.hooks?.onSessionRevoked) {
912
1403
  await this.config.hooks.onSessionRevoked(
@@ -917,10 +1408,7 @@ var BaseProcedureFactory = class {
917
1408
  }
918
1409
  }
919
1410
  if (!skipCurrentSession) {
920
- await this.config.prisma.user.update({
921
- where: { id: userId },
922
- data: { isActive: false }
923
- });
1411
+ await this.config.database.user.update(userId, { isActive: false });
924
1412
  }
925
1413
  return { success: true, revokedCount: sessionsToRevoke.length };
926
1414
  });
@@ -935,10 +1423,7 @@ var BaseProcedureFactory = class {
935
1423
  message: "New password cannot be the same as current password"
936
1424
  });
937
1425
  }
938
- const user = await this.config.prisma.user.findUnique({
939
- where: { id: userId },
940
- select: { password: true }
941
- });
1426
+ const user = await this.config.database.user.findById(userId);
942
1427
  if (!user) {
943
1428
  throw new TRPCError2({ code: "NOT_FOUND", message: "User not found" });
944
1429
  }
@@ -956,14 +1441,8 @@ var BaseProcedureFactory = class {
956
1441
  });
957
1442
  }
958
1443
  const hashedPassword = await hashPassword(newPassword);
959
- await this.config.prisma.user.update({
960
- where: { id: userId },
961
- data: { password: hashedPassword }
962
- });
963
- await this.config.prisma.session.updateMany({
964
- where: { userId, revokedAt: null, NOT: { id: sessionId } },
965
- data: { revokedAt: /* @__PURE__ */ new Date() }
966
- });
1444
+ await this.config.database.user.update(userId, { password: hashedPassword });
1445
+ await this.config.database.session.revokeAllByUserId(userId, sessionId);
967
1446
  if (this.config.hooks?.onPasswordChanged) {
968
1447
  await this.config.hooks.onPasswordChanged(userId);
969
1448
  }
@@ -976,11 +1455,8 @@ var BaseProcedureFactory = class {
976
1455
  sendPasswordResetEmail() {
977
1456
  return this.procedure.input(requestPasswordResetSchema).mutation(async ({ input }) => {
978
1457
  const { email } = input;
979
- const user = await this.config.prisma.user.findFirst({
980
- where: { email: { equals: email, mode: "insensitive" }, status: "ACTIVE" },
981
- select: { id: true, password: true, email: true }
982
- });
983
- if (!user) {
1458
+ const user = await this.config.database.user.findByEmailInsensitive(email);
1459
+ if (!user || user.status !== "ACTIVE") {
984
1460
  return { message: "If an account exists with that email, a reset link has been sent." };
985
1461
  }
986
1462
  if (!user.password) {
@@ -989,10 +1465,8 @@ var BaseProcedureFactory = class {
989
1465
  message: "This account uses social login. Please use that method."
990
1466
  });
991
1467
  }
992
- await this.config.prisma.passwordReset.deleteMany({ where: { userId: user.id } });
993
- const passwordReset = await this.config.prisma.passwordReset.create({
994
- data: { userId: user.id }
995
- });
1468
+ await this.config.database.passwordReset.deleteAllByUserId(user.id);
1469
+ const passwordReset = await this.config.database.passwordReset.create(user.id);
996
1470
  if (this.config.emailService) {
997
1471
  await this.config.emailService.sendPasswordResetEmail(user.email, String(passwordReset.id));
998
1472
  }
@@ -1002,15 +1476,12 @@ var BaseProcedureFactory = class {
1002
1476
  checkPasswordReset() {
1003
1477
  return this.procedure.input(checkPasswordResetSchema).query(async ({ input }) => {
1004
1478
  const { token } = input;
1005
- const passwordReset = await this.config.prisma.passwordReset.findUnique({
1006
- where: { id: token },
1007
- select: { id: true, createdAt: true, userId: true }
1008
- });
1479
+ const passwordReset = await this.config.database.passwordReset.findById(token);
1009
1480
  if (!passwordReset) {
1010
1481
  throw new TRPCError2({ code: "NOT_FOUND", message: "Invalid reset token." });
1011
1482
  }
1012
1483
  if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
1013
- await this.config.prisma.passwordReset.delete({ where: { id: token } });
1484
+ await this.config.database.passwordReset.delete(token);
1014
1485
  throw new TRPCError2({ code: "FORBIDDEN", message: "Reset token expired." });
1015
1486
  }
1016
1487
  return { valid: true };
@@ -1019,31 +1490,23 @@ var BaseProcedureFactory = class {
1019
1490
  resetPassword() {
1020
1491
  return this.procedure.input(resetPasswordSchema).mutation(async ({ input }) => {
1021
1492
  const { token, password } = input;
1022
- const passwordReset = await this.config.prisma.passwordReset.findFirst({
1023
- where: { id: token },
1024
- select: { id: true, createdAt: true, userId: true }
1025
- });
1493
+ const passwordReset = await this.config.database.passwordReset.findById(token);
1026
1494
  if (!passwordReset) {
1027
1495
  throw new TRPCError2({ code: "NOT_FOUND", message: "Invalid reset token." });
1028
1496
  }
1029
1497
  if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
1030
- await this.config.prisma.passwordReset.delete({ where: { id: token } });
1498
+ await this.config.database.passwordReset.delete(token);
1031
1499
  throw new TRPCError2({ code: "FORBIDDEN", message: "Reset token expired." });
1032
1500
  }
1033
1501
  const hashedPassword = await hashPassword(password);
1034
- await this.config.prisma.user.update({
1035
- where: { id: passwordReset.userId },
1036
- data: { password: hashedPassword }
1037
- });
1038
- await this.config.prisma.passwordReset.delete({ where: { id: token } });
1039
- const sessionsToRevoke = await this.config.prisma.session.findMany({
1040
- where: { userId: passwordReset.userId, revokedAt: null },
1041
- select: { id: true, socketId: true, userId: true }
1042
- });
1043
- await this.config.prisma.session.updateMany({
1044
- where: { userId: passwordReset.userId, revokedAt: null },
1045
- data: { revokedAt: /* @__PURE__ */ new Date() }
1502
+ await this.config.database.user.update(passwordReset.userId, {
1503
+ password: hashedPassword
1046
1504
  });
1505
+ await this.config.database.passwordReset.delete(token);
1506
+ const sessionsToRevoke = await this.config.database.session.findActiveByUserId(
1507
+ passwordReset.userId
1508
+ );
1509
+ await this.config.database.session.revokeAllByUserId(passwordReset.userId);
1047
1510
  for (const session of sessionsToRevoke) {
1048
1511
  if (this.config.hooks?.onSessionRevoked) {
1049
1512
  await this.config.hooks.onSessionRevoked(
@@ -1080,9 +1543,9 @@ var BiometricProcedureFactory = class {
1080
1543
  return this.authProcedure.input(biometricVerifySchema).mutation(async ({ ctx }) => {
1081
1544
  this.checkConfig();
1082
1545
  const { userId } = ctx;
1083
- await this.config.prisma.user.update({
1084
- where: { id: userId },
1085
- data: { verifiedHumanAt: /* @__PURE__ */ new Date(), tag: "HUMAN" }
1546
+ await this.config.database.user.update(userId, {
1547
+ verifiedHumanAt: /* @__PURE__ */ new Date(),
1548
+ tag: "HUMAN"
1086
1549
  });
1087
1550
  if (this.config.hooks?.onBiometricVerified) {
1088
1551
  await this.config.hooks.onBiometricVerified(userId);
@@ -1094,10 +1557,7 @@ var BiometricProcedureFactory = class {
1094
1557
  return this.authProcedure.query(async ({ ctx }) => {
1095
1558
  this.checkConfig();
1096
1559
  const { userId } = ctx;
1097
- const user = await this.config.prisma.user.findUnique({
1098
- where: { id: userId },
1099
- select: { verifiedHumanAt: true }
1100
- });
1560
+ const user = await this.config.database.user.findById(userId);
1101
1561
  if (!user) {
1102
1562
  throw new TRPCError3({ code: "NOT_FOUND", message: "User not found" });
1103
1563
  }
@@ -1144,10 +1604,7 @@ var EmailVerificationProcedureFactory = class {
1144
1604
  return this.authProcedure.mutation(async ({ ctx }) => {
1145
1605
  this.checkConfig();
1146
1606
  const { userId } = ctx;
1147
- const user = await this.config.prisma.user.findUnique({
1148
- where: { id: userId, status: "ACTIVE" },
1149
- select: { id: true, email: true, emailVerificationStatus: true }
1150
- });
1607
+ const user = await this.config.database.user.findActiveById(userId);
1151
1608
  if (!user) {
1152
1609
  throw new TRPCError4({ code: "NOT_FOUND", message: "User not found" });
1153
1610
  }
@@ -1155,9 +1612,9 @@ var EmailVerificationProcedureFactory = class {
1155
1612
  return { message: "Email is already verified", emailSent: false };
1156
1613
  }
1157
1614
  const otp = randomUUID();
1158
- await this.config.prisma.user.update({
1159
- where: { id: userId },
1160
- data: { emailVerificationStatus: "PENDING", otpForEmailVerification: otp }
1615
+ await this.config.database.user.update(userId, {
1616
+ emailVerificationStatus: "PENDING",
1617
+ otpForEmailVerification: otp
1161
1618
  });
1162
1619
  if (this.config.emailService) {
1163
1620
  try {
@@ -1175,10 +1632,7 @@ var EmailVerificationProcedureFactory = class {
1175
1632
  this.checkConfig();
1176
1633
  const { userId } = ctx;
1177
1634
  const { code } = input;
1178
- const user = await this.config.prisma.user.findUnique({
1179
- where: { id: userId, status: "ACTIVE" },
1180
- select: { id: true, emailVerificationStatus: true, otpForEmailVerification: true }
1181
- });
1635
+ const user = await this.config.database.user.findActiveById(userId);
1182
1636
  if (!user) {
1183
1637
  throw new TRPCError4({ code: "NOT_FOUND", message: "User not found" });
1184
1638
  }
@@ -1188,9 +1642,9 @@ var EmailVerificationProcedureFactory = class {
1188
1642
  if (code !== user.otpForEmailVerification) {
1189
1643
  throw new TRPCError4({ code: "BAD_REQUEST", message: "Invalid verification code" });
1190
1644
  }
1191
- await this.config.prisma.user.update({
1192
- where: { id: userId },
1193
- data: { emailVerificationStatus: "VERIFIED", otpForEmailVerification: null }
1645
+ await this.config.database.user.update(userId, {
1646
+ emailVerificationStatus: "VERIFIED",
1647
+ otpForEmailVerification: null
1194
1648
  });
1195
1649
  if (this.config.hooks?.onEmailVerified) {
1196
1650
  await this.config.hooks.onEmailVerified(userId);
@@ -1202,10 +1656,7 @@ var EmailVerificationProcedureFactory = class {
1202
1656
  return this.authProcedure.query(async ({ ctx }) => {
1203
1657
  this.checkConfig();
1204
1658
  const { userId } = ctx;
1205
- const user = await this.config.prisma.user.findUnique({
1206
- where: { id: userId },
1207
- select: { emailVerificationStatus: true, email: true }
1208
- });
1659
+ const user = await this.config.database.user.findById(userId);
1209
1660
  if (!user) {
1210
1661
  throw new TRPCError4({ code: "NOT_FOUND", message: "User not found" });
1211
1662
  }
@@ -1259,23 +1710,7 @@ var OAuthLoginProcedureFactory = class {
1259
1710
  message: "Email not provided by OAuth provider"
1260
1711
  });
1261
1712
  }
1262
- let user = await this.config.prisma.user.findFirst({
1263
- where: {
1264
- OR: [{ email: { equals: email, mode: "insensitive" } }, { oauthId: { equals: oauthId } }]
1265
- },
1266
- select: {
1267
- id: true,
1268
- status: true,
1269
- email: true,
1270
- username: true,
1271
- password: true,
1272
- oauthProvider: true,
1273
- oauthId: true,
1274
- twoFaEnabled: true,
1275
- verifiedHumanAt: true,
1276
- emailVerificationStatus: true
1277
- }
1278
- });
1713
+ let user = await this.config.database.user.findByEmailOrOAuthId(email, oauthId);
1279
1714
  if (user?.oauthProvider && user.oauthProvider !== provider) {
1280
1715
  throw new TRPCError5({
1281
1716
  code: "BAD_REQUEST",
@@ -1290,19 +1725,17 @@ var OAuthLoginProcedureFactory = class {
1290
1725
  }
1291
1726
  if (!user) {
1292
1727
  const generateUsername = this.config.generateUsername ?? (() => `user_${Date.now()}`);
1293
- user = await this.config.prisma.user.create({
1294
- data: {
1295
- username: generateUsername(),
1296
- email,
1297
- password: null,
1298
- emailVerificationStatus: "VERIFIED",
1299
- oauthProvider: provider,
1300
- oauthId,
1301
- status: "ACTIVE",
1302
- tag: this.config.features.biometric ? "BOT" : "HUMAN",
1303
- twoFaEnabled: false,
1304
- verifiedHumanAt: null
1305
- }
1728
+ user = await this.config.database.user.create({
1729
+ username: generateUsername(),
1730
+ email,
1731
+ password: null,
1732
+ emailVerificationStatus: "VERIFIED",
1733
+ oauthProvider: provider,
1734
+ oauthId,
1735
+ status: "ACTIVE",
1736
+ tag: this.config.features.biometric ? "BOT" : "HUMAN",
1737
+ twoFaEnabled: false,
1738
+ verifiedHumanAt: null
1306
1739
  });
1307
1740
  if (this.config.hooks?.onUserCreated) {
1308
1741
  await this.config.hooks.onUserCreated(user.id, typedInput);
@@ -1318,24 +1751,11 @@ var OAuthLoginProcedureFactory = class {
1318
1751
  throw new TRPCError5({ code: "FORBIDDEN", message: "Your account has been banned." });
1319
1752
  }
1320
1753
  const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
1321
- const session = await this.config.prisma.session.create({
1322
- data: {
1323
- userId: user.id,
1324
- browserName: detectBrowser(userAgent),
1325
- socketId: null,
1326
- ...extraSessionData
1327
- },
1328
- select: {
1329
- id: true,
1330
- userId: true,
1331
- socketId: true,
1332
- browserName: true,
1333
- issuedAt: true,
1334
- lastUsed: true,
1335
- revokedAt: true,
1336
- deviceId: true,
1337
- twoFaSecret: true
1338
- }
1754
+ const session = await this.config.database.session.create({
1755
+ userId: user.id,
1756
+ browserName: detectBrowser(userAgent),
1757
+ socketId: null,
1758
+ ...extraSessionData
1339
1759
  });
1340
1760
  if (this.config.hooks?.onUserLogin) {
1341
1761
  await this.config.hooks.onUserLogin(user.id, session.id);
@@ -1392,10 +1812,7 @@ var TwoFaProcedureFactory = class {
1392
1812
  return this.authProcedure.mutation(async ({ ctx }) => {
1393
1813
  this.checkConfig();
1394
1814
  const { userId, sessionId } = ctx;
1395
- const user = await this.config.prisma.user.findFirst({
1396
- where: { id: userId },
1397
- select: { twoFaEnabled: true, oauthProvider: true, password: true }
1398
- });
1815
+ const user = await this.config.database.user.findById(userId);
1399
1816
  if (!user) {
1400
1817
  throw new TRPCError6({ code: "NOT_FOUND", message: "User not found." });
1401
1818
  }
@@ -1409,10 +1826,7 @@ var TwoFaProcedureFactory = class {
1409
1826
  throw new TRPCError6({ code: "BAD_REQUEST", message: "2FA already enabled." });
1410
1827
  }
1411
1828
  if (this.config.features.twoFaRequiresDevice !== false) {
1412
- const checkSession = await this.config.prisma.session.findFirst({
1413
- where: { userId, id: sessionId },
1414
- select: { deviceId: true }
1415
- });
1829
+ const checkSession = await this.config.database.session.findById(sessionId);
1416
1830
  if (!checkSession?.deviceId) {
1417
1831
  throw new TRPCError6({
1418
1832
  code: "BAD_REQUEST",
@@ -1420,23 +1834,11 @@ var TwoFaProcedureFactory = class {
1420
1834
  });
1421
1835
  }
1422
1836
  }
1423
- await this.config.prisma.session.updateMany({
1424
- where: { userId, revokedAt: null, NOT: { id: sessionId } },
1425
- data: { revokedAt: /* @__PURE__ */ new Date() }
1426
- });
1427
- await this.config.prisma.session.updateMany({
1428
- where: { userId, NOT: { id: sessionId } },
1429
- data: { twoFaSecret: null }
1430
- });
1837
+ await this.config.database.session.revokeAllByUserId(userId, sessionId);
1838
+ await this.config.database.session.clearTwoFaSecrets(userId, sessionId);
1431
1839
  const secret = generateTotpSecret();
1432
- await this.config.prisma.user.update({
1433
- where: { id: userId },
1434
- data: { twoFaEnabled: true }
1435
- });
1436
- await this.config.prisma.session.update({
1437
- where: { id: sessionId },
1438
- data: { twoFaSecret: secret }
1439
- });
1840
+ await this.config.database.user.update(userId, { twoFaEnabled: true });
1841
+ await this.config.database.session.update(sessionId, { twoFaSecret: secret });
1440
1842
  if (this.config.hooks?.onTwoFaStatusChanged) {
1441
1843
  await this.config.hooks.onTwoFaStatusChanged(userId, true);
1442
1844
  }
@@ -1448,10 +1850,7 @@ var TwoFaProcedureFactory = class {
1448
1850
  this.checkConfig();
1449
1851
  const { userId, sessionId } = ctx;
1450
1852
  const { password } = input;
1451
- const user = await this.config.prisma.user.findFirst({
1452
- where: { id: userId },
1453
- select: { password: true, status: true, oauthProvider: true }
1454
- });
1853
+ const user = await this.config.database.user.findById(userId);
1455
1854
  if (!user) {
1456
1855
  throw new TRPCError6({ code: "NOT_FOUND", message: "User not found." });
1457
1856
  }
@@ -1474,14 +1873,8 @@ var TwoFaProcedureFactory = class {
1474
1873
  if (!isMatch) {
1475
1874
  throw new TRPCError6({ code: "FORBIDDEN", message: "Incorrect password." });
1476
1875
  }
1477
- await this.config.prisma.user.update({
1478
- where: { id: userId },
1479
- data: { twoFaEnabled: false }
1480
- });
1481
- await this.config.prisma.session.update({
1482
- where: { id: sessionId },
1483
- data: { twoFaSecret: null }
1484
- });
1876
+ await this.config.database.user.update(userId, { twoFaEnabled: false });
1877
+ await this.config.database.session.update(sessionId, { twoFaSecret: null });
1485
1878
  if (this.config.hooks?.onTwoFaStatusChanged) {
1486
1879
  await this.config.hooks.onTwoFaStatusChanged(userId, false);
1487
1880
  }
@@ -1493,10 +1886,7 @@ var TwoFaProcedureFactory = class {
1493
1886
  this.checkConfig();
1494
1887
  const { userId, sessionId } = ctx;
1495
1888
  const { pushCode } = input;
1496
- const user = await this.config.prisma.user.findFirst({
1497
- where: { id: userId },
1498
- select: { twoFaEnabled: true, oauthProvider: true }
1499
- });
1889
+ const user = await this.config.database.user.findById(userId);
1500
1890
  if (user?.oauthProvider) {
1501
1891
  throw new TRPCError6({
1502
1892
  code: "FORBIDDEN",
@@ -1506,10 +1896,7 @@ var TwoFaProcedureFactory = class {
1506
1896
  if (!user?.twoFaEnabled) {
1507
1897
  throw new TRPCError6({ code: "BAD_REQUEST", message: "2FA not enabled." });
1508
1898
  }
1509
- const session = await this.config.prisma.session.findUnique({
1510
- where: { id: sessionId, userId },
1511
- select: { twoFaSecret: true, device: { select: { pushToken: true } } }
1512
- });
1899
+ const session = await this.config.database.session.findByIdWithDevice(sessionId, userId);
1513
1900
  if (!session?.device) {
1514
1901
  throw new TRPCError6({ code: "BAD_REQUEST", message: "Invalid request" });
1515
1902
  }
@@ -1521,10 +1908,7 @@ var TwoFaProcedureFactory = class {
1521
1908
  return { secret: session.twoFaSecret };
1522
1909
  }
1523
1910
  const secret = generateTotpSecret();
1524
- await this.config.prisma.session.update({
1525
- where: { id: sessionId },
1526
- data: { twoFaSecret: secret }
1527
- });
1911
+ await this.config.database.session.update(sessionId, { twoFaSecret: secret });
1528
1912
  return { secret };
1529
1913
  });
1530
1914
  }
@@ -1532,11 +1916,8 @@ var TwoFaProcedureFactory = class {
1532
1916
  return this.procedure.input(twoFaResetSchema).mutation(async ({ input }) => {
1533
1917
  this.checkConfig();
1534
1918
  const { username, password } = input;
1535
- const user = await this.config.prisma.user.findFirst({
1536
- where: { username: { equals: username, mode: "insensitive" }, twoFaEnabled: true },
1537
- select: { id: true, password: true, email: true }
1538
- });
1539
- if (!user) {
1919
+ const user = await this.config.database.user.findByUsernameInsensitive(username);
1920
+ if (!user || !user.twoFaEnabled) {
1540
1921
  throw new TRPCError6({ code: "UNAUTHORIZED", message: "Invalid credentials." });
1541
1922
  }
1542
1923
  if (!user.password) {
@@ -1551,9 +1932,7 @@ var TwoFaProcedureFactory = class {
1551
1932
  }
1552
1933
  const otp = generateOtp();
1553
1934
  const expiresAt = new Date(Date.now() + this.config.tokenSettings.otpValidityMs);
1554
- await this.config.prisma.oTP.create({
1555
- data: { userId: user.id, code: otp, expiresAt }
1556
- });
1935
+ await this.config.database.otp.create({ userId: user.id, code: otp, expiresAt });
1557
1936
  if (this.config.emailService) {
1558
1937
  await this.config.emailService.sendOTPEmail(user.email, otp);
1559
1938
  }
@@ -1564,30 +1943,17 @@ var TwoFaProcedureFactory = class {
1564
1943
  return this.procedure.input(twoFaResetVerifySchema).mutation(async ({ input }) => {
1565
1944
  this.checkConfig();
1566
1945
  const { code, username } = input;
1567
- const user = await this.config.prisma.user.findFirst({
1568
- where: { username: { equals: username, mode: "insensitive" } },
1569
- select: { id: true }
1570
- });
1946
+ const user = await this.config.database.user.findByUsernameInsensitive(username);
1571
1947
  if (!user) {
1572
1948
  throw new TRPCError6({ code: "NOT_FOUND", message: "User not found" });
1573
1949
  }
1574
- const otp = await this.config.prisma.oTP.findFirst({
1575
- where: { userId: user.id, code, expiresAt: { gte: /* @__PURE__ */ new Date() } }
1576
- });
1950
+ const otp = await this.config.database.otp.findValidByUserAndCode(user.id, code);
1577
1951
  if (!otp) {
1578
1952
  throw new TRPCError6({ code: "FORBIDDEN", message: "Invalid or expired OTP" });
1579
1953
  }
1580
- await this.config.prisma.oTP.delete({
1581
- where: { id: otp.id }
1582
- });
1583
- await this.config.prisma.user.update({
1584
- where: { id: user.id },
1585
- data: { twoFaEnabled: false }
1586
- });
1587
- await this.config.prisma.session.updateMany({
1588
- where: { userId: user.id },
1589
- data: { twoFaSecret: null }
1590
- });
1954
+ await this.config.database.otp.delete(otp.id);
1955
+ await this.config.database.user.update(user.id, { twoFaEnabled: false });
1956
+ await this.config.database.session.clearTwoFaSecrets(user.id);
1591
1957
  return { success: true, message: "2FA has been reset." };
1592
1958
  });
1593
1959
  }
@@ -1596,36 +1962,14 @@ var TwoFaProcedureFactory = class {
1596
1962
  this.checkConfig();
1597
1963
  const { userId, sessionId } = ctx;
1598
1964
  const { pushToken } = input;
1599
- await this.config.prisma.session.updateMany({
1600
- where: {
1601
- userId,
1602
- id: { not: sessionId },
1603
- revokedAt: null,
1604
- device: { pushToken }
1605
- },
1606
- data: { revokedAt: /* @__PURE__ */ new Date() }
1607
- });
1608
- const checkDevice = await this.config.prisma.device.findFirst({
1609
- where: {
1610
- pushToken,
1611
- sessions: { some: { id: sessionId } },
1612
- users: { some: { id: userId } }
1613
- },
1614
- select: { id: true }
1615
- });
1965
+ await this.config.database.session.revokeByDevicePushToken(userId, pushToken, sessionId);
1966
+ const checkDevice = await this.config.database.device.findByTokenSessionAndUser(
1967
+ pushToken,
1968
+ sessionId,
1969
+ userId
1970
+ );
1616
1971
  if (!checkDevice) {
1617
- await this.config.prisma.device.upsert({
1618
- where: { pushToken },
1619
- create: {
1620
- pushToken,
1621
- sessions: { connect: { id: sessionId } },
1622
- users: { connect: { id: userId } }
1623
- },
1624
- update: {
1625
- sessions: { connect: { id: sessionId } },
1626
- users: { connect: { id: userId } }
1627
- }
1628
- });
1972
+ await this.config.database.device.upsertByPushToken(pushToken, sessionId, userId);
1629
1973
  }
1630
1974
  return { registered: true };
1631
1975
  });
@@ -1635,30 +1979,13 @@ var TwoFaProcedureFactory = class {
1635
1979
  this.checkConfig();
1636
1980
  const { userId } = ctx;
1637
1981
  const { pushToken } = input;
1638
- const device = await this.config.prisma.device.findFirst({
1639
- where: {
1640
- users: { some: { id: userId } },
1641
- pushToken
1642
- },
1643
- select: { id: true }
1644
- });
1982
+ const device = await this.config.database.device.findByUserAndToken(userId, pushToken);
1645
1983
  if (device) {
1646
- await this.config.prisma.session.updateMany({
1647
- where: { userId, deviceId: device.id },
1648
- data: { deviceId: null }
1649
- });
1650
- await this.config.prisma.device.update({
1651
- where: { id: device.id },
1652
- data: { users: { disconnect: { id: userId } } }
1653
- });
1654
- const remainingUsers = await this.config.prisma.device.findUnique({
1655
- where: { id: device.id },
1656
- select: { users: { select: { id: true }, take: 1 } }
1657
- });
1658
- if (!remainingUsers?.users.length) {
1659
- await this.config.prisma.device.delete({
1660
- where: { id: device.id }
1661
- });
1984
+ await this.config.database.session.clearDeviceId(userId, device.id);
1985
+ await this.config.database.device.disconnectUser(device.id, userId);
1986
+ const hasUsers = await this.config.database.device.hasRemainingUsers(device.id);
1987
+ if (!hasUsers) {
1988
+ await this.config.database.device.delete(device.id);
1662
1989
  }
1663
1990
  }
1664
1991
  return { deregistered: true };
@@ -1670,13 +1997,15 @@ var TwoFaProcedureFactory = class {
1670
1997
  import { initTRPC } from "@trpc/server";
1671
1998
  import SuperJSON from "superjson";
1672
1999
  import { ZodError } from "zod";
2000
+ function hasStringProp(obj, key) {
2001
+ return typeof obj === "object" && obj !== null && key in obj && typeof obj[key] === "string";
2002
+ }
1673
2003
  function isPrismaConnectionError(error) {
1674
2004
  if (!error || typeof error !== "object") {
1675
2005
  return false;
1676
2006
  }
1677
- const errorCode = error.code;
1678
- if (errorCode && typeof errorCode === "string") {
1679
- const codeMatch = errorCode.match(/^P(\d+)$/);
2007
+ if (hasStringProp(error, "code")) {
2008
+ const codeMatch = error.code.match(/^P(\d+)$/);
1680
2009
  if (codeMatch) {
1681
2010
  const codeNum = parseInt(codeMatch[1], 10);
1682
2011
  if (codeNum >= 1e3 && codeNum <= 1003) {
@@ -1686,14 +2015,13 @@ function isPrismaConnectionError(error) {
1686
2015
  }
1687
2016
  const constructorName = error.constructor?.name || "";
1688
2017
  if (constructorName.includes("Prisma")) {
1689
- const errorMessage = error.message?.toLowerCase() || "";
2018
+ const errorMessage = hasStringProp(error, "message") ? error.message.toLowerCase() : "";
1690
2019
  if (errorMessage.includes("can't reach database") || errorMessage.includes("authentication failed") || errorMessage.includes("database server") || errorMessage.includes("timeout") || errorMessage.includes("connection")) {
1691
2020
  return true;
1692
2021
  }
1693
2022
  }
1694
- const cause = error.cause;
1695
- if (cause) {
1696
- return isPrismaConnectionError(cause);
2023
+ if ("cause" in error) {
2024
+ return isPrismaConnectionError(error.cause);
1697
2025
  }
1698
2026
  return false;
1699
2027
  }
@@ -1739,8 +2067,9 @@ function createBaseProcedure(t, authGuard) {
1739
2067
  }
1740
2068
  function getClientIp(req) {
1741
2069
  const forwarded = req.headers["x-forwarded-for"];
1742
- if (forwarded) {
1743
- return forwarded.split(",")[0]?.trim();
2070
+ const forwardedStr = Array.isArray(forwarded) ? forwarded[0] : forwarded;
2071
+ if (forwardedStr) {
2072
+ return forwardedStr.split(",")[0]?.trim();
1744
2073
  }
1745
2074
  return req.socket.remoteAddress || void 0;
1746
2075
  }
@@ -1758,7 +2087,7 @@ var AuthRouterFactory = class {
1758
2087
  constructor(userConfig) {
1759
2088
  this.userConfig = userConfig;
1760
2089
  this.config = createAuthConfig(this.userConfig);
1761
- this.schemas = createSchemas(this.config.schemaExtensions);
2090
+ this.schemas = createSchemas(this.userConfig.schemaExtensions);
1762
2091
  this.t = createTrpcBuilder(this.config);
1763
2092
  this.authGuard = createAuthGuard(this.config, this.t);
1764
2093
  this.procedure = createBaseProcedure(this.t, this.authGuard);
@@ -1815,8 +2144,12 @@ export {
1815
2144
  createAuthRouter,
1816
2145
  createAuthToken,
1817
2146
  createConsoleEmailAdapter,
2147
+ createDrizzleAdapter,
1818
2148
  createNoopEmailAdapter,
1819
2149
  createOAuthVerifier,
2150
+ createPrismaAdapter,
2151
+ createSessionWithToken,
2152
+ createSessionWithTokenAndCookie,
1820
2153
  decodeToken,
1821
2154
  defaultAuthConfig,
1822
2155
  defaultCookieSettings,