@rebasepro/server-mongo 0.17.3 → 0.18.0
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/LICENSE +0 -1
- package/README.md +4 -0
- package/dist/MongoBootstrapper.d.ts +0 -1
- package/dist/auth/ensure-collections.d.ts +0 -1
- package/dist/auth/services.d.ts +0 -1
- package/dist/connection.d.ts +0 -1
- package/dist/db/MongoConditionBuilder.d.ts +0 -1
- package/dist/db/MongoDataService.d.ts +0 -1
- package/dist/db/securityRuleFilter.d.ts +0 -1
- package/dist/factory.d.ts +0 -1
- package/dist/history/ensure-history-collection.d.ts +0 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.es.js +83 -79
- package/dist/index.es.js.map +1 -1
- package/dist/schema/plan-schema-change.d.ts +0 -1
- package/dist/services/MongoDriver.d.ts +0 -1
- package/dist/services/MongoHistoryService.d.ts +0 -1
- package/dist/services/MongoRealtimeService.d.ts +0 -1
- package/dist/websocket.d.ts +0 -1
- package/package.json +28 -24
- package/dist/MongoBootstrapper.d.ts.map +0 -1
- package/dist/auth/ensure-collections.d.ts.map +0 -1
- package/dist/auth/services.d.ts.map +0 -1
- package/dist/connection.d.ts.map +0 -1
- package/dist/db/MongoConditionBuilder.d.ts.map +0 -1
- package/dist/db/MongoDataService.d.ts.map +0 -1
- package/dist/db/securityRuleFilter.d.ts.map +0 -1
- package/dist/factory.d.ts.map +0 -1
- package/dist/history/ensure-history-collection.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/schema/plan-schema-change.d.ts.map +0 -1
- package/dist/services/MongoDriver.d.ts.map +0 -1
- package/dist/services/MongoHistoryService.d.ts.map +0 -1
- package/dist/services/MongoRealtimeService.d.ts.map +0 -1
- package/dist/websocket.d.ts.map +0 -1
- package/src/MongoBootstrapper.ts +0 -204
- package/src/auth/ensure-collections.ts +0 -153
- package/src/auth/services.ts +0 -866
- package/src/connection.ts +0 -60
- package/src/db/MongoConditionBuilder.ts +0 -348
- package/src/db/MongoDataService.ts +0 -412
- package/src/db/securityRuleFilter.ts +0 -398
- package/src/factory.ts +0 -331
- package/src/history/ensure-history-collection.ts +0 -22
- package/src/index.ts +0 -25
- package/src/schema/plan-schema-change.ts +0 -159
- package/src/services/MongoDriver.ts +0 -950
- package/src/services/MongoHistoryService.ts +0 -186
- package/src/services/MongoRealtimeService.ts +0 -592
- package/src/websocket.ts +0 -387
package/src/auth/services.ts
DELETED
|
@@ -1,866 +0,0 @@
|
|
|
1
|
-
import { Db, ObjectId } from "mongodb";
|
|
2
|
-
import { normalizeEmail } from "@rebasepro/common";
|
|
3
|
-
|
|
4
|
-
/** Loose document type that allows string _id values (Rebase convention). */
|
|
5
|
-
export interface MongoDoc { _id?: string; [key: string]: any; }
|
|
6
|
-
import {
|
|
7
|
-
UserRepository,
|
|
8
|
-
RoleRepository,
|
|
9
|
-
TokenRepository,
|
|
10
|
-
AuthRepository,
|
|
11
|
-
UserData,
|
|
12
|
-
CreateUserData,
|
|
13
|
-
RoleData,
|
|
14
|
-
CreateRoleData,
|
|
15
|
-
RefreshTokenInfo,
|
|
16
|
-
RefreshTokenSession,
|
|
17
|
-
PasswordResetTokenInfo,
|
|
18
|
-
MagicLinkTokenInfo,
|
|
19
|
-
UserIdentityData,
|
|
20
|
-
ListUsersOptions,
|
|
21
|
-
PaginatedUsersResult,
|
|
22
|
-
MfaFactor,
|
|
23
|
-
MfaChallengeInfo,
|
|
24
|
-
ApiError
|
|
25
|
-
} from "@rebasepro/server";
|
|
26
|
-
|
|
27
|
-
export type Role = RoleData;
|
|
28
|
-
|
|
29
|
-
function escapeRegExp(str: string): string {
|
|
30
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function toUser(doc: any): UserData {
|
|
34
|
-
return {
|
|
35
|
-
id: doc._id || doc.id,
|
|
36
|
-
email: doc.email,
|
|
37
|
-
passwordHash: doc.passwordHash ?? null,
|
|
38
|
-
displayName: doc.displayName ?? null,
|
|
39
|
-
photoUrl: doc.photoUrl ?? null,
|
|
40
|
-
emailVerified: doc.emailVerified ?? false,
|
|
41
|
-
emailVerificationToken: doc.emailVerificationToken ?? null,
|
|
42
|
-
emailVerificationSentAt: doc.emailVerificationSentAt ? new Date(doc.emailVerificationSentAt) : null,
|
|
43
|
-
createdAt: new Date(doc.createdAt),
|
|
44
|
-
updatedAt: new Date(doc.updatedAt)
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export class MongoUserService implements UserRepository {
|
|
49
|
-
constructor(private db: Db) {}
|
|
50
|
-
|
|
51
|
-
private get collection() {
|
|
52
|
-
return this.db.collection<MongoDoc>("rebase_users");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
private get identitiesCollection() {
|
|
56
|
-
return this.db.collection<MongoDoc>("rebase_user_identities");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
private get userRolesCollection() {
|
|
60
|
-
return this.db.collection<MongoDoc>("rebase_user_roles");
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
private get rolesCollection() {
|
|
64
|
-
return this.db.collection<MongoDoc>("rebase_roles");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async createUser(data: CreateUserData): Promise<UserData> {
|
|
68
|
-
const id = new ObjectId().toString();
|
|
69
|
-
const now = new Date();
|
|
70
|
-
const doc = {
|
|
71
|
-
_id: id,
|
|
72
|
-
id,
|
|
73
|
-
email: normalizeEmail(data.email),
|
|
74
|
-
passwordHash: data.passwordHash ?? null,
|
|
75
|
-
displayName: data.displayName ?? null,
|
|
76
|
-
photoUrl: data.photoUrl ?? null,
|
|
77
|
-
emailVerified: data.emailVerified ?? false,
|
|
78
|
-
createdAt: now,
|
|
79
|
-
updatedAt: now
|
|
80
|
-
};
|
|
81
|
-
try {
|
|
82
|
-
await this.collection.insertOne(doc);
|
|
83
|
-
} catch (error) {
|
|
84
|
-
// 11000 is Mongo's duplicate key, and the unique index on `email`
|
|
85
|
-
// in `ensure-collections.ts` is what raises it. Same answer as
|
|
86
|
-
// Postgres gives for its 23505, and the same answer the route
|
|
87
|
-
// gives when its pre-check sees the row — see
|
|
88
|
-
// `UserRepository.createUser`.
|
|
89
|
-
if ((error as { code?: number })?.code === 11000) {
|
|
90
|
-
throw ApiError.conflict("Email already registered", "EMAIL_EXISTS");
|
|
91
|
-
}
|
|
92
|
-
throw error;
|
|
93
|
-
}
|
|
94
|
-
return toUser(doc);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
async getUserById(id: string): Promise<UserData | null> {
|
|
98
|
-
const doc = await this.collection.findOne({ id });
|
|
99
|
-
return doc ? toUser(doc) : null;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async getUserByEmail(email: string): Promise<UserData | null> {
|
|
103
|
-
const doc = await this.collection.findOne({ email: normalizeEmail(email) });
|
|
104
|
-
return doc ? toUser(doc) : null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async getUserByIdentity(provider: string, providerId: string): Promise<UserData | null> {
|
|
108
|
-
const identity = await this.identitiesCollection.findOne({ provider,
|
|
109
|
-
providerId });
|
|
110
|
-
if (!identity) return null;
|
|
111
|
-
return this.getUserById(identity.uid);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
async getUserIdentities(uid: string): Promise<UserIdentityData[]> {
|
|
115
|
-
const docs = await this.identitiesCollection.find({ uid }).toArray();
|
|
116
|
-
return docs.map(doc => ({
|
|
117
|
-
id: doc.id,
|
|
118
|
-
uid: doc.uid,
|
|
119
|
-
provider: doc.provider,
|
|
120
|
-
providerId: doc.providerId,
|
|
121
|
-
profileData: doc.profileData ?? null,
|
|
122
|
-
createdAt: new Date(doc.createdAt),
|
|
123
|
-
updatedAt: new Date(doc.updatedAt)
|
|
124
|
-
}));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
async linkUserIdentity(uid: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void> {
|
|
128
|
-
const now = new Date();
|
|
129
|
-
await this.identitiesCollection.updateOne(
|
|
130
|
-
{ provider,
|
|
131
|
-
providerId },
|
|
132
|
-
{
|
|
133
|
-
$setOnInsert: {
|
|
134
|
-
_id: new ObjectId().toString(),
|
|
135
|
-
id: new ObjectId().toString(),
|
|
136
|
-
uid,
|
|
137
|
-
provider,
|
|
138
|
-
providerId,
|
|
139
|
-
createdAt: now
|
|
140
|
-
},
|
|
141
|
-
$set: {
|
|
142
|
-
profileData: profileData ?? null,
|
|
143
|
-
updatedAt: now
|
|
144
|
-
}
|
|
145
|
-
},
|
|
146
|
-
{ upsert: true }
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
async updateUser(id: string, data: Partial<Omit<CreateUserData, "id">>): Promise<UserData | null> {
|
|
151
|
-
const updateData: Record<string, unknown> = { ...data,
|
|
152
|
-
updatedAt: new Date() };
|
|
153
|
-
if (typeof updateData.email === "string") updateData.email = normalizeEmail(updateData.email);
|
|
154
|
-
|
|
155
|
-
await this.collection.updateOne({ id }, { $set: updateData });
|
|
156
|
-
return this.getUserById(id);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async deleteUser(id: string): Promise<void> {
|
|
160
|
-
await this.collection.deleteOne({ id });
|
|
161
|
-
await this.identitiesCollection.deleteMany({ uid: id });
|
|
162
|
-
await this.userRolesCollection.deleteMany({ uid: id });
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async listUsers(): Promise<UserData[]> {
|
|
166
|
-
const docs = await this.collection.find().toArray();
|
|
167
|
-
return docs.map(toUser);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
async listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult> {
|
|
171
|
-
const limit = options?.limit ?? 25;
|
|
172
|
-
const offset = options?.offset ?? 0;
|
|
173
|
-
const search = options?.search?.trim() || "";
|
|
174
|
-
const orderBy = options?.orderBy || "createdAt";
|
|
175
|
-
const orderDir = options?.orderDir || "desc";
|
|
176
|
-
const roleId = options?.roleId;
|
|
177
|
-
|
|
178
|
-
const query: Record<string, unknown> = {};
|
|
179
|
-
|
|
180
|
-
if (search) {
|
|
181
|
-
const escapedSearch = escapeRegExp(search);
|
|
182
|
-
query.$or = [
|
|
183
|
-
{ email: { $regex: escapedSearch,
|
|
184
|
-
$options: "i" } },
|
|
185
|
-
{ displayName: { $regex: escapedSearch,
|
|
186
|
-
$options: "i" } }
|
|
187
|
-
];
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (roleId) {
|
|
191
|
-
const userRoles = await this.userRolesCollection.find({ roleId }).toArray();
|
|
192
|
-
const userIds = userRoles.map(ur => ur.uid);
|
|
193
|
-
query.id = { $in: userIds };
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const sort: Record<string, 1 | -1> = {};
|
|
197
|
-
sort[orderBy] = orderDir === "asc" ? 1 : -1;
|
|
198
|
-
|
|
199
|
-
const total = await this.collection.countDocuments(query);
|
|
200
|
-
const docs = await this.collection.find(query).sort(sort).skip(offset).limit(limit).toArray();
|
|
201
|
-
|
|
202
|
-
return {
|
|
203
|
-
users: docs.map(toUser),
|
|
204
|
-
total,
|
|
205
|
-
limit,
|
|
206
|
-
offset
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
async updatePassword(id: string, passwordHash: string): Promise<void> {
|
|
211
|
-
await this.collection.updateOne(
|
|
212
|
-
{ id },
|
|
213
|
-
{ $set: { passwordHash,
|
|
214
|
-
updatedAt: new Date() } }
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
async setEmailVerified(id: string, verified: boolean): Promise<void> {
|
|
219
|
-
await this.collection.updateOne(
|
|
220
|
-
{ id },
|
|
221
|
-
{ $set: { emailVerified: verified,
|
|
222
|
-
emailVerificationToken: null,
|
|
223
|
-
updatedAt: new Date() } }
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
async setVerificationToken(id: string, token: string | null): Promise<void> {
|
|
228
|
-
await this.collection.updateOne(
|
|
229
|
-
{ id },
|
|
230
|
-
{ $set: { emailVerificationToken: token,
|
|
231
|
-
emailVerificationSentAt: token ? new Date() : null,
|
|
232
|
-
updatedAt: new Date() } }
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
async getUserByVerificationToken(token: string): Promise<UserData | null> {
|
|
237
|
-
const doc = await this.collection.findOne({ emailVerificationToken: token });
|
|
238
|
-
return doc ? toUser(doc) : null;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
async getUserRoles(uid: string): Promise<RoleData[]> {
|
|
242
|
-
const userRoles = await this.userRolesCollection.find({ uid }).toArray();
|
|
243
|
-
const roleIds = userRoles.map(ur => ur.roleId);
|
|
244
|
-
if (roleIds.length === 0) return [];
|
|
245
|
-
|
|
246
|
-
const roles = await this.rolesCollection.find({ id: { $in: roleIds } }).toArray();
|
|
247
|
-
return roles.map(r => ({
|
|
248
|
-
id: r.id,
|
|
249
|
-
name: r.name,
|
|
250
|
-
isAdmin: r.isAdmin ?? false,
|
|
251
|
-
defaultPermissions: r.defaultPermissions ?? null,
|
|
252
|
-
collectionPermissions: r.collectionPermissions ?? null
|
|
253
|
-
}));
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
async getUserRoleIds(uid: string): Promise<string[]> {
|
|
257
|
-
const userRoles = await this.userRolesCollection.find({ uid }).toArray();
|
|
258
|
-
return userRoles.map(ur => ur.roleId);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
async setUserRoles(uid: string, roleIds: string[]): Promise<void> {
|
|
262
|
-
await this.userRolesCollection.deleteMany({ uid });
|
|
263
|
-
if (roleIds.length > 0) {
|
|
264
|
-
const docs = roleIds.map(roleId => ({
|
|
265
|
-
_id: new ObjectId().toString(),
|
|
266
|
-
uid,
|
|
267
|
-
roleId
|
|
268
|
-
}));
|
|
269
|
-
await this.userRolesCollection.insertMany(docs);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
async assignDefaultRole(uid: string, roleId: string): Promise<void> {
|
|
274
|
-
await this.userRolesCollection.updateOne(
|
|
275
|
-
{ uid,
|
|
276
|
-
roleId },
|
|
277
|
-
{ $setOnInsert: { _id: new ObjectId().toString(),
|
|
278
|
-
uid,
|
|
279
|
-
roleId } },
|
|
280
|
-
{ upsert: true }
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
async getUserWithRoles(uid: string): Promise<{ user: UserData; roles: RoleData[] } | null> {
|
|
285
|
-
const user = await this.getUserById(uid);
|
|
286
|
-
if (!user) return null;
|
|
287
|
-
const roles = await this.getUserRoles(uid);
|
|
288
|
-
return { user,
|
|
289
|
-
roles };
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
export class MongoRoleService implements RoleRepository {
|
|
294
|
-
constructor(private db: Db) {}
|
|
295
|
-
|
|
296
|
-
private get collection() {
|
|
297
|
-
return this.db.collection<MongoDoc>("rebase_roles");
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
async getRoleById(id: string): Promise<RoleData | null> {
|
|
301
|
-
const doc = await this.collection.findOne({ id });
|
|
302
|
-
if (!doc) return null;
|
|
303
|
-
return {
|
|
304
|
-
id: doc.id,
|
|
305
|
-
name: doc.name,
|
|
306
|
-
isAdmin: doc.isAdmin ?? false,
|
|
307
|
-
defaultPermissions: doc.defaultPermissions ?? null,
|
|
308
|
-
collectionPermissions: doc.collectionPermissions ?? null
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
async listRoles(): Promise<RoleData[]> {
|
|
313
|
-
const docs = await this.collection.find().sort({ name: 1 }).toArray();
|
|
314
|
-
return docs.map(doc => ({
|
|
315
|
-
id: doc.id,
|
|
316
|
-
name: doc.name,
|
|
317
|
-
isAdmin: doc.isAdmin ?? false,
|
|
318
|
-
defaultPermissions: doc.defaultPermissions ?? null,
|
|
319
|
-
collectionPermissions: doc.collectionPermissions ?? null
|
|
320
|
-
}));
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
async createRole(data: CreateRoleData): Promise<RoleData> {
|
|
324
|
-
const doc = {
|
|
325
|
-
_id: data.id,
|
|
326
|
-
id: data.id,
|
|
327
|
-
name: data.name,
|
|
328
|
-
isAdmin: data.isAdmin ?? false,
|
|
329
|
-
defaultPermissions: data.defaultPermissions ?? null,
|
|
330
|
-
collectionPermissions: data.collectionPermissions ?? null
|
|
331
|
-
};
|
|
332
|
-
await this.collection.insertOne(doc);
|
|
333
|
-
return { ...doc } as RoleData;
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
async updateRole(id: string, data: Partial<Omit<RoleData, "id">>): Promise<RoleData | null> {
|
|
337
|
-
await this.collection.updateOne({ id }, { $set: data });
|
|
338
|
-
return this.getRoleById(id);
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
async deleteRole(id: string): Promise<void> {
|
|
342
|
-
await this.collection.deleteOne({ id });
|
|
343
|
-
await this.db.collection("rebase_user_roles").deleteMany({ roleId: id });
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
export class MongoRefreshTokenService {
|
|
348
|
-
constructor(private db: Db) {}
|
|
349
|
-
|
|
350
|
-
private get collection() {
|
|
351
|
-
return this.db.collection<MongoDoc>("rebase_refresh_tokens");
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
private toInfo(doc: MongoDoc): RefreshTokenInfo {
|
|
355
|
-
return {
|
|
356
|
-
id: doc.id,
|
|
357
|
-
uid: doc.uid,
|
|
358
|
-
tokenHash: doc.tokenHash,
|
|
359
|
-
expiresAt: new Date(doc.expiresAt),
|
|
360
|
-
createdAt: new Date(doc.createdAt),
|
|
361
|
-
userAgent: doc.userAgent,
|
|
362
|
-
ipAddress: doc.ipAddress,
|
|
363
|
-
sessionId: doc.sessionId,
|
|
364
|
-
rotatedAt: doc.rotatedAt ? new Date(doc.rotatedAt) : null,
|
|
365
|
-
revoked: Boolean(doc.revoked),
|
|
366
|
-
sessionStartedAt: new Date(doc.sessionStartedAt || doc.createdAt)
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
async createToken(
|
|
371
|
-
uid: string,
|
|
372
|
-
tokenHash: string,
|
|
373
|
-
expiresAt: Date,
|
|
374
|
-
userAgent?: string,
|
|
375
|
-
ipAddress?: string,
|
|
376
|
-
session?: RefreshTokenSession
|
|
377
|
-
): Promise<void> {
|
|
378
|
-
const safeUserAgent = userAgent || "";
|
|
379
|
-
const safeIpAddress = ipAddress || "";
|
|
380
|
-
|
|
381
|
-
// No deleteMany first. Tokens of one sign-in accumulate under a shared
|
|
382
|
-
// sessionId and are pruned once nobody can still be holding them —
|
|
383
|
-
// evicting by (uid, userAgent, ipAddress) is what used to sign out a
|
|
384
|
-
// second browser profile behind the same address.
|
|
385
|
-
const now = new Date();
|
|
386
|
-
await this.collection.insertOne({
|
|
387
|
-
_id: new ObjectId().toString(),
|
|
388
|
-
id: new ObjectId().toString(),
|
|
389
|
-
uid,
|
|
390
|
-
tokenHash,
|
|
391
|
-
expiresAt,
|
|
392
|
-
createdAt: now,
|
|
393
|
-
userAgent: safeUserAgent,
|
|
394
|
-
ipAddress: safeIpAddress,
|
|
395
|
-
sessionId: session?.id ?? new ObjectId().toString(),
|
|
396
|
-
sessionStartedAt: session?.startedAt ?? now,
|
|
397
|
-
rotatedAt: null,
|
|
398
|
-
revoked: false
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
async findByHash(tokenHash: string): Promise<RefreshTokenInfo | null> {
|
|
403
|
-
const doc = await this.collection.findOne({ tokenHash });
|
|
404
|
-
return doc ? this.toInfo(doc) : null;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/** Superseded, not gone — see the Postgres service for why that matters. */
|
|
408
|
-
async markRotated(tokenHash: string): Promise<void> {
|
|
409
|
-
await this.collection.updateOne({ tokenHash }, { $set: { rotatedAt: new Date() } });
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
async revokeSession(sessionId: string): Promise<void> {
|
|
413
|
-
await this.collection.updateMany(
|
|
414
|
-
{ sessionId },
|
|
415
|
-
{ $set: { revoked: true, rotatedAt: new Date() } }
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
async prune(uid: string, sessionId: string, supersededBefore: Date): Promise<void> {
|
|
420
|
-
await this.collection.deleteMany({
|
|
421
|
-
uid,
|
|
422
|
-
$or: [
|
|
423
|
-
{ expiresAt: { $lt: new Date() } },
|
|
424
|
-
{ sessionId, rotatedAt: { $ne: null, $lt: supersededBefore } }
|
|
425
|
-
]
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
async getTokensValidAfter(uid: string): Promise<Date | null> {
|
|
430
|
-
const user = await this.db.collection<MongoDoc>("rebase_users").findOne({ id: uid });
|
|
431
|
-
return user?.tokensValidAfter ? new Date(user.tokensValidAfter) : null;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
async setTokensValidAfter(uid: string, at: Date): Promise<void> {
|
|
435
|
-
await this.db.collection<MongoDoc>("rebase_users")
|
|
436
|
-
.updateOne({ id: uid }, { $set: { tokensValidAfter: at } });
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
async deleteByHash(tokenHash: string): Promise<void> {
|
|
440
|
-
await this.collection.deleteOne({ tokenHash });
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
async deleteAllForUser(uid: string): Promise<void> {
|
|
444
|
-
await this.collection.deleteMany({ uid });
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
async listForUser(uid: string): Promise<RefreshTokenInfo[]> {
|
|
448
|
-
const docs = await this.collection.find({ uid }).sort({ createdAt: 1 }).toArray();
|
|
449
|
-
return docs.map(doc => this.toInfo(doc));
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
async deleteById(id: string, uid: string): Promise<void> {
|
|
453
|
-
await this.collection.deleteOne({ id,
|
|
454
|
-
uid });
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
export class MongoPasswordResetTokenService {
|
|
459
|
-
constructor(private db: Db) {}
|
|
460
|
-
|
|
461
|
-
private get collection() {
|
|
462
|
-
return this.db.collection<MongoDoc>("rebase_password_reset_tokens");
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
async createToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {
|
|
466
|
-
await this.collection.deleteMany({ uid,
|
|
467
|
-
usedAt: null });
|
|
468
|
-
|
|
469
|
-
await this.collection.insertOne({
|
|
470
|
-
_id: new ObjectId().toString(),
|
|
471
|
-
uid,
|
|
472
|
-
tokenHash,
|
|
473
|
-
expiresAt,
|
|
474
|
-
usedAt: null
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
async findValidByHash(tokenHash: string): Promise<{ uid: string; expiresAt: Date } | null> {
|
|
479
|
-
const doc = await this.collection.findOne({
|
|
480
|
-
tokenHash,
|
|
481
|
-
usedAt: null,
|
|
482
|
-
expiresAt: { $gt: new Date() }
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
if (!doc) return null;
|
|
486
|
-
|
|
487
|
-
return {
|
|
488
|
-
uid: doc.uid,
|
|
489
|
-
expiresAt: new Date(doc.expiresAt)
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
async markAsUsed(tokenHash: string): Promise<void> {
|
|
494
|
-
await this.collection.updateOne(
|
|
495
|
-
{ tokenHash },
|
|
496
|
-
{ $set: { usedAt: new Date() } }
|
|
497
|
-
);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
async deleteAllForUser(uid: string): Promise<void> {
|
|
501
|
-
await this.collection.deleteMany({ uid });
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
async deleteExpired(): Promise<void> {
|
|
505
|
-
await this.collection.deleteMany({ expiresAt: { $lt: new Date() } });
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
export class MongoTokenRepository implements TokenRepository {
|
|
510
|
-
private refreshTokenService: MongoRefreshTokenService;
|
|
511
|
-
private passwordResetTokenService: MongoPasswordResetTokenService;
|
|
512
|
-
|
|
513
|
-
constructor(private db: Db) {
|
|
514
|
-
this.refreshTokenService = new MongoRefreshTokenService(db);
|
|
515
|
-
this.passwordResetTokenService = new MongoPasswordResetTokenService(db);
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
async createRefreshToken(uid: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string, session?: RefreshTokenSession): Promise<void> {
|
|
519
|
-
await this.refreshTokenService.createToken(uid, tokenHash, expiresAt, userAgent, ipAddress, session);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
async markRefreshTokenRotated(tokenHash: string): Promise<void> {
|
|
523
|
-
await this.refreshTokenService.markRotated(tokenHash);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
async revokeRefreshTokenSession(sessionId: string): Promise<void> {
|
|
527
|
-
await this.refreshTokenService.revokeSession(sessionId);
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
async pruneRefreshTokens(uid: string, sessionId: string, supersededBefore: Date): Promise<void> {
|
|
531
|
-
await this.refreshTokenService.prune(uid, sessionId, supersededBefore);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
async getTokensValidAfter(uid: string): Promise<Date | null> {
|
|
535
|
-
return this.refreshTokenService.getTokensValidAfter(uid);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
async setTokensValidAfter(uid: string, at: Date): Promise<void> {
|
|
539
|
-
await this.refreshTokenService.setTokensValidAfter(uid, at);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
async findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null> {
|
|
543
|
-
return this.refreshTokenService.findByHash(tokenHash);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
async deleteRefreshToken(tokenHash: string): Promise<void> {
|
|
547
|
-
await this.refreshTokenService.deleteByHash(tokenHash);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
async deleteAllRefreshTokensForUser(uid: string): Promise<void> {
|
|
551
|
-
await this.refreshTokenService.deleteAllForUser(uid);
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
async listRefreshTokensForUser(uid: string): Promise<RefreshTokenInfo[]> {
|
|
555
|
-
return this.refreshTokenService.listForUser(uid);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
async deleteRefreshTokenById(id: string, uid: string): Promise<void> {
|
|
559
|
-
await this.refreshTokenService.deleteById(id, uid);
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
async createPasswordResetToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {
|
|
563
|
-
await this.passwordResetTokenService.createToken(uid, tokenHash, expiresAt);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
async findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null> {
|
|
567
|
-
return this.passwordResetTokenService.findValidByHash(tokenHash);
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
async markPasswordResetTokenUsed(tokenHash: string): Promise<void> {
|
|
571
|
-
await this.passwordResetTokenService.markAsUsed(tokenHash);
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
async deleteAllPasswordResetTokensForUser(uid: string): Promise<void> {
|
|
575
|
-
await this.passwordResetTokenService.deleteAllForUser(uid);
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
async deleteExpiredTokens(): Promise<void> {
|
|
579
|
-
await this.passwordResetTokenService.deleteExpired();
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
// Magic link token operations
|
|
583
|
-
|
|
584
|
-
async createMagicLinkToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {
|
|
585
|
-
const col = this.db.collection("magic_link_tokens");
|
|
586
|
-
await col.deleteMany({ uid, usedAt: null });
|
|
587
|
-
await col.insertOne({ uid, tokenHash, expiresAt, usedAt: null, createdAt: new Date() });
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
async findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null> {
|
|
591
|
-
const col = this.db.collection("magic_link_tokens");
|
|
592
|
-
const doc = await col.findOne({ tokenHash, usedAt: null, expiresAt: { $gt: new Date() } });
|
|
593
|
-
if (!doc) return null;
|
|
594
|
-
return { uid: doc.uid as string, expiresAt: doc.expiresAt as Date };
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
async markMagicLinkTokenUsed(tokenHash: string): Promise<void> {
|
|
598
|
-
const col = this.db.collection("magic_link_tokens");
|
|
599
|
-
await col.updateOne({ tokenHash }, { $set: { usedAt: new Date() } });
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
export class MongoAuthRepository implements AuthRepository {
|
|
604
|
-
private userService: MongoUserService;
|
|
605
|
-
private roleService: MongoRoleService;
|
|
606
|
-
private tokenRepository: MongoTokenRepository;
|
|
607
|
-
|
|
608
|
-
constructor(private db: Db) {
|
|
609
|
-
this.userService = new MongoUserService(db);
|
|
610
|
-
this.roleService = new MongoRoleService(db);
|
|
611
|
-
this.tokenRepository = new MongoTokenRepository(db);
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
async createUser(data: CreateUserData): Promise<UserData> {
|
|
615
|
-
return this.userService.createUser(data);
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
async getUserById(id: string): Promise<UserData | null> {
|
|
619
|
-
return this.userService.getUserById(id);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
async getUserByEmail(email: string): Promise<UserData | null> {
|
|
623
|
-
return this.userService.getUserByEmail(email);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
async getUserByIdentity(provider: string, providerId: string): Promise<UserData | null> {
|
|
627
|
-
return this.userService.getUserByIdentity(provider, providerId);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
async getUserIdentities(uid: string): Promise<UserIdentityData[]> {
|
|
631
|
-
return this.userService.getUserIdentities(uid);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
async linkUserIdentity(uid: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void> {
|
|
635
|
-
return this.userService.linkUserIdentity(uid, provider, providerId, profileData);
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
async updateUser(id: string, data: Partial<Omit<CreateUserData, "id">>): Promise<UserData | null> {
|
|
639
|
-
return this.userService.updateUser(id, data);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
async deleteUser(id: string): Promise<void> {
|
|
643
|
-
await this.userService.deleteUser(id);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
async listUsers(): Promise<UserData[]> {
|
|
647
|
-
return this.userService.listUsers();
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
async listUsersPaginated(options?: ListUsersOptions): Promise<PaginatedUsersResult> {
|
|
651
|
-
return this.userService.listUsersPaginated(options);
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
async updatePassword(id: string, passwordHash: string): Promise<void> {
|
|
655
|
-
await this.userService.updatePassword(id, passwordHash);
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
async setEmailVerified(id: string, verified: boolean): Promise<void> {
|
|
659
|
-
await this.userService.setEmailVerified(id, verified);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
async setVerificationToken(id: string, token: string | null): Promise<void> {
|
|
663
|
-
await this.userService.setVerificationToken(id, token);
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
async getUserByVerificationToken(token: string): Promise<UserData | null> {
|
|
667
|
-
return this.userService.getUserByVerificationToken(token);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
async getUserRoles(uid: string): Promise<RoleData[]> {
|
|
671
|
-
return this.userService.getUserRoles(uid);
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
async getUserRoleIds(uid: string): Promise<string[]> {
|
|
675
|
-
return this.userService.getUserRoleIds(uid);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
async setUserRoles(uid: string, roleIds: string[]): Promise<void> {
|
|
679
|
-
await this.userService.setUserRoles(uid, roleIds);
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
async assignDefaultRole(uid: string, roleId: string): Promise<void> {
|
|
683
|
-
await this.userService.assignDefaultRole(uid, roleId);
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
async getUserWithRoles(uid: string): Promise<{ user: UserData; roles: RoleData[] } | null> {
|
|
687
|
-
return this.userService.getUserWithRoles(uid);
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
async getRoleById(id: string): Promise<RoleData | null> {
|
|
691
|
-
return this.roleService.getRoleById(id);
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
async listRoles(): Promise<RoleData[]> {
|
|
695
|
-
return this.roleService.listRoles();
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
async createRole(data: CreateRoleData): Promise<RoleData> {
|
|
699
|
-
return this.roleService.createRole(data);
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
async updateRole(id: string, data: Partial<Omit<RoleData, "id">>): Promise<RoleData | null> {
|
|
703
|
-
return this.roleService.updateRole(id, data);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
async deleteRole(id: string): Promise<void> {
|
|
707
|
-
await this.roleService.deleteRole(id);
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
async createRefreshToken(uid: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string, session?: RefreshTokenSession): Promise<void> {
|
|
711
|
-
await this.tokenRepository.createRefreshToken(uid, tokenHash, expiresAt, userAgent, ipAddress, session);
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
async markRefreshTokenRotated(tokenHash: string): Promise<void> {
|
|
715
|
-
await this.tokenRepository.markRefreshTokenRotated(tokenHash);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
async revokeRefreshTokenSession(sessionId: string): Promise<void> {
|
|
719
|
-
await this.tokenRepository.revokeRefreshTokenSession(sessionId);
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
async pruneRefreshTokens(uid: string, sessionId: string, supersededBefore: Date): Promise<void> {
|
|
723
|
-
await this.tokenRepository.pruneRefreshTokens(uid, sessionId, supersededBefore);
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
async getTokensValidAfter(uid: string): Promise<Date | null> {
|
|
727
|
-
return this.tokenRepository.getTokensValidAfter(uid);
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
async setTokensValidAfter(uid: string, at: Date): Promise<void> {
|
|
731
|
-
await this.tokenRepository.setTokensValidAfter(uid, at);
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
async findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null> {
|
|
735
|
-
return this.tokenRepository.findRefreshTokenByHash(tokenHash);
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
async deleteRefreshToken(tokenHash: string): Promise<void> {
|
|
739
|
-
await this.tokenRepository.deleteRefreshToken(tokenHash);
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
async deleteAllRefreshTokensForUser(uid: string): Promise<void> {
|
|
743
|
-
await this.tokenRepository.deleteAllRefreshTokensForUser(uid);
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
async listRefreshTokensForUser(uid: string): Promise<RefreshTokenInfo[]> {
|
|
747
|
-
return this.tokenRepository.listRefreshTokensForUser(uid);
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
async deleteRefreshTokenById(id: string, uid: string): Promise<void> {
|
|
751
|
-
await this.tokenRepository.deleteRefreshTokenById(id, uid);
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
async createPasswordResetToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {
|
|
755
|
-
await this.tokenRepository.createPasswordResetToken(uid, tokenHash, expiresAt);
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
async findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null> {
|
|
759
|
-
return this.tokenRepository.findValidPasswordResetToken(tokenHash);
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
async markPasswordResetTokenUsed(tokenHash: string): Promise<void> {
|
|
763
|
-
await this.tokenRepository.markPasswordResetTokenUsed(tokenHash);
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
async deleteAllPasswordResetTokensForUser(uid: string): Promise<void> {
|
|
767
|
-
await this.tokenRepository.deleteAllPasswordResetTokensForUser(uid);
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
async deleteExpiredTokens(): Promise<void> {
|
|
771
|
-
await this.tokenRepository.deleteExpiredTokens();
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
// Magic link token operations
|
|
775
|
-
|
|
776
|
-
async createMagicLinkToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {
|
|
777
|
-
await this.tokenRepository.createMagicLinkToken(uid, tokenHash, expiresAt);
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
async findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null> {
|
|
781
|
-
return this.tokenRepository.findValidMagicLinkToken(tokenHash);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
async markMagicLinkTokenUsed(tokenHash: string): Promise<void> {
|
|
785
|
-
await this.tokenRepository.markMagicLinkTokenUsed(tokenHash);
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
// ── MFA: not implemented on this engine ──────────────────────────────
|
|
789
|
-
//
|
|
790
|
-
// The routes are mounted for every backend, so `POST /auth/mfa/enroll`
|
|
791
|
-
// is live here and reaches these. Throwing a bare `Error` made every one
|
|
792
|
-
// of them a 500 — and a 500's message is sanitized on the way out, so the
|
|
793
|
-
// caller was told "Internal Server Error" while the reason stayed in the
|
|
794
|
-
// server log. Someone turning on two-factor auth got a server fault
|
|
795
|
-
// instead of an answer, every time, on this engine.
|
|
796
|
-
//
|
|
797
|
-
// 501 with the reason, which is what `init.ts` already does for an admin
|
|
798
|
-
// surface it mounts and cannot serve: "they answer 501 instead, and stay
|
|
799
|
-
// mounted to say why". The reads below stay as they are — no factor can
|
|
800
|
-
// exist here, so `[]`, `null` and `false` are true rather than merely
|
|
801
|
-
// convenient, and `assertMfaSatisfied` reading `false` correctly leaves
|
|
802
|
-
// the login gate inert.
|
|
803
|
-
async createMfaFactor(uid: string, factorType: "totp", secretEncrypted: string, friendlyName?: string): Promise<MfaFactor> {
|
|
804
|
-
throw new ApiError(
|
|
805
|
-
501,
|
|
806
|
-
"MFA_NOT_SUPPORTED",
|
|
807
|
-
"Multi-factor authentication is not implemented for the MongoDB backend, so a factor cannot be stored. Use a Postgres data source for accounts that need a second factor."
|
|
808
|
-
);
|
|
809
|
-
}
|
|
810
|
-
async getMfaFactors(uid: string): Promise<MfaFactor[]> {
|
|
811
|
-
return [];
|
|
812
|
-
}
|
|
813
|
-
async getMfaFactorById(factorId: string): Promise<(MfaFactor & { secretEncrypted: string }) | null> {
|
|
814
|
-
return null;
|
|
815
|
-
}
|
|
816
|
-
async verifyMfaFactor(factorId: string): Promise<void> {
|
|
817
|
-
throw new ApiError(
|
|
818
|
-
501,
|
|
819
|
-
"MFA_NOT_SUPPORTED",
|
|
820
|
-
"Multi-factor authentication is not implemented for the MongoDB backend, so a factor's verification cannot be stored. Use a Postgres data source for accounts that need a second factor."
|
|
821
|
-
);
|
|
822
|
-
}
|
|
823
|
-
async deleteMfaFactor(factorId: string, uid: string): Promise<void> {
|
|
824
|
-
throw new ApiError(
|
|
825
|
-
501,
|
|
826
|
-
"MFA_NOT_SUPPORTED",
|
|
827
|
-
"Multi-factor authentication is not implemented for the MongoDB backend, so a factor cannot be stored. Use a Postgres data source for accounts that need a second factor."
|
|
828
|
-
);
|
|
829
|
-
}
|
|
830
|
-
async createMfaChallenge(factorId: string, ipAddress?: string): Promise<MfaChallengeInfo> {
|
|
831
|
-
throw new ApiError(
|
|
832
|
-
501,
|
|
833
|
-
"MFA_NOT_SUPPORTED",
|
|
834
|
-
"Multi-factor authentication is not implemented for the MongoDB backend, so a challenge cannot be stored. Use a Postgres data source for accounts that need a second factor."
|
|
835
|
-
);
|
|
836
|
-
}
|
|
837
|
-
async getMfaChallengeById(challengeId: string): Promise<MfaChallengeInfo | null> {
|
|
838
|
-
return null;
|
|
839
|
-
}
|
|
840
|
-
async verifyMfaChallenge(challengeId: string): Promise<void> {
|
|
841
|
-
throw new ApiError(
|
|
842
|
-
501,
|
|
843
|
-
"MFA_NOT_SUPPORTED",
|
|
844
|
-
"Multi-factor authentication is not implemented for the MongoDB backend, so a challenge's verification cannot be stored. Use a Postgres data source for accounts that need a second factor."
|
|
845
|
-
);
|
|
846
|
-
}
|
|
847
|
-
async createRecoveryCodes(uid: string, codeHashes: string[]): Promise<void> {
|
|
848
|
-
throw new ApiError(
|
|
849
|
-
501,
|
|
850
|
-
"MFA_NOT_SUPPORTED",
|
|
851
|
-
"Multi-factor authentication is not implemented for the MongoDB backend, so recovery codes cannot be stored. Use a Postgres data source for accounts that need a second factor."
|
|
852
|
-
);
|
|
853
|
-
}
|
|
854
|
-
async useRecoveryCode(uid: string, codeHash: string): Promise<boolean> {
|
|
855
|
-
return false;
|
|
856
|
-
}
|
|
857
|
-
async getUnusedRecoveryCodeCount(uid: string): Promise<number> {
|
|
858
|
-
return 0;
|
|
859
|
-
}
|
|
860
|
-
async deleteAllRecoveryCodes(uid: string): Promise<void> {
|
|
861
|
-
// No-op
|
|
862
|
-
}
|
|
863
|
-
async hasVerifiedMfaFactors(uid: string): Promise<boolean> {
|
|
864
|
-
return false;
|
|
865
|
-
}
|
|
866
|
-
}
|