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