@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.js
CHANGED
|
@@ -42,8 +42,12 @@ __export(index_exports, {
|
|
|
42
42
|
createAuthRouter: () => createAuthRouter,
|
|
43
43
|
createAuthToken: () => createAuthToken,
|
|
44
44
|
createConsoleEmailAdapter: () => createConsoleEmailAdapter,
|
|
45
|
+
createDrizzleAdapter: () => createDrizzleAdapter,
|
|
45
46
|
createNoopEmailAdapter: () => createNoopEmailAdapter,
|
|
46
47
|
createOAuthVerifier: () => createOAuthVerifier,
|
|
48
|
+
createPrismaAdapter: () => createPrismaAdapter,
|
|
49
|
+
createSessionWithToken: () => createSessionWithToken,
|
|
50
|
+
createSessionWithTokenAndCookie: () => createSessionWithTokenAndCookie,
|
|
47
51
|
decodeToken: () => decodeToken,
|
|
48
52
|
defaultAuthConfig: () => defaultAuthConfig,
|
|
49
53
|
defaultCookieSettings: () => defaultCookieSettings,
|
|
@@ -78,6 +82,261 @@ module.exports = __toCommonJS(index_exports);
|
|
|
78
82
|
// src/middleware/authGuard.ts
|
|
79
83
|
var import_server = require("@trpc/server");
|
|
80
84
|
|
|
85
|
+
// src/adapters/prismaAdapter.ts
|
|
86
|
+
function createPrismaAdapter(prisma) {
|
|
87
|
+
const db = prisma;
|
|
88
|
+
return {
|
|
89
|
+
user: {
|
|
90
|
+
async findByEmailInsensitive(email) {
|
|
91
|
+
return db.user.findFirst({
|
|
92
|
+
where: { email: { equals: email, mode: "insensitive" } }
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
async findByUsernameInsensitive(username) {
|
|
96
|
+
return db.user.findFirst({
|
|
97
|
+
where: { username: { equals: username, mode: "insensitive" } }
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
async findByEmailOrUsernameInsensitive(identifier) {
|
|
101
|
+
return db.user.findFirst({
|
|
102
|
+
where: {
|
|
103
|
+
OR: [
|
|
104
|
+
{ email: { equals: identifier, mode: "insensitive" } },
|
|
105
|
+
{ username: { equals: identifier, mode: "insensitive" } }
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
async findByEmailOrOAuthId(email, oauthId) {
|
|
111
|
+
return db.user.findFirst({
|
|
112
|
+
where: {
|
|
113
|
+
OR: [
|
|
114
|
+
{ email: { equals: email, mode: "insensitive" } },
|
|
115
|
+
{ oauthId: { equals: oauthId } }
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
},
|
|
120
|
+
async findById(id) {
|
|
121
|
+
return db.user.findUnique({ where: { id } });
|
|
122
|
+
},
|
|
123
|
+
async findActiveById(id) {
|
|
124
|
+
return db.user.findUnique({
|
|
125
|
+
where: { id, status: "ACTIVE" }
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
async create(data) {
|
|
129
|
+
return db.user.create({ data });
|
|
130
|
+
},
|
|
131
|
+
async update(id, data) {
|
|
132
|
+
return db.user.update({ where: { id }, data });
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
session: {
|
|
136
|
+
async findById(id) {
|
|
137
|
+
const session = await db.session.findUnique({
|
|
138
|
+
where: { id },
|
|
139
|
+
select: {
|
|
140
|
+
id: true,
|
|
141
|
+
userId: true,
|
|
142
|
+
socketId: true,
|
|
143
|
+
twoFaSecret: true,
|
|
144
|
+
browserName: true,
|
|
145
|
+
issuedAt: true,
|
|
146
|
+
lastUsed: true,
|
|
147
|
+
revokedAt: true,
|
|
148
|
+
deviceId: true,
|
|
149
|
+
user: { select: { status: true, verifiedHumanAt: true } }
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return session;
|
|
153
|
+
},
|
|
154
|
+
async create(data) {
|
|
155
|
+
return db.session.create({ data });
|
|
156
|
+
},
|
|
157
|
+
async update(id, data) {
|
|
158
|
+
return db.session.update({ where: { id }, data });
|
|
159
|
+
},
|
|
160
|
+
async updateLastUsed(id) {
|
|
161
|
+
const session = await db.session.update({
|
|
162
|
+
where: { id },
|
|
163
|
+
data: { lastUsed: /* @__PURE__ */ new Date() },
|
|
164
|
+
select: {
|
|
165
|
+
id: true,
|
|
166
|
+
userId: true,
|
|
167
|
+
socketId: true,
|
|
168
|
+
twoFaSecret: true,
|
|
169
|
+
browserName: true,
|
|
170
|
+
issuedAt: true,
|
|
171
|
+
lastUsed: true,
|
|
172
|
+
revokedAt: true,
|
|
173
|
+
deviceId: true,
|
|
174
|
+
user: { select: { verifiedHumanAt: true } }
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
return session;
|
|
178
|
+
},
|
|
179
|
+
async revoke(id) {
|
|
180
|
+
await db.session.update({
|
|
181
|
+
where: { id },
|
|
182
|
+
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
async findActiveByUserId(userId, excludeSessionId) {
|
|
186
|
+
return db.session.findMany({
|
|
187
|
+
where: {
|
|
188
|
+
userId,
|
|
189
|
+
revokedAt: null,
|
|
190
|
+
...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
|
|
191
|
+
},
|
|
192
|
+
select: { id: true, socketId: true, userId: true }
|
|
193
|
+
});
|
|
194
|
+
},
|
|
195
|
+
async revokeAllByUserId(userId, excludeSessionId) {
|
|
196
|
+
await db.session.updateMany({
|
|
197
|
+
where: {
|
|
198
|
+
userId,
|
|
199
|
+
revokedAt: null,
|
|
200
|
+
...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
|
|
201
|
+
},
|
|
202
|
+
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
async findTwoFaSecretsByUserId(userId) {
|
|
206
|
+
return db.session.findMany({
|
|
207
|
+
where: { userId, twoFaSecret: { not: null } },
|
|
208
|
+
select: { twoFaSecret: true }
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
async clearTwoFaSecrets(userId, excludeSessionId) {
|
|
212
|
+
await db.session.updateMany({
|
|
213
|
+
where: {
|
|
214
|
+
userId,
|
|
215
|
+
...excludeSessionId ? { NOT: { id: excludeSessionId } } : {}
|
|
216
|
+
},
|
|
217
|
+
data: { twoFaSecret: null }
|
|
218
|
+
});
|
|
219
|
+
},
|
|
220
|
+
async findByIdWithDevice(id, userId) {
|
|
221
|
+
const session = await db.session.findUnique({
|
|
222
|
+
where: { id, userId },
|
|
223
|
+
select: {
|
|
224
|
+
twoFaSecret: true,
|
|
225
|
+
deviceId: true,
|
|
226
|
+
device: { select: { pushToken: true } }
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
return session;
|
|
230
|
+
},
|
|
231
|
+
async revokeByDevicePushToken(userId, pushToken, excludeSessionId) {
|
|
232
|
+
await db.session.updateMany({
|
|
233
|
+
where: {
|
|
234
|
+
userId,
|
|
235
|
+
id: { not: excludeSessionId },
|
|
236
|
+
revokedAt: null,
|
|
237
|
+
device: { pushToken }
|
|
238
|
+
},
|
|
239
|
+
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
240
|
+
});
|
|
241
|
+
},
|
|
242
|
+
async clearDeviceId(userId, deviceId) {
|
|
243
|
+
await db.session.updateMany({
|
|
244
|
+
where: { userId, deviceId },
|
|
245
|
+
data: { deviceId: null }
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
},
|
|
249
|
+
otp: {
|
|
250
|
+
async findValidByUserAndCode(userId, code) {
|
|
251
|
+
return db.oTP.findFirst({
|
|
252
|
+
where: { userId, code, expiresAt: { gte: /* @__PURE__ */ new Date() } }
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
async create(data) {
|
|
256
|
+
return db.oTP.create({ data });
|
|
257
|
+
},
|
|
258
|
+
async delete(id) {
|
|
259
|
+
await db.oTP.delete({ where: { id } });
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
passwordReset: {
|
|
263
|
+
async findById(id) {
|
|
264
|
+
return db.passwordReset.findUnique({
|
|
265
|
+
where: { id },
|
|
266
|
+
select: { id: true, createdAt: true, userId: true }
|
|
267
|
+
});
|
|
268
|
+
},
|
|
269
|
+
async create(userId) {
|
|
270
|
+
return db.passwordReset.create({
|
|
271
|
+
data: { userId }
|
|
272
|
+
});
|
|
273
|
+
},
|
|
274
|
+
async delete(id) {
|
|
275
|
+
await db.passwordReset.delete({ where: { id } });
|
|
276
|
+
},
|
|
277
|
+
async deleteAllByUserId(userId) {
|
|
278
|
+
await db.passwordReset.deleteMany({ where: { userId } });
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
device: {
|
|
282
|
+
async findByTokenSessionAndUser(pushToken, sessionId, userId) {
|
|
283
|
+
return db.device.findFirst({
|
|
284
|
+
where: {
|
|
285
|
+
pushToken,
|
|
286
|
+
sessions: { some: { id: sessionId } },
|
|
287
|
+
users: { some: { id: userId } }
|
|
288
|
+
},
|
|
289
|
+
select: { id: true }
|
|
290
|
+
});
|
|
291
|
+
},
|
|
292
|
+
async upsertByPushToken(pushToken, sessionId, userId) {
|
|
293
|
+
await db.device.upsert({
|
|
294
|
+
where: { pushToken },
|
|
295
|
+
create: {
|
|
296
|
+
pushToken,
|
|
297
|
+
sessions: { connect: { id: sessionId } },
|
|
298
|
+
users: { connect: { id: userId } }
|
|
299
|
+
},
|
|
300
|
+
update: {
|
|
301
|
+
sessions: { connect: { id: sessionId } },
|
|
302
|
+
users: { connect: { id: userId } }
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
async findByUserAndToken(userId, pushToken) {
|
|
307
|
+
return db.device.findFirst({
|
|
308
|
+
where: { users: { some: { id: userId } }, pushToken },
|
|
309
|
+
select: { id: true }
|
|
310
|
+
});
|
|
311
|
+
},
|
|
312
|
+
async disconnectUser(deviceId, userId) {
|
|
313
|
+
await db.device.update({
|
|
314
|
+
where: { id: deviceId },
|
|
315
|
+
data: { users: { disconnect: { id: userId } } }
|
|
316
|
+
});
|
|
317
|
+
},
|
|
318
|
+
async hasRemainingUsers(deviceId) {
|
|
319
|
+
const result = await db.device.findUnique({
|
|
320
|
+
where: { id: deviceId },
|
|
321
|
+
select: { users: { select: { id: true }, take: 1 } }
|
|
322
|
+
});
|
|
323
|
+
return (result?.users.length ?? 0) > 0;
|
|
324
|
+
},
|
|
325
|
+
async delete(id) {
|
|
326
|
+
await db.device.delete({ where: { id } });
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
admin: {
|
|
330
|
+
async findByUserId(userId) {
|
|
331
|
+
return db.admin.findFirst({
|
|
332
|
+
where: { userId },
|
|
333
|
+
select: { ip: true }
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
81
340
|
// src/adapters/email.ts
|
|
82
341
|
function createNoopEmailAdapter() {
|
|
83
342
|
return {
|
|
@@ -131,6 +390,294 @@ function createConsoleEmailAdapter() {
|
|
|
131
390
|
};
|
|
132
391
|
}
|
|
133
392
|
|
|
393
|
+
// src/adapters/drizzleAdapter.ts
|
|
394
|
+
function createDrizzleAdapter(db, tables) {
|
|
395
|
+
const {
|
|
396
|
+
eq,
|
|
397
|
+
and,
|
|
398
|
+
or,
|
|
399
|
+
isNull,
|
|
400
|
+
isNotNull,
|
|
401
|
+
gte,
|
|
402
|
+
ne,
|
|
403
|
+
sql
|
|
404
|
+
} = require("drizzle-orm");
|
|
405
|
+
const { users, sessions, otps, passwordResets, devices, admins } = tables;
|
|
406
|
+
return {
|
|
407
|
+
user: {
|
|
408
|
+
async findByEmailInsensitive(email) {
|
|
409
|
+
const rows = await db.select().from(users).where(sql`lower(${users.email}) = lower(${email})`).limit(1);
|
|
410
|
+
return rows[0] ?? null;
|
|
411
|
+
},
|
|
412
|
+
async findByUsernameInsensitive(username) {
|
|
413
|
+
const rows = await db.select().from(users).where(sql`lower(${users.username}) = lower(${username})`).limit(1);
|
|
414
|
+
return rows[0] ?? null;
|
|
415
|
+
},
|
|
416
|
+
async findByEmailOrUsernameInsensitive(identifier) {
|
|
417
|
+
const rows = await db.select().from(users).where(
|
|
418
|
+
or(
|
|
419
|
+
sql`lower(${users.email}) = lower(${identifier})`,
|
|
420
|
+
sql`lower(${users.username}) = lower(${identifier})`
|
|
421
|
+
)
|
|
422
|
+
).limit(1);
|
|
423
|
+
return rows[0] ?? null;
|
|
424
|
+
},
|
|
425
|
+
async findByEmailOrOAuthId(email, oauthId) {
|
|
426
|
+
const rows = await db.select().from(users).where(
|
|
427
|
+
or(
|
|
428
|
+
sql`lower(${users.email}) = lower(${email})`,
|
|
429
|
+
eq(users.oauthId, oauthId)
|
|
430
|
+
)
|
|
431
|
+
).limit(1);
|
|
432
|
+
return rows[0] ?? null;
|
|
433
|
+
},
|
|
434
|
+
async findById(id) {
|
|
435
|
+
const rows = await db.select().from(users).where(eq(users.id, id)).limit(1);
|
|
436
|
+
return rows[0] ?? null;
|
|
437
|
+
},
|
|
438
|
+
async findActiveById(id) {
|
|
439
|
+
const rows = await db.select().from(users).where(and(eq(users.id, id), eq(users.status, "ACTIVE"))).limit(1);
|
|
440
|
+
return rows[0] ?? null;
|
|
441
|
+
},
|
|
442
|
+
async create(data) {
|
|
443
|
+
const rows = await db.insert(users).values(data).returning();
|
|
444
|
+
return rows[0];
|
|
445
|
+
},
|
|
446
|
+
async update(id, data) {
|
|
447
|
+
const rows = await db.update(users).set(data).where(eq(users.id, id)).returning();
|
|
448
|
+
return rows[0];
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
session: {
|
|
452
|
+
async findById(id) {
|
|
453
|
+
const rows = await db.select({
|
|
454
|
+
id: sessions.id,
|
|
455
|
+
userId: sessions.userId,
|
|
456
|
+
socketId: sessions.socketId,
|
|
457
|
+
twoFaSecret: sessions.twoFaSecret,
|
|
458
|
+
browserName: sessions.browserName,
|
|
459
|
+
issuedAt: sessions.issuedAt,
|
|
460
|
+
lastUsed: sessions.lastUsed,
|
|
461
|
+
revokedAt: sessions.revokedAt,
|
|
462
|
+
deviceId: sessions.deviceId,
|
|
463
|
+
user: {
|
|
464
|
+
status: users.status,
|
|
465
|
+
verifiedHumanAt: users.verifiedHumanAt
|
|
466
|
+
}
|
|
467
|
+
}).from(sessions).innerJoin(users, eq(sessions.userId, users.id)).where(eq(sessions.id, id)).limit(1);
|
|
468
|
+
return rows[0] ?? null;
|
|
469
|
+
},
|
|
470
|
+
async create(data) {
|
|
471
|
+
const rows = await db.insert(sessions).values(data).returning();
|
|
472
|
+
return rows[0];
|
|
473
|
+
},
|
|
474
|
+
async update(id, data) {
|
|
475
|
+
const rows = await db.update(sessions).set(data).where(eq(sessions.id, id)).returning();
|
|
476
|
+
return rows[0];
|
|
477
|
+
},
|
|
478
|
+
async updateLastUsed(id) {
|
|
479
|
+
await db.update(sessions).set({ lastUsed: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id));
|
|
480
|
+
const rows = await db.select({
|
|
481
|
+
id: sessions.id,
|
|
482
|
+
userId: sessions.userId,
|
|
483
|
+
socketId: sessions.socketId,
|
|
484
|
+
twoFaSecret: sessions.twoFaSecret,
|
|
485
|
+
browserName: sessions.browserName,
|
|
486
|
+
issuedAt: sessions.issuedAt,
|
|
487
|
+
lastUsed: sessions.lastUsed,
|
|
488
|
+
revokedAt: sessions.revokedAt,
|
|
489
|
+
deviceId: sessions.deviceId,
|
|
490
|
+
user: {
|
|
491
|
+
verifiedHumanAt: users.verifiedHumanAt
|
|
492
|
+
}
|
|
493
|
+
}).from(sessions).innerJoin(users, eq(sessions.userId, users.id)).where(eq(sessions.id, id)).limit(1);
|
|
494
|
+
return rows[0];
|
|
495
|
+
},
|
|
496
|
+
async revoke(id) {
|
|
497
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(eq(sessions.id, id));
|
|
498
|
+
},
|
|
499
|
+
async findActiveByUserId(userId, excludeSessionId) {
|
|
500
|
+
const conditions = [eq(sessions.userId, userId), isNull(sessions.revokedAt)];
|
|
501
|
+
if (excludeSessionId !== void 0) {
|
|
502
|
+
conditions.push(ne(sessions.id, excludeSessionId));
|
|
503
|
+
}
|
|
504
|
+
const activeRows = await db.select({
|
|
505
|
+
id: sessions.id,
|
|
506
|
+
socketId: sessions.socketId,
|
|
507
|
+
userId: sessions.userId
|
|
508
|
+
}).from(sessions).where(and(...conditions));
|
|
509
|
+
return activeRows;
|
|
510
|
+
},
|
|
511
|
+
async revokeAllByUserId(userId, excludeSessionId) {
|
|
512
|
+
const conditions = [eq(sessions.userId, userId), isNull(sessions.revokedAt)];
|
|
513
|
+
if (excludeSessionId !== void 0) {
|
|
514
|
+
conditions.push(ne(sessions.id, excludeSessionId));
|
|
515
|
+
}
|
|
516
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(and(...conditions));
|
|
517
|
+
},
|
|
518
|
+
async findTwoFaSecretsByUserId(userId) {
|
|
519
|
+
const secretRows = await db.select({ twoFaSecret: sessions.twoFaSecret }).from(sessions).where(and(eq(sessions.userId, userId), isNotNull(sessions.twoFaSecret)));
|
|
520
|
+
return secretRows;
|
|
521
|
+
},
|
|
522
|
+
async clearTwoFaSecrets(userId, excludeSessionId) {
|
|
523
|
+
const conditions = [eq(sessions.userId, userId)];
|
|
524
|
+
if (excludeSessionId !== void 0) {
|
|
525
|
+
conditions.push(ne(sessions.id, excludeSessionId));
|
|
526
|
+
}
|
|
527
|
+
await db.update(sessions).set({ twoFaSecret: null }).where(and(...conditions));
|
|
528
|
+
},
|
|
529
|
+
async findByIdWithDevice(id, userId) {
|
|
530
|
+
const rows = await db.select({
|
|
531
|
+
twoFaSecret: sessions.twoFaSecret,
|
|
532
|
+
deviceId: sessions.deviceId,
|
|
533
|
+
device: {
|
|
534
|
+
pushToken: devices.pushToken
|
|
535
|
+
}
|
|
536
|
+
}).from(sessions).leftJoin(devices, eq(sessions.deviceId, devices.id)).where(and(eq(sessions.id, id), eq(sessions.userId, userId))).limit(1);
|
|
537
|
+
if (!rows[0]) return null;
|
|
538
|
+
const row = rows[0];
|
|
539
|
+
const device = row.device;
|
|
540
|
+
return {
|
|
541
|
+
twoFaSecret: row.twoFaSecret,
|
|
542
|
+
deviceId: row.deviceId,
|
|
543
|
+
device: device?.pushToken ? { pushToken: device.pushToken } : null
|
|
544
|
+
};
|
|
545
|
+
},
|
|
546
|
+
async revokeByDevicePushToken(userId, pushToken, excludeSessionId) {
|
|
547
|
+
const deviceRows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
548
|
+
if (!deviceRows[0]) return;
|
|
549
|
+
await db.update(sessions).set({ revokedAt: /* @__PURE__ */ new Date() }).where(
|
|
550
|
+
and(
|
|
551
|
+
eq(sessions.userId, userId),
|
|
552
|
+
ne(sessions.id, excludeSessionId),
|
|
553
|
+
isNull(sessions.revokedAt),
|
|
554
|
+
eq(sessions.deviceId, deviceRows[0].id)
|
|
555
|
+
)
|
|
556
|
+
);
|
|
557
|
+
},
|
|
558
|
+
async clearDeviceId(userId, deviceId) {
|
|
559
|
+
await db.update(sessions).set({ deviceId: null }).where(and(eq(sessions.userId, userId), eq(sessions.deviceId, deviceId)));
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
otp: {
|
|
563
|
+
async findValidByUserAndCode(userId, code) {
|
|
564
|
+
const rows = await db.select().from(otps).where(
|
|
565
|
+
and(eq(otps.userId, userId), eq(otps.code, code), gte(otps.expiresAt, /* @__PURE__ */ new Date()))
|
|
566
|
+
).limit(1);
|
|
567
|
+
return rows[0] ?? null;
|
|
568
|
+
},
|
|
569
|
+
async create(data) {
|
|
570
|
+
const rows = await db.insert(otps).values(data).returning();
|
|
571
|
+
return rows[0];
|
|
572
|
+
},
|
|
573
|
+
async delete(id) {
|
|
574
|
+
await db.delete(otps).where(eq(otps.id, id));
|
|
575
|
+
}
|
|
576
|
+
},
|
|
577
|
+
passwordReset: {
|
|
578
|
+
async findById(id) {
|
|
579
|
+
const rows = await db.select({
|
|
580
|
+
id: passwordResets.id,
|
|
581
|
+
createdAt: passwordResets.createdAt,
|
|
582
|
+
userId: passwordResets.userId
|
|
583
|
+
}).from(passwordResets).where(eq(passwordResets.id, id)).limit(1);
|
|
584
|
+
return rows[0] ?? null;
|
|
585
|
+
},
|
|
586
|
+
async create(userId) {
|
|
587
|
+
const rows = await db.insert(passwordResets).values({ userId }).returning();
|
|
588
|
+
return rows[0];
|
|
589
|
+
},
|
|
590
|
+
async delete(id) {
|
|
591
|
+
await db.delete(passwordResets).where(eq(passwordResets.id, id));
|
|
592
|
+
},
|
|
593
|
+
async deleteAllByUserId(userId) {
|
|
594
|
+
await db.delete(passwordResets).where(eq(passwordResets.userId, userId));
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
device: {
|
|
598
|
+
async findByTokenSessionAndUser(pushToken, sessionId, userId) {
|
|
599
|
+
const rows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
600
|
+
if (!rows[0]) return null;
|
|
601
|
+
if (tables.devicesToSessions && tables.devicesToUsers) {
|
|
602
|
+
const sessionLink = await db.select().from(tables.devicesToSessions).where(
|
|
603
|
+
and(
|
|
604
|
+
eq(tables.devicesToSessions.deviceId, rows[0].id),
|
|
605
|
+
eq(tables.devicesToSessions.sessionId, sessionId)
|
|
606
|
+
)
|
|
607
|
+
).limit(1);
|
|
608
|
+
const userLink = await db.select().from(tables.devicesToUsers).where(
|
|
609
|
+
and(
|
|
610
|
+
eq(tables.devicesToUsers.deviceId, rows[0].id),
|
|
611
|
+
eq(tables.devicesToUsers.userId, userId)
|
|
612
|
+
)
|
|
613
|
+
).limit(1);
|
|
614
|
+
if (!sessionLink[0] || !userLink[0]) return null;
|
|
615
|
+
}
|
|
616
|
+
return { id: rows[0].id };
|
|
617
|
+
},
|
|
618
|
+
async upsertByPushToken(pushToken, sessionId, userId) {
|
|
619
|
+
const existing = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
620
|
+
let deviceId;
|
|
621
|
+
if (existing[0]) {
|
|
622
|
+
deviceId = existing[0].id;
|
|
623
|
+
} else {
|
|
624
|
+
const insertedRows = await db.insert(devices).values({ pushToken }).returning({ id: devices.id });
|
|
625
|
+
deviceId = insertedRows[0].id;
|
|
626
|
+
}
|
|
627
|
+
if (tables.devicesToSessions) {
|
|
628
|
+
await db.insert(tables.devicesToSessions).values({ deviceId, sessionId }).onConflictDoNothing();
|
|
629
|
+
}
|
|
630
|
+
if (tables.devicesToUsers) {
|
|
631
|
+
await db.insert(tables.devicesToUsers).values({ deviceId, userId }).onConflictDoNothing();
|
|
632
|
+
}
|
|
633
|
+
await db.update(sessions).set({ deviceId }).where(eq(sessions.id, sessionId));
|
|
634
|
+
},
|
|
635
|
+
async findByUserAndToken(userId, pushToken) {
|
|
636
|
+
if (tables.devicesToUsers) {
|
|
637
|
+
const joinRows = await db.select({ id: devices.id }).from(devices).innerJoin(
|
|
638
|
+
tables.devicesToUsers,
|
|
639
|
+
eq(devices.id, tables.devicesToUsers.deviceId)
|
|
640
|
+
).where(
|
|
641
|
+
and(
|
|
642
|
+
eq(devices.pushToken, pushToken),
|
|
643
|
+
eq(tables.devicesToUsers.userId, userId)
|
|
644
|
+
)
|
|
645
|
+
).limit(1);
|
|
646
|
+
return joinRows[0] ? { id: joinRows[0].id } : null;
|
|
647
|
+
}
|
|
648
|
+
const rows = await db.select({ id: devices.id }).from(devices).where(eq(devices.pushToken, pushToken)).limit(1);
|
|
649
|
+
return rows[0] ? { id: rows[0].id } : null;
|
|
650
|
+
},
|
|
651
|
+
async disconnectUser(deviceId, userId) {
|
|
652
|
+
if (tables.devicesToUsers) {
|
|
653
|
+
await db.delete(tables.devicesToUsers).where(
|
|
654
|
+
and(
|
|
655
|
+
eq(tables.devicesToUsers.deviceId, deviceId),
|
|
656
|
+
eq(tables.devicesToUsers.userId, userId)
|
|
657
|
+
)
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
async hasRemainingUsers(deviceId) {
|
|
662
|
+
if (tables.devicesToUsers) {
|
|
663
|
+
const remainingRows = await db.select({ userId: tables.devicesToUsers.userId }).from(tables.devicesToUsers).where(eq(tables.devicesToUsers.deviceId, deviceId)).limit(1);
|
|
664
|
+
return remainingRows.length > 0;
|
|
665
|
+
}
|
|
666
|
+
return false;
|
|
667
|
+
},
|
|
668
|
+
async delete(id) {
|
|
669
|
+
await db.delete(devices).where(eq(devices.id, id));
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
admin: {
|
|
673
|
+
async findByUserId(userId) {
|
|
674
|
+
const rows = await db.select({ ip: admins.ip }).from(admins).where(eq(admins.userId, userId)).limit(1);
|
|
675
|
+
return rows[0] ?? null;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
134
681
|
// src/utilities/config.ts
|
|
135
682
|
var defaultTokenSettings = {
|
|
136
683
|
jwtExpiry: 30 * 24 * 60 * 60,
|
|
@@ -161,9 +708,16 @@ var defaultFeatures = {
|
|
|
161
708
|
otpLogin: true
|
|
162
709
|
};
|
|
163
710
|
function createAuthConfig(config) {
|
|
711
|
+
if (!config.database && !config.prisma) {
|
|
712
|
+
throw new Error(
|
|
713
|
+
"@factiii/auth: Provide either a `database` adapter or a `prisma` client in config."
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
const database = config.database ?? createPrismaAdapter(config.prisma);
|
|
164
717
|
const emailService = config.emailService ?? createNoopEmailAdapter();
|
|
165
718
|
return {
|
|
166
719
|
...config,
|
|
720
|
+
database,
|
|
167
721
|
features: { ...defaultFeatures, ...config.features },
|
|
168
722
|
tokenSettings: { ...defaultTokenSettings, ...config.tokenSettings },
|
|
169
723
|
cookieSettings: { ...defaultCookieSettings, ...config.cookieSettings },
|
|
@@ -281,6 +835,7 @@ function isTokenInvalidError(error) {
|
|
|
281
835
|
function createAuthGuard(config, t) {
|
|
282
836
|
const storageKeys = config.storageKeys ?? defaultStorageKeys;
|
|
283
837
|
const cookieSettings = { ...defaultCookieSettings, ...config.cookieSettings };
|
|
838
|
+
const database = config.database ?? createPrismaAdapter(config.prisma);
|
|
284
839
|
const revokeSession = async (ctx, sessionId, description, errorStack, path) => {
|
|
285
840
|
clearAuthCookie(ctx.res, cookieSettings, storageKeys);
|
|
286
841
|
if (config.hooks?.logError) {
|
|
@@ -317,15 +872,9 @@ ${errorStack}` : null,
|
|
|
317
872
|
}
|
|
318
873
|
if (sessionId) {
|
|
319
874
|
try {
|
|
320
|
-
await
|
|
321
|
-
where: { id: sessionId },
|
|
322
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
323
|
-
});
|
|
875
|
+
await database.session.revoke(sessionId);
|
|
324
876
|
if (config.hooks?.onSessionRevoked) {
|
|
325
|
-
const session = await
|
|
326
|
-
where: { id: sessionId },
|
|
327
|
-
select: { id: true, userId: true, socketId: true }
|
|
328
|
-
});
|
|
877
|
+
const session = await database.session.findById(sessionId);
|
|
329
878
|
if (session) {
|
|
330
879
|
await config.hooks.onSessionRevoked(session.userId, session.socketId, description);
|
|
331
880
|
}
|
|
@@ -350,23 +899,7 @@ ${errorStack}` : null,
|
|
|
350
899
|
secret: config.secrets.jwt,
|
|
351
900
|
ignoreExpiration: meta?.ignoreExpiration ?? false
|
|
352
901
|
});
|
|
353
|
-
const session = await
|
|
354
|
-
where: {
|
|
355
|
-
id: decodedToken.id
|
|
356
|
-
},
|
|
357
|
-
select: {
|
|
358
|
-
userId: true,
|
|
359
|
-
user: {
|
|
360
|
-
select: {
|
|
361
|
-
status: true,
|
|
362
|
-
verifiedHumanAt: true
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
revokedAt: true,
|
|
366
|
-
socketId: true,
|
|
367
|
-
id: true
|
|
368
|
-
}
|
|
369
|
-
});
|
|
902
|
+
const session = await database.session.findById(decodedToken.id);
|
|
370
903
|
if (!session) {
|
|
371
904
|
await revokeSession(
|
|
372
905
|
ctx,
|
|
@@ -420,10 +953,7 @@ ${errorStack}` : null,
|
|
|
420
953
|
});
|
|
421
954
|
}
|
|
422
955
|
if (meta?.adminRequired) {
|
|
423
|
-
const admin = await
|
|
424
|
-
where: { userId: session.userId },
|
|
425
|
-
select: { ip: true }
|
|
426
|
-
});
|
|
956
|
+
const admin = await database.admin.findByUserId(session.userId);
|
|
427
957
|
if (!admin || admin.ip !== ctx.ip) {
|
|
428
958
|
await revokeSession(
|
|
429
959
|
ctx,
|
|
@@ -613,6 +1143,36 @@ function validatePasswordStrength(password, minLength = 6) {
|
|
|
613
1143
|
return { valid: true };
|
|
614
1144
|
}
|
|
615
1145
|
|
|
1146
|
+
// src/utilities/session.ts
|
|
1147
|
+
async function createSessionWithToken(config, params) {
|
|
1148
|
+
const { userId, browserName, socketId, deviceId, extraSessionData } = params;
|
|
1149
|
+
const session = await config.database.session.create({
|
|
1150
|
+
userId,
|
|
1151
|
+
browserName,
|
|
1152
|
+
socketId,
|
|
1153
|
+
...deviceId != null ? { deviceId } : {},
|
|
1154
|
+
...extraSessionData
|
|
1155
|
+
});
|
|
1156
|
+
const user = await config.database.user.findById(userId);
|
|
1157
|
+
const accessToken = createAuthToken(
|
|
1158
|
+
{
|
|
1159
|
+
id: session.id,
|
|
1160
|
+
userId: session.userId,
|
|
1161
|
+
verifiedHumanAt: user?.verifiedHumanAt ?? null
|
|
1162
|
+
},
|
|
1163
|
+
{
|
|
1164
|
+
secret: config.secrets.jwt,
|
|
1165
|
+
expiresIn: config.tokenSettings.jwtExpiry
|
|
1166
|
+
}
|
|
1167
|
+
);
|
|
1168
|
+
return { accessToken, sessionId: session.id };
|
|
1169
|
+
}
|
|
1170
|
+
async function createSessionWithTokenAndCookie(config, params, res) {
|
|
1171
|
+
const result = await createSessionWithToken(config, params);
|
|
1172
|
+
setAuthCookie(res, result.accessToken, config.cookieSettings, config.storageKeys);
|
|
1173
|
+
return result;
|
|
1174
|
+
}
|
|
1175
|
+
|
|
616
1176
|
// src/utilities/totp.ts
|
|
617
1177
|
var import_crypto = __toESM(require("crypto"));
|
|
618
1178
|
var import_totp_generator = require("totp-generator");
|
|
@@ -756,19 +1316,14 @@ var BaseProcedureFactory = class {
|
|
|
756
1316
|
if (this.config.hooks?.beforeRegister) {
|
|
757
1317
|
await this.config.hooks.beforeRegister(typedInput);
|
|
758
1318
|
}
|
|
759
|
-
const usernameCheck = await this.config.
|
|
760
|
-
where: { username: { equals: username, mode: "insensitive" } }
|
|
761
|
-
});
|
|
1319
|
+
const usernameCheck = await this.config.database.user.findByUsernameInsensitive(username);
|
|
762
1320
|
if (usernameCheck) {
|
|
763
1321
|
throw new import_server2.TRPCError({
|
|
764
1322
|
code: "CONFLICT",
|
|
765
1323
|
message: "An account already exists with that username."
|
|
766
1324
|
});
|
|
767
1325
|
}
|
|
768
|
-
const emailCheck = await this.config.
|
|
769
|
-
where: { email: { equals: email, mode: "insensitive" } },
|
|
770
|
-
select: { id: true }
|
|
771
|
-
});
|
|
1326
|
+
const emailCheck = await this.config.database.user.findByEmailInsensitive(email);
|
|
772
1327
|
if (emailCheck) {
|
|
773
1328
|
throw new import_server2.TRPCError({
|
|
774
1329
|
code: "CONFLICT",
|
|
@@ -776,30 +1331,25 @@ var BaseProcedureFactory = class {
|
|
|
776
1331
|
});
|
|
777
1332
|
}
|
|
778
1333
|
const hashedPassword = await hashPassword(password);
|
|
779
|
-
const user = await this.config.
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
verifiedHumanAt: null
|
|
789
|
-
}
|
|
1334
|
+
const user = await this.config.database.user.create({
|
|
1335
|
+
username,
|
|
1336
|
+
email,
|
|
1337
|
+
password: hashedPassword,
|
|
1338
|
+
status: "ACTIVE",
|
|
1339
|
+
tag: this.config.features.biometric ? "BOT" : "HUMAN",
|
|
1340
|
+
twoFaEnabled: false,
|
|
1341
|
+
emailVerificationStatus: "UNVERIFIED",
|
|
1342
|
+
verifiedHumanAt: null
|
|
790
1343
|
});
|
|
791
1344
|
if (this.config.hooks?.onUserCreated) {
|
|
792
1345
|
await this.config.hooks.onUserCreated(user.id, typedInput);
|
|
793
1346
|
}
|
|
794
1347
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
795
|
-
const session = await this.config.
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
...extraSessionData
|
|
801
|
-
},
|
|
802
|
-
select: { id: true, userId: true }
|
|
1348
|
+
const session = await this.config.database.session.create({
|
|
1349
|
+
userId: user.id,
|
|
1350
|
+
browserName: detectBrowser(userAgent),
|
|
1351
|
+
socketId: null,
|
|
1352
|
+
...extraSessionData
|
|
803
1353
|
});
|
|
804
1354
|
if (this.config.hooks?.onSessionCreated) {
|
|
805
1355
|
await this.config.hooks.onSessionCreated(session.id, typedInput);
|
|
@@ -837,24 +1387,7 @@ var BaseProcedureFactory = class {
|
|
|
837
1387
|
if (this.config.hooks?.beforeLogin) {
|
|
838
1388
|
await this.config.hooks.beforeLogin(typedInput);
|
|
839
1389
|
}
|
|
840
|
-
const user = await this.config.
|
|
841
|
-
where: {
|
|
842
|
-
OR: [
|
|
843
|
-
{ email: { equals: username, mode: "insensitive" } },
|
|
844
|
-
{ username: { equals: username, mode: "insensitive" } }
|
|
845
|
-
]
|
|
846
|
-
},
|
|
847
|
-
select: {
|
|
848
|
-
id: true,
|
|
849
|
-
status: true,
|
|
850
|
-
password: true,
|
|
851
|
-
twoFaEnabled: true,
|
|
852
|
-
email: true,
|
|
853
|
-
username: true,
|
|
854
|
-
oauthProvider: true,
|
|
855
|
-
verifiedHumanAt: true
|
|
856
|
-
}
|
|
857
|
-
});
|
|
1390
|
+
const user = await this.config.database.user.findByEmailOrUsernameInsensitive(username);
|
|
858
1391
|
if (!user) {
|
|
859
1392
|
throw new import_server2.TRPCError({
|
|
860
1393
|
code: "FORBIDDEN",
|
|
@@ -895,10 +1428,7 @@ var BaseProcedureFactory = class {
|
|
|
895
1428
|
};
|
|
896
1429
|
}
|
|
897
1430
|
let validCode = false;
|
|
898
|
-
const secrets = await this.config.
|
|
899
|
-
where: { userId: user.id, twoFaSecret: { not: null } },
|
|
900
|
-
select: { twoFaSecret: true }
|
|
901
|
-
});
|
|
1431
|
+
const secrets = await this.config.database.session.findTwoFaSecretsByUserId(user.id);
|
|
902
1432
|
for (const s of secrets) {
|
|
903
1433
|
if (s.twoFaSecret && await verifyTotp(code, cleanBase32String(s.twoFaSecret))) {
|
|
904
1434
|
validCode = true;
|
|
@@ -906,14 +1436,13 @@ var BaseProcedureFactory = class {
|
|
|
906
1436
|
}
|
|
907
1437
|
}
|
|
908
1438
|
if (!validCode) {
|
|
909
|
-
const checkOTP = await this.config.
|
|
910
|
-
|
|
911
|
-
|
|
1439
|
+
const checkOTP = await this.config.database.otp.findValidByUserAndCode(
|
|
1440
|
+
user.id,
|
|
1441
|
+
Number(code)
|
|
1442
|
+
);
|
|
912
1443
|
if (checkOTP) {
|
|
913
1444
|
validCode = true;
|
|
914
|
-
await this.config.
|
|
915
|
-
where: { id: checkOTP.id }
|
|
916
|
-
});
|
|
1445
|
+
await this.config.database.otp.delete(checkOTP.id);
|
|
917
1446
|
}
|
|
918
1447
|
}
|
|
919
1448
|
if (!validCode) {
|
|
@@ -924,24 +1453,11 @@ var BaseProcedureFactory = class {
|
|
|
924
1453
|
}
|
|
925
1454
|
}
|
|
926
1455
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
927
|
-
const session = await this.config.
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
...extraSessionData
|
|
933
|
-
},
|
|
934
|
-
select: {
|
|
935
|
-
id: true,
|
|
936
|
-
userId: true,
|
|
937
|
-
socketId: true,
|
|
938
|
-
browserName: true,
|
|
939
|
-
issuedAt: true,
|
|
940
|
-
lastUsed: true,
|
|
941
|
-
revokedAt: true,
|
|
942
|
-
deviceId: true,
|
|
943
|
-
twoFaSecret: true
|
|
944
|
-
}
|
|
1456
|
+
const session = await this.config.database.session.create({
|
|
1457
|
+
userId: user.id,
|
|
1458
|
+
browserName: detectBrowser(userAgent),
|
|
1459
|
+
socketId: null,
|
|
1460
|
+
...extraSessionData
|
|
945
1461
|
});
|
|
946
1462
|
if (this.config.hooks?.onUserLogin) {
|
|
947
1463
|
await this.config.hooks.onUserLogin(user.id, session.id);
|
|
@@ -972,15 +1488,9 @@ var BaseProcedureFactory = class {
|
|
|
972
1488
|
return this.authProcedure.meta({ ignoreExpiration: true }).mutation(async ({ ctx }) => {
|
|
973
1489
|
const { userId, sessionId } = ctx;
|
|
974
1490
|
if (sessionId) {
|
|
975
|
-
await this.config.
|
|
976
|
-
where: { id: sessionId },
|
|
977
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
978
|
-
});
|
|
1491
|
+
await this.config.database.session.revoke(sessionId);
|
|
979
1492
|
if (userId) {
|
|
980
|
-
await this.config.
|
|
981
|
-
where: { id: userId },
|
|
982
|
-
data: { isActive: false }
|
|
983
|
-
});
|
|
1493
|
+
await this.config.database.user.update(userId, { isActive: false });
|
|
984
1494
|
}
|
|
985
1495
|
if (this.config.hooks?.afterLogout) {
|
|
986
1496
|
await this.config.hooks.afterLogout(userId, sessionId, ctx.socketId);
|
|
@@ -992,15 +1502,7 @@ var BaseProcedureFactory = class {
|
|
|
992
1502
|
}
|
|
993
1503
|
refresh() {
|
|
994
1504
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
995
|
-
const session = await this.config.
|
|
996
|
-
where: { id: ctx.sessionId },
|
|
997
|
-
data: { lastUsed: /* @__PURE__ */ new Date() },
|
|
998
|
-
select: {
|
|
999
|
-
id: true,
|
|
1000
|
-
userId: true,
|
|
1001
|
-
user: { select: { verifiedHumanAt: true } }
|
|
1002
|
-
}
|
|
1003
|
-
});
|
|
1505
|
+
const session = await this.config.database.session.updateLastUsed(ctx.sessionId);
|
|
1004
1506
|
if (this.config.hooks?.onRefresh) {
|
|
1005
1507
|
this.config.hooks.onRefresh(session.userId).catch(() => {
|
|
1006
1508
|
});
|
|
@@ -1025,22 +1527,14 @@ var BaseProcedureFactory = class {
|
|
|
1025
1527
|
return this.authProcedure.input(endAllSessionsSchema).mutation(async ({ ctx, input }) => {
|
|
1026
1528
|
const { skipCurrentSession } = input;
|
|
1027
1529
|
const { userId, sessionId } = ctx;
|
|
1028
|
-
const sessionsToRevoke = await this.config.
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
await this.config.prisma.session.updateMany({
|
|
1037
|
-
where: {
|
|
1038
|
-
userId,
|
|
1039
|
-
revokedAt: null,
|
|
1040
|
-
...skipCurrentSession ? { NOT: { id: sessionId } } : {}
|
|
1041
|
-
},
|
|
1042
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1043
|
-
});
|
|
1530
|
+
const sessionsToRevoke = await this.config.database.session.findActiveByUserId(
|
|
1531
|
+
userId,
|
|
1532
|
+
skipCurrentSession ? sessionId : void 0
|
|
1533
|
+
);
|
|
1534
|
+
await this.config.database.session.revokeAllByUserId(
|
|
1535
|
+
userId,
|
|
1536
|
+
skipCurrentSession ? sessionId : void 0
|
|
1537
|
+
);
|
|
1044
1538
|
for (const session of sessionsToRevoke) {
|
|
1045
1539
|
if (this.config.hooks?.onSessionRevoked) {
|
|
1046
1540
|
await this.config.hooks.onSessionRevoked(
|
|
@@ -1051,10 +1545,7 @@ var BaseProcedureFactory = class {
|
|
|
1051
1545
|
}
|
|
1052
1546
|
}
|
|
1053
1547
|
if (!skipCurrentSession) {
|
|
1054
|
-
await this.config.
|
|
1055
|
-
where: { id: userId },
|
|
1056
|
-
data: { isActive: false }
|
|
1057
|
-
});
|
|
1548
|
+
await this.config.database.user.update(userId, { isActive: false });
|
|
1058
1549
|
}
|
|
1059
1550
|
return { success: true, revokedCount: sessionsToRevoke.length };
|
|
1060
1551
|
});
|
|
@@ -1069,10 +1560,7 @@ var BaseProcedureFactory = class {
|
|
|
1069
1560
|
message: "New password cannot be the same as current password"
|
|
1070
1561
|
});
|
|
1071
1562
|
}
|
|
1072
|
-
const user = await this.config.
|
|
1073
|
-
where: { id: userId },
|
|
1074
|
-
select: { password: true }
|
|
1075
|
-
});
|
|
1563
|
+
const user = await this.config.database.user.findById(userId);
|
|
1076
1564
|
if (!user) {
|
|
1077
1565
|
throw new import_server2.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1078
1566
|
}
|
|
@@ -1090,14 +1578,8 @@ var BaseProcedureFactory = class {
|
|
|
1090
1578
|
});
|
|
1091
1579
|
}
|
|
1092
1580
|
const hashedPassword = await hashPassword(newPassword);
|
|
1093
|
-
await this.config.
|
|
1094
|
-
|
|
1095
|
-
data: { password: hashedPassword }
|
|
1096
|
-
});
|
|
1097
|
-
await this.config.prisma.session.updateMany({
|
|
1098
|
-
where: { userId, revokedAt: null, NOT: { id: sessionId } },
|
|
1099
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1100
|
-
});
|
|
1581
|
+
await this.config.database.user.update(userId, { password: hashedPassword });
|
|
1582
|
+
await this.config.database.session.revokeAllByUserId(userId, sessionId);
|
|
1101
1583
|
if (this.config.hooks?.onPasswordChanged) {
|
|
1102
1584
|
await this.config.hooks.onPasswordChanged(userId);
|
|
1103
1585
|
}
|
|
@@ -1110,11 +1592,8 @@ var BaseProcedureFactory = class {
|
|
|
1110
1592
|
sendPasswordResetEmail() {
|
|
1111
1593
|
return this.procedure.input(requestPasswordResetSchema).mutation(async ({ input }) => {
|
|
1112
1594
|
const { email } = input;
|
|
1113
|
-
const user = await this.config.
|
|
1114
|
-
|
|
1115
|
-
select: { id: true, password: true, email: true }
|
|
1116
|
-
});
|
|
1117
|
-
if (!user) {
|
|
1595
|
+
const user = await this.config.database.user.findByEmailInsensitive(email);
|
|
1596
|
+
if (!user || user.status !== "ACTIVE") {
|
|
1118
1597
|
return { message: "If an account exists with that email, a reset link has been sent." };
|
|
1119
1598
|
}
|
|
1120
1599
|
if (!user.password) {
|
|
@@ -1123,10 +1602,8 @@ var BaseProcedureFactory = class {
|
|
|
1123
1602
|
message: "This account uses social login. Please use that method."
|
|
1124
1603
|
});
|
|
1125
1604
|
}
|
|
1126
|
-
await this.config.
|
|
1127
|
-
const passwordReset = await this.config.
|
|
1128
|
-
data: { userId: user.id }
|
|
1129
|
-
});
|
|
1605
|
+
await this.config.database.passwordReset.deleteAllByUserId(user.id);
|
|
1606
|
+
const passwordReset = await this.config.database.passwordReset.create(user.id);
|
|
1130
1607
|
if (this.config.emailService) {
|
|
1131
1608
|
await this.config.emailService.sendPasswordResetEmail(user.email, String(passwordReset.id));
|
|
1132
1609
|
}
|
|
@@ -1136,15 +1613,12 @@ var BaseProcedureFactory = class {
|
|
|
1136
1613
|
checkPasswordReset() {
|
|
1137
1614
|
return this.procedure.input(checkPasswordResetSchema).query(async ({ input }) => {
|
|
1138
1615
|
const { token } = input;
|
|
1139
|
-
const passwordReset = await this.config.
|
|
1140
|
-
where: { id: token },
|
|
1141
|
-
select: { id: true, createdAt: true, userId: true }
|
|
1142
|
-
});
|
|
1616
|
+
const passwordReset = await this.config.database.passwordReset.findById(token);
|
|
1143
1617
|
if (!passwordReset) {
|
|
1144
1618
|
throw new import_server2.TRPCError({ code: "NOT_FOUND", message: "Invalid reset token." });
|
|
1145
1619
|
}
|
|
1146
1620
|
if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
|
|
1147
|
-
await this.config.
|
|
1621
|
+
await this.config.database.passwordReset.delete(token);
|
|
1148
1622
|
throw new import_server2.TRPCError({ code: "FORBIDDEN", message: "Reset token expired." });
|
|
1149
1623
|
}
|
|
1150
1624
|
return { valid: true };
|
|
@@ -1153,31 +1627,23 @@ var BaseProcedureFactory = class {
|
|
|
1153
1627
|
resetPassword() {
|
|
1154
1628
|
return this.procedure.input(resetPasswordSchema).mutation(async ({ input }) => {
|
|
1155
1629
|
const { token, password } = input;
|
|
1156
|
-
const passwordReset = await this.config.
|
|
1157
|
-
where: { id: token },
|
|
1158
|
-
select: { id: true, createdAt: true, userId: true }
|
|
1159
|
-
});
|
|
1630
|
+
const passwordReset = await this.config.database.passwordReset.findById(token);
|
|
1160
1631
|
if (!passwordReset) {
|
|
1161
1632
|
throw new import_server2.TRPCError({ code: "NOT_FOUND", message: "Invalid reset token." });
|
|
1162
1633
|
}
|
|
1163
1634
|
if (passwordReset.createdAt.getTime() + this.config.tokenSettings.passwordResetExpiryMs < Date.now()) {
|
|
1164
|
-
await this.config.
|
|
1635
|
+
await this.config.database.passwordReset.delete(token);
|
|
1165
1636
|
throw new import_server2.TRPCError({ code: "FORBIDDEN", message: "Reset token expired." });
|
|
1166
1637
|
}
|
|
1167
1638
|
const hashedPassword = await hashPassword(password);
|
|
1168
|
-
await this.config.
|
|
1169
|
-
|
|
1170
|
-
data: { password: hashedPassword }
|
|
1171
|
-
});
|
|
1172
|
-
await this.config.prisma.passwordReset.delete({ where: { id: token } });
|
|
1173
|
-
const sessionsToRevoke = await this.config.prisma.session.findMany({
|
|
1174
|
-
where: { userId: passwordReset.userId, revokedAt: null },
|
|
1175
|
-
select: { id: true, socketId: true, userId: true }
|
|
1176
|
-
});
|
|
1177
|
-
await this.config.prisma.session.updateMany({
|
|
1178
|
-
where: { userId: passwordReset.userId, revokedAt: null },
|
|
1179
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1639
|
+
await this.config.database.user.update(passwordReset.userId, {
|
|
1640
|
+
password: hashedPassword
|
|
1180
1641
|
});
|
|
1642
|
+
await this.config.database.passwordReset.delete(token);
|
|
1643
|
+
const sessionsToRevoke = await this.config.database.session.findActiveByUserId(
|
|
1644
|
+
passwordReset.userId
|
|
1645
|
+
);
|
|
1646
|
+
await this.config.database.session.revokeAllByUserId(passwordReset.userId);
|
|
1181
1647
|
for (const session of sessionsToRevoke) {
|
|
1182
1648
|
if (this.config.hooks?.onSessionRevoked) {
|
|
1183
1649
|
await this.config.hooks.onSessionRevoked(
|
|
@@ -1214,9 +1680,9 @@ var BiometricProcedureFactory = class {
|
|
|
1214
1680
|
return this.authProcedure.input(biometricVerifySchema).mutation(async ({ ctx }) => {
|
|
1215
1681
|
this.checkConfig();
|
|
1216
1682
|
const { userId } = ctx;
|
|
1217
|
-
await this.config.
|
|
1218
|
-
|
|
1219
|
-
|
|
1683
|
+
await this.config.database.user.update(userId, {
|
|
1684
|
+
verifiedHumanAt: /* @__PURE__ */ new Date(),
|
|
1685
|
+
tag: "HUMAN"
|
|
1220
1686
|
});
|
|
1221
1687
|
if (this.config.hooks?.onBiometricVerified) {
|
|
1222
1688
|
await this.config.hooks.onBiometricVerified(userId);
|
|
@@ -1228,10 +1694,7 @@ var BiometricProcedureFactory = class {
|
|
|
1228
1694
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
1229
1695
|
this.checkConfig();
|
|
1230
1696
|
const { userId } = ctx;
|
|
1231
|
-
const user = await this.config.
|
|
1232
|
-
where: { id: userId },
|
|
1233
|
-
select: { verifiedHumanAt: true }
|
|
1234
|
-
});
|
|
1697
|
+
const user = await this.config.database.user.findById(userId);
|
|
1235
1698
|
if (!user) {
|
|
1236
1699
|
throw new import_server3.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1237
1700
|
}
|
|
@@ -1278,10 +1741,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1278
1741
|
return this.authProcedure.mutation(async ({ ctx }) => {
|
|
1279
1742
|
this.checkConfig();
|
|
1280
1743
|
const { userId } = ctx;
|
|
1281
|
-
const user = await this.config.
|
|
1282
|
-
where: { id: userId, status: "ACTIVE" },
|
|
1283
|
-
select: { id: true, email: true, emailVerificationStatus: true }
|
|
1284
|
-
});
|
|
1744
|
+
const user = await this.config.database.user.findActiveById(userId);
|
|
1285
1745
|
if (!user) {
|
|
1286
1746
|
throw new import_server4.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1287
1747
|
}
|
|
@@ -1289,9 +1749,9 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1289
1749
|
return { message: "Email is already verified", emailSent: false };
|
|
1290
1750
|
}
|
|
1291
1751
|
const otp = (0, import_node_crypto.randomUUID)();
|
|
1292
|
-
await this.config.
|
|
1293
|
-
|
|
1294
|
-
|
|
1752
|
+
await this.config.database.user.update(userId, {
|
|
1753
|
+
emailVerificationStatus: "PENDING",
|
|
1754
|
+
otpForEmailVerification: otp
|
|
1295
1755
|
});
|
|
1296
1756
|
if (this.config.emailService) {
|
|
1297
1757
|
try {
|
|
@@ -1309,10 +1769,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1309
1769
|
this.checkConfig();
|
|
1310
1770
|
const { userId } = ctx;
|
|
1311
1771
|
const { code } = input;
|
|
1312
|
-
const user = await this.config.
|
|
1313
|
-
where: { id: userId, status: "ACTIVE" },
|
|
1314
|
-
select: { id: true, emailVerificationStatus: true, otpForEmailVerification: true }
|
|
1315
|
-
});
|
|
1772
|
+
const user = await this.config.database.user.findActiveById(userId);
|
|
1316
1773
|
if (!user) {
|
|
1317
1774
|
throw new import_server4.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1318
1775
|
}
|
|
@@ -1322,9 +1779,9 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1322
1779
|
if (code !== user.otpForEmailVerification) {
|
|
1323
1780
|
throw new import_server4.TRPCError({ code: "BAD_REQUEST", message: "Invalid verification code" });
|
|
1324
1781
|
}
|
|
1325
|
-
await this.config.
|
|
1326
|
-
|
|
1327
|
-
|
|
1782
|
+
await this.config.database.user.update(userId, {
|
|
1783
|
+
emailVerificationStatus: "VERIFIED",
|
|
1784
|
+
otpForEmailVerification: null
|
|
1328
1785
|
});
|
|
1329
1786
|
if (this.config.hooks?.onEmailVerified) {
|
|
1330
1787
|
await this.config.hooks.onEmailVerified(userId);
|
|
@@ -1336,10 +1793,7 @@ var EmailVerificationProcedureFactory = class {
|
|
|
1336
1793
|
return this.authProcedure.query(async ({ ctx }) => {
|
|
1337
1794
|
this.checkConfig();
|
|
1338
1795
|
const { userId } = ctx;
|
|
1339
|
-
const user = await this.config.
|
|
1340
|
-
where: { id: userId },
|
|
1341
|
-
select: { emailVerificationStatus: true, email: true }
|
|
1342
|
-
});
|
|
1796
|
+
const user = await this.config.database.user.findById(userId);
|
|
1343
1797
|
if (!user) {
|
|
1344
1798
|
throw new import_server4.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1345
1799
|
}
|
|
@@ -1393,23 +1847,7 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1393
1847
|
message: "Email not provided by OAuth provider"
|
|
1394
1848
|
});
|
|
1395
1849
|
}
|
|
1396
|
-
let user = await this.config.
|
|
1397
|
-
where: {
|
|
1398
|
-
OR: [{ email: { equals: email, mode: "insensitive" } }, { oauthId: { equals: oauthId } }]
|
|
1399
|
-
},
|
|
1400
|
-
select: {
|
|
1401
|
-
id: true,
|
|
1402
|
-
status: true,
|
|
1403
|
-
email: true,
|
|
1404
|
-
username: true,
|
|
1405
|
-
password: true,
|
|
1406
|
-
oauthProvider: true,
|
|
1407
|
-
oauthId: true,
|
|
1408
|
-
twoFaEnabled: true,
|
|
1409
|
-
verifiedHumanAt: true,
|
|
1410
|
-
emailVerificationStatus: true
|
|
1411
|
-
}
|
|
1412
|
-
});
|
|
1850
|
+
let user = await this.config.database.user.findByEmailOrOAuthId(email, oauthId);
|
|
1413
1851
|
if (user?.oauthProvider && user.oauthProvider !== provider) {
|
|
1414
1852
|
throw new import_server5.TRPCError({
|
|
1415
1853
|
code: "BAD_REQUEST",
|
|
@@ -1424,19 +1862,17 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1424
1862
|
}
|
|
1425
1863
|
if (!user) {
|
|
1426
1864
|
const generateUsername = this.config.generateUsername ?? (() => `user_${Date.now()}`);
|
|
1427
|
-
user = await this.config.
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
verifiedHumanAt: null
|
|
1439
|
-
}
|
|
1865
|
+
user = await this.config.database.user.create({
|
|
1866
|
+
username: generateUsername(),
|
|
1867
|
+
email,
|
|
1868
|
+
password: null,
|
|
1869
|
+
emailVerificationStatus: "VERIFIED",
|
|
1870
|
+
oauthProvider: provider,
|
|
1871
|
+
oauthId,
|
|
1872
|
+
status: "ACTIVE",
|
|
1873
|
+
tag: this.config.features.biometric ? "BOT" : "HUMAN",
|
|
1874
|
+
twoFaEnabled: false,
|
|
1875
|
+
verifiedHumanAt: null
|
|
1440
1876
|
});
|
|
1441
1877
|
if (this.config.hooks?.onUserCreated) {
|
|
1442
1878
|
await this.config.hooks.onUserCreated(user.id, typedInput);
|
|
@@ -1452,24 +1888,11 @@ var OAuthLoginProcedureFactory = class {
|
|
|
1452
1888
|
throw new import_server5.TRPCError({ code: "FORBIDDEN", message: "Your account has been banned." });
|
|
1453
1889
|
}
|
|
1454
1890
|
const extraSessionData = this.config.hooks?.getSessionData ? await this.config.hooks.getSessionData(typedInput) : {};
|
|
1455
|
-
const session = await this.config.
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
...extraSessionData
|
|
1461
|
-
},
|
|
1462
|
-
select: {
|
|
1463
|
-
id: true,
|
|
1464
|
-
userId: true,
|
|
1465
|
-
socketId: true,
|
|
1466
|
-
browserName: true,
|
|
1467
|
-
issuedAt: true,
|
|
1468
|
-
lastUsed: true,
|
|
1469
|
-
revokedAt: true,
|
|
1470
|
-
deviceId: true,
|
|
1471
|
-
twoFaSecret: true
|
|
1472
|
-
}
|
|
1891
|
+
const session = await this.config.database.session.create({
|
|
1892
|
+
userId: user.id,
|
|
1893
|
+
browserName: detectBrowser(userAgent),
|
|
1894
|
+
socketId: null,
|
|
1895
|
+
...extraSessionData
|
|
1473
1896
|
});
|
|
1474
1897
|
if (this.config.hooks?.onUserLogin) {
|
|
1475
1898
|
await this.config.hooks.onUserLogin(user.id, session.id);
|
|
@@ -1526,10 +1949,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1526
1949
|
return this.authProcedure.mutation(async ({ ctx }) => {
|
|
1527
1950
|
this.checkConfig();
|
|
1528
1951
|
const { userId, sessionId } = ctx;
|
|
1529
|
-
const user = await this.config.
|
|
1530
|
-
where: { id: userId },
|
|
1531
|
-
select: { twoFaEnabled: true, oauthProvider: true, password: true }
|
|
1532
|
-
});
|
|
1952
|
+
const user = await this.config.database.user.findById(userId);
|
|
1533
1953
|
if (!user) {
|
|
1534
1954
|
throw new import_server6.TRPCError({ code: "NOT_FOUND", message: "User not found." });
|
|
1535
1955
|
}
|
|
@@ -1543,10 +1963,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1543
1963
|
throw new import_server6.TRPCError({ code: "BAD_REQUEST", message: "2FA already enabled." });
|
|
1544
1964
|
}
|
|
1545
1965
|
if (this.config.features.twoFaRequiresDevice !== false) {
|
|
1546
|
-
const checkSession = await this.config.
|
|
1547
|
-
where: { userId, id: sessionId },
|
|
1548
|
-
select: { deviceId: true }
|
|
1549
|
-
});
|
|
1966
|
+
const checkSession = await this.config.database.session.findById(sessionId);
|
|
1550
1967
|
if (!checkSession?.deviceId) {
|
|
1551
1968
|
throw new import_server6.TRPCError({
|
|
1552
1969
|
code: "BAD_REQUEST",
|
|
@@ -1554,23 +1971,11 @@ var TwoFaProcedureFactory = class {
|
|
|
1554
1971
|
});
|
|
1555
1972
|
}
|
|
1556
1973
|
}
|
|
1557
|
-
await this.config.
|
|
1558
|
-
|
|
1559
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1560
|
-
});
|
|
1561
|
-
await this.config.prisma.session.updateMany({
|
|
1562
|
-
where: { userId, NOT: { id: sessionId } },
|
|
1563
|
-
data: { twoFaSecret: null }
|
|
1564
|
-
});
|
|
1974
|
+
await this.config.database.session.revokeAllByUserId(userId, sessionId);
|
|
1975
|
+
await this.config.database.session.clearTwoFaSecrets(userId, sessionId);
|
|
1565
1976
|
const secret = generateTotpSecret();
|
|
1566
|
-
await this.config.
|
|
1567
|
-
|
|
1568
|
-
data: { twoFaEnabled: true }
|
|
1569
|
-
});
|
|
1570
|
-
await this.config.prisma.session.update({
|
|
1571
|
-
where: { id: sessionId },
|
|
1572
|
-
data: { twoFaSecret: secret }
|
|
1573
|
-
});
|
|
1977
|
+
await this.config.database.user.update(userId, { twoFaEnabled: true });
|
|
1978
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: secret });
|
|
1574
1979
|
if (this.config.hooks?.onTwoFaStatusChanged) {
|
|
1575
1980
|
await this.config.hooks.onTwoFaStatusChanged(userId, true);
|
|
1576
1981
|
}
|
|
@@ -1582,10 +1987,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1582
1987
|
this.checkConfig();
|
|
1583
1988
|
const { userId, sessionId } = ctx;
|
|
1584
1989
|
const { password } = input;
|
|
1585
|
-
const user = await this.config.
|
|
1586
|
-
where: { id: userId },
|
|
1587
|
-
select: { password: true, status: true, oauthProvider: true }
|
|
1588
|
-
});
|
|
1990
|
+
const user = await this.config.database.user.findById(userId);
|
|
1589
1991
|
if (!user) {
|
|
1590
1992
|
throw new import_server6.TRPCError({ code: "NOT_FOUND", message: "User not found." });
|
|
1591
1993
|
}
|
|
@@ -1608,14 +2010,8 @@ var TwoFaProcedureFactory = class {
|
|
|
1608
2010
|
if (!isMatch) {
|
|
1609
2011
|
throw new import_server6.TRPCError({ code: "FORBIDDEN", message: "Incorrect password." });
|
|
1610
2012
|
}
|
|
1611
|
-
await this.config.
|
|
1612
|
-
|
|
1613
|
-
data: { twoFaEnabled: false }
|
|
1614
|
-
});
|
|
1615
|
-
await this.config.prisma.session.update({
|
|
1616
|
-
where: { id: sessionId },
|
|
1617
|
-
data: { twoFaSecret: null }
|
|
1618
|
-
});
|
|
2013
|
+
await this.config.database.user.update(userId, { twoFaEnabled: false });
|
|
2014
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: null });
|
|
1619
2015
|
if (this.config.hooks?.onTwoFaStatusChanged) {
|
|
1620
2016
|
await this.config.hooks.onTwoFaStatusChanged(userId, false);
|
|
1621
2017
|
}
|
|
@@ -1627,10 +2023,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1627
2023
|
this.checkConfig();
|
|
1628
2024
|
const { userId, sessionId } = ctx;
|
|
1629
2025
|
const { pushCode } = input;
|
|
1630
|
-
const user = await this.config.
|
|
1631
|
-
where: { id: userId },
|
|
1632
|
-
select: { twoFaEnabled: true, oauthProvider: true }
|
|
1633
|
-
});
|
|
2026
|
+
const user = await this.config.database.user.findById(userId);
|
|
1634
2027
|
if (user?.oauthProvider) {
|
|
1635
2028
|
throw new import_server6.TRPCError({
|
|
1636
2029
|
code: "FORBIDDEN",
|
|
@@ -1640,10 +2033,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1640
2033
|
if (!user?.twoFaEnabled) {
|
|
1641
2034
|
throw new import_server6.TRPCError({ code: "BAD_REQUEST", message: "2FA not enabled." });
|
|
1642
2035
|
}
|
|
1643
|
-
const session = await this.config.
|
|
1644
|
-
where: { id: sessionId, userId },
|
|
1645
|
-
select: { twoFaSecret: true, device: { select: { pushToken: true } } }
|
|
1646
|
-
});
|
|
2036
|
+
const session = await this.config.database.session.findByIdWithDevice(sessionId, userId);
|
|
1647
2037
|
if (!session?.device) {
|
|
1648
2038
|
throw new import_server6.TRPCError({ code: "BAD_REQUEST", message: "Invalid request" });
|
|
1649
2039
|
}
|
|
@@ -1655,10 +2045,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1655
2045
|
return { secret: session.twoFaSecret };
|
|
1656
2046
|
}
|
|
1657
2047
|
const secret = generateTotpSecret();
|
|
1658
|
-
await this.config.
|
|
1659
|
-
where: { id: sessionId },
|
|
1660
|
-
data: { twoFaSecret: secret }
|
|
1661
|
-
});
|
|
2048
|
+
await this.config.database.session.update(sessionId, { twoFaSecret: secret });
|
|
1662
2049
|
return { secret };
|
|
1663
2050
|
});
|
|
1664
2051
|
}
|
|
@@ -1666,11 +2053,8 @@ var TwoFaProcedureFactory = class {
|
|
|
1666
2053
|
return this.procedure.input(twoFaResetSchema).mutation(async ({ input }) => {
|
|
1667
2054
|
this.checkConfig();
|
|
1668
2055
|
const { username, password } = input;
|
|
1669
|
-
const user = await this.config.
|
|
1670
|
-
|
|
1671
|
-
select: { id: true, password: true, email: true }
|
|
1672
|
-
});
|
|
1673
|
-
if (!user) {
|
|
2056
|
+
const user = await this.config.database.user.findByUsernameInsensitive(username);
|
|
2057
|
+
if (!user || !user.twoFaEnabled) {
|
|
1674
2058
|
throw new import_server6.TRPCError({ code: "UNAUTHORIZED", message: "Invalid credentials." });
|
|
1675
2059
|
}
|
|
1676
2060
|
if (!user.password) {
|
|
@@ -1685,9 +2069,7 @@ var TwoFaProcedureFactory = class {
|
|
|
1685
2069
|
}
|
|
1686
2070
|
const otp = generateOtp();
|
|
1687
2071
|
const expiresAt = new Date(Date.now() + this.config.tokenSettings.otpValidityMs);
|
|
1688
|
-
await this.config.
|
|
1689
|
-
data: { userId: user.id, code: otp, expiresAt }
|
|
1690
|
-
});
|
|
2072
|
+
await this.config.database.otp.create({ userId: user.id, code: otp, expiresAt });
|
|
1691
2073
|
if (this.config.emailService) {
|
|
1692
2074
|
await this.config.emailService.sendOTPEmail(user.email, otp);
|
|
1693
2075
|
}
|
|
@@ -1698,30 +2080,17 @@ var TwoFaProcedureFactory = class {
|
|
|
1698
2080
|
return this.procedure.input(twoFaResetVerifySchema).mutation(async ({ input }) => {
|
|
1699
2081
|
this.checkConfig();
|
|
1700
2082
|
const { code, username } = input;
|
|
1701
|
-
const user = await this.config.
|
|
1702
|
-
where: { username: { equals: username, mode: "insensitive" } },
|
|
1703
|
-
select: { id: true }
|
|
1704
|
-
});
|
|
2083
|
+
const user = await this.config.database.user.findByUsernameInsensitive(username);
|
|
1705
2084
|
if (!user) {
|
|
1706
2085
|
throw new import_server6.TRPCError({ code: "NOT_FOUND", message: "User not found" });
|
|
1707
2086
|
}
|
|
1708
|
-
const otp = await this.config.
|
|
1709
|
-
where: { userId: user.id, code, expiresAt: { gte: /* @__PURE__ */ new Date() } }
|
|
1710
|
-
});
|
|
2087
|
+
const otp = await this.config.database.otp.findValidByUserAndCode(user.id, code);
|
|
1711
2088
|
if (!otp) {
|
|
1712
2089
|
throw new import_server6.TRPCError({ code: "FORBIDDEN", message: "Invalid or expired OTP" });
|
|
1713
2090
|
}
|
|
1714
|
-
await this.config.
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
await this.config.prisma.user.update({
|
|
1718
|
-
where: { id: user.id },
|
|
1719
|
-
data: { twoFaEnabled: false }
|
|
1720
|
-
});
|
|
1721
|
-
await this.config.prisma.session.updateMany({
|
|
1722
|
-
where: { userId: user.id },
|
|
1723
|
-
data: { twoFaSecret: null }
|
|
1724
|
-
});
|
|
2091
|
+
await this.config.database.otp.delete(otp.id);
|
|
2092
|
+
await this.config.database.user.update(user.id, { twoFaEnabled: false });
|
|
2093
|
+
await this.config.database.session.clearTwoFaSecrets(user.id);
|
|
1725
2094
|
return { success: true, message: "2FA has been reset." };
|
|
1726
2095
|
});
|
|
1727
2096
|
}
|
|
@@ -1730,36 +2099,14 @@ var TwoFaProcedureFactory = class {
|
|
|
1730
2099
|
this.checkConfig();
|
|
1731
2100
|
const { userId, sessionId } = ctx;
|
|
1732
2101
|
const { pushToken } = input;
|
|
1733
|
-
await this.config.
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
},
|
|
1740
|
-
data: { revokedAt: /* @__PURE__ */ new Date() }
|
|
1741
|
-
});
|
|
1742
|
-
const checkDevice = await this.config.prisma.device.findFirst({
|
|
1743
|
-
where: {
|
|
1744
|
-
pushToken,
|
|
1745
|
-
sessions: { some: { id: sessionId } },
|
|
1746
|
-
users: { some: { id: userId } }
|
|
1747
|
-
},
|
|
1748
|
-
select: { id: true }
|
|
1749
|
-
});
|
|
2102
|
+
await this.config.database.session.revokeByDevicePushToken(userId, pushToken, sessionId);
|
|
2103
|
+
const checkDevice = await this.config.database.device.findByTokenSessionAndUser(
|
|
2104
|
+
pushToken,
|
|
2105
|
+
sessionId,
|
|
2106
|
+
userId
|
|
2107
|
+
);
|
|
1750
2108
|
if (!checkDevice) {
|
|
1751
|
-
await this.config.
|
|
1752
|
-
where: { pushToken },
|
|
1753
|
-
create: {
|
|
1754
|
-
pushToken,
|
|
1755
|
-
sessions: { connect: { id: sessionId } },
|
|
1756
|
-
users: { connect: { id: userId } }
|
|
1757
|
-
},
|
|
1758
|
-
update: {
|
|
1759
|
-
sessions: { connect: { id: sessionId } },
|
|
1760
|
-
users: { connect: { id: userId } }
|
|
1761
|
-
}
|
|
1762
|
-
});
|
|
2109
|
+
await this.config.database.device.upsertByPushToken(pushToken, sessionId, userId);
|
|
1763
2110
|
}
|
|
1764
2111
|
return { registered: true };
|
|
1765
2112
|
});
|
|
@@ -1769,30 +2116,13 @@ var TwoFaProcedureFactory = class {
|
|
|
1769
2116
|
this.checkConfig();
|
|
1770
2117
|
const { userId } = ctx;
|
|
1771
2118
|
const { pushToken } = input;
|
|
1772
|
-
const device = await this.config.
|
|
1773
|
-
where: {
|
|
1774
|
-
users: { some: { id: userId } },
|
|
1775
|
-
pushToken
|
|
1776
|
-
},
|
|
1777
|
-
select: { id: true }
|
|
1778
|
-
});
|
|
2119
|
+
const device = await this.config.database.device.findByUserAndToken(userId, pushToken);
|
|
1779
2120
|
if (device) {
|
|
1780
|
-
await this.config.
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
where: { id: device.id },
|
|
1786
|
-
data: { users: { disconnect: { id: userId } } }
|
|
1787
|
-
});
|
|
1788
|
-
const remainingUsers = await this.config.prisma.device.findUnique({
|
|
1789
|
-
where: { id: device.id },
|
|
1790
|
-
select: { users: { select: { id: true }, take: 1 } }
|
|
1791
|
-
});
|
|
1792
|
-
if (!remainingUsers?.users.length) {
|
|
1793
|
-
await this.config.prisma.device.delete({
|
|
1794
|
-
where: { id: device.id }
|
|
1795
|
-
});
|
|
2121
|
+
await this.config.database.session.clearDeviceId(userId, device.id);
|
|
2122
|
+
await this.config.database.device.disconnectUser(device.id, userId);
|
|
2123
|
+
const hasUsers = await this.config.database.device.hasRemainingUsers(device.id);
|
|
2124
|
+
if (!hasUsers) {
|
|
2125
|
+
await this.config.database.device.delete(device.id);
|
|
1796
2126
|
}
|
|
1797
2127
|
}
|
|
1798
2128
|
return { deregistered: true };
|
|
@@ -1804,13 +2134,15 @@ var TwoFaProcedureFactory = class {
|
|
|
1804
2134
|
var import_server7 = require("@trpc/server");
|
|
1805
2135
|
var import_superjson = __toESM(require("superjson"));
|
|
1806
2136
|
var import_zod2 = require("zod");
|
|
2137
|
+
function hasStringProp(obj, key) {
|
|
2138
|
+
return typeof obj === "object" && obj !== null && key in obj && typeof obj[key] === "string";
|
|
2139
|
+
}
|
|
1807
2140
|
function isPrismaConnectionError(error) {
|
|
1808
2141
|
if (!error || typeof error !== "object") {
|
|
1809
2142
|
return false;
|
|
1810
2143
|
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
const codeMatch = errorCode.match(/^P(\d+)$/);
|
|
2144
|
+
if (hasStringProp(error, "code")) {
|
|
2145
|
+
const codeMatch = error.code.match(/^P(\d+)$/);
|
|
1814
2146
|
if (codeMatch) {
|
|
1815
2147
|
const codeNum = parseInt(codeMatch[1], 10);
|
|
1816
2148
|
if (codeNum >= 1e3 && codeNum <= 1003) {
|
|
@@ -1820,14 +2152,13 @@ function isPrismaConnectionError(error) {
|
|
|
1820
2152
|
}
|
|
1821
2153
|
const constructorName = error.constructor?.name || "";
|
|
1822
2154
|
if (constructorName.includes("Prisma")) {
|
|
1823
|
-
const errorMessage = error.message
|
|
2155
|
+
const errorMessage = hasStringProp(error, "message") ? error.message.toLowerCase() : "";
|
|
1824
2156
|
if (errorMessage.includes("can't reach database") || errorMessage.includes("authentication failed") || errorMessage.includes("database server") || errorMessage.includes("timeout") || errorMessage.includes("connection")) {
|
|
1825
2157
|
return true;
|
|
1826
2158
|
}
|
|
1827
2159
|
}
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
return isPrismaConnectionError(cause);
|
|
2160
|
+
if ("cause" in error) {
|
|
2161
|
+
return isPrismaConnectionError(error.cause);
|
|
1831
2162
|
}
|
|
1832
2163
|
return false;
|
|
1833
2164
|
}
|
|
@@ -1873,8 +2204,9 @@ function createBaseProcedure(t, authGuard) {
|
|
|
1873
2204
|
}
|
|
1874
2205
|
function getClientIp(req) {
|
|
1875
2206
|
const forwarded = req.headers["x-forwarded-for"];
|
|
1876
|
-
|
|
1877
|
-
|
|
2207
|
+
const forwardedStr = Array.isArray(forwarded) ? forwarded[0] : forwarded;
|
|
2208
|
+
if (forwardedStr) {
|
|
2209
|
+
return forwardedStr.split(",")[0]?.trim();
|
|
1878
2210
|
}
|
|
1879
2211
|
return req.socket.remoteAddress || void 0;
|
|
1880
2212
|
}
|
|
@@ -1892,7 +2224,7 @@ var AuthRouterFactory = class {
|
|
|
1892
2224
|
constructor(userConfig) {
|
|
1893
2225
|
this.userConfig = userConfig;
|
|
1894
2226
|
this.config = createAuthConfig(this.userConfig);
|
|
1895
|
-
this.schemas = createSchemas(this.
|
|
2227
|
+
this.schemas = createSchemas(this.userConfig.schemaExtensions);
|
|
1896
2228
|
this.t = createTrpcBuilder(this.config);
|
|
1897
2229
|
this.authGuard = createAuthGuard(this.config, this.t);
|
|
1898
2230
|
this.procedure = createBaseProcedure(this.t, this.authGuard);
|
|
@@ -1950,8 +2282,12 @@ function createAuthRouter(config) {
|
|
|
1950
2282
|
createAuthRouter,
|
|
1951
2283
|
createAuthToken,
|
|
1952
2284
|
createConsoleEmailAdapter,
|
|
2285
|
+
createDrizzleAdapter,
|
|
1953
2286
|
createNoopEmailAdapter,
|
|
1954
2287
|
createOAuthVerifier,
|
|
2288
|
+
createPrismaAdapter,
|
|
2289
|
+
createSessionWithToken,
|
|
2290
|
+
createSessionWithTokenAndCookie,
|
|
1955
2291
|
decodeToken,
|
|
1956
2292
|
defaultAuthConfig,
|
|
1957
2293
|
defaultCookieSettings,
|