@factiii/auth 0.5.2 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/init.mjs +449 -64
- package/dist/{chunk-EHI4P63M.mjs → chunk-KUYH4DBN.mjs} +8 -0
- package/dist/index.d.mts +228 -16
- package/dist/index.d.ts +228 -16
- package/dist/index.js +678 -381
- package/dist/index.mjs +678 -382
- package/dist/validators.d.mts +1 -1
- package/dist/validators.d.ts +1 -1
- package/dist/validators.mjs +1 -1
- package/package.json +24 -3
- package/dist/{hooks-BXNxNK4S.d.mts → hooks-yHGJ7C6_.d.mts} +2 -2
- package/dist/{hooks-BXNxNK4S.d.ts → hooks-yHGJ7C6_.d.ts} +2 -2
package/dist/index.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",
|
|
@@ -756,14 +1252,12 @@ var BaseProcedureFactory = class {
|
|
|
756
1252
|
if (!code) {
|
|
757
1253
|
return {
|
|
758
1254
|
success: false,
|
|
759
|
-
requires2FA: true
|
|
1255
|
+
requires2FA: true,
|
|
1256
|
+
userId: user.id
|
|
760
1257
|
};
|
|
761
1258
|
}
|
|
762
1259
|
let validCode = false;
|
|
763
|
-
const secrets = await this.config.
|
|
764
|
-
where: { userId: user.id, twoFaSecret: { not: null } },
|
|
765
|
-
select: { twoFaSecret: true }
|
|
766
|
-
});
|
|
1260
|
+
const secrets = await this.config.database.session.findTwoFaSecretsByUserId(user.id);
|
|
767
1261
|
for (const s of secrets) {
|
|
768
1262
|
if (s.twoFaSecret && await verifyTotp(code, cleanBase32String(s.twoFaSecret))) {
|
|
769
1263
|
validCode = true;
|
|
@@ -771,14 +1265,13 @@ var BaseProcedureFactory = class {
|
|
|
771
1265
|
}
|
|
772
1266
|
}
|
|
773
1267
|
if (!validCode) {
|
|
774
|
-
const checkOTP = await this.config.
|
|
775
|
-
|
|
776
|
-
|
|
1268
|
+
const checkOTP = await this.config.database.otp.findValidByUserAndCode(
|
|
1269
|
+
user.id,
|
|
1270
|
+
Number(code)
|
|
1271
|
+
);
|
|
777
1272
|
if (checkOTP) {
|
|
778
1273
|
validCode = true;
|
|
779
|
-
await this.config.
|
|
780
|
-
where: { id: checkOTP.id }
|
|
781
|
-
});
|
|
1274
|
+
await this.config.database.otp.delete(checkOTP.id);
|
|
782
1275
|
}
|
|
783
1276
|
}
|
|
784
1277
|
if (!validCode) {
|
|
@@ -789,24 +1282,11 @@ var BaseProcedureFactory = class {
|
|
|
789
1282
|
}
|
|
790
1283
|
}
|
|
791
1284
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
792
|
-
const session = await this.config.
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
...extraSessionData
|
|
798
|
-
},
|
|
799
|
-
select: {
|
|
800
|
-
id: true,
|
|
801
|
-
userId: true,
|
|
802
|
-
socketId: true,
|
|
803
|
-
browserName: true,
|
|
804
|
-
issuedAt: true,
|
|
805
|
-
lastUsed: true,
|
|
806
|
-
revokedAt: true,
|
|
807
|
-
deviceId: true,
|
|
808
|
-
twoFaSecret: true
|
|
809
|
-
}
|
|
1285
|
+
const session = await this.config.database.session.create({
|
|
1286
|
+
userId: user.id,
|
|
1287
|
+
browserName: detectBrowser(userAgent),
|
|
1288
|
+
socketId: null,
|
|
1289
|
+
...extraSessionData
|
|
810
1290
|
});
|
|
811
1291
|
if (this.config.hooks?.onUserLogin) {
|
|
812
1292
|
await this.config.hooks.onUserLogin(user.id, session.id);
|
|
@@ -837,15 +1317,9 @@ var BaseProcedureFactory = class {
|
|
|
837
1317
|
return this.authProcedure.meta({ ignoreExpiration: true }).mutation(async ({ ctx }) => {
|
|
838
1318
|
const { userId, sessionId } = ctx;
|
|
839
1319
|
if (sessionId) {
|
|
840
|
-
await this.config.
|
|
841
|
-
where: { id: sessionId },
|
|
842
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
843
|
-
});
|
|
1320
|
+
await this.config.database.session.revoke(sessionId);
|
|
844
1321
|
if (userId) {
|
|
845
|
-
await this.config.
|
|
846
|
-
where: { id: userId },
|
|
847
|
-
data: { isActive: false }
|
|
848
|
-
});
|
|
1322
|
+
await this.config.database.user.update(userId, { isActive: false });
|
|
849
1323
|
}
|
|
850
1324
|
if (this.config.hooks?.afterLogout) {
|
|
851
1325
|
await this.config.hooks.afterLogout(userId, sessionId, ctx.socketId);
|
|
@@ -857,15 +1331,7 @@ var BaseProcedureFactory = class {
|
|
|
857
1331
|
}
|
|
858
1332
|
refresh() {
|
|
859
1333
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
860
|
-
const session = await this.config.
|
|
861
|
-
where: { id: ctx.sessionId },
|
|
862
|
-
data: { lastUsed: /* @__PURE__ */ new Date() },
|
|
863
|
-
select: {
|
|
864
|
-
id: true,
|
|
865
|
-
userId: true,
|
|
866
|
-
user: { select: { verifiedHumanAt: true } }
|
|
867
|
-
}
|
|
868
|
-
});
|
|
1334
|
+
const session = await this.config.database.session.updateLastUsed(ctx.sessionId);
|
|
869
1335
|
if (this.config.hooks?.onRefresh) {
|
|
870
1336
|
this.config.hooks.onRefresh(session.userId).catch(() => {
|
|
871
1337
|
});
|
|
@@ -890,22 +1356,14 @@ var BaseProcedureFactory = class {
|
|
|
890
1356
|
return this.authProcedure.input(endAllSessionsSchema).mutation(async ({ ctx, input }) => {
|
|
891
1357
|
const { skipCurrentSession } = input;
|
|
892
1358
|
const { userId, sessionId } = ctx;
|
|
893
|
-
const sessionsToRevoke = await this.config.
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
await this.config.prisma.session.updateMany({
|
|
902
|
-
where: {
|
|
903
|
-
userId,
|
|
904
|
-
revokedAt: null,
|
|
905
|
-
...skipCurrentSession ? { NOT: { id: sessionId } } : {}
|
|
906
|
-
},
|
|
907
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
908
|
-
});
|
|
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
|
+
);
|
|
909
1367
|
for (const session of sessionsToRevoke) {
|
|
910
1368
|
if (this.config.hooks?.onSessionRevoked) {
|
|
911
1369
|
await this.config.hooks.onSessionRevoked(
|
|
@@ -916,10 +1374,7 @@ var BaseProcedureFactory = class {
|
|
|
916
1374
|
}
|
|
917
1375
|
}
|
|
918
1376
|
if (!skipCurrentSession) {
|
|
919
|
-
await this.config.
|
|
920
|
-
where: { id: userId },
|
|
921
|
-
data: { isActive: false }
|
|
922
|
-
});
|
|
1377
|
+
await this.config.database.user.update(userId, { isActive: false });
|
|
923
1378
|
}
|
|
924
1379
|
return { success: true, revokedCount: sessionsToRevoke.length };
|
|
925
1380
|
});
|
|
@@ -934,10 +1389,7 @@ var BaseProcedureFactory = class {
|
|
|
934
1389
|
message: "New password cannot be the same as current password"
|
|
935
1390
|
});
|
|
936
1391
|
}
|
|
937
|
-
const user = await this.config.
|
|
938
|
-
where: { id: userId },
|
|
939
|
-
select: { password: true }
|
|
940
|
-
});
|
|
1392
|
+
const user = await this.config.database.user.findById(userId);
|
|
941
1393
|
if (!user) {
|
|
942
1394
|
throw new TRPCError2({ code: "NOT_FOUND", message: "User not found" });
|
|
943
1395
|
}
|
|
@@ -955,14 +1407,8 @@ var BaseProcedureFactory = class {
|
|
|
955
1407
|
});
|
|
956
1408
|
}
|
|
957
1409
|
const hashedPassword = await hashPassword(newPassword);
|
|
958
|
-
await this.config.
|
|
959
|
-
|
|
960
|
-
data: { password: hashedPassword }
|
|
961
|
-
});
|
|
962
|
-
await this.config.prisma.session.updateMany({
|
|
963
|
-
where: { userId, revokedAt: null, NOT: { id: sessionId } },
|
|
964
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
965
|
-
});
|
|
1410
|
+
await this.config.database.user.update(userId, { password: hashedPassword });
|
|
1411
|
+
await this.config.database.session.revokeAllByUserId(userId, sessionId);
|
|
966
1412
|
if (this.config.hooks?.onPasswordChanged) {
|
|
967
1413
|
await this.config.hooks.onPasswordChanged(userId);
|
|
968
1414
|
}
|
|
@@ -975,11 +1421,8 @@ var BaseProcedureFactory = class {
|
|
|
975
1421
|
sendPasswordResetEmail() {
|
|
976
1422
|
return this.procedure.input(requestPasswordResetSchema).mutation(async ({ input }) => {
|
|
977
1423
|
const { email } = input;
|
|
978
|
-
const user = await this.config.
|
|
979
|
-
|
|
980
|
-
select: { id: true, password: true, email: true }
|
|
981
|
-
});
|
|
982
|
-
if (!user) {
|
|
1424
|
+
const user = await this.config.database.user.findByEmailInsensitive(email);
|
|
1425
|
+
if (!user || user.status !== "ACTIVE") {
|
|
983
1426
|
return { message: "If an account exists with that email, a reset link has been sent." };
|
|
984
1427
|
}
|
|
985
1428
|
if (!user.password) {
|
|
@@ -988,10 +1431,8 @@ var BaseProcedureFactory = class {
|
|
|
988
1431
|
message: "This account uses social login. Please use that method."
|
|
989
1432
|
});
|
|
990
1433
|
}
|
|
991
|
-
await this.config.
|
|
992
|
-
const passwordReset = await this.config.
|
|
993
|
-
data: { userId: user.id }
|
|
994
|
-
});
|
|
1434
|
+
await this.config.database.passwordReset.deleteAllByUserId(user.id);
|
|
1435
|
+
const passwordReset = await this.config.database.passwordReset.create(user.id);
|
|
995
1436
|
if (this.config.emailService) {
|
|
996
1437
|
await this.config.emailService.sendPasswordResetEmail(user.email, String(passwordReset.id));
|
|
997
1438
|
}
|
|
@@ -1001,15 +1442,12 @@ var BaseProcedureFactory = class {
|
|
|
1001
1442
|
checkPasswordReset() {
|
|
1002
1443
|
return this.procedure.input(checkPasswordResetSchema).query(async ({ input }) => {
|
|
1003
1444
|
const { token } = input;
|
|
1004
|
-
const passwordReset = await this.config.
|
|
1005
|
-
where: { id: token },
|
|
1006
|
-
select: { id: true, createdAt: true, userId: true }
|
|
1007
|
-
});
|
|
1445
|
+
const passwordReset = await this.config.database.passwordReset.findById(token);
|
|
1008
1446
|
if (!passwordReset) {
|
|
1009
1447
|
throw new TRPCError2({ code: "NOT_FOUND", message: "Invalid reset token." });
|
|
1010
1448
|
}
|
|
1011
1449
|
if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
|
|
1012
|
-
await this.config.
|
|
1450
|
+
await this.config.database.passwordReset.delete(token);
|
|
1013
1451
|
throw new TRPCError2({ code: "FORBIDDEN", message: "Reset token expired." });
|
|
1014
1452
|
}
|
|
1015
1453
|
return { valid: true };
|
|
@@ -1018,31 +1456,23 @@ var BaseProcedureFactory = class {
|
|
|
1018
1456
|
resetPassword() {
|
|
1019
1457
|
return this.procedure.input(resetPasswordSchema).mutation(async ({ input }) => {
|
|
1020
1458
|
const { token, password } = input;
|
|
1021
|
-
const passwordReset = await this.config.
|
|
1022
|
-
where: { id: token },
|
|
1023
|
-
select: { id: true, createdAt: true, userId: true }
|
|
1024
|
-
});
|
|
1459
|
+
const passwordReset = await this.config.database.passwordReset.findById(token);
|
|
1025
1460
|
if (!passwordReset) {
|
|
1026
1461
|
throw new TRPCError2({ code: "NOT_FOUND", message: "Invalid reset token." });
|
|
1027
1462
|
}
|
|
1028
1463
|
if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
|
|
1029
|
-
await this.config.
|
|
1464
|
+
await this.config.database.passwordReset.delete(token);
|
|
1030
1465
|
throw new TRPCError2({ code: "FORBIDDEN", message: "Reset token expired." });
|
|
1031
1466
|
}
|
|
1032
1467
|
const hashedPassword = await hashPassword(password);
|
|
1033
|
-
await this.config.
|
|
1034
|
-
|
|
1035
|
-
data: { password: hashedPassword }
|
|
1036
|
-
});
|
|
1037
|
-
await this.config.prisma.passwordReset.delete({ where: { id: token } });
|
|
1038
|
-
const sessionsToRevoke = await this.config.prisma.session.findMany({
|
|
1039
|
-
where: { userId: passwordReset.userId, revokedAt: null },
|
|
1040
|
-
select: { id: true, socketId: true, userId: true }
|
|
1041
|
-
});
|
|
1042
|
-
await this.config.prisma.session.updateMany({
|
|
1043
|
-
where: { userId: passwordReset.userId, revokedAt: null },
|
|
1044
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1468
|
+
await this.config.database.user.update(passwordReset.userId, {
|
|
1469
|
+
password: hashedPassword
|
|
1045
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);
|
|
1046
1476
|
for (const session of sessionsToRevoke) {
|
|
1047
1477
|
if (this.config.hooks?.onSessionRevoked) {
|
|
1048
1478
|
await this.config.hooks.onSessionRevoked(
|
|
@@ -1079,9 +1509,9 @@ var BiometricProcedureFactory = class {
|
|
|
1079
1509
|
return this.authProcedure.input(biometricVerifySchema).mutation(async ({ ctx }) => {
|
|
1080
1510
|
this.checkConfig();
|
|
1081
1511
|
const { userId } = ctx;
|
|
1082
|
-
await this.config.
|
|
1083
|
-
|
|
1084
|
-
|
|
1512
|
+
await this.config.database.user.update(userId, {
|
|
1513
|
+
verifiedHumanAt: /* @__PURE__ */ new Date(),
|
|
1514
|
+
tag: "HUMAN"
|
|
1085
1515
|
});
|
|
1086
1516
|
if (this.config.hooks?.onBiometricVerified) {
|
|
1087
1517
|
await this.config.hooks.onBiometricVerified(userId);
|
|
@@ -1093,10 +1523,7 @@ var BiometricProcedureFactory = class {
|
|
|
1093
1523
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
1094
1524
|
this.checkConfig();
|
|
1095
1525
|
const { userId } = ctx;
|
|
1096
|
-
const user = await this.config.
|
|
1097
|
-
where: { id: userId },
|
|
1098
|
-
select: { verifiedHumanAt: true }
|
|
1099
|
-
});
|
|
1526
|
+
const user = await this.config.database.user.findById(userId);
|
|
1100
1527
|
if (!user) {
|
|
1101
1528
|
throw new TRPCError3({ code: "NOT_FOUND", message: "User not found" });
|
|
1102
1529
|
}
|
|
@@ -1143,10 +1570,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1143
1570
|
return this.authProcedure.mutation(async ({ ctx }) => {
|
|
1144
1571
|
this.checkConfig();
|
|
1145
1572
|
const { userId } = ctx;
|
|
1146
|
-
const user = await this.config.
|
|
1147
|
-
where: { id: userId, status: "ACTIVE" },
|
|
1148
|
-
select: { id: true, email: true, emailVerificationStatus: true }
|
|
1149
|
-
});
|
|
1573
|
+
const user = await this.config.database.user.findActiveById(userId);
|
|
1150
1574
|
if (!user) {
|
|
1151
1575
|
throw new TRPCError4({ code: "NOT_FOUND", message: "User not found" });
|
|
1152
1576
|
}
|
|
@@ -1154,9 +1578,9 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1154
1578
|
return { message: "Email is already verified", emailSent: false };
|
|
1155
1579
|
}
|
|
1156
1580
|
const otp = randomUUID();
|
|
1157
|
-
await this.config.
|
|
1158
|
-
|
|
1159
|
-
|
|
1581
|
+
await this.config.database.user.update(userId, {
|
|
1582
|
+
emailVerificationStatus: "PENDING",
|
|
1583
|
+
otpForEmailVerification: otp
|
|
1160
1584
|
});
|
|
1161
1585
|
if (this.config.emailService) {
|
|
1162
1586
|
try {
|
|
@@ -1174,10 +1598,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1174
1598
|
this.checkConfig();
|
|
1175
1599
|
const { userId } = ctx;
|
|
1176
1600
|
const { code } = input;
|
|
1177
|
-
const user = await this.config.
|
|
1178
|
-
where: { id: userId, status: "ACTIVE" },
|
|
1179
|
-
select: { id: true, emailVerificationStatus: true, otpForEmailVerification: true }
|
|
1180
|
-
});
|
|
1601
|
+
const user = await this.config.database.user.findActiveById(userId);
|
|
1181
1602
|
if (!user) {
|
|
1182
1603
|
throw new TRPCError4({ code: "NOT_FOUND", message: "User not found" });
|
|
1183
1604
|
}
|
|
@@ -1187,9 +1608,9 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1187
1608
|
if (code !== user.otpForEmailVerification) {
|
|
1188
1609
|
throw new TRPCError4({ code: "BAD_REQUEST", message: "Invalid verification code" });
|
|
1189
1610
|
}
|
|
1190
|
-
await this.config.
|
|
1191
|
-
|
|
1192
|
-
|
|
1611
|
+
await this.config.database.user.update(userId, {
|
|
1612
|
+
emailVerificationStatus: "VERIFIED",
|
|
1613
|
+
otpForEmailVerification: null
|
|
1193
1614
|
});
|
|
1194
1615
|
if (this.config.hooks?.onEmailVerified) {
|
|
1195
1616
|
await this.config.hooks.onEmailVerified(userId);
|
|
@@ -1201,10 +1622,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1201
1622
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
1202
1623
|
this.checkConfig();
|
|
1203
1624
|
const { userId } = ctx;
|
|
1204
|
-
const user = await this.config.
|
|
1205
|
-
where: { id: userId },
|
|
1206
|
-
select: { emailVerificationStatus: true, email: true }
|
|
1207
|
-
});
|
|
1625
|
+
const user = await this.config.database.user.findById(userId);
|
|
1208
1626
|
if (!user) {
|
|
1209
1627
|
throw new TRPCError4({ code: "NOT_FOUND", message: "User not found" });
|
|
1210
1628
|
}
|
|
@@ -1258,23 +1676,7 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1258
1676
|
message: "Email not provided by OAuth provider"
|
|
1259
1677
|
});
|
|
1260
1678
|
}
|
|
1261
|
-
let user = await this.config.
|
|
1262
|
-
where: {
|
|
1263
|
-
OR: [{ email: { equals: email, mode: "insensitive" } }, { oauthId: { equals: oauthId } }]
|
|
1264
|
-
},
|
|
1265
|
-
select: {
|
|
1266
|
-
id: true,
|
|
1267
|
-
status: true,
|
|
1268
|
-
email: true,
|
|
1269
|
-
username: true,
|
|
1270
|
-
password: true,
|
|
1271
|
-
oauthProvider: true,
|
|
1272
|
-
oauthId: true,
|
|
1273
|
-
twoFaEnabled: true,
|
|
1274
|
-
verifiedHumanAt: true,
|
|
1275
|
-
emailVerificationStatus: true
|
|
1276
|
-
}
|
|
1277
|
-
});
|
|
1679
|
+
let user = await this.config.database.user.findByEmailOrOAuthId(email, oauthId);
|
|
1278
1680
|
if (user?.oauthProvider && user.oauthProvider !== provider) {
|
|
1279
1681
|
throw new TRPCError5({
|
|
1280
1682
|
code: "BAD_REQUEST",
|
|
@@ -1289,19 +1691,17 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1289
1691
|
}
|
|
1290
1692
|
if (!user) {
|
|
1291
1693
|
const generateUsername = this.config.generateUsername ?? (() => `user_${Date.now()}`);
|
|
1292
|
-
user = await this.config.
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
verifiedHumanAt: null
|
|
1304
|
-
}
|
|
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
|
|
1305
1705
|
});
|
|
1306
1706
|
if (this.config.hooks?.onUserCreated) {
|
|
1307
1707
|
await this.config.hooks.onUserCreated(user.id, typedInput);
|
|
@@ -1317,24 +1717,11 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1317
1717
|
throw new TRPCError5({ code: "FORBIDDEN", message: "Your account has been banned." });
|
|
1318
1718
|
}
|
|
1319
1719
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
1320
|
-
const session = await this.config.
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
...extraSessionData
|
|
1326
|
-
},
|
|
1327
|
-
select: {
|
|
1328
|
-
id: true,
|
|
1329
|
-
userId: true,
|
|
1330
|
-
socketId: true,
|
|
1331
|
-
browserName: true,
|
|
1332
|
-
issuedAt: true,
|
|
1333
|
-
lastUsed: true,
|
|
1334
|
-
revokedAt: true,
|
|
1335
|
-
deviceId: true,
|
|
1336
|
-
twoFaSecret: true
|
|
1337
|
-
}
|
|
1720
|
+
const session = await this.config.database.session.create({
|
|
1721
|
+
userId: user.id,
|
|
1722
|
+
browserName: detectBrowser(userAgent),
|
|
1723
|
+
socketId: null,
|
|
1724
|
+
...extraSessionData
|
|
1338
1725
|
});
|
|
1339
1726
|
if (this.config.hooks?.onUserLogin) {
|
|
1340
1727
|
await this.config.hooks.onUserLogin(user.id, session.id);
|
|
@@ -1391,10 +1778,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1391
1778
|
return this.authProcedure.mutation(async ({ ctx }) => {
|
|
1392
1779
|
this.checkConfig();
|
|
1393
1780
|
const { userId, sessionId } = ctx;
|
|
1394
|
-
const user = await this.config.
|
|
1395
|
-
where: { id: userId },
|
|
1396
|
-
select: { twoFaEnabled: true, oauthProvider: true, password: true }
|
|
1397
|
-
});
|
|
1781
|
+
const user = await this.config.database.user.findById(userId);
|
|
1398
1782
|
if (!user) {
|
|
1399
1783
|
throw new TRPCError6({ code: "NOT_FOUND", message: "User not found." });
|
|
1400
1784
|
}
|
|
@@ -1408,10 +1792,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1408
1792
|
throw new TRPCError6({ code: "BAD_REQUEST", message: "2FA already enabled." });
|
|
1409
1793
|
}
|
|
1410
1794
|
if (this.config.features.twoFaRequiresDevice !== false) {
|
|
1411
|
-
const checkSession = await this.config.
|
|
1412
|
-
where: { userId, id: sessionId },
|
|
1413
|
-
select: { deviceId: true }
|
|
1414
|
-
});
|
|
1795
|
+
const checkSession = await this.config.database.session.findById(sessionId);
|
|
1415
1796
|
if (!checkSession?.deviceId) {
|
|
1416
1797
|
throw new TRPCError6({
|
|
1417
1798
|
code: "BAD_REQUEST",
|
|
@@ -1419,23 +1800,11 @@ var TwoFaProcedureFactory = class {
|
|
|
1419
1800
|
});
|
|
1420
1801
|
}
|
|
1421
1802
|
}
|
|
1422
|
-
await this.config.
|
|
1423
|
-
|
|
1424
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1425
|
-
});
|
|
1426
|
-
await this.config.prisma.session.updateMany({
|
|
1427
|
-
where: { userId, NOT: { id: sessionId } },
|
|
1428
|
-
data: { twoFaSecret: null }
|
|
1429
|
-
});
|
|
1803
|
+
await this.config.database.session.revokeAllByUserId(userId, sessionId);
|
|
1804
|
+
await this.config.database.session.clearTwoFaSecrets(userId, sessionId);
|
|
1430
1805
|
const secret = generateTotpSecret();
|
|
1431
|
-
await this.config.
|
|
1432
|
-
|
|
1433
|
-
data: { twoFaEnabled: true }
|
|
1434
|
-
});
|
|
1435
|
-
await this.config.prisma.session.update({
|
|
1436
|
-
where: { id: sessionId },
|
|
1437
|
-
data: { twoFaSecret: secret }
|
|
1438
|
-
});
|
|
1806
|
+
await this.config.database.user.update(userId, { twoFaEnabled: true });
|
|
1807
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: secret });
|
|
1439
1808
|
if (this.config.hooks?.onTwoFaStatusChanged) {
|
|
1440
1809
|
await this.config.hooks.onTwoFaStatusChanged(userId, true);
|
|
1441
1810
|
}
|
|
@@ -1447,10 +1816,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1447
1816
|
this.checkConfig();
|
|
1448
1817
|
const { userId, sessionId } = ctx;
|
|
1449
1818
|
const { password } = input;
|
|
1450
|
-
const user = await this.config.
|
|
1451
|
-
where: { id: userId },
|
|
1452
|
-
select: { password: true, status: true, oauthProvider: true }
|
|
1453
|
-
});
|
|
1819
|
+
const user = await this.config.database.user.findById(userId);
|
|
1454
1820
|
if (!user) {
|
|
1455
1821
|
throw new TRPCError6({ code: "NOT_FOUND", message: "User not found." });
|
|
1456
1822
|
}
|
|
@@ -1473,14 +1839,8 @@ var TwoFaProcedureFactory = class {
|
|
|
1473
1839
|
if (!isMatch) {
|
|
1474
1840
|
throw new TRPCError6({ code: "FORBIDDEN", message: "Incorrect password." });
|
|
1475
1841
|
}
|
|
1476
|
-
await this.config.
|
|
1477
|
-
|
|
1478
|
-
data: { twoFaEnabled: false }
|
|
1479
|
-
});
|
|
1480
|
-
await this.config.prisma.session.update({
|
|
1481
|
-
where: { id: sessionId },
|
|
1482
|
-
data: { twoFaSecret: null }
|
|
1483
|
-
});
|
|
1842
|
+
await this.config.database.user.update(userId, { twoFaEnabled: false });
|
|
1843
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: null });
|
|
1484
1844
|
if (this.config.hooks?.onTwoFaStatusChanged) {
|
|
1485
1845
|
await this.config.hooks.onTwoFaStatusChanged(userId, false);
|
|
1486
1846
|
}
|
|
@@ -1492,10 +1852,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1492
1852
|
this.checkConfig();
|
|
1493
1853
|
const { userId, sessionId } = ctx;
|
|
1494
1854
|
const { pushCode } = input;
|
|
1495
|
-
const user = await this.config.
|
|
1496
|
-
where: { id: userId },
|
|
1497
|
-
select: { twoFaEnabled: true, oauthProvider: true }
|
|
1498
|
-
});
|
|
1855
|
+
const user = await this.config.database.user.findById(userId);
|
|
1499
1856
|
if (user?.oauthProvider) {
|
|
1500
1857
|
throw new TRPCError6({
|
|
1501
1858
|
code: "FORBIDDEN",
|
|
@@ -1505,10 +1862,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1505
1862
|
if (!user?.twoFaEnabled) {
|
|
1506
1863
|
throw new TRPCError6({ code: "BAD_REQUEST", message: "2FA not enabled." });
|
|
1507
1864
|
}
|
|
1508
|
-
const session = await this.config.
|
|
1509
|
-
where: { id: sessionId, userId },
|
|
1510
|
-
select: { twoFaSecret: true, device: { select: { pushToken: true } } }
|
|
1511
|
-
});
|
|
1865
|
+
const session = await this.config.database.session.findByIdWithDevice(sessionId, userId);
|
|
1512
1866
|
if (!session?.device) {
|
|
1513
1867
|
throw new TRPCError6({ code: "BAD_REQUEST", message: "Invalid request" });
|
|
1514
1868
|
}
|
|
@@ -1520,10 +1874,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1520
1874
|
return { secret: session.twoFaSecret };
|
|
1521
1875
|
}
|
|
1522
1876
|
const secret = generateTotpSecret();
|
|
1523
|
-
await this.config.
|
|
1524
|
-
where: { id: sessionId },
|
|
1525
|
-
data: { twoFaSecret: secret }
|
|
1526
|
-
});
|
|
1877
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: secret });
|
|
1527
1878
|
return { secret };
|
|
1528
1879
|
});
|
|
1529
1880
|
}
|
|
@@ -1531,11 +1882,8 @@ var TwoFaProcedureFactory = class {
|
|
|
1531
1882
|
return this.procedure.input(twoFaResetSchema).mutation(async ({ input }) => {
|
|
1532
1883
|
this.checkConfig();
|
|
1533
1884
|
const { username, password } = input;
|
|
1534
|
-
const user = await this.config.
|
|
1535
|
-
|
|
1536
|
-
select: { id: true, password: true, email: true }
|
|
1537
|
-
});
|
|
1538
|
-
if (!user) {
|
|
1885
|
+
const user = await this.config.database.user.findByUsernameInsensitive(username);
|
|
1886
|
+
if (!user || !user.twoFaEnabled) {
|
|
1539
1887
|
throw new TRPCError6({ code: "UNAUTHORIZED", message: "Invalid credentials." });
|
|
1540
1888
|
}
|
|
1541
1889
|
if (!user.password) {
|
|
@@ -1550,9 +1898,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1550
1898
|
}
|
|
1551
1899
|
const otp = generateOtp();
|
|
1552
1900
|
const expiresAt = new Date(Date.now() + this.config.tokenSettings.otpValidityMs);
|
|
1553
|
-
await this.config.
|
|
1554
|
-
data: { userId: user.id, code: otp, expiresAt }
|
|
1555
|
-
});
|
|
1901
|
+
await this.config.database.otp.create({ userId: user.id, code: otp, expiresAt });
|
|
1556
1902
|
if (this.config.emailService) {
|
|
1557
1903
|
await this.config.emailService.sendOTPEmail(user.email, otp);
|
|
1558
1904
|
}
|
|
@@ -1563,30 +1909,17 @@ var TwoFaProcedureFactory = class {
|
|
|
1563
1909
|
return this.procedure.input(twoFaResetVerifySchema).mutation(async ({ input }) => {
|
|
1564
1910
|
this.checkConfig();
|
|
1565
1911
|
const { code, username } = input;
|
|
1566
|
-
const user = await this.config.
|
|
1567
|
-
where: { username: { equals: username, mode: "insensitive" } },
|
|
1568
|
-
select: { id: true }
|
|
1569
|
-
});
|
|
1912
|
+
const user = await this.config.database.user.findByUsernameInsensitive(username);
|
|
1570
1913
|
if (!user) {
|
|
1571
1914
|
throw new TRPCError6({ code: "NOT_FOUND", message: "User not found" });
|
|
1572
1915
|
}
|
|
1573
|
-
const otp = await this.config.
|
|
1574
|
-
where: { userId: user.id, code, expiresAt: { gte: /* @__PURE__ */ new Date() } }
|
|
1575
|
-
});
|
|
1916
|
+
const otp = await this.config.database.otp.findValidByUserAndCode(user.id, code);
|
|
1576
1917
|
if (!otp) {
|
|
1577
1918
|
throw new TRPCError6({ code: "FORBIDDEN", message: "Invalid or expired OTP" });
|
|
1578
1919
|
}
|
|
1579
|
-
await this.config.
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
await this.config.prisma.user.update({
|
|
1583
|
-
where: { id: user.id },
|
|
1584
|
-
data: { twoFaEnabled: false }
|
|
1585
|
-
});
|
|
1586
|
-
await this.config.prisma.session.updateMany({
|
|
1587
|
-
where: { userId: user.id },
|
|
1588
|
-
data: { twoFaSecret: null }
|
|
1589
|
-
});
|
|
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);
|
|
1590
1923
|
return { success: true, message: "2FA has been reset." };
|
|
1591
1924
|
});
|
|
1592
1925
|
}
|
|
@@ -1595,36 +1928,14 @@ var TwoFaProcedureFactory = class {
|
|
|
1595
1928
|
this.checkConfig();
|
|
1596
1929
|
const { userId, sessionId } = ctx;
|
|
1597
1930
|
const { pushToken } = input;
|
|
1598
|
-
await this.config.
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
},
|
|
1605
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1606
|
-
});
|
|
1607
|
-
const checkDevice = await this.config.prisma.device.findFirst({
|
|
1608
|
-
where: {
|
|
1609
|
-
pushToken,
|
|
1610
|
-
sessions: { some: { id: sessionId } },
|
|
1611
|
-
users: { some: { id: userId } }
|
|
1612
|
-
},
|
|
1613
|
-
select: { id: true }
|
|
1614
|
-
});
|
|
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
|
+
);
|
|
1615
1937
|
if (!checkDevice) {
|
|
1616
|
-
await this.config.
|
|
1617
|
-
where: { pushToken },
|
|
1618
|
-
create: {
|
|
1619
|
-
pushToken,
|
|
1620
|
-
sessions: { connect: { id: sessionId } },
|
|
1621
|
-
users: { connect: { id: userId } }
|
|
1622
|
-
},
|
|
1623
|
-
update: {
|
|
1624
|
-
sessions: { connect: { id: sessionId } },
|
|
1625
|
-
users: { connect: { id: userId } }
|
|
1626
|
-
}
|
|
1627
|
-
});
|
|
1938
|
+
await this.config.database.device.upsertByPushToken(pushToken, sessionId, userId);
|
|
1628
1939
|
}
|
|
1629
1940
|
return { registered: true };
|
|
1630
1941
|
});
|
|
@@ -1634,30 +1945,13 @@ var TwoFaProcedureFactory = class {
|
|
|
1634
1945
|
this.checkConfig();
|
|
1635
1946
|
const { userId } = ctx;
|
|
1636
1947
|
const { pushToken } = input;
|
|
1637
|
-
const device = await this.config.
|
|
1638
|
-
where: {
|
|
1639
|
-
users: { some: { id: userId } },
|
|
1640
|
-
pushToken
|
|
1641
|
-
},
|
|
1642
|
-
select: { id: true }
|
|
1643
|
-
});
|
|
1948
|
+
const device = await this.config.database.device.findByUserAndToken(userId, pushToken);
|
|
1644
1949
|
if (device) {
|
|
1645
|
-
await this.config.
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
where: { id: device.id },
|
|
1651
|
-
data: { users: { disconnect: { id: userId } } }
|
|
1652
|
-
});
|
|
1653
|
-
const remainingUsers = await this.config.prisma.device.findUnique({
|
|
1654
|
-
where: { id: device.id },
|
|
1655
|
-
select: { users: { select: { id: true }, take: 1 } }
|
|
1656
|
-
});
|
|
1657
|
-
if (!remainingUsers?.users.length) {
|
|
1658
|
-
await this.config.prisma.device.delete({
|
|
1659
|
-
where: { id: device.id }
|
|
1660
|
-
});
|
|
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);
|
|
1661
1955
|
}
|
|
1662
1956
|
}
|
|
1663
1957
|
return { deregistered: true };
|
|
@@ -1814,8 +2108,10 @@ export {
|
|
|
1814
2108
|
createAuthRouter,
|
|
1815
2109
|
createAuthToken,
|
|
1816
2110
|
createConsoleEmailAdapter,
|
|
2111
|
+
createDrizzleAdapter,
|
|
1817
2112
|
createNoopEmailAdapter,
|
|
1818
2113
|
createOAuthVerifier,
|
|
2114
|
+
createPrismaAdapter,
|
|
1819
2115
|
decodeToken,
|
|
1820
2116
|
defaultAuthConfig,
|
|
1821
2117
|
defaultCookieSettings,
|