@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.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
__require,
|
|
2
3
|
biometricVerifySchema,
|
|
3
4
|
changePasswordSchema,
|
|
4
5
|
checkPasswordResetSchema,
|
|
@@ -17,11 +18,265 @@ import {
|
|
|
17
18
|
twoFaResetVerifySchema,
|
|
18
19
|
twoFaVerifySchema,
|
|
19
20
|
verifyEmailSchema
|
|
20
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-KUYH4DBN.mjs";
|
|
21
22
|
|
|
22
23
|
// src/middleware/authGuard.ts
|
|
23
24
|
import { TRPCError } from "@trpc/server";
|
|
24
25
|
|
|
26
|
+
// src/adapters/prismaAdapter.ts
|
|
27
|
+
function createPrismaAdapter(prisma) {
|
|
28
|
+
return {
|
|
29
|
+
user: {
|
|
30
|
+
async findByEmailInsensitive(email) {
|
|
31
|
+
return prisma.user.findFirst({
|
|
32
|
+
where: { email: { equals: email, mode: "insensitive" } }
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
async findByUsernameInsensitive(username) {
|
|
36
|
+
return prisma.user.findFirst({
|
|
37
|
+
where: { username: { equals: username, mode: "insensitive" } }
|
|
38
|
+
});
|
|
39
|
+
},
|
|
40
|
+
async findByEmailOrUsernameInsensitive(identifier) {
|
|
41
|
+
return prisma.user.findFirst({
|
|
42
|
+
where: {
|
|
43
|
+
OR: [
|
|
44
|
+
{ email: { equals: identifier, mode: "insensitive" } },
|
|
45
|
+
{ username: { equals: identifier, mode: "insensitive" } }
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
async findByEmailOrOAuthId(email, oauthId) {
|
|
51
|
+
return prisma.user.findFirst({
|
|
52
|
+
where: {
|
|
53
|
+
OR: [
|
|
54
|
+
{ email: { equals: email, mode: "insensitive" } },
|
|
55
|
+
{ oauthId: { equals: oauthId } }
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
async findById(id) {
|
|
61
|
+
return prisma.user.findUnique({ where: { id } });
|
|
62
|
+
},
|
|
63
|
+
async findActiveById(id) {
|
|
64
|
+
return prisma.user.findUnique({
|
|
65
|
+
where: { id, status: "ACTIVE" }
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
async create(data) {
|
|
69
|
+
return prisma.user.create({ data });
|
|
70
|
+
},
|
|
71
|
+
async update(id, data) {
|
|
72
|
+
return prisma.user.update({ where: { id }, data });
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
session: {
|
|
76
|
+
async findById(id) {
|
|
77
|
+
const session = await prisma.session.findUnique({
|
|
78
|
+
where: { id },
|
|
79
|
+
select: {
|
|
80
|
+
id: true,
|
|
81
|
+
userId: true,
|
|
82
|
+
socketId: true,
|
|
83
|
+
twoFaSecret: true,
|
|
84
|
+
browserName: true,
|
|
85
|
+
issuedAt: true,
|
|
86
|
+
lastUsed: true,
|
|
87
|
+
revokedAt: true,
|
|
88
|
+
deviceId: true,
|
|
89
|
+
user: { select: { status: true, verifiedHumanAt: true } }
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return session;
|
|
93
|
+
},
|
|
94
|
+
async create(data) {
|
|
95
|
+
return prisma.session.create({ data });
|
|
96
|
+
},
|
|
97
|
+
async update(id, data) {
|
|
98
|
+
return prisma.session.update({ where: { id }, data });
|
|
99
|
+
},
|
|
100
|
+
async updateLastUsed(id) {
|
|
101
|
+
const session = await prisma.session.update({
|
|
102
|
+
where: { id },
|
|
103
|
+
data: { lastUsed: /* @__PURE__ */ new Date() },
|
|
104
|
+
select: {
|
|
105
|
+
id: true,
|
|
106
|
+
userId: true,
|
|
107
|
+
socketId: true,
|
|
108
|
+
twoFaSecret: true,
|
|
109
|
+
browserName: true,
|
|
110
|
+
issuedAt: true,
|
|
111
|
+
lastUsed: true,
|
|
112
|
+
revokedAt: true,
|
|
113
|
+
deviceId: true,
|
|
114
|
+
user: { select: { verifiedHumanAt: true } }
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
return session;
|
|
118
|
+
},
|
|
119
|
+
async revoke(id) {
|
|
120
|
+
await prisma.session.update({
|
|
121
|
+
where: { id },
|
|
122
|
+
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
async findActiveByUserId(userId, excludeSessionId) {
|
|
126
|
+
return prisma.session.findMany({
|
|
127
|
+
where: {
|
|
128
|
+
userId,
|
|
129
|
+
revokedAt: null,
|
|
130
|
+
...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
|
|
131
|
+
},
|
|
132
|
+
select: { id: true, socketId: true, userId: true }
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
async revokeAllByUserId(userId, excludeSessionId) {
|
|
136
|
+
await prisma.session.updateMany({
|
|
137
|
+
where: {
|
|
138
|
+
userId,
|
|
139
|
+
revokedAt: null,
|
|
140
|
+
...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
|
|
141
|
+
},
|
|
142
|
+
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
143
|
+
});
|
|
144
|
+
},
|
|
145
|
+
async findTwoFaSecretsByUserId(userId) {
|
|
146
|
+
return prisma.session.findMany({
|
|
147
|
+
where: { userId, twoFaSecret: { not: null } },
|
|
148
|
+
select: { twoFaSecret: true }
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
async clearTwoFaSecrets(userId, excludeSessionId) {
|
|
152
|
+
await prisma.session.updateMany({
|
|
153
|
+
where: {
|
|
154
|
+
userId,
|
|
155
|
+
...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
|
|
156
|
+
},
|
|
157
|
+
data: { twoFaSecret: null }
|
|
158
|
+
});
|
|
159
|
+
},
|
|
160
|
+
async findByIdWithDevice(id, userId) {
|
|
161
|
+
const session = await prisma.session.findUnique({
|
|
162
|
+
where: { id, userId },
|
|
163
|
+
select: {
|
|
164
|
+
twoFaSecret: true,
|
|
165
|
+
deviceId: true,
|
|
166
|
+
device: { select: { pushToken: true } }
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
return session;
|
|
170
|
+
},
|
|
171
|
+
async revokeByDevicePushToken(userId, pushToken, excludeSessionId) {
|
|
172
|
+
await prisma.session.updateMany({
|
|
173
|
+
where: {
|
|
174
|
+
userId,
|
|
175
|
+
id: { not: excludeSessionId },
|
|
176
|
+
revokedAt: null,
|
|
177
|
+
device: { pushToken }
|
|
178
|
+
},
|
|
179
|
+
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
180
|
+
});
|
|
181
|
+
},
|
|
182
|
+
async clearDeviceId(userId, deviceId) {
|
|
183
|
+
await prisma.session.updateMany({
|
|
184
|
+
where: { userId, deviceId },
|
|
185
|
+
data: { deviceId: null }
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
otp: {
|
|
190
|
+
async findValidByUserAndCode(userId, code) {
|
|
191
|
+
return prisma.oTP.findFirst({
|
|
192
|
+
where: { userId, code, expiresAt: { gte: /* @__PURE__ */ new Date() } }
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
async create(data) {
|
|
196
|
+
return prisma.oTP.create({ data });
|
|
197
|
+
},
|
|
198
|
+
async delete(id) {
|
|
199
|
+
await prisma.oTP.delete({ where: { id } });
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
passwordReset: {
|
|
203
|
+
async findById(id) {
|
|
204
|
+
return prisma.passwordReset.findUnique({
|
|
205
|
+
where: { id },
|
|
206
|
+
select: { id: true, createdAt: true, userId: true }
|
|
207
|
+
});
|
|
208
|
+
},
|
|
209
|
+
async create(userId) {
|
|
210
|
+
return prisma.passwordReset.create({
|
|
211
|
+
data: { userId }
|
|
212
|
+
});
|
|
213
|
+
},
|
|
214
|
+
async delete(id) {
|
|
215
|
+
await prisma.passwordReset.delete({ where: { id } });
|
|
216
|
+
},
|
|
217
|
+
async deleteAllByUserId(userId) {
|
|
218
|
+
await prisma.passwordReset.deleteMany({ where: { userId } });
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
device: {
|
|
222
|
+
async findByTokenSessionAndUser(pushToken, sessionId, userId) {
|
|
223
|
+
return prisma.device.findFirst({
|
|
224
|
+
where: {
|
|
225
|
+
pushToken,
|
|
226
|
+
sessions: { some: { id: sessionId } },
|
|
227
|
+
users: { some: { id: userId } }
|
|
228
|
+
},
|
|
229
|
+
select: { id: true }
|
|
230
|
+
});
|
|
231
|
+
},
|
|
232
|
+
async upsertByPushToken(pushToken, sessionId, userId) {
|
|
233
|
+
await prisma.device.upsert({
|
|
234
|
+
where: { pushToken },
|
|
235
|
+
create: {
|
|
236
|
+
pushToken,
|
|
237
|
+
sessions: { connect: { id: sessionId } },
|
|
238
|
+
users: { connect: { id: userId } }
|
|
239
|
+
},
|
|
240
|
+
update: {
|
|
241
|
+
sessions: { connect: { id: sessionId } },
|
|
242
|
+
users: { connect: { id: userId } }
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
},
|
|
246
|
+
async findByUserAndToken(userId, pushToken) {
|
|
247
|
+
return prisma.device.findFirst({
|
|
248
|
+
where: { users: { some: { id: userId } }, pushToken },
|
|
249
|
+
select: { id: true }
|
|
250
|
+
});
|
|
251
|
+
},
|
|
252
|
+
async disconnectUser(deviceId, userId) {
|
|
253
|
+
await prisma.device.update({
|
|
254
|
+
where: { id: deviceId },
|
|
255
|
+
data: { users: { disconnect: { id: userId } } }
|
|
256
|
+
});
|
|
257
|
+
},
|
|
258
|
+
async hasRemainingUsers(deviceId) {
|
|
259
|
+
const result = await prisma.device.findUnique({
|
|
260
|
+
where: { id: deviceId },
|
|
261
|
+
select: { users: { select: { id: true }, take: 1 } }
|
|
262
|
+
});
|
|
263
|
+
return (result?.users.length ?? 0) > 0;
|
|
264
|
+
},
|
|
265
|
+
async delete(id) {
|
|
266
|
+
await prisma.device.delete({ where: { id } });
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
admin: {
|
|
270
|
+
async findByUserId(userId) {
|
|
271
|
+
return prisma.admin.findFirst({
|
|
272
|
+
where: { userId },
|
|
273
|
+
select: { ip: true }
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
25
280
|
// src/adapters/email.ts
|
|
26
281
|
function createNoopEmailAdapter() {
|
|
27
282
|
return {
|
|
@@ -75,6 +330,291 @@ function createConsoleEmailAdapter() {
|
|
|
75
330
|
};
|
|
76
331
|
}
|
|
77
332
|
|
|
333
|
+
// src/adapters/drizzleAdapter.ts
|
|
334
|
+
function createDrizzleAdapter(db, tables) {
|
|
335
|
+
const {
|
|
336
|
+
eq,
|
|
337
|
+
and,
|
|
338
|
+
or,
|
|
339
|
+
isNull,
|
|
340
|
+
isNotNull,
|
|
341
|
+
gte,
|
|
342
|
+
ne,
|
|
343
|
+
sql
|
|
344
|
+
} = __require("drizzle-orm");
|
|
345
|
+
const { users, sessions, otps, passwordResets, devices, admins } = tables;
|
|
346
|
+
return {
|
|
347
|
+
user: {
|
|
348
|
+
async findByEmailInsensitive(email) {
|
|
349
|
+
const rows = await db.select().from(users).where(sql`lower(${users.email}) = lower(${email})`).limit(1);
|
|
350
|
+
return rows[0] ?? null;
|
|
351
|
+
},
|
|
352
|
+
async findByUsernameInsensitive(username) {
|
|
353
|
+
const rows = await db.select().from(users).where(sql`lower(${users.username}) = lower(${username})`).limit(1);
|
|
354
|
+
return rows[0] ?? null;
|
|
355
|
+
},
|
|
356
|
+
async findByEmailOrUsernameInsensitive(identifier) {
|
|
357
|
+
const rows = await db.select().from(users).where(
|
|
358
|
+
or(
|
|
359
|
+
sql`lower(${users.email}) = lower(${identifier})`,
|
|
360
|
+
sql`lower(${users.username}) = lower(${identifier})`
|
|
361
|
+
)
|
|
362
|
+
).limit(1);
|
|
363
|
+
return rows[0] ?? null;
|
|
364
|
+
},
|
|
365
|
+
async findByEmailOrOAuthId(email, oauthId) {
|
|
366
|
+
const rows = await db.select().from(users).where(
|
|
367
|
+
or(
|
|
368
|
+
sql`lower(${users.email}) = lower(${email})`,
|
|
369
|
+
eq(users.oauthId, oauthId)
|
|
370
|
+
)
|
|
371
|
+
).limit(1);
|
|
372
|
+
return rows[0] ?? null;
|
|
373
|
+
},
|
|
374
|
+
async findById(id) {
|
|
375
|
+
const rows = await db.select().from(users).where(eq(users.id, id)).limit(1);
|
|
376
|
+
return rows[0] ?? null;
|
|
377
|
+
},
|
|
378
|
+
async findActiveById(id) {
|
|
379
|
+
const rows = await db.select().from(users).where(and(eq(users.id, id), eq(users.status, "ACTIVE"))).limit(1);
|
|
380
|
+
return rows[0] ?? null;
|
|
381
|
+
},
|
|
382
|
+
async create(data) {
|
|
383
|
+
const rows = await db.insert(users).values(data).returning();
|
|
384
|
+
return rows[0];
|
|
385
|
+
},
|
|
386
|
+
async update(id, data) {
|
|
387
|
+
const rows = await db.update(users).set(data).where(eq(users.id, id)).returning();
|
|
388
|
+
return rows[0];
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
session: {
|
|
392
|
+
async findById(id) {
|
|
393
|
+
const rows = await db.select({
|
|
394
|
+
id: sessions.id,
|
|
395
|
+
userId: sessions.userId,
|
|
396
|
+
socketId: sessions.socketId,
|
|
397
|
+
twoFaSecret: sessions.twoFaSecret,
|
|
398
|
+
browserName: sessions.browserName,
|
|
399
|
+
issuedAt: sessions.issuedAt,
|
|
400
|
+
lastUsed: sessions.lastUsed,
|
|
401
|
+
revokedAt: sessions.revokedAt,
|
|
402
|
+
deviceId: sessions.deviceId,
|
|
403
|
+
user: {
|
|
404
|
+
status: users.status,
|
|
405
|
+
verifiedHumanAt: users.verifiedHumanAt
|
|
406
|
+
}
|
|
407
|
+
}).from(sessions).innerJoin(users, eq(sessions.userId, users.id)).where(eq(sessions.id, id)).limit(1);
|
|
408
|
+
return rows[0] ?? null;
|
|
409
|
+
},
|
|
410
|
+
async create(data) {
|
|
411
|
+
const rows = await db.insert(sessions).values(data).returning();
|
|
412
|
+
return rows[0];
|
|
413
|
+
},
|
|
414
|
+
async update(id, data) {
|
|
415
|
+
const rows = await db.update(sessions).set(data).where(eq(sessions.id, id)).returning();
|
|
416
|
+
return rows[0];
|
|
417
|
+
},
|
|
418
|
+
async updateLastUsed(id) {
|
|
419
|
+
await db.update(sessions).set({ lastUsed: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id));
|
|
420
|
+
const rows = await db.select({
|
|
421
|
+
id: sessions.id,
|
|
422
|
+
userId: sessions.userId,
|
|
423
|
+
socketId: sessions.socketId,
|
|
424
|
+
twoFaSecret: sessions.twoFaSecret,
|
|
425
|
+
browserName: sessions.browserName,
|
|
426
|
+
issuedAt: sessions.issuedAt,
|
|
427
|
+
lastUsed: sessions.lastUsed,
|
|
428
|
+
revokedAt: sessions.revokedAt,
|
|
429
|
+
deviceId: sessions.deviceId,
|
|
430
|
+
user: {
|
|
431
|
+
verifiedHumanAt: users.verifiedHumanAt
|
|
432
|
+
}
|
|
433
|
+
}).from(sessions).innerJoin(users, eq(sessions.userId, users.id)).where(eq(sessions.id, id)).limit(1);
|
|
434
|
+
return rows[0];
|
|
435
|
+
},
|
|
436
|
+
async revoke(id) {
|
|
437
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id));
|
|
438
|
+
},
|
|
439
|
+
async findActiveByUserId(userId, excludeSessionId) {
|
|
440
|
+
const conditions = [eq(sessions.userId, userId), isNull(sessions.revokedAt)];
|
|
441
|
+
if (excludeSessionId !== void 0) {
|
|
442
|
+
conditions.push(ne(sessions.id, excludeSessionId));
|
|
443
|
+
}
|
|
444
|
+
return db.select({
|
|
445
|
+
id: sessions.id,
|
|
446
|
+
socketId: sessions.socketId,
|
|
447
|
+
userId: sessions.userId
|
|
448
|
+
}).from(sessions).where(and(...conditions));
|
|
449
|
+
},
|
|
450
|
+
async revokeAllByUserId(userId, excludeSessionId) {
|
|
451
|
+
const conditions = [eq(sessions.userId, userId), isNull(sessions.revokedAt)];
|
|
452
|
+
if (excludeSessionId !== void 0) {
|
|
453
|
+
conditions.push(ne(sessions.id, excludeSessionId));
|
|
454
|
+
}
|
|
455
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(and(...conditions));
|
|
456
|
+
},
|
|
457
|
+
async findTwoFaSecretsByUserId(userId) {
|
|
458
|
+
return db.select({ twoFaSecret: sessions.twoFaSecret }).from(sessions).where(and(eq(sessions.userId, userId), isNotNull(sessions.twoFaSecret)));
|
|
459
|
+
},
|
|
460
|
+
async clearTwoFaSecrets(userId, excludeSessionId) {
|
|
461
|
+
const conditions = [eq(sessions.userId, userId)];
|
|
462
|
+
if (excludeSessionId !== void 0) {
|
|
463
|
+
conditions.push(ne(sessions.id, excludeSessionId));
|
|
464
|
+
}
|
|
465
|
+
await db.update(sessions).set({ twoFaSecret: null }).where(and(...conditions));
|
|
466
|
+
},
|
|
467
|
+
async findByIdWithDevice(id, userId) {
|
|
468
|
+
const rows = await db.select({
|
|
469
|
+
twoFaSecret: sessions.twoFaSecret,
|
|
470
|
+
deviceId: sessions.deviceId,
|
|
471
|
+
device: {
|
|
472
|
+
pushToken: devices.pushToken
|
|
473
|
+
}
|
|
474
|
+
}).from(sessions).leftJoin(devices, eq(sessions.deviceId, devices.id)).where(and(eq(sessions.id, id), eq(sessions.userId, userId))).limit(1);
|
|
475
|
+
if (!rows[0]) return null;
|
|
476
|
+
const row = rows[0];
|
|
477
|
+
return {
|
|
478
|
+
twoFaSecret: row.twoFaSecret,
|
|
479
|
+
deviceId: row.deviceId,
|
|
480
|
+
device: row.device?.pushToken ? { pushToken: row.device.pushToken } : null
|
|
481
|
+
};
|
|
482
|
+
},
|
|
483
|
+
async revokeByDevicePushToken(userId, pushToken, excludeSessionId) {
|
|
484
|
+
const deviceRows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
485
|
+
if (!deviceRows[0]) return;
|
|
486
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(
|
|
487
|
+
and(
|
|
488
|
+
eq(sessions.userId, userId),
|
|
489
|
+
ne(sessions.id, excludeSessionId),
|
|
490
|
+
isNull(sessions.revokedAt),
|
|
491
|
+
eq(sessions.deviceId, deviceRows[0].id)
|
|
492
|
+
)
|
|
493
|
+
);
|
|
494
|
+
},
|
|
495
|
+
async clearDeviceId(userId, deviceId) {
|
|
496
|
+
await db.update(sessions).set({ deviceId: null }).where(and(eq(sessions.userId, userId), eq(sessions.deviceId, deviceId)));
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
otp: {
|
|
500
|
+
async findValidByUserAndCode(userId, code) {
|
|
501
|
+
const rows = await db.select().from(otps).where(
|
|
502
|
+
and(eq(otps.userId, userId), eq(otps.code, code), gte(otps.expiresAt, /* @__PURE__ */ new Date()))
|
|
503
|
+
).limit(1);
|
|
504
|
+
return rows[0] ?? null;
|
|
505
|
+
},
|
|
506
|
+
async create(data) {
|
|
507
|
+
const rows = await db.insert(otps).values(data).returning();
|
|
508
|
+
return rows[0];
|
|
509
|
+
},
|
|
510
|
+
async delete(id) {
|
|
511
|
+
await db.delete(otps).where(eq(otps.id, id));
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
passwordReset: {
|
|
515
|
+
async findById(id) {
|
|
516
|
+
const rows = await db.select({
|
|
517
|
+
id: passwordResets.id,
|
|
518
|
+
createdAt: passwordResets.createdAt,
|
|
519
|
+
userId: passwordResets.userId
|
|
520
|
+
}).from(passwordResets).where(eq(passwordResets.id, id)).limit(1);
|
|
521
|
+
return rows[0] ?? null;
|
|
522
|
+
},
|
|
523
|
+
async create(userId) {
|
|
524
|
+
const rows = await db.insert(passwordResets).values({ userId }).returning();
|
|
525
|
+
return rows[0];
|
|
526
|
+
},
|
|
527
|
+
async delete(id) {
|
|
528
|
+
await db.delete(passwordResets).where(eq(passwordResets.id, id));
|
|
529
|
+
},
|
|
530
|
+
async deleteAllByUserId(userId) {
|
|
531
|
+
await db.delete(passwordResets).where(eq(passwordResets.userId, userId));
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
device: {
|
|
535
|
+
async findByTokenSessionAndUser(pushToken, sessionId, userId) {
|
|
536
|
+
const rows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
537
|
+
if (!rows[0]) return null;
|
|
538
|
+
if (tables.devicesToSessions && tables.devicesToUsers) {
|
|
539
|
+
const sessionLink = await db.select().from(tables.devicesToSessions).where(
|
|
540
|
+
and(
|
|
541
|
+
eq(tables.devicesToSessions.deviceId, rows[0].id),
|
|
542
|
+
eq(tables.devicesToSessions.sessionId, sessionId)
|
|
543
|
+
)
|
|
544
|
+
).limit(1);
|
|
545
|
+
const userLink = await db.select().from(tables.devicesToUsers).where(
|
|
546
|
+
and(
|
|
547
|
+
eq(tables.devicesToUsers.deviceId, rows[0].id),
|
|
548
|
+
eq(tables.devicesToUsers.userId, userId)
|
|
549
|
+
)
|
|
550
|
+
).limit(1);
|
|
551
|
+
if (!sessionLink[0] || !userLink[0]) return null;
|
|
552
|
+
}
|
|
553
|
+
return { id: rows[0].id };
|
|
554
|
+
},
|
|
555
|
+
async upsertByPushToken(pushToken, sessionId, userId) {
|
|
556
|
+
const existing = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
557
|
+
let deviceId;
|
|
558
|
+
if (existing[0]) {
|
|
559
|
+
deviceId = existing[0].id;
|
|
560
|
+
} else {
|
|
561
|
+
const rows = await db.insert(devices).values({ pushToken }).returning({ id: devices.id });
|
|
562
|
+
deviceId = rows[0].id;
|
|
563
|
+
}
|
|
564
|
+
if (tables.devicesToSessions) {
|
|
565
|
+
await db.insert(tables.devicesToSessions).values({ deviceId, sessionId }).onConflictDoNothing();
|
|
566
|
+
}
|
|
567
|
+
if (tables.devicesToUsers) {
|
|
568
|
+
await db.insert(tables.devicesToUsers).values({ deviceId, userId }).onConflictDoNothing();
|
|
569
|
+
}
|
|
570
|
+
await db.update(sessions).set({ deviceId }).where(eq(sessions.id, sessionId));
|
|
571
|
+
},
|
|
572
|
+
async findByUserAndToken(userId, pushToken) {
|
|
573
|
+
if (tables.devicesToUsers) {
|
|
574
|
+
const rows2 = await db.select({ id: devices.id }).from(devices).innerJoin(
|
|
575
|
+
tables.devicesToUsers,
|
|
576
|
+
eq(devices.id, tables.devicesToUsers.deviceId)
|
|
577
|
+
).where(
|
|
578
|
+
and(
|
|
579
|
+
eq(devices.pushToken, pushToken),
|
|
580
|
+
eq(tables.devicesToUsers.userId, userId)
|
|
581
|
+
)
|
|
582
|
+
).limit(1);
|
|
583
|
+
return rows2[0] ? { id: rows2[0].id } : null;
|
|
584
|
+
}
|
|
585
|
+
const rows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
586
|
+
return rows[0] ? { id: rows[0].id } : null;
|
|
587
|
+
},
|
|
588
|
+
async disconnectUser(deviceId, userId) {
|
|
589
|
+
if (tables.devicesToUsers) {
|
|
590
|
+
await db.delete(tables.devicesToUsers).where(
|
|
591
|
+
and(
|
|
592
|
+
eq(tables.devicesToUsers.deviceId, deviceId),
|
|
593
|
+
eq(tables.devicesToUsers.userId, userId)
|
|
594
|
+
)
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
},
|
|
598
|
+
async hasRemainingUsers(deviceId) {
|
|
599
|
+
if (tables.devicesToUsers) {
|
|
600
|
+
const rows = await db.select({ userId: tables.devicesToUsers.userId }).from(tables.devicesToUsers).where(eq(tables.devicesToUsers.deviceId, deviceId)).limit(1);
|
|
601
|
+
return rows.length > 0;
|
|
602
|
+
}
|
|
603
|
+
return false;
|
|
604
|
+
},
|
|
605
|
+
async delete(id) {
|
|
606
|
+
await db.delete(devices).where(eq(devices.id, id));
|
|
607
|
+
}
|
|
608
|
+
},
|
|
609
|
+
admin: {
|
|
610
|
+
async findByUserId(userId) {
|
|
611
|
+
const rows = await db.select({ ip: admins.ip }).from(admins).where(eq(admins.userId, userId)).limit(1);
|
|
612
|
+
return rows[0] ?? null;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
|
|
78
618
|
// src/utilities/config.ts
|
|
79
619
|
var defaultTokenSettings = {
|
|
80
620
|
jwtExpiry: 30 * 24 * 60 * 60,
|
|
@@ -105,9 +645,16 @@ var defaultFeatures = {
|
|
|
105
645
|
otpLogin: true
|
|
106
646
|
};
|
|
107
647
|
function createAuthConfig(config) {
|
|
648
|
+
if (!config.database && !config.prisma) {
|
|
649
|
+
throw new Error(
|
|
650
|
+
"@factiii/auth: Provide either a `database` adapter or a `prisma` client in config."
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
const database = config.database ?? createPrismaAdapter(config.prisma);
|
|
108
654
|
const emailService = config.emailService ?? createNoopEmailAdapter();
|
|
109
655
|
return {
|
|
110
656
|
...config,
|
|
657
|
+
database,
|
|
111
658
|
features: { ...defaultFeatures, ...config.features },
|
|
112
659
|
tokenSettings: { ...defaultTokenSettings, ...config.tokenSettings },
|
|
113
660
|
cookieSettings: { ...defaultCookieSettings, ...config.cookieSettings },
|
|
@@ -225,6 +772,7 @@ function isTokenInvalidError(error) {
|
|
|
225
772
|
function createAuthGuard(config, t) {
|
|
226
773
|
const storageKeys = config.storageKeys ?? defaultStorageKeys;
|
|
227
774
|
const cookieSettings = { ...defaultCookieSettings, ...config.cookieSettings };
|
|
775
|
+
const database = config.database ?? createPrismaAdapter(config.prisma);
|
|
228
776
|
const revokeSession = async (ctx, sessionId, description, errorStack, path) => {
|
|
229
777
|
clearAuthCookie(ctx.res, cookieSettings, storageKeys);
|
|
230
778
|
if (config.hooks?.logError) {
|
|
@@ -261,15 +809,9 @@ ${errorStack}` : null,
|
|
|
261
809
|
}
|
|
262
810
|
if (sessionId) {
|
|
263
811
|
try {
|
|
264
|
-
await
|
|
265
|
-
where: { id: sessionId },
|
|
266
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
267
|
-
});
|
|
812
|
+
await database.session.revoke(sessionId);
|
|
268
813
|
if (config.hooks?.onSessionRevoked) {
|
|
269
|
-
const session = await
|
|
270
|
-
where: { id: sessionId },
|
|
271
|
-
select: { id: true, userId: true, socketId: true }
|
|
272
|
-
});
|
|
814
|
+
const session = await database.session.findById(sessionId);
|
|
273
815
|
if (session) {
|
|
274
816
|
await config.hooks.onSessionRevoked(session.userId, session.socketId, description);
|
|
275
817
|
}
|
|
@@ -294,23 +836,7 @@ ${errorStack}` : null,
|
|
|
294
836
|
secret: config.secrets.jwt,
|
|
295
837
|
ignoreExpiration: meta?.ignoreExpiration ?? false
|
|
296
838
|
});
|
|
297
|
-
const session = await
|
|
298
|
-
where: {
|
|
299
|
-
id: decodedToken.id
|
|
300
|
-
},
|
|
301
|
-
select: {
|
|
302
|
-
userId: true,
|
|
303
|
-
user: {
|
|
304
|
-
select: {
|
|
305
|
-
status: true,
|
|
306
|
-
verifiedHumanAt: true
|
|
307
|
-
}
|
|
308
|
-
},
|
|
309
|
-
revokedAt: true,
|
|
310
|
-
socketId: true,
|
|
311
|
-
id: true
|
|
312
|
-
}
|
|
313
|
-
});
|
|
839
|
+
const session = await database.session.findById(decodedToken.id);
|
|
314
840
|
if (!session) {
|
|
315
841
|
await revokeSession(
|
|
316
842
|
ctx,
|
|
@@ -364,10 +890,7 @@ ${errorStack}` : null,
|
|
|
364
890
|
});
|
|
365
891
|
}
|
|
366
892
|
if (meta?.adminRequired) {
|
|
367
|
-
const admin = await
|
|
368
|
-
where: { userId: session.userId },
|
|
369
|
-
select: { ip: true }
|
|
370
|
-
});
|
|
893
|
+
const admin = await database.admin.findByUserId(session.userId);
|
|
371
894
|
if (!admin || admin.ip !== ctx.ip) {
|
|
372
895
|
await revokeSession(
|
|
373
896
|
ctx,
|
|
@@ -622,19 +1145,14 @@ var BaseProcedureFactory = class {
|
|
|
622
1145
|
if (this.config.hooks?.beforeRegister) {
|
|
623
1146
|
await this.config.hooks.beforeRegister(typedInput);
|
|
624
1147
|
}
|
|
625
|
-
const usernameCheck = await this.config.
|
|
626
|
-
where: { username: { equals: username, mode: "insensitive" } }
|
|
627
|
-
});
|
|
1148
|
+
const usernameCheck = await this.config.database.user.findByUsernameInsensitive(username);
|
|
628
1149
|
if (usernameCheck) {
|
|
629
1150
|
throw new TRPCError2({
|
|
630
1151
|
code: "CONFLICT",
|
|
631
1152
|
message: "An account already exists with that username."
|
|
632
1153
|
});
|
|
633
1154
|
}
|
|
634
|
-
const emailCheck = await this.config.
|
|
635
|
-
where: { email: { equals: email, mode: "insensitive" } },
|
|
636
|
-
select: { id: true }
|
|
637
|
-
});
|
|
1155
|
+
const emailCheck = await this.config.database.user.findByEmailInsensitive(email);
|
|
638
1156
|
if (emailCheck) {
|
|
639
1157
|
throw new TRPCError2({
|
|
640
1158
|
code: "CONFLICT",
|
|
@@ -642,30 +1160,25 @@ var BaseProcedureFactory = class {
|
|
|
642
1160
|
});
|
|
643
1161
|
}
|
|
644
1162
|
const hashedPassword = await hashPassword(password);
|
|
645
|
-
const user = await this.config.
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
verifiedHumanAt: null
|
|
655
|
-
}
|
|
1163
|
+
const user = await this.config.database.user.create({
|
|
1164
|
+
username,
|
|
1165
|
+
email,
|
|
1166
|
+
password: hashedPassword,
|
|
1167
|
+
status: "ACTIVE",
|
|
1168
|
+
tag: this.config.features.biometric ? "BOT" : "HUMAN",
|
|
1169
|
+
twoFaEnabled: false,
|
|
1170
|
+
emailVerificationStatus: "UNVERIFIED",
|
|
1171
|
+
verifiedHumanAt: null
|
|
656
1172
|
});
|
|
657
1173
|
if (this.config.hooks?.onUserCreated) {
|
|
658
1174
|
await this.config.hooks.onUserCreated(user.id, typedInput);
|
|
659
1175
|
}
|
|
660
1176
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
661
|
-
const session = await this.config.
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
...extraSessionData
|
|
667
|
-
},
|
|
668
|
-
select: { id: true, userId: true }
|
|
1177
|
+
const session = await this.config.database.session.create({
|
|
1178
|
+
userId: user.id,
|
|
1179
|
+
browserName: detectBrowser(userAgent),
|
|
1180
|
+
socketId: null,
|
|
1181
|
+
...extraSessionData
|
|
669
1182
|
});
|
|
670
1183
|
if (this.config.hooks?.onSessionCreated) {
|
|
671
1184
|
await this.config.hooks.onSessionCreated(session.id, typedInput);
|
|
@@ -703,24 +1216,7 @@ var BaseProcedureFactory = class {
|
|
|
703
1216
|
if (this.config.hooks?.beforeLogin) {
|
|
704
1217
|
await this.config.hooks.beforeLogin(typedInput);
|
|
705
1218
|
}
|
|
706
|
-
const user = await this.config.
|
|
707
|
-
where: {
|
|
708
|
-
OR: [
|
|
709
|
-
{ email: { equals: username, mode: "insensitive" } },
|
|
710
|
-
{ username: { equals: username, mode: "insensitive" } }
|
|
711
|
-
]
|
|
712
|
-
},
|
|
713
|
-
select: {
|
|
714
|
-
id: true,
|
|
715
|
-
status: true,
|
|
716
|
-
password: true,
|
|
717
|
-
twoFaEnabled: true,
|
|
718
|
-
email: true,
|
|
719
|
-
username: true,
|
|
720
|
-
oauthProvider: true,
|
|
721
|
-
verifiedHumanAt: true
|
|
722
|
-
}
|
|
723
|
-
});
|
|
1219
|
+
const user = await this.config.database.user.findByEmailOrUsernameInsensitive(username);
|
|
724
1220
|
if (!user) {
|
|
725
1221
|
throw new TRPCError2({
|
|
726
1222
|
code: "FORBIDDEN",
|
|
@@ -761,10 +1257,7 @@ var BaseProcedureFactory = class {
|
|
|
761
1257
|
};
|
|
762
1258
|
}
|
|
763
1259
|
let validCode = false;
|
|
764
|
-
const secrets = await this.config.
|
|
765
|
-
where: { userId: user.id, twoFaSecret: { not: null } },
|
|
766
|
-
select: { twoFaSecret: true }
|
|
767
|
-
});
|
|
1260
|
+
const secrets = await this.config.database.session.findTwoFaSecretsByUserId(user.id);
|
|
768
1261
|
for (const s of secrets) {
|
|
769
1262
|
if (s.twoFaSecret && await verifyTotp(code, cleanBase32String(s.twoFaSecret))) {
|
|
770
1263
|
validCode = true;
|
|
@@ -772,14 +1265,13 @@ var BaseProcedureFactory = class {
|
|
|
772
1265
|
}
|
|
773
1266
|
}
|
|
774
1267
|
if (!validCode) {
|
|
775
|
-
const checkOTP = await this.config.
|
|
776
|
-
|
|
777
|
-
|
|
1268
|
+
const checkOTP = await this.config.database.otp.findValidByUserAndCode(
|
|
1269
|
+
user.id,
|
|
1270
|
+
Number(code)
|
|
1271
|
+
);
|
|
778
1272
|
if (checkOTP) {
|
|
779
1273
|
validCode = true;
|
|
780
|
-
await this.config.
|
|
781
|
-
where: { id: checkOTP.id }
|
|
782
|
-
});
|
|
1274
|
+
await this.config.database.otp.delete(checkOTP.id);
|
|
783
1275
|
}
|
|
784
1276
|
}
|
|
785
1277
|
if (!validCode) {
|
|
@@ -790,24 +1282,11 @@ var BaseProcedureFactory = class {
|
|
|
790
1282
|
}
|
|
791
1283
|
}
|
|
792
1284
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
793
|
-
const session = await this.config.
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
...extraSessionData
|
|
799
|
-
},
|
|
800
|
-
select: {
|
|
801
|
-
id: true,
|
|
802
|
-
userId: true,
|
|
803
|
-
socketId: true,
|
|
804
|
-
browserName: true,
|
|
805
|
-
issuedAt: true,
|
|
806
|
-
lastUsed: true,
|
|
807
|
-
revokedAt: true,
|
|
808
|
-
deviceId: true,
|
|
809
|
-
twoFaSecret: true
|
|
810
|
-
}
|
|
1285
|
+
const session = await this.config.database.session.create({
|
|
1286
|
+
userId: user.id,
|
|
1287
|
+
browserName: detectBrowser(userAgent),
|
|
1288
|
+
socketId: null,
|
|
1289
|
+
...extraSessionData
|
|
811
1290
|
});
|
|
812
1291
|
if (this.config.hooks?.onUserLogin) {
|
|
813
1292
|
await this.config.hooks.onUserLogin(user.id, session.id);
|
|
@@ -838,15 +1317,9 @@ var BaseProcedureFactory = class {
|
|
|
838
1317
|
return this.authProcedure.meta({ ignoreExpiration: true }).mutation(async ({ ctx }) => {
|
|
839
1318
|
const { userId, sessionId } = ctx;
|
|
840
1319
|
if (sessionId) {
|
|
841
|
-
await this.config.
|
|
842
|
-
where: { id: sessionId },
|
|
843
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
844
|
-
});
|
|
1320
|
+
await this.config.database.session.revoke(sessionId);
|
|
845
1321
|
if (userId) {
|
|
846
|
-
await this.config.
|
|
847
|
-
where: { id: userId },
|
|
848
|
-
data: { isActive: false }
|
|
849
|
-
});
|
|
1322
|
+
await this.config.database.user.update(userId, { isActive: false });
|
|
850
1323
|
}
|
|
851
1324
|
if (this.config.hooks?.afterLogout) {
|
|
852
1325
|
await this.config.hooks.afterLogout(userId, sessionId, ctx.socketId);
|
|
@@ -858,15 +1331,7 @@ var BaseProcedureFactory = class {
|
|
|
858
1331
|
}
|
|
859
1332
|
refresh() {
|
|
860
1333
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
861
|
-
const session = await this.config.
|
|
862
|
-
where: { id: ctx.sessionId },
|
|
863
|
-
data: { lastUsed: /* @__PURE__ */ new Date() },
|
|
864
|
-
select: {
|
|
865
|
-
id: true,
|
|
866
|
-
userId: true,
|
|
867
|
-
user: { select: { verifiedHumanAt: true } }
|
|
868
|
-
}
|
|
869
|
-
});
|
|
1334
|
+
const session = await this.config.database.session.updateLastUsed(ctx.sessionId);
|
|
870
1335
|
if (this.config.hooks?.onRefresh) {
|
|
871
1336
|
this.config.hooks.onRefresh(session.userId).catch(() => {
|
|
872
1337
|
});
|
|
@@ -891,22 +1356,14 @@ var BaseProcedureFactory = class {
|
|
|
891
1356
|
return this.authProcedure.input(endAllSessionsSchema).mutation(async ({ ctx, input }) => {
|
|
892
1357
|
const { skipCurrentSession } = input;
|
|
893
1358
|
const { userId, sessionId } = ctx;
|
|
894
|
-
const sessionsToRevoke = await this.config.
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
await this.config.prisma.session.updateMany({
|
|
903
|
-
where: {
|
|
904
|
-
userId,
|
|
905
|
-
revokedAt: null,
|
|
906
|
-
...skipCurrentSession ? { NOT: { id: sessionId } } : {}
|
|
907
|
-
},
|
|
908
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
909
|
-
});
|
|
1359
|
+
const sessionsToRevoke = await this.config.database.session.findActiveByUserId(
|
|
1360
|
+
userId,
|
|
1361
|
+
skipCurrentSession ? sessionId : void 0
|
|
1362
|
+
);
|
|
1363
|
+
await this.config.database.session.revokeAllByUserId(
|
|
1364
|
+
userId,
|
|
1365
|
+
skipCurrentSession ? sessionId : void 0
|
|
1366
|
+
);
|
|
910
1367
|
for (const session of sessionsToRevoke) {
|
|
911
1368
|
if (this.config.hooks?.onSessionRevoked) {
|
|
912
1369
|
await this.config.hooks.onSessionRevoked(
|
|
@@ -917,10 +1374,7 @@ var BaseProcedureFactory = class {
|
|
|
917
1374
|
}
|
|
918
1375
|
}
|
|
919
1376
|
if (!skipCurrentSession) {
|
|
920
|
-
await this.config.
|
|
921
|
-
where: { id: userId },
|
|
922
|
-
data: { isActive: false }
|
|
923
|
-
});
|
|
1377
|
+
await this.config.database.user.update(userId, { isActive: false });
|
|
924
1378
|
}
|
|
925
1379
|
return { success: true, revokedCount: sessionsToRevoke.length };
|
|
926
1380
|
});
|
|
@@ -935,10 +1389,7 @@ var BaseProcedureFactory = class {
|
|
|
935
1389
|
message: "New password cannot be the same as current password"
|
|
936
1390
|
});
|
|
937
1391
|
}
|
|
938
|
-
const user = await this.config.
|
|
939
|
-
where: { id: userId },
|
|
940
|
-
select: { password: true }
|
|
941
|
-
});
|
|
1392
|
+
const user = await this.config.database.user.findById(userId);
|
|
942
1393
|
if (!user) {
|
|
943
1394
|
throw new TRPCError2({ code: "NOT_FOUND", message: "User not found" });
|
|
944
1395
|
}
|
|
@@ -956,14 +1407,8 @@ var BaseProcedureFactory = class {
|
|
|
956
1407
|
});
|
|
957
1408
|
}
|
|
958
1409
|
const hashedPassword = await hashPassword(newPassword);
|
|
959
|
-
await this.config.
|
|
960
|
-
|
|
961
|
-
data: { password: hashedPassword }
|
|
962
|
-
});
|
|
963
|
-
await this.config.prisma.session.updateMany({
|
|
964
|
-
where: { userId, revokedAt: null, NOT: { id: sessionId } },
|
|
965
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
966
|
-
});
|
|
1410
|
+
await this.config.database.user.update(userId, { password: hashedPassword });
|
|
1411
|
+
await this.config.database.session.revokeAllByUserId(userId, sessionId);
|
|
967
1412
|
if (this.config.hooks?.onPasswordChanged) {
|
|
968
1413
|
await this.config.hooks.onPasswordChanged(userId);
|
|
969
1414
|
}
|
|
@@ -976,11 +1421,8 @@ var BaseProcedureFactory = class {
|
|
|
976
1421
|
sendPasswordResetEmail() {
|
|
977
1422
|
return this.procedure.input(requestPasswordResetSchema).mutation(async ({ input }) => {
|
|
978
1423
|
const { email } = input;
|
|
979
|
-
const user = await this.config.
|
|
980
|
-
|
|
981
|
-
select: { id: true, password: true, email: true }
|
|
982
|
-
});
|
|
983
|
-
if (!user) {
|
|
1424
|
+
const user = await this.config.database.user.findByEmailInsensitive(email);
|
|
1425
|
+
if (!user || user.status !== "ACTIVE") {
|
|
984
1426
|
return { message: "If an account exists with that email, a reset link has been sent." };
|
|
985
1427
|
}
|
|
986
1428
|
if (!user.password) {
|
|
@@ -989,10 +1431,8 @@ var BaseProcedureFactory = class {
|
|
|
989
1431
|
message: "This account uses social login. Please use that method."
|
|
990
1432
|
});
|
|
991
1433
|
}
|
|
992
|
-
await this.config.
|
|
993
|
-
const passwordReset = await this.config.
|
|
994
|
-
data: { userId: user.id }
|
|
995
|
-
});
|
|
1434
|
+
await this.config.database.passwordReset.deleteAllByUserId(user.id);
|
|
1435
|
+
const passwordReset = await this.config.database.passwordReset.create(user.id);
|
|
996
1436
|
if (this.config.emailService) {
|
|
997
1437
|
await this.config.emailService.sendPasswordResetEmail(user.email, String(passwordReset.id));
|
|
998
1438
|
}
|
|
@@ -1002,15 +1442,12 @@ var BaseProcedureFactory = class {
|
|
|
1002
1442
|
checkPasswordReset() {
|
|
1003
1443
|
return this.procedure.input(checkPasswordResetSchema).query(async ({ input }) => {
|
|
1004
1444
|
const { token } = input;
|
|
1005
|
-
const passwordReset = await this.config.
|
|
1006
|
-
where: { id: token },
|
|
1007
|
-
select: { id: true, createdAt: true, userId: true }
|
|
1008
|
-
});
|
|
1445
|
+
const passwordReset = await this.config.database.passwordReset.findById(token);
|
|
1009
1446
|
if (!passwordReset) {
|
|
1010
1447
|
throw new TRPCError2({ code: "NOT_FOUND", message: "Invalid reset token." });
|
|
1011
1448
|
}
|
|
1012
1449
|
if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
|
|
1013
|
-
await this.config.
|
|
1450
|
+
await this.config.database.passwordReset.delete(token);
|
|
1014
1451
|
throw new TRPCError2({ code: "FORBIDDEN", message: "Reset token expired." });
|
|
1015
1452
|
}
|
|
1016
1453
|
return { valid: true };
|
|
@@ -1019,31 +1456,23 @@ var BaseProcedureFactory = class {
|
|
|
1019
1456
|
resetPassword() {
|
|
1020
1457
|
return this.procedure.input(resetPasswordSchema).mutation(async ({ input }) => {
|
|
1021
1458
|
const { token, password } = input;
|
|
1022
|
-
const passwordReset = await this.config.
|
|
1023
|
-
where: { id: token },
|
|
1024
|
-
select: { id: true, createdAt: true, userId: true }
|
|
1025
|
-
});
|
|
1459
|
+
const passwordReset = await this.config.database.passwordReset.findById(token);
|
|
1026
1460
|
if (!passwordReset) {
|
|
1027
1461
|
throw new TRPCError2({ code: "NOT_FOUND", message: "Invalid reset token." });
|
|
1028
1462
|
}
|
|
1029
1463
|
if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
|
|
1030
|
-
await this.config.
|
|
1464
|
+
await this.config.database.passwordReset.delete(token);
|
|
1031
1465
|
throw new TRPCError2({ code: "FORBIDDEN", message: "Reset token expired." });
|
|
1032
1466
|
}
|
|
1033
1467
|
const hashedPassword = await hashPassword(password);
|
|
1034
|
-
await this.config.
|
|
1035
|
-
|
|
1036
|
-
data: { password: hashedPassword }
|
|
1037
|
-
});
|
|
1038
|
-
await this.config.prisma.passwordReset.delete({ where: { id: token } });
|
|
1039
|
-
const sessionsToRevoke = await this.config.prisma.session.findMany({
|
|
1040
|
-
where: { userId: passwordReset.userId, revokedAt: null },
|
|
1041
|
-
select: { id: true, socketId: true, userId: true }
|
|
1042
|
-
});
|
|
1043
|
-
await this.config.prisma.session.updateMany({
|
|
1044
|
-
where: { userId: passwordReset.userId, revokedAt: null },
|
|
1045
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1468
|
+
await this.config.database.user.update(passwordReset.userId, {
|
|
1469
|
+
password: hashedPassword
|
|
1046
1470
|
});
|
|
1471
|
+
await this.config.database.passwordReset.delete(token);
|
|
1472
|
+
const sessionsToRevoke = await this.config.database.session.findActiveByUserId(
|
|
1473
|
+
passwordReset.userId
|
|
1474
|
+
);
|
|
1475
|
+
await this.config.database.session.revokeAllByUserId(passwordReset.userId);
|
|
1047
1476
|
for (const session of sessionsToRevoke) {
|
|
1048
1477
|
if (this.config.hooks?.onSessionRevoked) {
|
|
1049
1478
|
await this.config.hooks.onSessionRevoked(
|
|
@@ -1080,9 +1509,9 @@ var BiometricProcedureFactory = class {
|
|
|
1080
1509
|
return this.authProcedure.input(biometricVerifySchema).mutation(async ({ ctx }) => {
|
|
1081
1510
|
this.checkConfig();
|
|
1082
1511
|
const { userId } = ctx;
|
|
1083
|
-
await this.config.
|
|
1084
|
-
|
|
1085
|
-
|
|
1512
|
+
await this.config.database.user.update(userId, {
|
|
1513
|
+
verifiedHumanAt: /* @__PURE__ */ new Date(),
|
|
1514
|
+
tag: "HUMAN"
|
|
1086
1515
|
});
|
|
1087
1516
|
if (this.config.hooks?.onBiometricVerified) {
|
|
1088
1517
|
await this.config.hooks.onBiometricVerified(userId);
|
|
@@ -1094,10 +1523,7 @@ var BiometricProcedureFactory = class {
|
|
|
1094
1523
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
1095
1524
|
this.checkConfig();
|
|
1096
1525
|
const { userId } = ctx;
|
|
1097
|
-
const user = await this.config.
|
|
1098
|
-
where: { id: userId },
|
|
1099
|
-
select: { verifiedHumanAt: true }
|
|
1100
|
-
});
|
|
1526
|
+
const user = await this.config.database.user.findById(userId);
|
|
1101
1527
|
if (!user) {
|
|
1102
1528
|
throw new TRPCError3({ code: "NOT_FOUND", message: "User not found" });
|
|
1103
1529
|
}
|
|
@@ -1144,10 +1570,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1144
1570
|
return this.authProcedure.mutation(async ({ ctx }) => {
|
|
1145
1571
|
this.checkConfig();
|
|
1146
1572
|
const { userId } = ctx;
|
|
1147
|
-
const user = await this.config.
|
|
1148
|
-
where: { id: userId, status: "ACTIVE" },
|
|
1149
|
-
select: { id: true, email: true, emailVerificationStatus: true }
|
|
1150
|
-
});
|
|
1573
|
+
const user = await this.config.database.user.findActiveById(userId);
|
|
1151
1574
|
if (!user) {
|
|
1152
1575
|
throw new TRPCError4({ code: "NOT_FOUND", message: "User not found" });
|
|
1153
1576
|
}
|
|
@@ -1155,9 +1578,9 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1155
1578
|
return { message: "Email is already verified", emailSent: false };
|
|
1156
1579
|
}
|
|
1157
1580
|
const otp = randomUUID();
|
|
1158
|
-
await this.config.
|
|
1159
|
-
|
|
1160
|
-
|
|
1581
|
+
await this.config.database.user.update(userId, {
|
|
1582
|
+
emailVerificationStatus: "PENDING",
|
|
1583
|
+
otpForEmailVerification: otp
|
|
1161
1584
|
});
|
|
1162
1585
|
if (this.config.emailService) {
|
|
1163
1586
|
try {
|
|
@@ -1175,10 +1598,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1175
1598
|
this.checkConfig();
|
|
1176
1599
|
const { userId } = ctx;
|
|
1177
1600
|
const { code } = input;
|
|
1178
|
-
const user = await this.config.
|
|
1179
|
-
where: { id: userId, status: "ACTIVE" },
|
|
1180
|
-
select: { id: true, emailVerificationStatus: true, otpForEmailVerification: true }
|
|
1181
|
-
});
|
|
1601
|
+
const user = await this.config.database.user.findActiveById(userId);
|
|
1182
1602
|
if (!user) {
|
|
1183
1603
|
throw new TRPCError4({ code: "NOT_FOUND", message: "User not found" });
|
|
1184
1604
|
}
|
|
@@ -1188,9 +1608,9 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1188
1608
|
if (code !== user.otpForEmailVerification) {
|
|
1189
1609
|
throw new TRPCError4({ code: "BAD_REQUEST", message: "Invalid verification code" });
|
|
1190
1610
|
}
|
|
1191
|
-
await this.config.
|
|
1192
|
-
|
|
1193
|
-
|
|
1611
|
+
await this.config.database.user.update(userId, {
|
|
1612
|
+
emailVerificationStatus: "VERIFIED",
|
|
1613
|
+
otpForEmailVerification: null
|
|
1194
1614
|
});
|
|
1195
1615
|
if (this.config.hooks?.onEmailVerified) {
|
|
1196
1616
|
await this.config.hooks.onEmailVerified(userId);
|
|
@@ -1202,10 +1622,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1202
1622
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
1203
1623
|
this.checkConfig();
|
|
1204
1624
|
const { userId } = ctx;
|
|
1205
|
-
const user = await this.config.
|
|
1206
|
-
where: { id: userId },
|
|
1207
|
-
select: { emailVerificationStatus: true, email: true }
|
|
1208
|
-
});
|
|
1625
|
+
const user = await this.config.database.user.findById(userId);
|
|
1209
1626
|
if (!user) {
|
|
1210
1627
|
throw new TRPCError4({ code: "NOT_FOUND", message: "User not found" });
|
|
1211
1628
|
}
|
|
@@ -1259,23 +1676,7 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1259
1676
|
message: "Email not provided by OAuth provider"
|
|
1260
1677
|
});
|
|
1261
1678
|
}
|
|
1262
|
-
let user = await this.config.
|
|
1263
|
-
where: {
|
|
1264
|
-
OR: [{ email: { equals: email, mode: "insensitive" } }, { oauthId: { equals: oauthId } }]
|
|
1265
|
-
},
|
|
1266
|
-
select: {
|
|
1267
|
-
id: true,
|
|
1268
|
-
status: true,
|
|
1269
|
-
email: true,
|
|
1270
|
-
username: true,
|
|
1271
|
-
password: true,
|
|
1272
|
-
oauthProvider: true,
|
|
1273
|
-
oauthId: true,
|
|
1274
|
-
twoFaEnabled: true,
|
|
1275
|
-
verifiedHumanAt: true,
|
|
1276
|
-
emailVerificationStatus: true
|
|
1277
|
-
}
|
|
1278
|
-
});
|
|
1679
|
+
let user = await this.config.database.user.findByEmailOrOAuthId(email, oauthId);
|
|
1279
1680
|
if (user?.oauthProvider && user.oauthProvider !== provider) {
|
|
1280
1681
|
throw new TRPCError5({
|
|
1281
1682
|
code: "BAD_REQUEST",
|
|
@@ -1290,19 +1691,17 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1290
1691
|
}
|
|
1291
1692
|
if (!user) {
|
|
1292
1693
|
const generateUsername = this.config.generateUsername ?? (() => `user_${Date.now()}`);
|
|
1293
|
-
user = await this.config.
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
verifiedHumanAt: null
|
|
1305
|
-
}
|
|
1694
|
+
user = await this.config.database.user.create({
|
|
1695
|
+
username: generateUsername(),
|
|
1696
|
+
email,
|
|
1697
|
+
password: null,
|
|
1698
|
+
emailVerificationStatus: "VERIFIED",
|
|
1699
|
+
oauthProvider: provider,
|
|
1700
|
+
oauthId,
|
|
1701
|
+
status: "ACTIVE",
|
|
1702
|
+
tag: this.config.features.biometric ? "BOT" : "HUMAN",
|
|
1703
|
+
twoFaEnabled: false,
|
|
1704
|
+
verifiedHumanAt: null
|
|
1306
1705
|
});
|
|
1307
1706
|
if (this.config.hooks?.onUserCreated) {
|
|
1308
1707
|
await this.config.hooks.onUserCreated(user.id, typedInput);
|
|
@@ -1318,24 +1717,11 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1318
1717
|
throw new TRPCError5({ code: "FORBIDDEN", message: "Your account has been banned." });
|
|
1319
1718
|
}
|
|
1320
1719
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
1321
|
-
const session = await this.config.
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
...extraSessionData
|
|
1327
|
-
},
|
|
1328
|
-
select: {
|
|
1329
|
-
id: true,
|
|
1330
|
-
userId: true,
|
|
1331
|
-
socketId: true,
|
|
1332
|
-
browserName: true,
|
|
1333
|
-
issuedAt: true,
|
|
1334
|
-
lastUsed: true,
|
|
1335
|
-
revokedAt: true,
|
|
1336
|
-
deviceId: true,
|
|
1337
|
-
twoFaSecret: true
|
|
1338
|
-
}
|
|
1720
|
+
const session = await this.config.database.session.create({
|
|
1721
|
+
userId: user.id,
|
|
1722
|
+
browserName: detectBrowser(userAgent),
|
|
1723
|
+
socketId: null,
|
|
1724
|
+
...extraSessionData
|
|
1339
1725
|
});
|
|
1340
1726
|
if (this.config.hooks?.onUserLogin) {
|
|
1341
1727
|
await this.config.hooks.onUserLogin(user.id, session.id);
|
|
@@ -1392,10 +1778,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1392
1778
|
return this.authProcedure.mutation(async ({ ctx }) => {
|
|
1393
1779
|
this.checkConfig();
|
|
1394
1780
|
const { userId, sessionId } = ctx;
|
|
1395
|
-
const user = await this.config.
|
|
1396
|
-
where: { id: userId },
|
|
1397
|
-
select: { twoFaEnabled: true, oauthProvider: true, password: true }
|
|
1398
|
-
});
|
|
1781
|
+
const user = await this.config.database.user.findById(userId);
|
|
1399
1782
|
if (!user) {
|
|
1400
1783
|
throw new TRPCError6({ code: "NOT_FOUND", message: "User not found." });
|
|
1401
1784
|
}
|
|
@@ -1409,10 +1792,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1409
1792
|
throw new TRPCError6({ code: "BAD_REQUEST", message: "2FA already enabled." });
|
|
1410
1793
|
}
|
|
1411
1794
|
if (this.config.features.twoFaRequiresDevice !== false) {
|
|
1412
|
-
const checkSession = await this.config.
|
|
1413
|
-
where: { userId, id: sessionId },
|
|
1414
|
-
select: { deviceId: true }
|
|
1415
|
-
});
|
|
1795
|
+
const checkSession = await this.config.database.session.findById(sessionId);
|
|
1416
1796
|
if (!checkSession?.deviceId) {
|
|
1417
1797
|
throw new TRPCError6({
|
|
1418
1798
|
code: "BAD_REQUEST",
|
|
@@ -1420,23 +1800,11 @@ var TwoFaProcedureFactory = class {
|
|
|
1420
1800
|
});
|
|
1421
1801
|
}
|
|
1422
1802
|
}
|
|
1423
|
-
await this.config.
|
|
1424
|
-
|
|
1425
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1426
|
-
});
|
|
1427
|
-
await this.config.prisma.session.updateMany({
|
|
1428
|
-
where: { userId, NOT: { id: sessionId } },
|
|
1429
|
-
data: { twoFaSecret: null }
|
|
1430
|
-
});
|
|
1803
|
+
await this.config.database.session.revokeAllByUserId(userId, sessionId);
|
|
1804
|
+
await this.config.database.session.clearTwoFaSecrets(userId, sessionId);
|
|
1431
1805
|
const secret = generateTotpSecret();
|
|
1432
|
-
await this.config.
|
|
1433
|
-
|
|
1434
|
-
data: { twoFaEnabled: true }
|
|
1435
|
-
});
|
|
1436
|
-
await this.config.prisma.session.update({
|
|
1437
|
-
where: { id: sessionId },
|
|
1438
|
-
data: { twoFaSecret: secret }
|
|
1439
|
-
});
|
|
1806
|
+
await this.config.database.user.update(userId, { twoFaEnabled: true });
|
|
1807
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: secret });
|
|
1440
1808
|
if (this.config.hooks?.onTwoFaStatusChanged) {
|
|
1441
1809
|
await this.config.hooks.onTwoFaStatusChanged(userId, true);
|
|
1442
1810
|
}
|
|
@@ -1448,10 +1816,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1448
1816
|
this.checkConfig();
|
|
1449
1817
|
const { userId, sessionId } = ctx;
|
|
1450
1818
|
const { password } = input;
|
|
1451
|
-
const user = await this.config.
|
|
1452
|
-
where: { id: userId },
|
|
1453
|
-
select: { password: true, status: true, oauthProvider: true }
|
|
1454
|
-
});
|
|
1819
|
+
const user = await this.config.database.user.findById(userId);
|
|
1455
1820
|
if (!user) {
|
|
1456
1821
|
throw new TRPCError6({ code: "NOT_FOUND", message: "User not found." });
|
|
1457
1822
|
}
|
|
@@ -1474,14 +1839,8 @@ var TwoFaProcedureFactory = class {
|
|
|
1474
1839
|
if (!isMatch) {
|
|
1475
1840
|
throw new TRPCError6({ code: "FORBIDDEN", message: "Incorrect password." });
|
|
1476
1841
|
}
|
|
1477
|
-
await this.config.
|
|
1478
|
-
|
|
1479
|
-
data: { twoFaEnabled: false }
|
|
1480
|
-
});
|
|
1481
|
-
await this.config.prisma.session.update({
|
|
1482
|
-
where: { id: sessionId },
|
|
1483
|
-
data: { twoFaSecret: null }
|
|
1484
|
-
});
|
|
1842
|
+
await this.config.database.user.update(userId, { twoFaEnabled: false });
|
|
1843
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: null });
|
|
1485
1844
|
if (this.config.hooks?.onTwoFaStatusChanged) {
|
|
1486
1845
|
await this.config.hooks.onTwoFaStatusChanged(userId, false);
|
|
1487
1846
|
}
|
|
@@ -1493,10 +1852,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1493
1852
|
this.checkConfig();
|
|
1494
1853
|
const { userId, sessionId } = ctx;
|
|
1495
1854
|
const { pushCode } = input;
|
|
1496
|
-
const user = await this.config.
|
|
1497
|
-
where: { id: userId },
|
|
1498
|
-
select: { twoFaEnabled: true, oauthProvider: true }
|
|
1499
|
-
});
|
|
1855
|
+
const user = await this.config.database.user.findById(userId);
|
|
1500
1856
|
if (user?.oauthProvider) {
|
|
1501
1857
|
throw new TRPCError6({
|
|
1502
1858
|
code: "FORBIDDEN",
|
|
@@ -1506,10 +1862,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1506
1862
|
if (!user?.twoFaEnabled) {
|
|
1507
1863
|
throw new TRPCError6({ code: "BAD_REQUEST", message: "2FA not enabled." });
|
|
1508
1864
|
}
|
|
1509
|
-
const session = await this.config.
|
|
1510
|
-
where: { id: sessionId, userId },
|
|
1511
|
-
select: { twoFaSecret: true, device: { select: { pushToken: true } } }
|
|
1512
|
-
});
|
|
1865
|
+
const session = await this.config.database.session.findByIdWithDevice(sessionId, userId);
|
|
1513
1866
|
if (!session?.device) {
|
|
1514
1867
|
throw new TRPCError6({ code: "BAD_REQUEST", message: "Invalid request" });
|
|
1515
1868
|
}
|
|
@@ -1521,10 +1874,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1521
1874
|
return { secret: session.twoFaSecret };
|
|
1522
1875
|
}
|
|
1523
1876
|
const secret = generateTotpSecret();
|
|
1524
|
-
await this.config.
|
|
1525
|
-
where: { id: sessionId },
|
|
1526
|
-
data: { twoFaSecret: secret }
|
|
1527
|
-
});
|
|
1877
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: secret });
|
|
1528
1878
|
return { secret };
|
|
1529
1879
|
});
|
|
1530
1880
|
}
|
|
@@ -1532,11 +1882,8 @@ var TwoFaProcedureFactory = class {
|
|
|
1532
1882
|
return this.procedure.input(twoFaResetSchema).mutation(async ({ input }) => {
|
|
1533
1883
|
this.checkConfig();
|
|
1534
1884
|
const { username, password } = input;
|
|
1535
|
-
const user = await this.config.
|
|
1536
|
-
|
|
1537
|
-
select: { id: true, password: true, email: true }
|
|
1538
|
-
});
|
|
1539
|
-
if (!user) {
|
|
1885
|
+
const user = await this.config.database.user.findByUsernameInsensitive(username);
|
|
1886
|
+
if (!user || !user.twoFaEnabled) {
|
|
1540
1887
|
throw new TRPCError6({ code: "UNAUTHORIZED", message: "Invalid credentials." });
|
|
1541
1888
|
}
|
|
1542
1889
|
if (!user.password) {
|
|
@@ -1551,9 +1898,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1551
1898
|
}
|
|
1552
1899
|
const otp = generateOtp();
|
|
1553
1900
|
const expiresAt = new Date(Date.now() + this.config.tokenSettings.otpValidityMs);
|
|
1554
|
-
await this.config.
|
|
1555
|
-
data: { userId: user.id, code: otp, expiresAt }
|
|
1556
|
-
});
|
|
1901
|
+
await this.config.database.otp.create({ userId: user.id, code: otp, expiresAt });
|
|
1557
1902
|
if (this.config.emailService) {
|
|
1558
1903
|
await this.config.emailService.sendOTPEmail(user.email, otp);
|
|
1559
1904
|
}
|
|
@@ -1564,30 +1909,17 @@ var TwoFaProcedureFactory = class {
|
|
|
1564
1909
|
return this.procedure.input(twoFaResetVerifySchema).mutation(async ({ input }) => {
|
|
1565
1910
|
this.checkConfig();
|
|
1566
1911
|
const { code, username } = input;
|
|
1567
|
-
const user = await this.config.
|
|
1568
|
-
where: { username: { equals: username, mode: "insensitive" } },
|
|
1569
|
-
select: { id: true }
|
|
1570
|
-
});
|
|
1912
|
+
const user = await this.config.database.user.findByUsernameInsensitive(username);
|
|
1571
1913
|
if (!user) {
|
|
1572
1914
|
throw new TRPCError6({ code: "NOT_FOUND", message: "User not found" });
|
|
1573
1915
|
}
|
|
1574
|
-
const otp = await this.config.
|
|
1575
|
-
where: { userId: user.id, code, expiresAt: { gte: /* @__PURE__ */ new Date() } }
|
|
1576
|
-
});
|
|
1916
|
+
const otp = await this.config.database.otp.findValidByUserAndCode(user.id, code);
|
|
1577
1917
|
if (!otp) {
|
|
1578
1918
|
throw new TRPCError6({ code: "FORBIDDEN", message: "Invalid or expired OTP" });
|
|
1579
1919
|
}
|
|
1580
|
-
await this.config.
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
await this.config.prisma.user.update({
|
|
1584
|
-
where: { id: user.id },
|
|
1585
|
-
data: { twoFaEnabled: false }
|
|
1586
|
-
});
|
|
1587
|
-
await this.config.prisma.session.updateMany({
|
|
1588
|
-
where: { userId: user.id },
|
|
1589
|
-
data: { twoFaSecret: null }
|
|
1590
|
-
});
|
|
1920
|
+
await this.config.database.otp.delete(otp.id);
|
|
1921
|
+
await this.config.database.user.update(user.id, { twoFaEnabled: false });
|
|
1922
|
+
await this.config.database.session.clearTwoFaSecrets(user.id);
|
|
1591
1923
|
return { success: true, message: "2FA has been reset." };
|
|
1592
1924
|
});
|
|
1593
1925
|
}
|
|
@@ -1596,36 +1928,14 @@ var TwoFaProcedureFactory = class {
|
|
|
1596
1928
|
this.checkConfig();
|
|
1597
1929
|
const { userId, sessionId } = ctx;
|
|
1598
1930
|
const { pushToken } = input;
|
|
1599
|
-
await this.config.
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
},
|
|
1606
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1607
|
-
});
|
|
1608
|
-
const checkDevice = await this.config.prisma.device.findFirst({
|
|
1609
|
-
where: {
|
|
1610
|
-
pushToken,
|
|
1611
|
-
sessions: { some: { id: sessionId } },
|
|
1612
|
-
users: { some: { id: userId } }
|
|
1613
|
-
},
|
|
1614
|
-
select: { id: true }
|
|
1615
|
-
});
|
|
1931
|
+
await this.config.database.session.revokeByDevicePushToken(userId, pushToken, sessionId);
|
|
1932
|
+
const checkDevice = await this.config.database.device.findByTokenSessionAndUser(
|
|
1933
|
+
pushToken,
|
|
1934
|
+
sessionId,
|
|
1935
|
+
userId
|
|
1936
|
+
);
|
|
1616
1937
|
if (!checkDevice) {
|
|
1617
|
-
await this.config.
|
|
1618
|
-
where: { pushToken },
|
|
1619
|
-
create: {
|
|
1620
|
-
pushToken,
|
|
1621
|
-
sessions: { connect: { id: sessionId } },
|
|
1622
|
-
users: { connect: { id: userId } }
|
|
1623
|
-
},
|
|
1624
|
-
update: {
|
|
1625
|
-
sessions: { connect: { id: sessionId } },
|
|
1626
|
-
users: { connect: { id: userId } }
|
|
1627
|
-
}
|
|
1628
|
-
});
|
|
1938
|
+
await this.config.database.device.upsertByPushToken(pushToken, sessionId, userId);
|
|
1629
1939
|
}
|
|
1630
1940
|
return { registered: true };
|
|
1631
1941
|
});
|
|
@@ -1635,30 +1945,13 @@ var TwoFaProcedureFactory = class {
|
|
|
1635
1945
|
this.checkConfig();
|
|
1636
1946
|
const { userId } = ctx;
|
|
1637
1947
|
const { pushToken } = input;
|
|
1638
|
-
const device = await this.config.
|
|
1639
|
-
where: {
|
|
1640
|
-
users: { some: { id: userId } },
|
|
1641
|
-
pushToken
|
|
1642
|
-
},
|
|
1643
|
-
select: { id: true }
|
|
1644
|
-
});
|
|
1948
|
+
const device = await this.config.database.device.findByUserAndToken(userId, pushToken);
|
|
1645
1949
|
if (device) {
|
|
1646
|
-
await this.config.
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
where: { id: device.id },
|
|
1652
|
-
data: { users: { disconnect: { id: userId } } }
|
|
1653
|
-
});
|
|
1654
|
-
const remainingUsers = await this.config.prisma.device.findUnique({
|
|
1655
|
-
where: { id: device.id },
|
|
1656
|
-
select: { users: { select: { id: true }, take: 1 } }
|
|
1657
|
-
});
|
|
1658
|
-
if (!remainingUsers?.users.length) {
|
|
1659
|
-
await this.config.prisma.device.delete({
|
|
1660
|
-
where: { id: device.id }
|
|
1661
|
-
});
|
|
1950
|
+
await this.config.database.session.clearDeviceId(userId, device.id);
|
|
1951
|
+
await this.config.database.device.disconnectUser(device.id, userId);
|
|
1952
|
+
const hasUsers = await this.config.database.device.hasRemainingUsers(device.id);
|
|
1953
|
+
if (!hasUsers) {
|
|
1954
|
+
await this.config.database.device.delete(device.id);
|
|
1662
1955
|
}
|
|
1663
1956
|
}
|
|
1664
1957
|
return { deregistered: true };
|
|
@@ -1815,8 +2108,10 @@ export {
|
|
|
1815
2108
|
createAuthRouter,
|
|
1816
2109
|
createAuthToken,
|
|
1817
2110
|
createConsoleEmailAdapter,
|
|
2111
|
+
createDrizzleAdapter,
|
|
1818
2112
|
createNoopEmailAdapter,
|
|
1819
2113
|
createOAuthVerifier,
|
|
2114
|
+
createPrismaAdapter,
|
|
1820
2115
|
decodeToken,
|
|
1821
2116
|
defaultAuthConfig,
|
|
1822
2117
|
defaultCookieSettings,
|