@factiii/auth 0.5.3 → 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 +226 -16
- package/dist/index.d.ts +226 -16
- package/dist/index.js +676 -380
- package/dist/index.mjs +676 -381
- 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",
|
|
@@ -895,10 +1392,7 @@ var BaseProcedureFactory = class {
|
|
|
895
1392
|
};
|
|
896
1393
|
}
|
|
897
1394
|
let validCode = false;
|
|
898
|
-
const secrets = await this.config.
|
|
899
|
-
where: { userId: user.id, twoFaSecret: { not: null } },
|
|
900
|
-
select: { twoFaSecret: true }
|
|
901
|
-
});
|
|
1395
|
+
const secrets = await this.config.database.session.findTwoFaSecretsByUserId(user.id);
|
|
902
1396
|
for (const s of secrets) {
|
|
903
1397
|
if (s.twoFaSecret && await verifyTotp(code, cleanBase32String(s.twoFaSecret))) {
|
|
904
1398
|
validCode = true;
|
|
@@ -906,14 +1400,13 @@ var BaseProcedureFactory = class {
|
|
|
906
1400
|
}
|
|
907
1401
|
}
|
|
908
1402
|
if (!validCode) {
|
|
909
|
-
const checkOTP = await this.config.
|
|
910
|
-
|
|
911
|
-
|
|
1403
|
+
const checkOTP = await this.config.database.otp.findValidByUserAndCode(
|
|
1404
|
+
user.id,
|
|
1405
|
+
Number(code)
|
|
1406
|
+
);
|
|
912
1407
|
if (checkOTP) {
|
|
913
1408
|
validCode = true;
|
|
914
|
-
await this.config.
|
|
915
|
-
where: { id: checkOTP.id }
|
|
916
|
-
});
|
|
1409
|
+
await this.config.database.otp.delete(checkOTP.id);
|
|
917
1410
|
}
|
|
918
1411
|
}
|
|
919
1412
|
if (!validCode) {
|
|
@@ -924,24 +1417,11 @@ var BaseProcedureFactory = class {
|
|
|
924
1417
|
}
|
|
925
1418
|
}
|
|
926
1419
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
927
|
-
const session = await this.config.
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
...extraSessionData
|
|
933
|
-
},
|
|
934
|
-
select: {
|
|
935
|
-
id: true,
|
|
936
|
-
userId: true,
|
|
937
|
-
socketId: true,
|
|
938
|
-
browserName: true,
|
|
939
|
-
issuedAt: true,
|
|
940
|
-
lastUsed: true,
|
|
941
|
-
revokedAt: true,
|
|
942
|
-
deviceId: true,
|
|
943
|
-
twoFaSecret: true
|
|
944
|
-
}
|
|
1420
|
+
const session = await this.config.database.session.create({
|
|
1421
|
+
userId: user.id,
|
|
1422
|
+
browserName: detectBrowser(userAgent),
|
|
1423
|
+
socketId: null,
|
|
1424
|
+
...extraSessionData
|
|
945
1425
|
});
|
|
946
1426
|
if (this.config.hooks?.onUserLogin) {
|
|
947
1427
|
await this.config.hooks.onUserLogin(user.id, session.id);
|
|
@@ -972,15 +1452,9 @@ var BaseProcedureFactory = class {
|
|
|
972
1452
|
return this.authProcedure.meta({ ignoreExpiration: true }).mutation(async ({ ctx }) => {
|
|
973
1453
|
const { userId, sessionId } = ctx;
|
|
974
1454
|
if (sessionId) {
|
|
975
|
-
await this.config.
|
|
976
|
-
where: { id: sessionId },
|
|
977
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
978
|
-
});
|
|
1455
|
+
await this.config.database.session.revoke(sessionId);
|
|
979
1456
|
if (userId) {
|
|
980
|
-
await this.config.
|
|
981
|
-
where: { id: userId },
|
|
982
|
-
data: { isActive: false }
|
|
983
|
-
});
|
|
1457
|
+
await this.config.database.user.update(userId, { isActive: false });
|
|
984
1458
|
}
|
|
985
1459
|
if (this.config.hooks?.afterLogout) {
|
|
986
1460
|
await this.config.hooks.afterLogout(userId, sessionId, ctx.socketId);
|
|
@@ -992,15 +1466,7 @@ var BaseProcedureFactory = class {
|
|
|
992
1466
|
}
|
|
993
1467
|
refresh() {
|
|
994
1468
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
995
|
-
const session = await this.config.
|
|
996
|
-
where: { id: ctx.sessionId },
|
|
997
|
-
data: { lastUsed: /* @__PURE__ */ new Date() },
|
|
998
|
-
select: {
|
|
999
|
-
id: true,
|
|
1000
|
-
userId: true,
|
|
1001
|
-
user: { select: { verifiedHumanAt: true } }
|
|
1002
|
-
}
|
|
1003
|
-
});
|
|
1469
|
+
const session = await this.config.database.session.updateLastUsed(ctx.sessionId);
|
|
1004
1470
|
if (this.config.hooks?.onRefresh) {
|
|
1005
1471
|
this.config.hooks.onRefresh(session.userId).catch(() => {
|
|
1006
1472
|
});
|
|
@@ -1025,22 +1491,14 @@ var BaseProcedureFactory = class {
|
|
|
1025
1491
|
return this.authProcedure.input(endAllSessionsSchema).mutation(async ({ ctx, input }) => {
|
|
1026
1492
|
const { skipCurrentSession } = input;
|
|
1027
1493
|
const { userId, sessionId } = ctx;
|
|
1028
|
-
const sessionsToRevoke = await this.config.
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
await this.config.prisma.session.updateMany({
|
|
1037
|
-
where: {
|
|
1038
|
-
userId,
|
|
1039
|
-
revokedAt: null,
|
|
1040
|
-
...skipCurrentSession ? { NOT: { id: sessionId } } : {}
|
|
1041
|
-
},
|
|
1042
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1043
|
-
});
|
|
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
|
+
);
|
|
1044
1502
|
for (const session of sessionsToRevoke) {
|
|
1045
1503
|
if (this.config.hooks?.onSessionRevoked) {
|
|
1046
1504
|
await this.config.hooks.onSessionRevoked(
|
|
@@ -1051,10 +1509,7 @@ var BaseProcedureFactory = class {
|
|
|
1051
1509
|
}
|
|
1052
1510
|
}
|
|
1053
1511
|
if (!skipCurrentSession) {
|
|
1054
|
-
await this.config.
|
|
1055
|
-
where: { id: userId },
|
|
1056
|
-
data: { isActive: false }
|
|
1057
|
-
});
|
|
1512
|
+
await this.config.database.user.update(userId, { isActive: false });
|
|
1058
1513
|
}
|
|
1059
1514
|
return { success: true, revokedCount: sessionsToRevoke.length };
|
|
1060
1515
|
});
|
|
@@ -1069,10 +1524,7 @@ var BaseProcedureFactory = class {
|
|
|
1069
1524
|
message: "New password cannot be the same as current password"
|
|
1070
1525
|
});
|
|
1071
1526
|
}
|
|
1072
|
-
const user = await this.config.
|
|
1073
|
-
where: { id: userId },
|
|
1074
|
-
select: { password: true }
|
|
1075
|
-
});
|
|
1527
|
+
const user = await this.config.database.user.findById(userId);
|
|
1076
1528
|
if (!user) {
|
|
1077
1529
|
throw new import_server2.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1078
1530
|
}
|
|
@@ -1090,14 +1542,8 @@ var BaseProcedureFactory = class {
|
|
|
1090
1542
|
});
|
|
1091
1543
|
}
|
|
1092
1544
|
const hashedPassword = await hashPassword(newPassword);
|
|
1093
|
-
await this.config.
|
|
1094
|
-
|
|
1095
|
-
data: { password: hashedPassword }
|
|
1096
|
-
});
|
|
1097
|
-
await this.config.prisma.session.updateMany({
|
|
1098
|
-
where: { userId, revokedAt: null, NOT: { id: sessionId } },
|
|
1099
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1100
|
-
});
|
|
1545
|
+
await this.config.database.user.update(userId, { password: hashedPassword });
|
|
1546
|
+
await this.config.database.session.revokeAllByUserId(userId, sessionId);
|
|
1101
1547
|
if (this.config.hooks?.onPasswordChanged) {
|
|
1102
1548
|
await this.config.hooks.onPasswordChanged(userId);
|
|
1103
1549
|
}
|
|
@@ -1110,11 +1556,8 @@ var BaseProcedureFactory = class {
|
|
|
1110
1556
|
sendPasswordResetEmail() {
|
|
1111
1557
|
return this.procedure.input(requestPasswordResetSchema).mutation(async ({ input }) => {
|
|
1112
1558
|
const { email } = input;
|
|
1113
|
-
const user = await this.config.
|
|
1114
|
-
|
|
1115
|
-
select: { id: true, password: true, email: true }
|
|
1116
|
-
});
|
|
1117
|
-
if (!user) {
|
|
1559
|
+
const user = await this.config.database.user.findByEmailInsensitive(email);
|
|
1560
|
+
if (!user || user.status !== "ACTIVE") {
|
|
1118
1561
|
return { message: "If an account exists with that email, a reset link has been sent." };
|
|
1119
1562
|
}
|
|
1120
1563
|
if (!user.password) {
|
|
@@ -1123,10 +1566,8 @@ var BaseProcedureFactory = class {
|
|
|
1123
1566
|
message: "This account uses social login. Please use that method."
|
|
1124
1567
|
});
|
|
1125
1568
|
}
|
|
1126
|
-
await this.config.
|
|
1127
|
-
const passwordReset = await this.config.
|
|
1128
|
-
data: { userId: user.id }
|
|
1129
|
-
});
|
|
1569
|
+
await this.config.database.passwordReset.deleteAllByUserId(user.id);
|
|
1570
|
+
const passwordReset = await this.config.database.passwordReset.create(user.id);
|
|
1130
1571
|
if (this.config.emailService) {
|
|
1131
1572
|
await this.config.emailService.sendPasswordResetEmail(user.email, String(passwordReset.id));
|
|
1132
1573
|
}
|
|
@@ -1136,15 +1577,12 @@ var BaseProcedureFactory = class {
|
|
|
1136
1577
|
checkPasswordReset() {
|
|
1137
1578
|
return this.procedure.input(checkPasswordResetSchema).query(async ({ input }) => {
|
|
1138
1579
|
const { token } = input;
|
|
1139
|
-
const passwordReset = await this.config.
|
|
1140
|
-
where: { id: token },
|
|
1141
|
-
select: { id: true, createdAt: true, userId: true }
|
|
1142
|
-
});
|
|
1580
|
+
const passwordReset = await this.config.database.passwordReset.findById(token);
|
|
1143
1581
|
if (!passwordReset) {
|
|
1144
1582
|
throw new import_server2.TRPCError({ code: "NOT_FOUND", message: "Invalid reset token." });
|
|
1145
1583
|
}
|
|
1146
1584
|
if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
|
|
1147
|
-
await this.config.
|
|
1585
|
+
await this.config.database.passwordReset.delete(token);
|
|
1148
1586
|
throw new import_server2.TRPCError({ code: "FORBIDDEN", message: "Reset token expired." });
|
|
1149
1587
|
}
|
|
1150
1588
|
return { valid: true };
|
|
@@ -1153,31 +1591,23 @@ var BaseProcedureFactory = class {
|
|
|
1153
1591
|
resetPassword() {
|
|
1154
1592
|
return this.procedure.input(resetPasswordSchema).mutation(async ({ input }) => {
|
|
1155
1593
|
const { token, password } = input;
|
|
1156
|
-
const passwordReset = await this.config.
|
|
1157
|
-
where: { id: token },
|
|
1158
|
-
select: { id: true, createdAt: true, userId: true }
|
|
1159
|
-
});
|
|
1594
|
+
const passwordReset = await this.config.database.passwordReset.findById(token);
|
|
1160
1595
|
if (!passwordReset) {
|
|
1161
1596
|
throw new import_server2.TRPCError({ code: "NOT_FOUND", message: "Invalid reset token." });
|
|
1162
1597
|
}
|
|
1163
1598
|
if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
|
|
1164
|
-
await this.config.
|
|
1599
|
+
await this.config.database.passwordReset.delete(token);
|
|
1165
1600
|
throw new import_server2.TRPCError({ code: "FORBIDDEN", message: "Reset token expired." });
|
|
1166
1601
|
}
|
|
1167
1602
|
const hashedPassword = await hashPassword(password);
|
|
1168
|
-
await this.config.
|
|
1169
|
-
|
|
1170
|
-
data: { password: hashedPassword }
|
|
1171
|
-
});
|
|
1172
|
-
await this.config.prisma.passwordReset.delete({ where: { id: token } });
|
|
1173
|
-
const sessionsToRevoke = await this.config.prisma.session.findMany({
|
|
1174
|
-
where: { userId: passwordReset.userId, revokedAt: null },
|
|
1175
|
-
select: { id: true, socketId: true, userId: true }
|
|
1176
|
-
});
|
|
1177
|
-
await this.config.prisma.session.updateMany({
|
|
1178
|
-
where: { userId: passwordReset.userId, revokedAt: null },
|
|
1179
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1603
|
+
await this.config.database.user.update(passwordReset.userId, {
|
|
1604
|
+
password: hashedPassword
|
|
1180
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);
|
|
1181
1611
|
for (const session of sessionsToRevoke) {
|
|
1182
1612
|
if (this.config.hooks?.onSessionRevoked) {
|
|
1183
1613
|
await this.config.hooks.onSessionRevoked(
|
|
@@ -1214,9 +1644,9 @@ var BiometricProcedureFactory = class {
|
|
|
1214
1644
|
return this.authProcedure.input(biometricVerifySchema).mutation(async ({ ctx }) => {
|
|
1215
1645
|
this.checkConfig();
|
|
1216
1646
|
const { userId } = ctx;
|
|
1217
|
-
await this.config.
|
|
1218
|
-
|
|
1219
|
-
|
|
1647
|
+
await this.config.database.user.update(userId, {
|
|
1648
|
+
verifiedHumanAt: /* @__PURE__ */ new Date(),
|
|
1649
|
+
tag: "HUMAN"
|
|
1220
1650
|
});
|
|
1221
1651
|
if (this.config.hooks?.onBiometricVerified) {
|
|
1222
1652
|
await this.config.hooks.onBiometricVerified(userId);
|
|
@@ -1228,10 +1658,7 @@ var BiometricProcedureFactory = class {
|
|
|
1228
1658
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
1229
1659
|
this.checkConfig();
|
|
1230
1660
|
const { userId } = ctx;
|
|
1231
|
-
const user = await this.config.
|
|
1232
|
-
where: { id: userId },
|
|
1233
|
-
select: { verifiedHumanAt: true }
|
|
1234
|
-
});
|
|
1661
|
+
const user = await this.config.database.user.findById(userId);
|
|
1235
1662
|
if (!user) {
|
|
1236
1663
|
throw new import_server3.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1237
1664
|
}
|
|
@@ -1278,10 +1705,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1278
1705
|
return this.authProcedure.mutation(async ({ ctx }) => {
|
|
1279
1706
|
this.checkConfig();
|
|
1280
1707
|
const { userId } = ctx;
|
|
1281
|
-
const user = await this.config.
|
|
1282
|
-
where: { id: userId, status: "ACTIVE" },
|
|
1283
|
-
select: { id: true, email: true, emailVerificationStatus: true }
|
|
1284
|
-
});
|
|
1708
|
+
const user = await this.config.database.user.findActiveById(userId);
|
|
1285
1709
|
if (!user) {
|
|
1286
1710
|
throw new import_server4.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1287
1711
|
}
|
|
@@ -1289,9 +1713,9 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1289
1713
|
return { message: "Email is already verified", emailSent: false };
|
|
1290
1714
|
}
|
|
1291
1715
|
const otp = (0, import_node_crypto.randomUUID)();
|
|
1292
|
-
await this.config.
|
|
1293
|
-
|
|
1294
|
-
|
|
1716
|
+
await this.config.database.user.update(userId, {
|
|
1717
|
+
emailVerificationStatus: "PENDING",
|
|
1718
|
+
otpForEmailVerification: otp
|
|
1295
1719
|
});
|
|
1296
1720
|
if (this.config.emailService) {
|
|
1297
1721
|
try {
|
|
@@ -1309,10 +1733,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1309
1733
|
this.checkConfig();
|
|
1310
1734
|
const { userId } = ctx;
|
|
1311
1735
|
const { code } = input;
|
|
1312
|
-
const user = await this.config.
|
|
1313
|
-
where: { id: userId, status: "ACTIVE" },
|
|
1314
|
-
select: { id: true, emailVerificationStatus: true, otpForEmailVerification: true }
|
|
1315
|
-
});
|
|
1736
|
+
const user = await this.config.database.user.findActiveById(userId);
|
|
1316
1737
|
if (!user) {
|
|
1317
1738
|
throw new import_server4.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1318
1739
|
}
|
|
@@ -1322,9 +1743,9 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1322
1743
|
if (code !== user.otpForEmailVerification) {
|
|
1323
1744
|
throw new import_server4.TRPCError({ code: "BAD_REQUEST", message: "Invalid verification code" });
|
|
1324
1745
|
}
|
|
1325
|
-
await this.config.
|
|
1326
|
-
|
|
1327
|
-
|
|
1746
|
+
await this.config.database.user.update(userId, {
|
|
1747
|
+
emailVerificationStatus: "VERIFIED",
|
|
1748
|
+
otpForEmailVerification: null
|
|
1328
1749
|
});
|
|
1329
1750
|
if (this.config.hooks?.onEmailVerified) {
|
|
1330
1751
|
await this.config.hooks.onEmailVerified(userId);
|
|
@@ -1336,10 +1757,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1336
1757
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
1337
1758
|
this.checkConfig();
|
|
1338
1759
|
const { userId } = ctx;
|
|
1339
|
-
const user = await this.config.
|
|
1340
|
-
where: { id: userId },
|
|
1341
|
-
select: { emailVerificationStatus: true, email: true }
|
|
1342
|
-
});
|
|
1760
|
+
const user = await this.config.database.user.findById(userId);
|
|
1343
1761
|
if (!user) {
|
|
1344
1762
|
throw new import_server4.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1345
1763
|
}
|
|
@@ -1393,23 +1811,7 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1393
1811
|
message: "Email not provided by OAuth provider"
|
|
1394
1812
|
});
|
|
1395
1813
|
}
|
|
1396
|
-
let user = await this.config.
|
|
1397
|
-
where: {
|
|
1398
|
-
OR: [{ email: { equals: email, mode: "insensitive" } }, { oauthId: { equals: oauthId } }]
|
|
1399
|
-
},
|
|
1400
|
-
select: {
|
|
1401
|
-
id: true,
|
|
1402
|
-
status: true,
|
|
1403
|
-
email: true,
|
|
1404
|
-
username: true,
|
|
1405
|
-
password: true,
|
|
1406
|
-
oauthProvider: true,
|
|
1407
|
-
oauthId: true,
|
|
1408
|
-
twoFaEnabled: true,
|
|
1409
|
-
verifiedHumanAt: true,
|
|
1410
|
-
emailVerificationStatus: true
|
|
1411
|
-
}
|
|
1412
|
-
});
|
|
1814
|
+
let user = await this.config.database.user.findByEmailOrOAuthId(email, oauthId);
|
|
1413
1815
|
if (user?.oauthProvider && user.oauthProvider !== provider) {
|
|
1414
1816
|
throw new import_server5.TRPCError({
|
|
1415
1817
|
code: "BAD_REQUEST",
|
|
@@ -1424,19 +1826,17 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1424
1826
|
}
|
|
1425
1827
|
if (!user) {
|
|
1426
1828
|
const generateUsername = this.config.generateUsername ?? (() => `user_${Date.now()}`);
|
|
1427
|
-
user = await this.config.
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
verifiedHumanAt: null
|
|
1439
|
-
}
|
|
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
|
|
1440
1840
|
});
|
|
1441
1841
|
if (this.config.hooks?.onUserCreated) {
|
|
1442
1842
|
await this.config.hooks.onUserCreated(user.id, typedInput);
|
|
@@ -1452,24 +1852,11 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1452
1852
|
throw new import_server5.TRPCError({ code: "FORBIDDEN", message: "Your account has been banned." });
|
|
1453
1853
|
}
|
|
1454
1854
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
1455
|
-
const session = await this.config.
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
...extraSessionData
|
|
1461
|
-
},
|
|
1462
|
-
select: {
|
|
1463
|
-
id: true,
|
|
1464
|
-
userId: true,
|
|
1465
|
-
socketId: true,
|
|
1466
|
-
browserName: true,
|
|
1467
|
-
issuedAt: true,
|
|
1468
|
-
lastUsed: true,
|
|
1469
|
-
revokedAt: true,
|
|
1470
|
-
deviceId: true,
|
|
1471
|
-
twoFaSecret: true
|
|
1472
|
-
}
|
|
1855
|
+
const session = await this.config.database.session.create({
|
|
1856
|
+
userId: user.id,
|
|
1857
|
+
browserName: detectBrowser(userAgent),
|
|
1858
|
+
socketId: null,
|
|
1859
|
+
...extraSessionData
|
|
1473
1860
|
});
|
|
1474
1861
|
if (this.config.hooks?.onUserLogin) {
|
|
1475
1862
|
await this.config.hooks.onUserLogin(user.id, session.id);
|
|
@@ -1526,10 +1913,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1526
1913
|
return this.authProcedure.mutation(async ({ ctx }) => {
|
|
1527
1914
|
this.checkConfig();
|
|
1528
1915
|
const { userId, sessionId } = ctx;
|
|
1529
|
-
const user = await this.config.
|
|
1530
|
-
where: { id: userId },
|
|
1531
|
-
select: { twoFaEnabled: true, oauthProvider: true, password: true }
|
|
1532
|
-
});
|
|
1916
|
+
const user = await this.config.database.user.findById(userId);
|
|
1533
1917
|
if (!user) {
|
|
1534
1918
|
throw new import_server6.TRPCError({ code: "NOT_FOUND", message: "User not found." });
|
|
1535
1919
|
}
|
|
@@ -1543,10 +1927,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1543
1927
|
throw new import_server6.TRPCError({ code: "BAD_REQUEST", message: "2FA already enabled." });
|
|
1544
1928
|
}
|
|
1545
1929
|
if (this.config.features.twoFaRequiresDevice !== false) {
|
|
1546
|
-
const checkSession = await this.config.
|
|
1547
|
-
where: { userId, id: sessionId },
|
|
1548
|
-
select: { deviceId: true }
|
|
1549
|
-
});
|
|
1930
|
+
const checkSession = await this.config.database.session.findById(sessionId);
|
|
1550
1931
|
if (!checkSession?.deviceId) {
|
|
1551
1932
|
throw new import_server6.TRPCError({
|
|
1552
1933
|
code: "BAD_REQUEST",
|
|
@@ -1554,23 +1935,11 @@ var TwoFaProcedureFactory = class {
|
|
|
1554
1935
|
});
|
|
1555
1936
|
}
|
|
1556
1937
|
}
|
|
1557
|
-
await this.config.
|
|
1558
|
-
|
|
1559
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1560
|
-
});
|
|
1561
|
-
await this.config.prisma.session.updateMany({
|
|
1562
|
-
where: { userId, NOT: { id: sessionId } },
|
|
1563
|
-
data: { twoFaSecret: null }
|
|
1564
|
-
});
|
|
1938
|
+
await this.config.database.session.revokeAllByUserId(userId, sessionId);
|
|
1939
|
+
await this.config.database.session.clearTwoFaSecrets(userId, sessionId);
|
|
1565
1940
|
const secret = generateTotpSecret();
|
|
1566
|
-
await this.config.
|
|
1567
|
-
|
|
1568
|
-
data: { twoFaEnabled: true }
|
|
1569
|
-
});
|
|
1570
|
-
await this.config.prisma.session.update({
|
|
1571
|
-
where: { id: sessionId },
|
|
1572
|
-
data: { twoFaSecret: secret }
|
|
1573
|
-
});
|
|
1941
|
+
await this.config.database.user.update(userId, { twoFaEnabled: true });
|
|
1942
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: secret });
|
|
1574
1943
|
if (this.config.hooks?.onTwoFaStatusChanged) {
|
|
1575
1944
|
await this.config.hooks.onTwoFaStatusChanged(userId, true);
|
|
1576
1945
|
}
|
|
@@ -1582,10 +1951,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1582
1951
|
this.checkConfig();
|
|
1583
1952
|
const { userId, sessionId } = ctx;
|
|
1584
1953
|
const { password } = input;
|
|
1585
|
-
const user = await this.config.
|
|
1586
|
-
where: { id: userId },
|
|
1587
|
-
select: { password: true, status: true, oauthProvider: true }
|
|
1588
|
-
});
|
|
1954
|
+
const user = await this.config.database.user.findById(userId);
|
|
1589
1955
|
if (!user) {
|
|
1590
1956
|
throw new import_server6.TRPCError({ code: "NOT_FOUND", message: "User not found." });
|
|
1591
1957
|
}
|
|
@@ -1608,14 +1974,8 @@ var TwoFaProcedureFactory = class {
|
|
|
1608
1974
|
if (!isMatch) {
|
|
1609
1975
|
throw new import_server6.TRPCError({ code: "FORBIDDEN", message: "Incorrect password." });
|
|
1610
1976
|
}
|
|
1611
|
-
await this.config.
|
|
1612
|
-
|
|
1613
|
-
data: { twoFaEnabled: false }
|
|
1614
|
-
});
|
|
1615
|
-
await this.config.prisma.session.update({
|
|
1616
|
-
where: { id: sessionId },
|
|
1617
|
-
data: { twoFaSecret: null }
|
|
1618
|
-
});
|
|
1977
|
+
await this.config.database.user.update(userId, { twoFaEnabled: false });
|
|
1978
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: null });
|
|
1619
1979
|
if (this.config.hooks?.onTwoFaStatusChanged) {
|
|
1620
1980
|
await this.config.hooks.onTwoFaStatusChanged(userId, false);
|
|
1621
1981
|
}
|
|
@@ -1627,10 +1987,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1627
1987
|
this.checkConfig();
|
|
1628
1988
|
const { userId, sessionId } = ctx;
|
|
1629
1989
|
const { pushCode } = input;
|
|
1630
|
-
const user = await this.config.
|
|
1631
|
-
where: { id: userId },
|
|
1632
|
-
select: { twoFaEnabled: true, oauthProvider: true }
|
|
1633
|
-
});
|
|
1990
|
+
const user = await this.config.database.user.findById(userId);
|
|
1634
1991
|
if (user?.oauthProvider) {
|
|
1635
1992
|
throw new import_server6.TRPCError({
|
|
1636
1993
|
code: "FORBIDDEN",
|
|
@@ -1640,10 +1997,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1640
1997
|
if (!user?.twoFaEnabled) {
|
|
1641
1998
|
throw new import_server6.TRPCError({ code: "BAD_REQUEST", message: "2FA not enabled." });
|
|
1642
1999
|
}
|
|
1643
|
-
const session = await this.config.
|
|
1644
|
-
where: { id: sessionId, userId },
|
|
1645
|
-
select: { twoFaSecret: true, device: { select: { pushToken: true } } }
|
|
1646
|
-
});
|
|
2000
|
+
const session = await this.config.database.session.findByIdWithDevice(sessionId, userId);
|
|
1647
2001
|
if (!session?.device) {
|
|
1648
2002
|
throw new import_server6.TRPCError({ code: "BAD_REQUEST", message: "Invalid request" });
|
|
1649
2003
|
}
|
|
@@ -1655,10 +2009,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1655
2009
|
return { secret: session.twoFaSecret };
|
|
1656
2010
|
}
|
|
1657
2011
|
const secret = generateTotpSecret();
|
|
1658
|
-
await this.config.
|
|
1659
|
-
where: { id: sessionId },
|
|
1660
|
-
data: { twoFaSecret: secret }
|
|
1661
|
-
});
|
|
2012
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: secret });
|
|
1662
2013
|
return { secret };
|
|
1663
2014
|
});
|
|
1664
2015
|
}
|
|
@@ -1666,11 +2017,8 @@ var TwoFaProcedureFactory = class {
|
|
|
1666
2017
|
return this.procedure.input(twoFaResetSchema).mutation(async ({ input }) => {
|
|
1667
2018
|
this.checkConfig();
|
|
1668
2019
|
const { username, password } = input;
|
|
1669
|
-
const user = await this.config.
|
|
1670
|
-
|
|
1671
|
-
select: { id: true, password: true, email: true }
|
|
1672
|
-
});
|
|
1673
|
-
if (!user) {
|
|
2020
|
+
const user = await this.config.database.user.findByUsernameInsensitive(username);
|
|
2021
|
+
if (!user || !user.twoFaEnabled) {
|
|
1674
2022
|
throw new import_server6.TRPCError({ code: "UNAUTHORIZED", message: "Invalid credentials." });
|
|
1675
2023
|
}
|
|
1676
2024
|
if (!user.password) {
|
|
@@ -1685,9 +2033,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1685
2033
|
}
|
|
1686
2034
|
const otp = generateOtp();
|
|
1687
2035
|
const expiresAt = new Date(Date.now() + this.config.tokenSettings.otpValidityMs);
|
|
1688
|
-
await this.config.
|
|
1689
|
-
data: { userId: user.id, code: otp, expiresAt }
|
|
1690
|
-
});
|
|
2036
|
+
await this.config.database.otp.create({ userId: user.id, code: otp, expiresAt });
|
|
1691
2037
|
if (this.config.emailService) {
|
|
1692
2038
|
await this.config.emailService.sendOTPEmail(user.email, otp);
|
|
1693
2039
|
}
|
|
@@ -1698,30 +2044,17 @@ var TwoFaProcedureFactory = class {
|
|
|
1698
2044
|
return this.procedure.input(twoFaResetVerifySchema).mutation(async ({ input }) => {
|
|
1699
2045
|
this.checkConfig();
|
|
1700
2046
|
const { code, username } = input;
|
|
1701
|
-
const user = await this.config.
|
|
1702
|
-
where: { username: { equals: username, mode: "insensitive" } },
|
|
1703
|
-
select: { id: true }
|
|
1704
|
-
});
|
|
2047
|
+
const user = await this.config.database.user.findByUsernameInsensitive(username);
|
|
1705
2048
|
if (!user) {
|
|
1706
2049
|
throw new import_server6.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1707
2050
|
}
|
|
1708
|
-
const otp = await this.config.
|
|
1709
|
-
where: { userId: user.id, code, expiresAt: { gte: /* @__PURE__ */ new Date() } }
|
|
1710
|
-
});
|
|
2051
|
+
const otp = await this.config.database.otp.findValidByUserAndCode(user.id, code);
|
|
1711
2052
|
if (!otp) {
|
|
1712
2053
|
throw new import_server6.TRPCError({ code: "FORBIDDEN", message: "Invalid or expired OTP" });
|
|
1713
2054
|
}
|
|
1714
|
-
await this.config.
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
await this.config.prisma.user.update({
|
|
1718
|
-
where: { id: user.id },
|
|
1719
|
-
data: { twoFaEnabled: false }
|
|
1720
|
-
});
|
|
1721
|
-
await this.config.prisma.session.updateMany({
|
|
1722
|
-
where: { userId: user.id },
|
|
1723
|
-
data: { twoFaSecret: null }
|
|
1724
|
-
});
|
|
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);
|
|
1725
2058
|
return { success: true, message: "2FA has been reset." };
|
|
1726
2059
|
});
|
|
1727
2060
|
}
|
|
@@ -1730,36 +2063,14 @@ var TwoFaProcedureFactory = class {
|
|
|
1730
2063
|
this.checkConfig();
|
|
1731
2064
|
const { userId, sessionId } = ctx;
|
|
1732
2065
|
const { pushToken } = input;
|
|
1733
|
-
await this.config.
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
},
|
|
1740
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1741
|
-
});
|
|
1742
|
-
const checkDevice = await this.config.prisma.device.findFirst({
|
|
1743
|
-
where: {
|
|
1744
|
-
pushToken,
|
|
1745
|
-
sessions: { some: { id: sessionId } },
|
|
1746
|
-
users: { some: { id: userId } }
|
|
1747
|
-
},
|
|
1748
|
-
select: { id: true }
|
|
1749
|
-
});
|
|
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
|
+
);
|
|
1750
2072
|
if (!checkDevice) {
|
|
1751
|
-
await this.config.
|
|
1752
|
-
where: { pushToken },
|
|
1753
|
-
create: {
|
|
1754
|
-
pushToken,
|
|
1755
|
-
sessions: { connect: { id: sessionId } },
|
|
1756
|
-
users: { connect: { id: userId } }
|
|
1757
|
-
},
|
|
1758
|
-
update: {
|
|
1759
|
-
sessions: { connect: { id: sessionId } },
|
|
1760
|
-
users: { connect: { id: userId } }
|
|
1761
|
-
}
|
|
1762
|
-
});
|
|
2073
|
+
await this.config.database.device.upsertByPushToken(pushToken, sessionId, userId);
|
|
1763
2074
|
}
|
|
1764
2075
|
return { registered: true };
|
|
1765
2076
|
});
|
|
@@ -1769,30 +2080,13 @@ var TwoFaProcedureFactory = class {
|
|
|
1769
2080
|
this.checkConfig();
|
|
1770
2081
|
const { userId } = ctx;
|
|
1771
2082
|
const { pushToken } = input;
|
|
1772
|
-
const device = await this.config.
|
|
1773
|
-
where: {
|
|
1774
|
-
users: { some: { id: userId } },
|
|
1775
|
-
pushToken
|
|
1776
|
-
},
|
|
1777
|
-
select: { id: true }
|
|
1778
|
-
});
|
|
2083
|
+
const device = await this.config.database.device.findByUserAndToken(userId, pushToken);
|
|
1779
2084
|
if (device) {
|
|
1780
|
-
await this.config.
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
where: { id: device.id },
|
|
1786
|
-
data: { users: { disconnect: { id: userId } } }
|
|
1787
|
-
});
|
|
1788
|
-
const remainingUsers = await this.config.prisma.device.findUnique({
|
|
1789
|
-
where: { id: device.id },
|
|
1790
|
-
select: { users: { select: { id: true }, take: 1 } }
|
|
1791
|
-
});
|
|
1792
|
-
if (!remainingUsers?.users.length) {
|
|
1793
|
-
await this.config.prisma.device.delete({
|
|
1794
|
-
where: { id: device.id }
|
|
1795
|
-
});
|
|
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);
|
|
1796
2090
|
}
|
|
1797
2091
|
}
|
|
1798
2092
|
return { deregistered: true };
|
|
@@ -1950,8 +2244,10 @@ function createAuthRouter(config) {
|
|
|
1950
2244
|
createAuthRouter,
|
|
1951
2245
|
createAuthToken,
|
|
1952
2246
|
createConsoleEmailAdapter,
|
|
2247
|
+
createDrizzleAdapter,
|
|
1953
2248
|
createNoopEmailAdapter,
|
|
1954
2249
|
createOAuthVerifier,
|
|
2250
|
+
createPrismaAdapter,
|
|
1955
2251
|
decodeToken,
|
|
1956
2252
|
defaultAuthConfig,
|
|
1957
2253
|
defaultCookieSettings,
|