@factiii/auth 0.5.2 → 0.5.4
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/bin/init.mjs +449 -64
- package/dist/{chunk-EHI4P63M.mjs → chunk-KUYH4DBN.mjs} +8 -0
- package/dist/index.d.mts +228 -16
- package/dist/index.d.ts +228 -16
- package/dist/index.js +678 -381
- package/dist/index.mjs +678 -382
- package/dist/validators.d.mts +1 -1
- package/dist/validators.d.ts +1 -1
- package/dist/validators.mjs +1 -1
- package/package.json +24 -3
- package/dist/{hooks-BXNxNK4S.d.mts → hooks-yHGJ7C6_.d.mts} +2 -2
- package/dist/{hooks-BXNxNK4S.d.ts → hooks-yHGJ7C6_.d.ts} +2 -2
package/dist/index.js
CHANGED
|
@@ -42,8 +42,10 @@ __export(index_exports, {
|
|
|
42
42
|
createAuthRouter: () => createAuthRouter,
|
|
43
43
|
createAuthToken: () => createAuthToken,
|
|
44
44
|
createConsoleEmailAdapter: () => createConsoleEmailAdapter,
|
|
45
|
+
createDrizzleAdapter: () => createDrizzleAdapter,
|
|
45
46
|
createNoopEmailAdapter: () => createNoopEmailAdapter,
|
|
46
47
|
createOAuthVerifier: () => createOAuthVerifier,
|
|
48
|
+
createPrismaAdapter: () => createPrismaAdapter,
|
|
47
49
|
decodeToken: () => decodeToken,
|
|
48
50
|
defaultAuthConfig: () => defaultAuthConfig,
|
|
49
51
|
defaultCookieSettings: () => defaultCookieSettings,
|
|
@@ -78,6 +80,260 @@ module.exports = __toCommonJS(index_exports);
|
|
|
78
80
|
// src/middleware/authGuard.ts
|
|
79
81
|
var import_server = require("@trpc/server");
|
|
80
82
|
|
|
83
|
+
// src/adapters/prismaAdapter.ts
|
|
84
|
+
function createPrismaAdapter(prisma) {
|
|
85
|
+
return {
|
|
86
|
+
user: {
|
|
87
|
+
async findByEmailInsensitive(email) {
|
|
88
|
+
return prisma.user.findFirst({
|
|
89
|
+
where: { email: { equals: email, mode: "insensitive" } }
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
async findByUsernameInsensitive(username) {
|
|
93
|
+
return prisma.user.findFirst({
|
|
94
|
+
where: { username: { equals: username, mode: "insensitive" } }
|
|
95
|
+
});
|
|
96
|
+
},
|
|
97
|
+
async findByEmailOrUsernameInsensitive(identifier) {
|
|
98
|
+
return prisma.user.findFirst({
|
|
99
|
+
where: {
|
|
100
|
+
OR: [
|
|
101
|
+
{ email: { equals: identifier, mode: "insensitive" } },
|
|
102
|
+
{ username: { equals: identifier, mode: "insensitive" } }
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
},
|
|
107
|
+
async findByEmailOrOAuthId(email, oauthId) {
|
|
108
|
+
return prisma.user.findFirst({
|
|
109
|
+
where: {
|
|
110
|
+
OR: [
|
|
111
|
+
{ email: { equals: email, mode: "insensitive" } },
|
|
112
|
+
{ oauthId: { equals: oauthId } }
|
|
113
|
+
]
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
async findById(id) {
|
|
118
|
+
return prisma.user.findUnique({ where: { id } });
|
|
119
|
+
},
|
|
120
|
+
async findActiveById(id) {
|
|
121
|
+
return prisma.user.findUnique({
|
|
122
|
+
where: { id, status: "ACTIVE" }
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
async create(data) {
|
|
126
|
+
return prisma.user.create({ data });
|
|
127
|
+
},
|
|
128
|
+
async update(id, data) {
|
|
129
|
+
return prisma.user.update({ where: { id }, data });
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
session: {
|
|
133
|
+
async findById(id) {
|
|
134
|
+
const session = await prisma.session.findUnique({
|
|
135
|
+
where: { id },
|
|
136
|
+
select: {
|
|
137
|
+
id: true,
|
|
138
|
+
userId: true,
|
|
139
|
+
socketId: true,
|
|
140
|
+
twoFaSecret: true,
|
|
141
|
+
browserName: true,
|
|
142
|
+
issuedAt: true,
|
|
143
|
+
lastUsed: true,
|
|
144
|
+
revokedAt: true,
|
|
145
|
+
deviceId: true,
|
|
146
|
+
user: { select: { status: true, verifiedHumanAt: true } }
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
return session;
|
|
150
|
+
},
|
|
151
|
+
async create(data) {
|
|
152
|
+
return prisma.session.create({ data });
|
|
153
|
+
},
|
|
154
|
+
async update(id, data) {
|
|
155
|
+
return prisma.session.update({ where: { id }, data });
|
|
156
|
+
},
|
|
157
|
+
async updateLastUsed(id) {
|
|
158
|
+
const session = await prisma.session.update({
|
|
159
|
+
where: { id },
|
|
160
|
+
data: { lastUsed: /* @__PURE__ */ new Date() },
|
|
161
|
+
select: {
|
|
162
|
+
id: true,
|
|
163
|
+
userId: true,
|
|
164
|
+
socketId: true,
|
|
165
|
+
twoFaSecret: true,
|
|
166
|
+
browserName: true,
|
|
167
|
+
issuedAt: true,
|
|
168
|
+
lastUsed: true,
|
|
169
|
+
revokedAt: true,
|
|
170
|
+
deviceId: true,
|
|
171
|
+
user: { select: { verifiedHumanAt: true } }
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
return session;
|
|
175
|
+
},
|
|
176
|
+
async revoke(id) {
|
|
177
|
+
await prisma.session.update({
|
|
178
|
+
where: { id },
|
|
179
|
+
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
async findActiveByUserId(userId, excludeSessionId) {
|
|
183
|
+
return prisma.session.findMany({
|
|
184
|
+
where: {
|
|
185
|
+
userId,
|
|
186
|
+
revokedAt: null,
|
|
187
|
+
...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
|
|
188
|
+
},
|
|
189
|
+
select: { id: true, socketId: true, userId: true }
|
|
190
|
+
});
|
|
191
|
+
},
|
|
192
|
+
async revokeAllByUserId(userId, excludeSessionId) {
|
|
193
|
+
await prisma.session.updateMany({
|
|
194
|
+
where: {
|
|
195
|
+
userId,
|
|
196
|
+
revokedAt: null,
|
|
197
|
+
...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
|
|
198
|
+
},
|
|
199
|
+
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
200
|
+
});
|
|
201
|
+
},
|
|
202
|
+
async findTwoFaSecretsByUserId(userId) {
|
|
203
|
+
return prisma.session.findMany({
|
|
204
|
+
where: { userId, twoFaSecret: { not: null } },
|
|
205
|
+
select: { twoFaSecret: true }
|
|
206
|
+
});
|
|
207
|
+
},
|
|
208
|
+
async clearTwoFaSecrets(userId, excludeSessionId) {
|
|
209
|
+
await prisma.session.updateMany({
|
|
210
|
+
where: {
|
|
211
|
+
userId,
|
|
212
|
+
...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
|
|
213
|
+
},
|
|
214
|
+
data: { twoFaSecret: null }
|
|
215
|
+
});
|
|
216
|
+
},
|
|
217
|
+
async findByIdWithDevice(id, userId) {
|
|
218
|
+
const session = await prisma.session.findUnique({
|
|
219
|
+
where: { id, userId },
|
|
220
|
+
select: {
|
|
221
|
+
twoFaSecret: true,
|
|
222
|
+
deviceId: true,
|
|
223
|
+
device: { select: { pushToken: true } }
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
return session;
|
|
227
|
+
},
|
|
228
|
+
async revokeByDevicePushToken(userId, pushToken, excludeSessionId) {
|
|
229
|
+
await prisma.session.updateMany({
|
|
230
|
+
where: {
|
|
231
|
+
userId,
|
|
232
|
+
id: { not: excludeSessionId },
|
|
233
|
+
revokedAt: null,
|
|
234
|
+
device: { pushToken }
|
|
235
|
+
},
|
|
236
|
+
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
237
|
+
});
|
|
238
|
+
},
|
|
239
|
+
async clearDeviceId(userId, deviceId) {
|
|
240
|
+
await prisma.session.updateMany({
|
|
241
|
+
where: { userId, deviceId },
|
|
242
|
+
data: { deviceId: null }
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
otp: {
|
|
247
|
+
async findValidByUserAndCode(userId, code) {
|
|
248
|
+
return prisma.oTP.findFirst({
|
|
249
|
+
where: { userId, code, expiresAt: { gte: /* @__PURE__ */ new Date() } }
|
|
250
|
+
});
|
|
251
|
+
},
|
|
252
|
+
async create(data) {
|
|
253
|
+
return prisma.oTP.create({ data });
|
|
254
|
+
},
|
|
255
|
+
async delete(id) {
|
|
256
|
+
await prisma.oTP.delete({ where: { id } });
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
passwordReset: {
|
|
260
|
+
async findById(id) {
|
|
261
|
+
return prisma.passwordReset.findUnique({
|
|
262
|
+
where: { id },
|
|
263
|
+
select: { id: true, createdAt: true, userId: true }
|
|
264
|
+
});
|
|
265
|
+
},
|
|
266
|
+
async create(userId) {
|
|
267
|
+
return prisma.passwordReset.create({
|
|
268
|
+
data: { userId }
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
async delete(id) {
|
|
272
|
+
await prisma.passwordReset.delete({ where: { id } });
|
|
273
|
+
},
|
|
274
|
+
async deleteAllByUserId(userId) {
|
|
275
|
+
await prisma.passwordReset.deleteMany({ where: { userId } });
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
device: {
|
|
279
|
+
async findByTokenSessionAndUser(pushToken, sessionId, userId) {
|
|
280
|
+
return prisma.device.findFirst({
|
|
281
|
+
where: {
|
|
282
|
+
pushToken,
|
|
283
|
+
sessions: { some: { id: sessionId } },
|
|
284
|
+
users: { some: { id: userId } }
|
|
285
|
+
},
|
|
286
|
+
select: { id: true }
|
|
287
|
+
});
|
|
288
|
+
},
|
|
289
|
+
async upsertByPushToken(pushToken, sessionId, userId) {
|
|
290
|
+
await prisma.device.upsert({
|
|
291
|
+
where: { pushToken },
|
|
292
|
+
create: {
|
|
293
|
+
pushToken,
|
|
294
|
+
sessions: { connect: { id: sessionId } },
|
|
295
|
+
users: { connect: { id: userId } }
|
|
296
|
+
},
|
|
297
|
+
update: {
|
|
298
|
+
sessions: { connect: { id: sessionId } },
|
|
299
|
+
users: { connect: { id: userId } }
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
},
|
|
303
|
+
async findByUserAndToken(userId, pushToken) {
|
|
304
|
+
return prisma.device.findFirst({
|
|
305
|
+
where: { users: { some: { id: userId } }, pushToken },
|
|
306
|
+
select: { id: true }
|
|
307
|
+
});
|
|
308
|
+
},
|
|
309
|
+
async disconnectUser(deviceId, userId) {
|
|
310
|
+
await prisma.device.update({
|
|
311
|
+
where: { id: deviceId },
|
|
312
|
+
data: { users: { disconnect: { id: userId } } }
|
|
313
|
+
});
|
|
314
|
+
},
|
|
315
|
+
async hasRemainingUsers(deviceId) {
|
|
316
|
+
const result = await prisma.device.findUnique({
|
|
317
|
+
where: { id: deviceId },
|
|
318
|
+
select: { users: { select: { id: true }, take: 1 } }
|
|
319
|
+
});
|
|
320
|
+
return (result?.users.length ?? 0) > 0;
|
|
321
|
+
},
|
|
322
|
+
async delete(id) {
|
|
323
|
+
await prisma.device.delete({ where: { id } });
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
admin: {
|
|
327
|
+
async findByUserId(userId) {
|
|
328
|
+
return prisma.admin.findFirst({
|
|
329
|
+
where: { userId },
|
|
330
|
+
select: { ip: true }
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
81
337
|
// src/adapters/email.ts
|
|
82
338
|
function createNoopEmailAdapter() {
|
|
83
339
|
return {
|
|
@@ -131,6 +387,291 @@ function createConsoleEmailAdapter() {
|
|
|
131
387
|
};
|
|
132
388
|
}
|
|
133
389
|
|
|
390
|
+
// src/adapters/drizzleAdapter.ts
|
|
391
|
+
function createDrizzleAdapter(db, tables) {
|
|
392
|
+
const {
|
|
393
|
+
eq,
|
|
394
|
+
and,
|
|
395
|
+
or,
|
|
396
|
+
isNull,
|
|
397
|
+
isNotNull,
|
|
398
|
+
gte,
|
|
399
|
+
ne,
|
|
400
|
+
sql
|
|
401
|
+
} = require("drizzle-orm");
|
|
402
|
+
const { users, sessions, otps, passwordResets, devices, admins } = tables;
|
|
403
|
+
return {
|
|
404
|
+
user: {
|
|
405
|
+
async findByEmailInsensitive(email) {
|
|
406
|
+
const rows = await db.select().from(users).where(sql`lower(${users.email}) = lower(${email})`).limit(1);
|
|
407
|
+
return rows[0] ?? null;
|
|
408
|
+
},
|
|
409
|
+
async findByUsernameInsensitive(username) {
|
|
410
|
+
const rows = await db.select().from(users).where(sql`lower(${users.username}) = lower(${username})`).limit(1);
|
|
411
|
+
return rows[0] ?? null;
|
|
412
|
+
},
|
|
413
|
+
async findByEmailOrUsernameInsensitive(identifier) {
|
|
414
|
+
const rows = await db.select().from(users).where(
|
|
415
|
+
or(
|
|
416
|
+
sql`lower(${users.email}) = lower(${identifier})`,
|
|
417
|
+
sql`lower(${users.username}) = lower(${identifier})`
|
|
418
|
+
)
|
|
419
|
+
).limit(1);
|
|
420
|
+
return rows[0] ?? null;
|
|
421
|
+
},
|
|
422
|
+
async findByEmailOrOAuthId(email, oauthId) {
|
|
423
|
+
const rows = await db.select().from(users).where(
|
|
424
|
+
or(
|
|
425
|
+
sql`lower(${users.email}) = lower(${email})`,
|
|
426
|
+
eq(users.oauthId, oauthId)
|
|
427
|
+
)
|
|
428
|
+
).limit(1);
|
|
429
|
+
return rows[0] ?? null;
|
|
430
|
+
},
|
|
431
|
+
async findById(id) {
|
|
432
|
+
const rows = await db.select().from(users).where(eq(users.id, id)).limit(1);
|
|
433
|
+
return rows[0] ?? null;
|
|
434
|
+
},
|
|
435
|
+
async findActiveById(id) {
|
|
436
|
+
const rows = await db.select().from(users).where(and(eq(users.id, id), eq(users.status, "ACTIVE"))).limit(1);
|
|
437
|
+
return rows[0] ?? null;
|
|
438
|
+
},
|
|
439
|
+
async create(data) {
|
|
440
|
+
const rows = await db.insert(users).values(data).returning();
|
|
441
|
+
return rows[0];
|
|
442
|
+
},
|
|
443
|
+
async update(id, data) {
|
|
444
|
+
const rows = await db.update(users).set(data).where(eq(users.id, id)).returning();
|
|
445
|
+
return rows[0];
|
|
446
|
+
}
|
|
447
|
+
},
|
|
448
|
+
session: {
|
|
449
|
+
async findById(id) {
|
|
450
|
+
const rows = await db.select({
|
|
451
|
+
id: sessions.id,
|
|
452
|
+
userId: sessions.userId,
|
|
453
|
+
socketId: sessions.socketId,
|
|
454
|
+
twoFaSecret: sessions.twoFaSecret,
|
|
455
|
+
browserName: sessions.browserName,
|
|
456
|
+
issuedAt: sessions.issuedAt,
|
|
457
|
+
lastUsed: sessions.lastUsed,
|
|
458
|
+
revokedAt: sessions.revokedAt,
|
|
459
|
+
deviceId: sessions.deviceId,
|
|
460
|
+
user: {
|
|
461
|
+
status: users.status,
|
|
462
|
+
verifiedHumanAt: users.verifiedHumanAt
|
|
463
|
+
}
|
|
464
|
+
}).from(sessions).innerJoin(users, eq(sessions.userId, users.id)).where(eq(sessions.id, id)).limit(1);
|
|
465
|
+
return rows[0] ?? null;
|
|
466
|
+
},
|
|
467
|
+
async create(data) {
|
|
468
|
+
const rows = await db.insert(sessions).values(data).returning();
|
|
469
|
+
return rows[0];
|
|
470
|
+
},
|
|
471
|
+
async update(id, data) {
|
|
472
|
+
const rows = await db.update(sessions).set(data).where(eq(sessions.id, id)).returning();
|
|
473
|
+
return rows[0];
|
|
474
|
+
},
|
|
475
|
+
async updateLastUsed(id) {
|
|
476
|
+
await db.update(sessions).set({ lastUsed: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id));
|
|
477
|
+
const rows = await db.select({
|
|
478
|
+
id: sessions.id,
|
|
479
|
+
userId: sessions.userId,
|
|
480
|
+
socketId: sessions.socketId,
|
|
481
|
+
twoFaSecret: sessions.twoFaSecret,
|
|
482
|
+
browserName: sessions.browserName,
|
|
483
|
+
issuedAt: sessions.issuedAt,
|
|
484
|
+
lastUsed: sessions.lastUsed,
|
|
485
|
+
revokedAt: sessions.revokedAt,
|
|
486
|
+
deviceId: sessions.deviceId,
|
|
487
|
+
user: {
|
|
488
|
+
verifiedHumanAt: users.verifiedHumanAt
|
|
489
|
+
}
|
|
490
|
+
}).from(sessions).innerJoin(users, eq(sessions.userId, users.id)).where(eq(sessions.id, id)).limit(1);
|
|
491
|
+
return rows[0];
|
|
492
|
+
},
|
|
493
|
+
async revoke(id) {
|
|
494
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id));
|
|
495
|
+
},
|
|
496
|
+
async findActiveByUserId(userId, excludeSessionId) {
|
|
497
|
+
const conditions = [eq(sessions.userId, userId), isNull(sessions.revokedAt)];
|
|
498
|
+
if (excludeSessionId !== void 0) {
|
|
499
|
+
conditions.push(ne(sessions.id, excludeSessionId));
|
|
500
|
+
}
|
|
501
|
+
return db.select({
|
|
502
|
+
id: sessions.id,
|
|
503
|
+
socketId: sessions.socketId,
|
|
504
|
+
userId: sessions.userId
|
|
505
|
+
}).from(sessions).where(and(...conditions));
|
|
506
|
+
},
|
|
507
|
+
async revokeAllByUserId(userId, excludeSessionId) {
|
|
508
|
+
const conditions = [eq(sessions.userId, userId), isNull(sessions.revokedAt)];
|
|
509
|
+
if (excludeSessionId !== void 0) {
|
|
510
|
+
conditions.push(ne(sessions.id, excludeSessionId));
|
|
511
|
+
}
|
|
512
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(and(...conditions));
|
|
513
|
+
},
|
|
514
|
+
async findTwoFaSecretsByUserId(userId) {
|
|
515
|
+
return db.select({ twoFaSecret: sessions.twoFaSecret }).from(sessions).where(and(eq(sessions.userId, userId), isNotNull(sessions.twoFaSecret)));
|
|
516
|
+
},
|
|
517
|
+
async clearTwoFaSecrets(userId, excludeSessionId) {
|
|
518
|
+
const conditions = [eq(sessions.userId, userId)];
|
|
519
|
+
if (excludeSessionId !== void 0) {
|
|
520
|
+
conditions.push(ne(sessions.id, excludeSessionId));
|
|
521
|
+
}
|
|
522
|
+
await db.update(sessions).set({ twoFaSecret: null }).where(and(...conditions));
|
|
523
|
+
},
|
|
524
|
+
async findByIdWithDevice(id, userId) {
|
|
525
|
+
const rows = await db.select({
|
|
526
|
+
twoFaSecret: sessions.twoFaSecret,
|
|
527
|
+
deviceId: sessions.deviceId,
|
|
528
|
+
device: {
|
|
529
|
+
pushToken: devices.pushToken
|
|
530
|
+
}
|
|
531
|
+
}).from(sessions).leftJoin(devices, eq(sessions.deviceId, devices.id)).where(and(eq(sessions.id, id), eq(sessions.userId, userId))).limit(1);
|
|
532
|
+
if (!rows[0]) return null;
|
|
533
|
+
const row = rows[0];
|
|
534
|
+
return {
|
|
535
|
+
twoFaSecret: row.twoFaSecret,
|
|
536
|
+
deviceId: row.deviceId,
|
|
537
|
+
device: row.device?.pushToken ? { pushToken: row.device.pushToken } : null
|
|
538
|
+
};
|
|
539
|
+
},
|
|
540
|
+
async revokeByDevicePushToken(userId, pushToken, excludeSessionId) {
|
|
541
|
+
const deviceRows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
542
|
+
if (!deviceRows[0]) return;
|
|
543
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(
|
|
544
|
+
and(
|
|
545
|
+
eq(sessions.userId, userId),
|
|
546
|
+
ne(sessions.id, excludeSessionId),
|
|
547
|
+
isNull(sessions.revokedAt),
|
|
548
|
+
eq(sessions.deviceId, deviceRows[0].id)
|
|
549
|
+
)
|
|
550
|
+
);
|
|
551
|
+
},
|
|
552
|
+
async clearDeviceId(userId, deviceId) {
|
|
553
|
+
await db.update(sessions).set({ deviceId: null }).where(and(eq(sessions.userId, userId), eq(sessions.deviceId, deviceId)));
|
|
554
|
+
}
|
|
555
|
+
},
|
|
556
|
+
otp: {
|
|
557
|
+
async findValidByUserAndCode(userId, code) {
|
|
558
|
+
const rows = await db.select().from(otps).where(
|
|
559
|
+
and(eq(otps.userId, userId), eq(otps.code, code), gte(otps.expiresAt, /* @__PURE__ */ new Date()))
|
|
560
|
+
).limit(1);
|
|
561
|
+
return rows[0] ?? null;
|
|
562
|
+
},
|
|
563
|
+
async create(data) {
|
|
564
|
+
const rows = await db.insert(otps).values(data).returning();
|
|
565
|
+
return rows[0];
|
|
566
|
+
},
|
|
567
|
+
async delete(id) {
|
|
568
|
+
await db.delete(otps).where(eq(otps.id, id));
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
passwordReset: {
|
|
572
|
+
async findById(id) {
|
|
573
|
+
const rows = await db.select({
|
|
574
|
+
id: passwordResets.id,
|
|
575
|
+
createdAt: passwordResets.createdAt,
|
|
576
|
+
userId: passwordResets.userId
|
|
577
|
+
}).from(passwordResets).where(eq(passwordResets.id, id)).limit(1);
|
|
578
|
+
return rows[0] ?? null;
|
|
579
|
+
},
|
|
580
|
+
async create(userId) {
|
|
581
|
+
const rows = await db.insert(passwordResets).values({ userId }).returning();
|
|
582
|
+
return rows[0];
|
|
583
|
+
},
|
|
584
|
+
async delete(id) {
|
|
585
|
+
await db.delete(passwordResets).where(eq(passwordResets.id, id));
|
|
586
|
+
},
|
|
587
|
+
async deleteAllByUserId(userId) {
|
|
588
|
+
await db.delete(passwordResets).where(eq(passwordResets.userId, userId));
|
|
589
|
+
}
|
|
590
|
+
},
|
|
591
|
+
device: {
|
|
592
|
+
async findByTokenSessionAndUser(pushToken, sessionId, userId) {
|
|
593
|
+
const rows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
594
|
+
if (!rows[0]) return null;
|
|
595
|
+
if (tables.devicesToSessions && tables.devicesToUsers) {
|
|
596
|
+
const sessionLink = await db.select().from(tables.devicesToSessions).where(
|
|
597
|
+
and(
|
|
598
|
+
eq(tables.devicesToSessions.deviceId, rows[0].id),
|
|
599
|
+
eq(tables.devicesToSessions.sessionId, sessionId)
|
|
600
|
+
)
|
|
601
|
+
).limit(1);
|
|
602
|
+
const userLink = await db.select().from(tables.devicesToUsers).where(
|
|
603
|
+
and(
|
|
604
|
+
eq(tables.devicesToUsers.deviceId, rows[0].id),
|
|
605
|
+
eq(tables.devicesToUsers.userId, userId)
|
|
606
|
+
)
|
|
607
|
+
).limit(1);
|
|
608
|
+
if (!sessionLink[0] || !userLink[0]) return null;
|
|
609
|
+
}
|
|
610
|
+
return { id: rows[0].id };
|
|
611
|
+
},
|
|
612
|
+
async upsertByPushToken(pushToken, sessionId, userId) {
|
|
613
|
+
const existing = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
614
|
+
let deviceId;
|
|
615
|
+
if (existing[0]) {
|
|
616
|
+
deviceId = existing[0].id;
|
|
617
|
+
} else {
|
|
618
|
+
const rows = await db.insert(devices).values({ pushToken }).returning({ id: devices.id });
|
|
619
|
+
deviceId = rows[0].id;
|
|
620
|
+
}
|
|
621
|
+
if (tables.devicesToSessions) {
|
|
622
|
+
await db.insert(tables.devicesToSessions).values({ deviceId, sessionId }).onConflictDoNothing();
|
|
623
|
+
}
|
|
624
|
+
if (tables.devicesToUsers) {
|
|
625
|
+
await db.insert(tables.devicesToUsers).values({ deviceId, userId }).onConflictDoNothing();
|
|
626
|
+
}
|
|
627
|
+
await db.update(sessions).set({ deviceId }).where(eq(sessions.id, sessionId));
|
|
628
|
+
},
|
|
629
|
+
async findByUserAndToken(userId, pushToken) {
|
|
630
|
+
if (tables.devicesToUsers) {
|
|
631
|
+
const rows2 = await db.select({ id: devices.id }).from(devices).innerJoin(
|
|
632
|
+
tables.devicesToUsers,
|
|
633
|
+
eq(devices.id, tables.devicesToUsers.deviceId)
|
|
634
|
+
).where(
|
|
635
|
+
and(
|
|
636
|
+
eq(devices.pushToken, pushToken),
|
|
637
|
+
eq(tables.devicesToUsers.userId, userId)
|
|
638
|
+
)
|
|
639
|
+
).limit(1);
|
|
640
|
+
return rows2[0] ? { id: rows2[0].id } : null;
|
|
641
|
+
}
|
|
642
|
+
const rows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
643
|
+
return rows[0] ? { id: rows[0].id } : null;
|
|
644
|
+
},
|
|
645
|
+
async disconnectUser(deviceId, userId) {
|
|
646
|
+
if (tables.devicesToUsers) {
|
|
647
|
+
await db.delete(tables.devicesToUsers).where(
|
|
648
|
+
and(
|
|
649
|
+
eq(tables.devicesToUsers.deviceId, deviceId),
|
|
650
|
+
eq(tables.devicesToUsers.userId, userId)
|
|
651
|
+
)
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
async hasRemainingUsers(deviceId) {
|
|
656
|
+
if (tables.devicesToUsers) {
|
|
657
|
+
const rows = await db.select({ userId: tables.devicesToUsers.userId }).from(tables.devicesToUsers).where(eq(tables.devicesToUsers.deviceId, deviceId)).limit(1);
|
|
658
|
+
return rows.length > 0;
|
|
659
|
+
}
|
|
660
|
+
return false;
|
|
661
|
+
},
|
|
662
|
+
async delete(id) {
|
|
663
|
+
await db.delete(devices).where(eq(devices.id, id));
|
|
664
|
+
}
|
|
665
|
+
},
|
|
666
|
+
admin: {
|
|
667
|
+
async findByUserId(userId) {
|
|
668
|
+
const rows = await db.select({ ip: admins.ip }).from(admins).where(eq(admins.userId, userId)).limit(1);
|
|
669
|
+
return rows[0] ?? null;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
134
675
|
// src/utilities/config.ts
|
|
135
676
|
var defaultTokenSettings = {
|
|
136
677
|
jwtExpiry: 30 * 24 * 60 * 60,
|
|
@@ -161,9 +702,16 @@ var defaultFeatures = {
|
|
|
161
702
|
otpLogin: true
|
|
162
703
|
};
|
|
163
704
|
function createAuthConfig(config) {
|
|
705
|
+
if (!config.database && !config.prisma) {
|
|
706
|
+
throw new Error(
|
|
707
|
+
"@factiii/auth: Provide either a `database` adapter or a `prisma` client in config."
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
const database = config.database ?? createPrismaAdapter(config.prisma);
|
|
164
711
|
const emailService = config.emailService ?? createNoopEmailAdapter();
|
|
165
712
|
return {
|
|
166
713
|
...config,
|
|
714
|
+
database,
|
|
167
715
|
features: { ...defaultFeatures, ...config.features },
|
|
168
716
|
tokenSettings: { ...defaultTokenSettings, ...config.tokenSettings },
|
|
169
717
|
cookieSettings: { ...defaultCookieSettings, ...config.cookieSettings },
|
|
@@ -281,6 +829,7 @@ function isTokenInvalidError(error) {
|
|
|
281
829
|
function createAuthGuard(config, t) {
|
|
282
830
|
const storageKeys = config.storageKeys ?? defaultStorageKeys;
|
|
283
831
|
const cookieSettings = { ...defaultCookieSettings, ...config.cookieSettings };
|
|
832
|
+
const database = config.database ?? createPrismaAdapter(config.prisma);
|
|
284
833
|
const revokeSession = async (ctx, sessionId, description, errorStack, path) => {
|
|
285
834
|
clearAuthCookie(ctx.res, cookieSettings, storageKeys);
|
|
286
835
|
if (config.hooks?.logError) {
|
|
@@ -317,15 +866,9 @@ ${errorStack}` : null,
|
|
|
317
866
|
}
|
|
318
867
|
if (sessionId) {
|
|
319
868
|
try {
|
|
320
|
-
await
|
|
321
|
-
where: { id: sessionId },
|
|
322
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
323
|
-
});
|
|
869
|
+
await database.session.revoke(sessionId);
|
|
324
870
|
if (config.hooks?.onSessionRevoked) {
|
|
325
|
-
const session = await
|
|
326
|
-
where: { id: sessionId },
|
|
327
|
-
select: { id: true, userId: true, socketId: true }
|
|
328
|
-
});
|
|
871
|
+
const session = await database.session.findById(sessionId);
|
|
329
872
|
if (session) {
|
|
330
873
|
await config.hooks.onSessionRevoked(session.userId, session.socketId, description);
|
|
331
874
|
}
|
|
@@ -350,23 +893,7 @@ ${errorStack}` : null,
|
|
|
350
893
|
secret: config.secrets.jwt,
|
|
351
894
|
ignoreExpiration: meta?.ignoreExpiration ?? false
|
|
352
895
|
});
|
|
353
|
-
const session = await
|
|
354
|
-
where: {
|
|
355
|
-
id: decodedToken.id
|
|
356
|
-
},
|
|
357
|
-
select: {
|
|
358
|
-
userId: true,
|
|
359
|
-
user: {
|
|
360
|
-
select: {
|
|
361
|
-
status: true,
|
|
362
|
-
verifiedHumanAt: true
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
revokedAt: true,
|
|
366
|
-
socketId: true,
|
|
367
|
-
id: true
|
|
368
|
-
}
|
|
369
|
-
});
|
|
896
|
+
const session = await database.session.findById(decodedToken.id);
|
|
370
897
|
if (!session) {
|
|
371
898
|
await revokeSession(
|
|
372
899
|
ctx,
|
|
@@ -420,10 +947,7 @@ ${errorStack}` : null,
|
|
|
420
947
|
});
|
|
421
948
|
}
|
|
422
949
|
if (meta?.adminRequired) {
|
|
423
|
-
const admin = await
|
|
424
|
-
where: { userId: session.userId },
|
|
425
|
-
select: { ip: true }
|
|
426
|
-
});
|
|
950
|
+
const admin = await database.admin.findByUserId(session.userId);
|
|
427
951
|
if (!admin || admin.ip !== ctx.ip) {
|
|
428
952
|
await revokeSession(
|
|
429
953
|
ctx,
|
|
@@ -756,19 +1280,14 @@ var BaseProcedureFactory = class {
|
|
|
756
1280
|
if (this.config.hooks?.beforeRegister) {
|
|
757
1281
|
await this.config.hooks.beforeRegister(typedInput);
|
|
758
1282
|
}
|
|
759
|
-
const usernameCheck = await this.config.
|
|
760
|
-
where: { username: { equals: username, mode: "insensitive" } }
|
|
761
|
-
});
|
|
1283
|
+
const usernameCheck = await this.config.database.user.findByUsernameInsensitive(username);
|
|
762
1284
|
if (usernameCheck) {
|
|
763
1285
|
throw new import_server2.TRPCError({
|
|
764
1286
|
code: "CONFLICT",
|
|
765
1287
|
message: "An account already exists with that username."
|
|
766
1288
|
});
|
|
767
1289
|
}
|
|
768
|
-
const emailCheck = await this.config.
|
|
769
|
-
where: { email: { equals: email, mode: "insensitive" } },
|
|
770
|
-
select: { id: true }
|
|
771
|
-
});
|
|
1290
|
+
const emailCheck = await this.config.database.user.findByEmailInsensitive(email);
|
|
772
1291
|
if (emailCheck) {
|
|
773
1292
|
throw new import_server2.TRPCError({
|
|
774
1293
|
code: "CONFLICT",
|
|
@@ -776,30 +1295,25 @@ var BaseProcedureFactory = class {
|
|
|
776
1295
|
});
|
|
777
1296
|
}
|
|
778
1297
|
const hashedPassword = await hashPassword(password);
|
|
779
|
-
const user = await this.config.
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
verifiedHumanAt: null
|
|
789
|
-
}
|
|
1298
|
+
const user = await this.config.database.user.create({
|
|
1299
|
+
username,
|
|
1300
|
+
email,
|
|
1301
|
+
password: hashedPassword,
|
|
1302
|
+
status: "ACTIVE",
|
|
1303
|
+
tag: this.config.features.biometric ? "BOT" : "HUMAN",
|
|
1304
|
+
twoFaEnabled: false,
|
|
1305
|
+
emailVerificationStatus: "UNVERIFIED",
|
|
1306
|
+
verifiedHumanAt: null
|
|
790
1307
|
});
|
|
791
1308
|
if (this.config.hooks?.onUserCreated) {
|
|
792
1309
|
await this.config.hooks.onUserCreated(user.id, typedInput);
|
|
793
1310
|
}
|
|
794
1311
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
795
|
-
const session = await this.config.
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
...extraSessionData
|
|
801
|
-
},
|
|
802
|
-
select: { id: true, userId: true }
|
|
1312
|
+
const session = await this.config.database.session.create({
|
|
1313
|
+
userId: user.id,
|
|
1314
|
+
browserName: detectBrowser(userAgent),
|
|
1315
|
+
socketId: null,
|
|
1316
|
+
...extraSessionData
|
|
803
1317
|
});
|
|
804
1318
|
if (this.config.hooks?.onSessionCreated) {
|
|
805
1319
|
await this.config.hooks.onSessionCreated(session.id, typedInput);
|
|
@@ -837,24 +1351,7 @@ var BaseProcedureFactory = class {
|
|
|
837
1351
|
if (this.config.hooks?.beforeLogin) {
|
|
838
1352
|
await this.config.hooks.beforeLogin(typedInput);
|
|
839
1353
|
}
|
|
840
|
-
const user = await this.config.
|
|
841
|
-
where: {
|
|
842
|
-
OR: [
|
|
843
|
-
{ email: { equals: username, mode: "insensitive" } },
|
|
844
|
-
{ username: { equals: username, mode: "insensitive" } }
|
|
845
|
-
]
|
|
846
|
-
},
|
|
847
|
-
select: {
|
|
848
|
-
id: true,
|
|
849
|
-
status: true,
|
|
850
|
-
password: true,
|
|
851
|
-
twoFaEnabled: true,
|
|
852
|
-
email: true,
|
|
853
|
-
username: true,
|
|
854
|
-
oauthProvider: true,
|
|
855
|
-
verifiedHumanAt: true
|
|
856
|
-
}
|
|
857
|
-
});
|
|
1354
|
+
const user = await this.config.database.user.findByEmailOrUsernameInsensitive(username);
|
|
858
1355
|
if (!user) {
|
|
859
1356
|
throw new import_server2.TRPCError({
|
|
860
1357
|
code: "FORBIDDEN",
|
|
@@ -890,14 +1387,12 @@ var BaseProcedureFactory = class {
|
|
|
890
1387
|
if (!code) {
|
|
891
1388
|
return {
|
|
892
1389
|
success: false,
|
|
893
|
-
requires2FA: true
|
|
1390
|
+
requires2FA: true,
|
|
1391
|
+
userId: user.id
|
|
894
1392
|
};
|
|
895
1393
|
}
|
|
896
1394
|
let validCode = false;
|
|
897
|
-
const secrets = await this.config.
|
|
898
|
-
where: { userId: user.id, twoFaSecret: { not: null } },
|
|
899
|
-
select: { twoFaSecret: true }
|
|
900
|
-
});
|
|
1395
|
+
const secrets = await this.config.database.session.findTwoFaSecretsByUserId(user.id);
|
|
901
1396
|
for (const s of secrets) {
|
|
902
1397
|
if (s.twoFaSecret && await verifyTotp(code, cleanBase32String(s.twoFaSecret))) {
|
|
903
1398
|
validCode = true;
|
|
@@ -905,14 +1400,13 @@ var BaseProcedureFactory = class {
|
|
|
905
1400
|
}
|
|
906
1401
|
}
|
|
907
1402
|
if (!validCode) {
|
|
908
|
-
const checkOTP = await this.config.
|
|
909
|
-
|
|
910
|
-
|
|
1403
|
+
const checkOTP = await this.config.database.otp.findValidByUserAndCode(
|
|
1404
|
+
user.id,
|
|
1405
|
+
Number(code)
|
|
1406
|
+
);
|
|
911
1407
|
if (checkOTP) {
|
|
912
1408
|
validCode = true;
|
|
913
|
-
await this.config.
|
|
914
|
-
where: { id: checkOTP.id }
|
|
915
|
-
});
|
|
1409
|
+
await this.config.database.otp.delete(checkOTP.id);
|
|
916
1410
|
}
|
|
917
1411
|
}
|
|
918
1412
|
if (!validCode) {
|
|
@@ -923,24 +1417,11 @@ var BaseProcedureFactory = class {
|
|
|
923
1417
|
}
|
|
924
1418
|
}
|
|
925
1419
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
926
|
-
const session = await this.config.
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
...extraSessionData
|
|
932
|
-
},
|
|
933
|
-
select: {
|
|
934
|
-
id: true,
|
|
935
|
-
userId: true,
|
|
936
|
-
socketId: true,
|
|
937
|
-
browserName: true,
|
|
938
|
-
issuedAt: true,
|
|
939
|
-
lastUsed: true,
|
|
940
|
-
revokedAt: true,
|
|
941
|
-
deviceId: true,
|
|
942
|
-
twoFaSecret: true
|
|
943
|
-
}
|
|
1420
|
+
const session = await this.config.database.session.create({
|
|
1421
|
+
userId: user.id,
|
|
1422
|
+
browserName: detectBrowser(userAgent),
|
|
1423
|
+
socketId: null,
|
|
1424
|
+
...extraSessionData
|
|
944
1425
|
});
|
|
945
1426
|
if (this.config.hooks?.onUserLogin) {
|
|
946
1427
|
await this.config.hooks.onUserLogin(user.id, session.id);
|
|
@@ -971,15 +1452,9 @@ var BaseProcedureFactory = class {
|
|
|
971
1452
|
return this.authProcedure.meta({ ignoreExpiration: true }).mutation(async ({ ctx }) => {
|
|
972
1453
|
const { userId, sessionId } = ctx;
|
|
973
1454
|
if (sessionId) {
|
|
974
|
-
await this.config.
|
|
975
|
-
where: { id: sessionId },
|
|
976
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
977
|
-
});
|
|
1455
|
+
await this.config.database.session.revoke(sessionId);
|
|
978
1456
|
if (userId) {
|
|
979
|
-
await this.config.
|
|
980
|
-
where: { id: userId },
|
|
981
|
-
data: { isActive: false }
|
|
982
|
-
});
|
|
1457
|
+
await this.config.database.user.update(userId, { isActive: false });
|
|
983
1458
|
}
|
|
984
1459
|
if (this.config.hooks?.afterLogout) {
|
|
985
1460
|
await this.config.hooks.afterLogout(userId, sessionId, ctx.socketId);
|
|
@@ -991,15 +1466,7 @@ var BaseProcedureFactory = class {
|
|
|
991
1466
|
}
|
|
992
1467
|
refresh() {
|
|
993
1468
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
994
|
-
const session = await this.config.
|
|
995
|
-
where: { id: ctx.sessionId },
|
|
996
|
-
data: { lastUsed: /* @__PURE__ */ new Date() },
|
|
997
|
-
select: {
|
|
998
|
-
id: true,
|
|
999
|
-
userId: true,
|
|
1000
|
-
user: { select: { verifiedHumanAt: true } }
|
|
1001
|
-
}
|
|
1002
|
-
});
|
|
1469
|
+
const session = await this.config.database.session.updateLastUsed(ctx.sessionId);
|
|
1003
1470
|
if (this.config.hooks?.onRefresh) {
|
|
1004
1471
|
this.config.hooks.onRefresh(session.userId).catch(() => {
|
|
1005
1472
|
});
|
|
@@ -1024,22 +1491,14 @@ var BaseProcedureFactory = class {
|
|
|
1024
1491
|
return this.authProcedure.input(endAllSessionsSchema).mutation(async ({ ctx, input }) => {
|
|
1025
1492
|
const { skipCurrentSession } = input;
|
|
1026
1493
|
const { userId, sessionId } = ctx;
|
|
1027
|
-
const sessionsToRevoke = await this.config.
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
await this.config.prisma.session.updateMany({
|
|
1036
|
-
where: {
|
|
1037
|
-
userId,
|
|
1038
|
-
revokedAt: null,
|
|
1039
|
-
...skipCurrentSession ? { NOT: { id: sessionId } } : {}
|
|
1040
|
-
},
|
|
1041
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1042
|
-
});
|
|
1494
|
+
const sessionsToRevoke = await this.config.database.session.findActiveByUserId(
|
|
1495
|
+
userId,
|
|
1496
|
+
skipCurrentSession ? sessionId : void 0
|
|
1497
|
+
);
|
|
1498
|
+
await this.config.database.session.revokeAllByUserId(
|
|
1499
|
+
userId,
|
|
1500
|
+
skipCurrentSession ? sessionId : void 0
|
|
1501
|
+
);
|
|
1043
1502
|
for (const session of sessionsToRevoke) {
|
|
1044
1503
|
if (this.config.hooks?.onSessionRevoked) {
|
|
1045
1504
|
await this.config.hooks.onSessionRevoked(
|
|
@@ -1050,10 +1509,7 @@ var BaseProcedureFactory = class {
|
|
|
1050
1509
|
}
|
|
1051
1510
|
}
|
|
1052
1511
|
if (!skipCurrentSession) {
|
|
1053
|
-
await this.config.
|
|
1054
|
-
where: { id: userId },
|
|
1055
|
-
data: { isActive: false }
|
|
1056
|
-
});
|
|
1512
|
+
await this.config.database.user.update(userId, { isActive: false });
|
|
1057
1513
|
}
|
|
1058
1514
|
return { success: true, revokedCount: sessionsToRevoke.length };
|
|
1059
1515
|
});
|
|
@@ -1068,10 +1524,7 @@ var BaseProcedureFactory = class {
|
|
|
1068
1524
|
message: "New password cannot be the same as current password"
|
|
1069
1525
|
});
|
|
1070
1526
|
}
|
|
1071
|
-
const user = await this.config.
|
|
1072
|
-
where: { id: userId },
|
|
1073
|
-
select: { password: true }
|
|
1074
|
-
});
|
|
1527
|
+
const user = await this.config.database.user.findById(userId);
|
|
1075
1528
|
if (!user) {
|
|
1076
1529
|
throw new import_server2.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1077
1530
|
}
|
|
@@ -1089,14 +1542,8 @@ var BaseProcedureFactory = class {
|
|
|
1089
1542
|
});
|
|
1090
1543
|
}
|
|
1091
1544
|
const hashedPassword = await hashPassword(newPassword);
|
|
1092
|
-
await this.config.
|
|
1093
|
-
|
|
1094
|
-
data: { password: hashedPassword }
|
|
1095
|
-
});
|
|
1096
|
-
await this.config.prisma.session.updateMany({
|
|
1097
|
-
where: { userId, revokedAt: null, NOT: { id: sessionId } },
|
|
1098
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1099
|
-
});
|
|
1545
|
+
await this.config.database.user.update(userId, { password: hashedPassword });
|
|
1546
|
+
await this.config.database.session.revokeAllByUserId(userId, sessionId);
|
|
1100
1547
|
if (this.config.hooks?.onPasswordChanged) {
|
|
1101
1548
|
await this.config.hooks.onPasswordChanged(userId);
|
|
1102
1549
|
}
|
|
@@ -1109,11 +1556,8 @@ var BaseProcedureFactory = class {
|
|
|
1109
1556
|
sendPasswordResetEmail() {
|
|
1110
1557
|
return this.procedure.input(requestPasswordResetSchema).mutation(async ({ input }) => {
|
|
1111
1558
|
const { email } = input;
|
|
1112
|
-
const user = await this.config.
|
|
1113
|
-
|
|
1114
|
-
select: { id: true, password: true, email: true }
|
|
1115
|
-
});
|
|
1116
|
-
if (!user) {
|
|
1559
|
+
const user = await this.config.database.user.findByEmailInsensitive(email);
|
|
1560
|
+
if (!user || user.status !== "ACTIVE") {
|
|
1117
1561
|
return { message: "If an account exists with that email, a reset link has been sent." };
|
|
1118
1562
|
}
|
|
1119
1563
|
if (!user.password) {
|
|
@@ -1122,10 +1566,8 @@ var BaseProcedureFactory = class {
|
|
|
1122
1566
|
message: "This account uses social login. Please use that method."
|
|
1123
1567
|
});
|
|
1124
1568
|
}
|
|
1125
|
-
await this.config.
|
|
1126
|
-
const passwordReset = await this.config.
|
|
1127
|
-
data: { userId: user.id }
|
|
1128
|
-
});
|
|
1569
|
+
await this.config.database.passwordReset.deleteAllByUserId(user.id);
|
|
1570
|
+
const passwordReset = await this.config.database.passwordReset.create(user.id);
|
|
1129
1571
|
if (this.config.emailService) {
|
|
1130
1572
|
await this.config.emailService.sendPasswordResetEmail(user.email, String(passwordReset.id));
|
|
1131
1573
|
}
|
|
@@ -1135,15 +1577,12 @@ var BaseProcedureFactory = class {
|
|
|
1135
1577
|
checkPasswordReset() {
|
|
1136
1578
|
return this.procedure.input(checkPasswordResetSchema).query(async ({ input }) => {
|
|
1137
1579
|
const { token } = input;
|
|
1138
|
-
const passwordReset = await this.config.
|
|
1139
|
-
where: { id: token },
|
|
1140
|
-
select: { id: true, createdAt: true, userId: true }
|
|
1141
|
-
});
|
|
1580
|
+
const passwordReset = await this.config.database.passwordReset.findById(token);
|
|
1142
1581
|
if (!passwordReset) {
|
|
1143
1582
|
throw new import_server2.TRPCError({ code: "NOT_FOUND", message: "Invalid reset token." });
|
|
1144
1583
|
}
|
|
1145
1584
|
if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
|
|
1146
|
-
await this.config.
|
|
1585
|
+
await this.config.database.passwordReset.delete(token);
|
|
1147
1586
|
throw new import_server2.TRPCError({ code: "FORBIDDEN", message: "Reset token expired." });
|
|
1148
1587
|
}
|
|
1149
1588
|
return { valid: true };
|
|
@@ -1152,31 +1591,23 @@ var BaseProcedureFactory = class {
|
|
|
1152
1591
|
resetPassword() {
|
|
1153
1592
|
return this.procedure.input(resetPasswordSchema).mutation(async ({ input }) => {
|
|
1154
1593
|
const { token, password } = input;
|
|
1155
|
-
const passwordReset = await this.config.
|
|
1156
|
-
where: { id: token },
|
|
1157
|
-
select: { id: true, createdAt: true, userId: true }
|
|
1158
|
-
});
|
|
1594
|
+
const passwordReset = await this.config.database.passwordReset.findById(token);
|
|
1159
1595
|
if (!passwordReset) {
|
|
1160
1596
|
throw new import_server2.TRPCError({ code: "NOT_FOUND", message: "Invalid reset token." });
|
|
1161
1597
|
}
|
|
1162
1598
|
if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
|
|
1163
|
-
await this.config.
|
|
1599
|
+
await this.config.database.passwordReset.delete(token);
|
|
1164
1600
|
throw new import_server2.TRPCError({ code: "FORBIDDEN", message: "Reset token expired." });
|
|
1165
1601
|
}
|
|
1166
1602
|
const hashedPassword = await hashPassword(password);
|
|
1167
|
-
await this.config.
|
|
1168
|
-
|
|
1169
|
-
data: { password: hashedPassword }
|
|
1170
|
-
});
|
|
1171
|
-
await this.config.prisma.passwordReset.delete({ where: { id: token } });
|
|
1172
|
-
const sessionsToRevoke = await this.config.prisma.session.findMany({
|
|
1173
|
-
where: { userId: passwordReset.userId, revokedAt: null },
|
|
1174
|
-
select: { id: true, socketId: true, userId: true }
|
|
1175
|
-
});
|
|
1176
|
-
await this.config.prisma.session.updateMany({
|
|
1177
|
-
where: { userId: passwordReset.userId, revokedAt: null },
|
|
1178
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1603
|
+
await this.config.database.user.update(passwordReset.userId, {
|
|
1604
|
+
password: hashedPassword
|
|
1179
1605
|
});
|
|
1606
|
+
await this.config.database.passwordReset.delete(token);
|
|
1607
|
+
const sessionsToRevoke = await this.config.database.session.findActiveByUserId(
|
|
1608
|
+
passwordReset.userId
|
|
1609
|
+
);
|
|
1610
|
+
await this.config.database.session.revokeAllByUserId(passwordReset.userId);
|
|
1180
1611
|
for (const session of sessionsToRevoke) {
|
|
1181
1612
|
if (this.config.hooks?.onSessionRevoked) {
|
|
1182
1613
|
await this.config.hooks.onSessionRevoked(
|
|
@@ -1213,9 +1644,9 @@ var BiometricProcedureFactory = class {
|
|
|
1213
1644
|
return this.authProcedure.input(biometricVerifySchema).mutation(async ({ ctx }) => {
|
|
1214
1645
|
this.checkConfig();
|
|
1215
1646
|
const { userId } = ctx;
|
|
1216
|
-
await this.config.
|
|
1217
|
-
|
|
1218
|
-
|
|
1647
|
+
await this.config.database.user.update(userId, {
|
|
1648
|
+
verifiedHumanAt: /* @__PURE__ */ new Date(),
|
|
1649
|
+
tag: "HUMAN"
|
|
1219
1650
|
});
|
|
1220
1651
|
if (this.config.hooks?.onBiometricVerified) {
|
|
1221
1652
|
await this.config.hooks.onBiometricVerified(userId);
|
|
@@ -1227,10 +1658,7 @@ var BiometricProcedureFactory = class {
|
|
|
1227
1658
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
1228
1659
|
this.checkConfig();
|
|
1229
1660
|
const { userId } = ctx;
|
|
1230
|
-
const user = await this.config.
|
|
1231
|
-
where: { id: userId },
|
|
1232
|
-
select: { verifiedHumanAt: true }
|
|
1233
|
-
});
|
|
1661
|
+
const user = await this.config.database.user.findById(userId);
|
|
1234
1662
|
if (!user) {
|
|
1235
1663
|
throw new import_server3.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1236
1664
|
}
|
|
@@ -1277,10 +1705,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1277
1705
|
return this.authProcedure.mutation(async ({ ctx }) => {
|
|
1278
1706
|
this.checkConfig();
|
|
1279
1707
|
const { userId } = ctx;
|
|
1280
|
-
const user = await this.config.
|
|
1281
|
-
where: { id: userId, status: "ACTIVE" },
|
|
1282
|
-
select: { id: true, email: true, emailVerificationStatus: true }
|
|
1283
|
-
});
|
|
1708
|
+
const user = await this.config.database.user.findActiveById(userId);
|
|
1284
1709
|
if (!user) {
|
|
1285
1710
|
throw new import_server4.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1286
1711
|
}
|
|
@@ -1288,9 +1713,9 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1288
1713
|
return { message: "Email is already verified", emailSent: false };
|
|
1289
1714
|
}
|
|
1290
1715
|
const otp = (0, import_node_crypto.randomUUID)();
|
|
1291
|
-
await this.config.
|
|
1292
|
-
|
|
1293
|
-
|
|
1716
|
+
await this.config.database.user.update(userId, {
|
|
1717
|
+
emailVerificationStatus: "PENDING",
|
|
1718
|
+
otpForEmailVerification: otp
|
|
1294
1719
|
});
|
|
1295
1720
|
if (this.config.emailService) {
|
|
1296
1721
|
try {
|
|
@@ -1308,10 +1733,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1308
1733
|
this.checkConfig();
|
|
1309
1734
|
const { userId } = ctx;
|
|
1310
1735
|
const { code } = input;
|
|
1311
|
-
const user = await this.config.
|
|
1312
|
-
where: { id: userId, status: "ACTIVE" },
|
|
1313
|
-
select: { id: true, emailVerificationStatus: true, otpForEmailVerification: true }
|
|
1314
|
-
});
|
|
1736
|
+
const user = await this.config.database.user.findActiveById(userId);
|
|
1315
1737
|
if (!user) {
|
|
1316
1738
|
throw new import_server4.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1317
1739
|
}
|
|
@@ -1321,9 +1743,9 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1321
1743
|
if (code !== user.otpForEmailVerification) {
|
|
1322
1744
|
throw new import_server4.TRPCError({ code: "BAD_REQUEST", message: "Invalid verification code" });
|
|
1323
1745
|
}
|
|
1324
|
-
await this.config.
|
|
1325
|
-
|
|
1326
|
-
|
|
1746
|
+
await this.config.database.user.update(userId, {
|
|
1747
|
+
emailVerificationStatus: "VERIFIED",
|
|
1748
|
+
otpForEmailVerification: null
|
|
1327
1749
|
});
|
|
1328
1750
|
if (this.config.hooks?.onEmailVerified) {
|
|
1329
1751
|
await this.config.hooks.onEmailVerified(userId);
|
|
@@ -1335,10 +1757,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1335
1757
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
1336
1758
|
this.checkConfig();
|
|
1337
1759
|
const { userId } = ctx;
|
|
1338
|
-
const user = await this.config.
|
|
1339
|
-
where: { id: userId },
|
|
1340
|
-
select: { emailVerificationStatus: true, email: true }
|
|
1341
|
-
});
|
|
1760
|
+
const user = await this.config.database.user.findById(userId);
|
|
1342
1761
|
if (!user) {
|
|
1343
1762
|
throw new import_server4.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1344
1763
|
}
|
|
@@ -1392,23 +1811,7 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1392
1811
|
message: "Email not provided by OAuth provider"
|
|
1393
1812
|
});
|
|
1394
1813
|
}
|
|
1395
|
-
let user = await this.config.
|
|
1396
|
-
where: {
|
|
1397
|
-
OR: [{ email: { equals: email, mode: "insensitive" } }, { oauthId: { equals: oauthId } }]
|
|
1398
|
-
},
|
|
1399
|
-
select: {
|
|
1400
|
-
id: true,
|
|
1401
|
-
status: true,
|
|
1402
|
-
email: true,
|
|
1403
|
-
username: true,
|
|
1404
|
-
password: true,
|
|
1405
|
-
oauthProvider: true,
|
|
1406
|
-
oauthId: true,
|
|
1407
|
-
twoFaEnabled: true,
|
|
1408
|
-
verifiedHumanAt: true,
|
|
1409
|
-
emailVerificationStatus: true
|
|
1410
|
-
}
|
|
1411
|
-
});
|
|
1814
|
+
let user = await this.config.database.user.findByEmailOrOAuthId(email, oauthId);
|
|
1412
1815
|
if (user?.oauthProvider && user.oauthProvider !== provider) {
|
|
1413
1816
|
throw new import_server5.TRPCError({
|
|
1414
1817
|
code: "BAD_REQUEST",
|
|
@@ -1423,19 +1826,17 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1423
1826
|
}
|
|
1424
1827
|
if (!user) {
|
|
1425
1828
|
const generateUsername = this.config.generateUsername ?? (() => `user_${Date.now()}`);
|
|
1426
|
-
user = await this.config.
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
verifiedHumanAt: null
|
|
1438
|
-
}
|
|
1829
|
+
user = await this.config.database.user.create({
|
|
1830
|
+
username: generateUsername(),
|
|
1831
|
+
email,
|
|
1832
|
+
password: null,
|
|
1833
|
+
emailVerificationStatus: "VERIFIED",
|
|
1834
|
+
oauthProvider: provider,
|
|
1835
|
+
oauthId,
|
|
1836
|
+
status: "ACTIVE",
|
|
1837
|
+
tag: this.config.features.biometric ? "BOT" : "HUMAN",
|
|
1838
|
+
twoFaEnabled: false,
|
|
1839
|
+
verifiedHumanAt: null
|
|
1439
1840
|
});
|
|
1440
1841
|
if (this.config.hooks?.onUserCreated) {
|
|
1441
1842
|
await this.config.hooks.onUserCreated(user.id, typedInput);
|
|
@@ -1451,24 +1852,11 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1451
1852
|
throw new import_server5.TRPCError({ code: "FORBIDDEN", message: "Your account has been banned." });
|
|
1452
1853
|
}
|
|
1453
1854
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
1454
|
-
const session = await this.config.
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
...extraSessionData
|
|
1460
|
-
},
|
|
1461
|
-
select: {
|
|
1462
|
-
id: true,
|
|
1463
|
-
userId: true,
|
|
1464
|
-
socketId: true,
|
|
1465
|
-
browserName: true,
|
|
1466
|
-
issuedAt: true,
|
|
1467
|
-
lastUsed: true,
|
|
1468
|
-
revokedAt: true,
|
|
1469
|
-
deviceId: true,
|
|
1470
|
-
twoFaSecret: true
|
|
1471
|
-
}
|
|
1855
|
+
const session = await this.config.database.session.create({
|
|
1856
|
+
userId: user.id,
|
|
1857
|
+
browserName: detectBrowser(userAgent),
|
|
1858
|
+
socketId: null,
|
|
1859
|
+
...extraSessionData
|
|
1472
1860
|
});
|
|
1473
1861
|
if (this.config.hooks?.onUserLogin) {
|
|
1474
1862
|
await this.config.hooks.onUserLogin(user.id, session.id);
|
|
@@ -1525,10 +1913,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1525
1913
|
return this.authProcedure.mutation(async ({ ctx }) => {
|
|
1526
1914
|
this.checkConfig();
|
|
1527
1915
|
const { userId, sessionId } = ctx;
|
|
1528
|
-
const user = await this.config.
|
|
1529
|
-
where: { id: userId },
|
|
1530
|
-
select: { twoFaEnabled: true, oauthProvider: true, password: true }
|
|
1531
|
-
});
|
|
1916
|
+
const user = await this.config.database.user.findById(userId);
|
|
1532
1917
|
if (!user) {
|
|
1533
1918
|
throw new import_server6.TRPCError({ code: "NOT_FOUND", message: "User not found." });
|
|
1534
1919
|
}
|
|
@@ -1542,10 +1927,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1542
1927
|
throw new import_server6.TRPCError({ code: "BAD_REQUEST", message: "2FA already enabled." });
|
|
1543
1928
|
}
|
|
1544
1929
|
if (this.config.features.twoFaRequiresDevice !== false) {
|
|
1545
|
-
const checkSession = await this.config.
|
|
1546
|
-
where: { userId, id: sessionId },
|
|
1547
|
-
select: { deviceId: true }
|
|
1548
|
-
});
|
|
1930
|
+
const checkSession = await this.config.database.session.findById(sessionId);
|
|
1549
1931
|
if (!checkSession?.deviceId) {
|
|
1550
1932
|
throw new import_server6.TRPCError({
|
|
1551
1933
|
code: "BAD_REQUEST",
|
|
@@ -1553,23 +1935,11 @@ var TwoFaProcedureFactory = class {
|
|
|
1553
1935
|
});
|
|
1554
1936
|
}
|
|
1555
1937
|
}
|
|
1556
|
-
await this.config.
|
|
1557
|
-
|
|
1558
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1559
|
-
});
|
|
1560
|
-
await this.config.prisma.session.updateMany({
|
|
1561
|
-
where: { userId, NOT: { id: sessionId } },
|
|
1562
|
-
data: { twoFaSecret: null }
|
|
1563
|
-
});
|
|
1938
|
+
await this.config.database.session.revokeAllByUserId(userId, sessionId);
|
|
1939
|
+
await this.config.database.session.clearTwoFaSecrets(userId, sessionId);
|
|
1564
1940
|
const secret = generateTotpSecret();
|
|
1565
|
-
await this.config.
|
|
1566
|
-
|
|
1567
|
-
data: { twoFaEnabled: true }
|
|
1568
|
-
});
|
|
1569
|
-
await this.config.prisma.session.update({
|
|
1570
|
-
where: { id: sessionId },
|
|
1571
|
-
data: { twoFaSecret: secret }
|
|
1572
|
-
});
|
|
1941
|
+
await this.config.database.user.update(userId, { twoFaEnabled: true });
|
|
1942
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: secret });
|
|
1573
1943
|
if (this.config.hooks?.onTwoFaStatusChanged) {
|
|
1574
1944
|
await this.config.hooks.onTwoFaStatusChanged(userId, true);
|
|
1575
1945
|
}
|
|
@@ -1581,10 +1951,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1581
1951
|
this.checkConfig();
|
|
1582
1952
|
const { userId, sessionId } = ctx;
|
|
1583
1953
|
const { password } = input;
|
|
1584
|
-
const user = await this.config.
|
|
1585
|
-
where: { id: userId },
|
|
1586
|
-
select: { password: true, status: true, oauthProvider: true }
|
|
1587
|
-
});
|
|
1954
|
+
const user = await this.config.database.user.findById(userId);
|
|
1588
1955
|
if (!user) {
|
|
1589
1956
|
throw new import_server6.TRPCError({ code: "NOT_FOUND", message: "User not found." });
|
|
1590
1957
|
}
|
|
@@ -1607,14 +1974,8 @@ var TwoFaProcedureFactory = class {
|
|
|
1607
1974
|
if (!isMatch) {
|
|
1608
1975
|
throw new import_server6.TRPCError({ code: "FORBIDDEN", message: "Incorrect password." });
|
|
1609
1976
|
}
|
|
1610
|
-
await this.config.
|
|
1611
|
-
|
|
1612
|
-
data: { twoFaEnabled: false }
|
|
1613
|
-
});
|
|
1614
|
-
await this.config.prisma.session.update({
|
|
1615
|
-
where: { id: sessionId },
|
|
1616
|
-
data: { twoFaSecret: null }
|
|
1617
|
-
});
|
|
1977
|
+
await this.config.database.user.update(userId, { twoFaEnabled: false });
|
|
1978
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: null });
|
|
1618
1979
|
if (this.config.hooks?.onTwoFaStatusChanged) {
|
|
1619
1980
|
await this.config.hooks.onTwoFaStatusChanged(userId, false);
|
|
1620
1981
|
}
|
|
@@ -1626,10 +1987,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1626
1987
|
this.checkConfig();
|
|
1627
1988
|
const { userId, sessionId } = ctx;
|
|
1628
1989
|
const { pushCode } = input;
|
|
1629
|
-
const user = await this.config.
|
|
1630
|
-
where: { id: userId },
|
|
1631
|
-
select: { twoFaEnabled: true, oauthProvider: true }
|
|
1632
|
-
});
|
|
1990
|
+
const user = await this.config.database.user.findById(userId);
|
|
1633
1991
|
if (user?.oauthProvider) {
|
|
1634
1992
|
throw new import_server6.TRPCError({
|
|
1635
1993
|
code: "FORBIDDEN",
|
|
@@ -1639,10 +1997,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1639
1997
|
if (!user?.twoFaEnabled) {
|
|
1640
1998
|
throw new import_server6.TRPCError({ code: "BAD_REQUEST", message: "2FA not enabled." });
|
|
1641
1999
|
}
|
|
1642
|
-
const session = await this.config.
|
|
1643
|
-
where: { id: sessionId, userId },
|
|
1644
|
-
select: { twoFaSecret: true, device: { select: { pushToken: true } } }
|
|
1645
|
-
});
|
|
2000
|
+
const session = await this.config.database.session.findByIdWithDevice(sessionId, userId);
|
|
1646
2001
|
if (!session?.device) {
|
|
1647
2002
|
throw new import_server6.TRPCError({ code: "BAD_REQUEST", message: "Invalid request" });
|
|
1648
2003
|
}
|
|
@@ -1654,10 +2009,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1654
2009
|
return { secret: session.twoFaSecret };
|
|
1655
2010
|
}
|
|
1656
2011
|
const secret = generateTotpSecret();
|
|
1657
|
-
await this.config.
|
|
1658
|
-
where: { id: sessionId },
|
|
1659
|
-
data: { twoFaSecret: secret }
|
|
1660
|
-
});
|
|
2012
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: secret });
|
|
1661
2013
|
return { secret };
|
|
1662
2014
|
});
|
|
1663
2015
|
}
|
|
@@ -1665,11 +2017,8 @@ var TwoFaProcedureFactory = class {
|
|
|
1665
2017
|
return this.procedure.input(twoFaResetSchema).mutation(async ({ input }) => {
|
|
1666
2018
|
this.checkConfig();
|
|
1667
2019
|
const { username, password } = input;
|
|
1668
|
-
const user = await this.config.
|
|
1669
|
-
|
|
1670
|
-
select: { id: true, password: true, email: true }
|
|
1671
|
-
});
|
|
1672
|
-
if (!user) {
|
|
2020
|
+
const user = await this.config.database.user.findByUsernameInsensitive(username);
|
|
2021
|
+
if (!user || !user.twoFaEnabled) {
|
|
1673
2022
|
throw new import_server6.TRPCError({ code: "UNAUTHORIZED", message: "Invalid credentials." });
|
|
1674
2023
|
}
|
|
1675
2024
|
if (!user.password) {
|
|
@@ -1684,9 +2033,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1684
2033
|
}
|
|
1685
2034
|
const otp = generateOtp();
|
|
1686
2035
|
const expiresAt = new Date(Date.now() + this.config.tokenSettings.otpValidityMs);
|
|
1687
|
-
await this.config.
|
|
1688
|
-
data: { userId: user.id, code: otp, expiresAt }
|
|
1689
|
-
});
|
|
2036
|
+
await this.config.database.otp.create({ userId: user.id, code: otp, expiresAt });
|
|
1690
2037
|
if (this.config.emailService) {
|
|
1691
2038
|
await this.config.emailService.sendOTPEmail(user.email, otp);
|
|
1692
2039
|
}
|
|
@@ -1697,30 +2044,17 @@ var TwoFaProcedureFactory = class {
|
|
|
1697
2044
|
return this.procedure.input(twoFaResetVerifySchema).mutation(async ({ input }) => {
|
|
1698
2045
|
this.checkConfig();
|
|
1699
2046
|
const { code, username } = input;
|
|
1700
|
-
const user = await this.config.
|
|
1701
|
-
where: { username: { equals: username, mode: "insensitive" } },
|
|
1702
|
-
select: { id: true }
|
|
1703
|
-
});
|
|
2047
|
+
const user = await this.config.database.user.findByUsernameInsensitive(username);
|
|
1704
2048
|
if (!user) {
|
|
1705
2049
|
throw new import_server6.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1706
2050
|
}
|
|
1707
|
-
const otp = await this.config.
|
|
1708
|
-
where: { userId: user.id, code, expiresAt: { gte: /* @__PURE__ */ new Date() } }
|
|
1709
|
-
});
|
|
2051
|
+
const otp = await this.config.database.otp.findValidByUserAndCode(user.id, code);
|
|
1710
2052
|
if (!otp) {
|
|
1711
2053
|
throw new import_server6.TRPCError({ code: "FORBIDDEN", message: "Invalid or expired OTP" });
|
|
1712
2054
|
}
|
|
1713
|
-
await this.config.
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
await this.config.prisma.user.update({
|
|
1717
|
-
where: { id: user.id },
|
|
1718
|
-
data: { twoFaEnabled: false }
|
|
1719
|
-
});
|
|
1720
|
-
await this.config.prisma.session.updateMany({
|
|
1721
|
-
where: { userId: user.id },
|
|
1722
|
-
data: { twoFaSecret: null }
|
|
1723
|
-
});
|
|
2055
|
+
await this.config.database.otp.delete(otp.id);
|
|
2056
|
+
await this.config.database.user.update(user.id, { twoFaEnabled: false });
|
|
2057
|
+
await this.config.database.session.clearTwoFaSecrets(user.id);
|
|
1724
2058
|
return { success: true, message: "2FA has been reset." };
|
|
1725
2059
|
});
|
|
1726
2060
|
}
|
|
@@ -1729,36 +2063,14 @@ var TwoFaProcedureFactory = class {
|
|
|
1729
2063
|
this.checkConfig();
|
|
1730
2064
|
const { userId, sessionId } = ctx;
|
|
1731
2065
|
const { pushToken } = input;
|
|
1732
|
-
await this.config.
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
},
|
|
1739
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1740
|
-
});
|
|
1741
|
-
const checkDevice = await this.config.prisma.device.findFirst({
|
|
1742
|
-
where: {
|
|
1743
|
-
pushToken,
|
|
1744
|
-
sessions: { some: { id: sessionId } },
|
|
1745
|
-
users: { some: { id: userId } }
|
|
1746
|
-
},
|
|
1747
|
-
select: { id: true }
|
|
1748
|
-
});
|
|
2066
|
+
await this.config.database.session.revokeByDevicePushToken(userId, pushToken, sessionId);
|
|
2067
|
+
const checkDevice = await this.config.database.device.findByTokenSessionAndUser(
|
|
2068
|
+
pushToken,
|
|
2069
|
+
sessionId,
|
|
2070
|
+
userId
|
|
2071
|
+
);
|
|
1749
2072
|
if (!checkDevice) {
|
|
1750
|
-
await this.config.
|
|
1751
|
-
where: { pushToken },
|
|
1752
|
-
create: {
|
|
1753
|
-
pushToken,
|
|
1754
|
-
sessions: { connect: { id: sessionId } },
|
|
1755
|
-
users: { connect: { id: userId } }
|
|
1756
|
-
},
|
|
1757
|
-
update: {
|
|
1758
|
-
sessions: { connect: { id: sessionId } },
|
|
1759
|
-
users: { connect: { id: userId } }
|
|
1760
|
-
}
|
|
1761
|
-
});
|
|
2073
|
+
await this.config.database.device.upsertByPushToken(pushToken, sessionId, userId);
|
|
1762
2074
|
}
|
|
1763
2075
|
return { registered: true };
|
|
1764
2076
|
});
|
|
@@ -1768,30 +2080,13 @@ var TwoFaProcedureFactory = class {
|
|
|
1768
2080
|
this.checkConfig();
|
|
1769
2081
|
const { userId } = ctx;
|
|
1770
2082
|
const { pushToken } = input;
|
|
1771
|
-
const device = await this.config.
|
|
1772
|
-
where: {
|
|
1773
|
-
users: { some: { id: userId } },
|
|
1774
|
-
pushToken
|
|
1775
|
-
},
|
|
1776
|
-
select: { id: true }
|
|
1777
|
-
});
|
|
2083
|
+
const device = await this.config.database.device.findByUserAndToken(userId, pushToken);
|
|
1778
2084
|
if (device) {
|
|
1779
|
-
await this.config.
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
where: { id: device.id },
|
|
1785
|
-
data: { users: { disconnect: { id: userId } } }
|
|
1786
|
-
});
|
|
1787
|
-
const remainingUsers = await this.config.prisma.device.findUnique({
|
|
1788
|
-
where: { id: device.id },
|
|
1789
|
-
select: { users: { select: { id: true }, take: 1 } }
|
|
1790
|
-
});
|
|
1791
|
-
if (!remainingUsers?.users.length) {
|
|
1792
|
-
await this.config.prisma.device.delete({
|
|
1793
|
-
where: { id: device.id }
|
|
1794
|
-
});
|
|
2085
|
+
await this.config.database.session.clearDeviceId(userId, device.id);
|
|
2086
|
+
await this.config.database.device.disconnectUser(device.id, userId);
|
|
2087
|
+
const hasUsers = await this.config.database.device.hasRemainingUsers(device.id);
|
|
2088
|
+
if (!hasUsers) {
|
|
2089
|
+
await this.config.database.device.delete(device.id);
|
|
1795
2090
|
}
|
|
1796
2091
|
}
|
|
1797
2092
|
return { deregistered: true };
|
|
@@ -1949,8 +2244,10 @@ function createAuthRouter(config) {
|
|
|
1949
2244
|
createAuthRouter,
|
|
1950
2245
|
createAuthToken,
|
|
1951
2246
|
createConsoleEmailAdapter,
|
|
2247
|
+
createDrizzleAdapter,
|
|
1952
2248
|
createNoopEmailAdapter,
|
|
1953
2249
|
createOAuthVerifier,
|
|
2250
|
+
createPrismaAdapter,
|
|
1954
2251
|
decodeToken,
|
|
1955
2252
|
defaultAuthConfig,
|
|
1956
2253
|
defaultCookieSettings,
|