@rebasepro/server-mongo 0.9.1-canary.ff338b5 → 0.10.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/dist/auth/services.d.ts +43 -43
- package/dist/auth/services.d.ts.map +1 -1
- package/dist/index.es.js +97 -97
- package/dist/index.es.js.map +1 -1
- package/dist/services/MongoRealtimeService.d.ts +5 -5
- package/dist/services/MongoRealtimeService.d.ts.map +1 -1
- package/dist/{websocket-DQlwCHFq.js → websocket-CoKu1mvm.js} +4 -4
- package/dist/websocket-CoKu1mvm.js.map +1 -0
- package/package.json +5 -5
- package/src/auth/services.ts +92 -92
- package/src/services/MongoDriver.ts +2 -2
- package/src/services/MongoRealtimeService.ts +10 -10
- package/src/websocket.ts +3 -3
- package/dist/websocket-DQlwCHFq.js.map +0 -1
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
import { Db, ChangeStream } from "mongodb";
|
|
8
8
|
import { RealtimeProvider, CollectionSubscriptionConfig, SingleSubscriptionConfig } from "@rebasepro/types";
|
|
9
9
|
import { WebSocket } from "ws";
|
|
10
|
-
import { MongoDriver } from "./MongoDriver";
|
|
10
|
+
import type { MongoDriver } from "./MongoDriver";
|
|
11
11
|
interface Subscription {
|
|
12
12
|
type: "collection" | "single";
|
|
13
13
|
config: CollectionSubscriptionConfig | SingleSubscriptionConfig;
|
|
14
14
|
changeStream?: ChangeStream;
|
|
15
15
|
callback?: (data: any) => void;
|
|
16
16
|
authContext?: {
|
|
17
|
-
|
|
17
|
+
uid: string;
|
|
18
18
|
roles: string[];
|
|
19
19
|
};
|
|
20
20
|
}
|
|
@@ -41,7 +41,7 @@ export declare class MongoRealtimeService implements RealtimeProvider {
|
|
|
41
41
|
*/
|
|
42
42
|
subscribeToCollection(subscriptionId: string, config: CollectionSubscriptionConfig & {
|
|
43
43
|
authContext?: {
|
|
44
|
-
|
|
44
|
+
uid: string;
|
|
45
45
|
roles: string[];
|
|
46
46
|
};
|
|
47
47
|
}, callback?: (rows: Record<string, unknown>[]) => void): void;
|
|
@@ -54,7 +54,7 @@ export declare class MongoRealtimeService implements RealtimeProvider {
|
|
|
54
54
|
*/
|
|
55
55
|
subscribeToOne(subscriptionId: string, config: SingleSubscriptionConfig & {
|
|
56
56
|
authContext?: {
|
|
57
|
-
|
|
57
|
+
uid: string;
|
|
58
58
|
roles: string[];
|
|
59
59
|
};
|
|
60
60
|
}, callback?: (row: Record<string, unknown> | null) => void): void;
|
|
@@ -95,7 +95,7 @@ export declare class MongoRealtimeService implements RealtimeProvider {
|
|
|
95
95
|
payload?: any;
|
|
96
96
|
subscriptionId?: string;
|
|
97
97
|
}, _authContext?: {
|
|
98
|
-
|
|
98
|
+
uid: string;
|
|
99
99
|
roles: unknown[];
|
|
100
100
|
}): Promise<void>;
|
|
101
101
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MongoRealtimeService.d.ts","sourceRoot":"","sources":["../../src/services/MongoRealtimeService.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,EAAE,EAAE,YAAY,EAA4C,MAAM,SAAS,CAAC;AACrF,OAAO,EAEH,gBAAgB,EAChB,4BAA4B,EAC5B,wBAAwB,EAG3B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAG/B,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"MongoRealtimeService.d.ts","sourceRoot":"","sources":["../../src/services/MongoRealtimeService.ts"],"names":[],"mappings":"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"}
|
|
@@ -81,7 +81,7 @@ function createMongoWebSocket(server, realtimeService, driver, authConfig, admin
|
|
|
81
81
|
type: "AUTH_SUCCESS",
|
|
82
82
|
requestId,
|
|
83
83
|
payload: {
|
|
84
|
-
|
|
84
|
+
uid: user.uid,
|
|
85
85
|
roles: user.roles
|
|
86
86
|
}
|
|
87
87
|
}));
|
|
@@ -119,7 +119,7 @@ function createMongoWebSocket(server, realtimeService, driver, authConfig, admin
|
|
|
119
119
|
const session = clientSessions.get(clientId);
|
|
120
120
|
if (session?.user && isDriverWithAuth(driver)) try {
|
|
121
121
|
const userForAuth = {
|
|
122
|
-
uid: session.user.
|
|
122
|
+
uid: session.user.uid,
|
|
123
123
|
email: session.user.email ?? "",
|
|
124
124
|
displayName: session.user.displayName ?? "",
|
|
125
125
|
photoURL: session.user.photoURL ?? "",
|
|
@@ -249,7 +249,7 @@ function createMongoWebSocket(server, realtimeService, driver, authConfig, admin
|
|
|
249
249
|
case "unsubscribe": {
|
|
250
250
|
const session = clientSessions.get(clientId);
|
|
251
251
|
const authContext = session?.user ? {
|
|
252
|
-
|
|
252
|
+
uid: session.user.uid,
|
|
253
253
|
roles: session.user.roles ?? []
|
|
254
254
|
} : void 0;
|
|
255
255
|
await realtimeService.handleClientMessage(clientId, {
|
|
@@ -278,4 +278,4 @@ function createMongoWebSocket(server, realtimeService, driver, authConfig, admin
|
|
|
278
278
|
//#endregion
|
|
279
279
|
export { createMongoWebSocket };
|
|
280
280
|
|
|
281
|
-
//# sourceMappingURL=websocket-
|
|
281
|
+
//# sourceMappingURL=websocket-CoKu1mvm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocket-CoKu1mvm.js","names":[],"sources":["../src/websocket.ts"],"sourcesContent":["import { RealtimeProvider, DataDriver, FetchCollectionProps, FetchOneProps, SaveProps, DeleteProps, TableMetadata, DatabaseAdmin, isSchemaAdmin, isDocumentAdmin, User } from \"@rebasepro/types\";\nimport { WebSocketServer, WebSocket } from \"ws\";\nimport { Server } from \"http\";\nimport { inspect } from \"util\";\nimport type { AccessTokenPayload } from \"@rebasepro/server\";\nimport { extractUserFromToken } from \"@rebasepro/server\";\nimport type { RebaseAuthConfig } from \"@rebasepro/server\";\nimport { MongoRealtimeService } from \"./services/MongoRealtimeService\";\nimport { MongoDriver } from \"./services/MongoDriver\";\nimport { logger } from \"@rebasepro/server\";\n\ninterface DriverWithAuth extends DataDriver {\n withAuth(user: Record<string, unknown>): Promise<DataDriver>;\n}\n\nfunction isDriverWithAuth(driver: DataDriver): driver is DriverWithAuth {\n return \"withAuth\" in driver && typeof (driver as Record<string, unknown>).withAuth === \"function\";\n}\n\ninterface ClientSession {\n ws: WebSocket;\n user?: AccessTokenPayload;\n authenticated: boolean;\n messageCount: number;\n messageWindowStart: number;\n}\n\nconst clientSessions = new Map<string, ClientSession>();\nconst WS_RATE_LIMIT = 2000;\nconst WS_RATE_WINDOW_MS = 60_000;\n\nconst ADMIN_ONLY_TYPES = new Set([\n \"EXECUTE_SQL\",\n \"FETCH_DATABASES\",\n \"FETCH_ROLES\",\n \"FETCH_UNMAPPED_TABLES\",\n \"FETCH_TABLE_METADATA\",\n \"FETCH_CURRENT_DATABASE\",\n \"CREATE_BRANCH\",\n \"DELETE_BRANCH\",\n \"LIST_BRANCHES\"\n]);\n\nfunction isAdminSession(session: ClientSession | undefined): boolean {\n if (!session?.user?.roles) return false;\n return session.user.roles.includes(\"admin\");\n}\n\nexport function createMongoWebSocket(\n server: Server,\n realtimeService: MongoRealtimeService,\n driver: MongoDriver,\n authConfig?: RebaseAuthConfig,\n admin?: DatabaseAdmin\n) {\n const isProduction = process.env.NODE_ENV === \"production\";\n const wsDebug = (...args: unknown[]) => { if (!isProduction) console.debug(...args); };\n const wss = new WebSocketServer({ server });\n\n wss.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EADDRINUSE\") return;\n logger.error(\"❌ [WebSocket Server] Error\", { error: err });\n });\n\n const requireAuth = authConfig?.requireAuth !== false && authConfig?.jwtSecret;\n\n wss.on(\"connection\", (ws) => {\n const clientId = `client_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n wsDebug(`WebSocket client connected: ${clientId}`);\n\n clientSessions.set(clientId, { ws,\nauthenticated: !requireAuth,\nmessageCount: 0,\nmessageWindowStart: Date.now() });\n realtimeService.addClient(clientId, ws);\n\n ws.on(\"close\", () => {\n wsDebug(`WebSocket client disconnected: ${clientId}`);\n clientSessions.delete(clientId);\n });\n\n ws.on(\"message\", async (message) => {\n let requestId: string | undefined;\n try {\n const { type, payload, requestId: reqId } = JSON.parse(message.toString());\n requestId = reqId;\n\n wsDebug(`[WS] ${clientId} → ${type}`, requestId ? `(${requestId})` : \"\");\n\n const sendError = (errType: \"ERROR\" | \"AUTH_ERROR\", code: string, msg: string) => {\n ws.send(JSON.stringify({ type: errType,\nrequestId,\npayload: { error: { message: msg,\ncode } } }));\n };\n\n if (type === \"AUTHENTICATE\") {\n const { token } = payload || {};\n if (!token) {\n sendError(\"AUTH_ERROR\", \"INVALID_INPUT\", \"Token is required\");\n return;\n }\n\n const user = extractUserFromToken(token);\n if (user) {\n const session = clientSessions.get(clientId);\n if (session) {\n session.user = user;\n session.authenticated = true;\n }\n ws.send(JSON.stringify({ type: \"AUTH_SUCCESS\",\nrequestId,\npayload: { uid: user.uid,\nroles: user.roles } }));\n } else {\n sendError(\"AUTH_ERROR\", \"INVALID_TOKEN\", \"Invalid or expired token\");\n }\n return;\n }\n\n if (requireAuth) {\n const session = clientSessions.get(clientId);\n if (!session?.authenticated) {\n sendError(\"ERROR\", \"UNAUTHORIZED\", \"Authentication required\");\n return;\n }\n }\n\n {\n const session = clientSessions.get(clientId);\n if (session) {\n const now = Date.now();\n if (now - session.messageWindowStart > WS_RATE_WINDOW_MS) {\n session.messageCount = 0;\n session.messageWindowStart = now;\n }\n session.messageCount++;\n if (session.messageCount > WS_RATE_LIMIT) {\n sendError(\"ERROR\", \"RATE_LIMITED\", \"Too many requests. Please slow down.\");\n return;\n }\n }\n }\n\n if (ADMIN_ONLY_TYPES.has(type)) {\n const session = clientSessions.get(clientId);\n if (!isAdminSession(session)) {\n sendError(\"ERROR\", \"FORBIDDEN\", \"Admin access required for this operation\");\n return;\n }\n }\n\n const getScopedDelegate = async (): Promise<DataDriver> => {\n const session = clientSessions.get(clientId);\n if (session?.user && isDriverWithAuth(driver)) {\n try {\n const userForAuth: User = {\n uid: session.user.uid,\n email: session.user.email ?? \"\",\n displayName: session.user.displayName ?? \"\",\n photoURL: session.user.photoURL ?? \"\",\n providerId: \"jwt\",\n isAnonymous: false,\n roles: session.user.roles ?? []\n };\n return await driver.withAuth(userForAuth);\n } catch (e) {\n logger.error(\"Failed to create authenticated delegate for WS request\", { error: e });\n return driver;\n }\n }\n return driver;\n };\n\n switch (type) {\n case \"FETCH_COLLECTION\": {\n const request: FetchCollectionProps = payload;\n const delegate = await getScopedDelegate();\n const rows = await delegate.fetchCollection(request);\n ws.send(JSON.stringify({ type: \"FETCH_COLLECTION_SUCCESS\",\npayload: { rows },\nrequestId }));\n break;\n }\n case \"FETCH_ONE\": {\n const request: FetchOneProps = payload;\n const delegate = await getScopedDelegate();\n const row = await delegate.fetchOne(request);\n ws.send(JSON.stringify({ type: \"FETCH_ONE_SUCCESS\",\npayload: { row },\nrequestId }));\n break;\n }\n case \"SAVE\": {\n const request: SaveProps = payload;\n const delegate = await getScopedDelegate();\n const row = await delegate.save(request);\n ws.send(JSON.stringify({ type: \"SAVE_SUCCESS\",\npayload: { row },\nrequestId }));\n break;\n }\n case \"DELETE\": {\n const request: DeleteProps = payload;\n const delegate = await getScopedDelegate();\n await delegate.delete(request);\n ws.send(JSON.stringify({ type: \"DELETE_SUCCESS\",\npayload: { success: true },\nrequestId }));\n break;\n }\n case \"CHECK_UNIQUE_FIELD\": {\n const { path, name, value, id, collection } = payload;\n const delegate = await getScopedDelegate();\n const isUnique = await delegate.checkUniqueField(path, name, value, id, collection);\n ws.send(JSON.stringify({ type: \"CHECK_UNIQUE_FIELD_SUCCESS\",\npayload: { isUnique },\nrequestId }));\n break;\n }\n case \"COUNT\": {\n const request: FetchCollectionProps = payload;\n const delegate = await getScopedDelegate();\n const count = await delegate.count!(request);\n ws.send(JSON.stringify({ type: \"COUNT_SUCCESS\",\npayload: { count },\nrequestId }));\n break;\n }\n case \"EXECUTE_SQL\": {\n const { sql, options } = payload;\n if (admin && isDocumentAdmin(admin) && admin.executeAggregate) {\n const result = await admin.executeAggregate(sql as Record<string, unknown>[]);\n ws.send(JSON.stringify({ type: \"EXECUTE_SQL_SUCCESS\",\npayload: { result },\nrequestId }));\n } else {\n ws.send(JSON.stringify({ type: \"ERROR\",\nrequestId,\npayload: { error: { message: \"SQL execution not supported for this driver\",\ncode: \"NOT_SUPPORTED\" } } }));\n }\n break;\n }\n case \"FETCH_UNMAPPED_TABLES\": {\n if (admin && isSchemaAdmin(admin)) {\n const tables = await admin.fetchUnmappedTables?.(payload?.mappedPaths) || [];\n ws.send(JSON.stringify({ type: \"FETCH_UNMAPPED_TABLES_SUCCESS\",\npayload: { tables },\nrequestId }));\n } else {\n ws.send(JSON.stringify({ type: \"FETCH_UNMAPPED_TABLES_SUCCESS\",\npayload: { tables: [] },\nrequestId }));\n }\n break;\n }\n case \"FETCH_TABLE_METADATA\": {\n const { tableName } = payload;\n if (admin && isSchemaAdmin(admin)) {\n const metadata = await admin.fetchTableMetadata?.(tableName);\n ws.send(JSON.stringify({ type: \"FETCH_TABLE_METADATA_SUCCESS\",\npayload: { metadata },\nrequestId }));\n } else {\n ws.send(JSON.stringify({ type: \"FETCH_TABLE_METADATA_SUCCESS\",\npayload: { metadata: null },\nrequestId }));\n }\n break;\n }\n case \"subscribe_collection\":\n case \"subscribe_one\":\n case \"unsubscribe\": {\n const session = clientSessions.get(clientId);\n const authContext = session?.user ? { uid: session.user.uid,\nroles: session.user.roles ?? [] } : undefined;\n await realtimeService.handleClientMessage(clientId, {\n type,\n payload,\n subscriptionId: payload?.subscriptionId\n }, authContext);\n break;\n }\n default:\n logger.error(\"❌ [WebSocket Server] Unknown message type\", { detail: type });\n }\n } catch (error: unknown) {\n const errorMessage = process.env.NODE_ENV === \"production\" ? \"An unexpected error occurred\" : (error instanceof Error ? error.message : \"An unexpected error occurred\");\n ws.send(JSON.stringify({ type: \"ERROR\",\nrequestId,\npayload: { error: { message: errorMessage,\ncode: \"INTERNAL_ERROR\" } } }));\n }\n });\n });\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/server-mongo",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.10.0",
|
|
5
5
|
"description": "MongoDB backend for Rebase",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"mongodb": "^7.3.0",
|
|
42
42
|
"ws": "^8.21.0",
|
|
43
|
-
"@rebasepro/common": "0.
|
|
44
|
-
"@rebasepro/types": "0.
|
|
45
|
-
"@rebasepro/utils": "0.
|
|
43
|
+
"@rebasepro/common": "0.10.0",
|
|
44
|
+
"@rebasepro/types": "0.10.0",
|
|
45
|
+
"@rebasepro/utils": "0.10.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/jest": "^30.0.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"vite": "^8.0.16"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@rebasepro/server": "0.
|
|
58
|
+
"@rebasepro/server": "0.10.0"
|
|
59
59
|
},
|
|
60
60
|
"peerDependenciesMeta": {
|
|
61
61
|
"@rebasepro/server": {
|
package/src/auth/services.ts
CHANGED
|
@@ -93,14 +93,14 @@ export class MongoUserService implements UserRepository {
|
|
|
93
93
|
const identity = await this.identitiesCollection.findOne({ provider,
|
|
94
94
|
providerId });
|
|
95
95
|
if (!identity) return null;
|
|
96
|
-
return this.getUserById(identity.
|
|
96
|
+
return this.getUserById(identity.uid);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
async getUserIdentities(
|
|
100
|
-
const docs = await this.identitiesCollection.find({
|
|
99
|
+
async getUserIdentities(uid: string): Promise<UserIdentityData[]> {
|
|
100
|
+
const docs = await this.identitiesCollection.find({ uid }).toArray();
|
|
101
101
|
return docs.map(doc => ({
|
|
102
102
|
id: doc.id,
|
|
103
|
-
|
|
103
|
+
uid: doc.uid,
|
|
104
104
|
provider: doc.provider,
|
|
105
105
|
providerId: doc.providerId,
|
|
106
106
|
profileData: doc.profileData ?? null,
|
|
@@ -109,7 +109,7 @@ providerId });
|
|
|
109
109
|
}));
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
async linkUserIdentity(
|
|
112
|
+
async linkUserIdentity(uid: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void> {
|
|
113
113
|
const now = new Date();
|
|
114
114
|
await this.identitiesCollection.updateOne(
|
|
115
115
|
{ provider,
|
|
@@ -118,7 +118,7 @@ providerId },
|
|
|
118
118
|
$setOnInsert: {
|
|
119
119
|
_id: new ObjectId().toString(),
|
|
120
120
|
id: new ObjectId().toString(),
|
|
121
|
-
|
|
121
|
+
uid,
|
|
122
122
|
provider,
|
|
123
123
|
providerId,
|
|
124
124
|
createdAt: now
|
|
@@ -143,8 +143,8 @@ updatedAt: new Date() };
|
|
|
143
143
|
|
|
144
144
|
async deleteUser(id: string): Promise<void> {
|
|
145
145
|
await this.collection.deleteOne({ id });
|
|
146
|
-
await this.identitiesCollection.deleteMany({
|
|
147
|
-
await this.userRolesCollection.deleteMany({
|
|
146
|
+
await this.identitiesCollection.deleteMany({ uid: id });
|
|
147
|
+
await this.userRolesCollection.deleteMany({ uid: id });
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
async listUsers(): Promise<UserData[]> {
|
|
@@ -174,7 +174,7 @@ $options: "i" } }
|
|
|
174
174
|
|
|
175
175
|
if (roleId) {
|
|
176
176
|
const userRoles = await this.userRolesCollection.find({ roleId }).toArray();
|
|
177
|
-
const userIds = userRoles.map(ur => ur.
|
|
177
|
+
const userIds = userRoles.map(ur => ur.uid);
|
|
178
178
|
query.id = { $in: userIds };
|
|
179
179
|
}
|
|
180
180
|
|
|
@@ -223,8 +223,8 @@ updatedAt: new Date() } }
|
|
|
223
223
|
return doc ? toUser(doc) : null;
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
-
async getUserRoles(
|
|
227
|
-
const userRoles = await this.userRolesCollection.find({
|
|
226
|
+
async getUserRoles(uid: string): Promise<RoleData[]> {
|
|
227
|
+
const userRoles = await this.userRolesCollection.find({ uid }).toArray();
|
|
228
228
|
const roleIds = userRoles.map(ur => ur.roleId);
|
|
229
229
|
if (roleIds.length === 0) return [];
|
|
230
230
|
|
|
@@ -238,38 +238,38 @@ updatedAt: new Date() } }
|
|
|
238
238
|
}));
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
async getUserRoleIds(
|
|
242
|
-
const userRoles = await this.userRolesCollection.find({
|
|
241
|
+
async getUserRoleIds(uid: string): Promise<string[]> {
|
|
242
|
+
const userRoles = await this.userRolesCollection.find({ uid }).toArray();
|
|
243
243
|
return userRoles.map(ur => ur.roleId);
|
|
244
244
|
}
|
|
245
245
|
|
|
246
|
-
async setUserRoles(
|
|
247
|
-
await this.userRolesCollection.deleteMany({
|
|
246
|
+
async setUserRoles(uid: string, roleIds: string[]): Promise<void> {
|
|
247
|
+
await this.userRolesCollection.deleteMany({ uid });
|
|
248
248
|
if (roleIds.length > 0) {
|
|
249
249
|
const docs = roleIds.map(roleId => ({
|
|
250
250
|
_id: new ObjectId().toString(),
|
|
251
|
-
|
|
251
|
+
uid,
|
|
252
252
|
roleId
|
|
253
253
|
}));
|
|
254
254
|
await this.userRolesCollection.insertMany(docs);
|
|
255
255
|
}
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
async assignDefaultRole(
|
|
258
|
+
async assignDefaultRole(uid: string, roleId: string): Promise<void> {
|
|
259
259
|
await this.userRolesCollection.updateOne(
|
|
260
|
-
{
|
|
260
|
+
{ uid,
|
|
261
261
|
roleId },
|
|
262
262
|
{ $setOnInsert: { _id: new ObjectId().toString(),
|
|
263
|
-
|
|
263
|
+
uid,
|
|
264
264
|
roleId } },
|
|
265
265
|
{ upsert: true }
|
|
266
266
|
);
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
async getUserWithRoles(
|
|
270
|
-
const user = await this.getUserById(
|
|
269
|
+
async getUserWithRoles(uid: string): Promise<{ user: UserData; roles: RoleData[] } | null> {
|
|
270
|
+
const user = await this.getUserById(uid);
|
|
271
271
|
if (!user) return null;
|
|
272
|
-
const roles = await this.getUserRoles(
|
|
272
|
+
const roles = await this.getUserRoles(uid);
|
|
273
273
|
return { user,
|
|
274
274
|
roles };
|
|
275
275
|
}
|
|
@@ -336,12 +336,12 @@ export class MongoRefreshTokenService {
|
|
|
336
336
|
return this.db.collection<MongoDoc>("rebase_refresh_tokens");
|
|
337
337
|
}
|
|
338
338
|
|
|
339
|
-
async createToken(
|
|
339
|
+
async createToken(uid: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void> {
|
|
340
340
|
const safeUserAgent = userAgent || "";
|
|
341
341
|
const safeIpAddress = ipAddress || "";
|
|
342
342
|
|
|
343
343
|
await this.collection.deleteMany({
|
|
344
|
-
|
|
344
|
+
uid,
|
|
345
345
|
userAgent: safeUserAgent,
|
|
346
346
|
ipAddress: safeIpAddress
|
|
347
347
|
});
|
|
@@ -349,7 +349,7 @@ export class MongoRefreshTokenService {
|
|
|
349
349
|
await this.collection.insertOne({
|
|
350
350
|
_id: new ObjectId().toString(),
|
|
351
351
|
id: new ObjectId().toString(),
|
|
352
|
-
|
|
352
|
+
uid,
|
|
353
353
|
tokenHash,
|
|
354
354
|
expiresAt,
|
|
355
355
|
createdAt: new Date(),
|
|
@@ -363,7 +363,7 @@ export class MongoRefreshTokenService {
|
|
|
363
363
|
if (!doc) return null;
|
|
364
364
|
return {
|
|
365
365
|
id: doc.id,
|
|
366
|
-
|
|
366
|
+
uid: doc.uid,
|
|
367
367
|
tokenHash: doc.tokenHash,
|
|
368
368
|
expiresAt: new Date(doc.expiresAt),
|
|
369
369
|
createdAt: new Date(doc.createdAt),
|
|
@@ -376,15 +376,15 @@ export class MongoRefreshTokenService {
|
|
|
376
376
|
await this.collection.deleteOne({ tokenHash });
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
-
async deleteAllForUser(
|
|
380
|
-
await this.collection.deleteMany({
|
|
379
|
+
async deleteAllForUser(uid: string): Promise<void> {
|
|
380
|
+
await this.collection.deleteMany({ uid });
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
-
async listForUser(
|
|
384
|
-
const docs = await this.collection.find({
|
|
383
|
+
async listForUser(uid: string): Promise<RefreshTokenInfo[]> {
|
|
384
|
+
const docs = await this.collection.find({ uid }).sort({ createdAt: 1 }).toArray();
|
|
385
385
|
return docs.map(doc => ({
|
|
386
386
|
id: doc.id,
|
|
387
|
-
|
|
387
|
+
uid: doc.uid,
|
|
388
388
|
tokenHash: doc.tokenHash,
|
|
389
389
|
expiresAt: new Date(doc.expiresAt),
|
|
390
390
|
createdAt: new Date(doc.createdAt),
|
|
@@ -393,9 +393,9 @@ export class MongoRefreshTokenService {
|
|
|
393
393
|
}));
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
async deleteById(id: string,
|
|
396
|
+
async deleteById(id: string, uid: string): Promise<void> {
|
|
397
397
|
await this.collection.deleteOne({ id,
|
|
398
|
-
|
|
398
|
+
uid });
|
|
399
399
|
}
|
|
400
400
|
}
|
|
401
401
|
|
|
@@ -406,20 +406,20 @@ export class MongoPasswordResetTokenService {
|
|
|
406
406
|
return this.db.collection<MongoDoc>("rebase_password_reset_tokens");
|
|
407
407
|
}
|
|
408
408
|
|
|
409
|
-
async createToken(
|
|
410
|
-
await this.collection.deleteMany({
|
|
409
|
+
async createToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {
|
|
410
|
+
await this.collection.deleteMany({ uid,
|
|
411
411
|
usedAt: null });
|
|
412
412
|
|
|
413
413
|
await this.collection.insertOne({
|
|
414
414
|
_id: new ObjectId().toString(),
|
|
415
|
-
|
|
415
|
+
uid,
|
|
416
416
|
tokenHash,
|
|
417
417
|
expiresAt,
|
|
418
418
|
usedAt: null
|
|
419
419
|
});
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
-
async findValidByHash(tokenHash: string): Promise<{
|
|
422
|
+
async findValidByHash(tokenHash: string): Promise<{ uid: string; expiresAt: Date } | null> {
|
|
423
423
|
const doc = await this.collection.findOne({
|
|
424
424
|
tokenHash,
|
|
425
425
|
usedAt: null,
|
|
@@ -429,7 +429,7 @@ usedAt: null });
|
|
|
429
429
|
if (!doc) return null;
|
|
430
430
|
|
|
431
431
|
return {
|
|
432
|
-
|
|
432
|
+
uid: doc.uid,
|
|
433
433
|
expiresAt: new Date(doc.expiresAt)
|
|
434
434
|
};
|
|
435
435
|
}
|
|
@@ -441,8 +441,8 @@ usedAt: null });
|
|
|
441
441
|
);
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
-
async deleteAllForUser(
|
|
445
|
-
await this.collection.deleteMany({
|
|
444
|
+
async deleteAllForUser(uid: string): Promise<void> {
|
|
445
|
+
await this.collection.deleteMany({ uid });
|
|
446
446
|
}
|
|
447
447
|
|
|
448
448
|
async deleteExpired(): Promise<void> {
|
|
@@ -459,8 +459,8 @@ export class MongoTokenRepository implements TokenRepository {
|
|
|
459
459
|
this.passwordResetTokenService = new MongoPasswordResetTokenService(db);
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
-
async createRefreshToken(
|
|
463
|
-
await this.refreshTokenService.createToken(
|
|
462
|
+
async createRefreshToken(uid: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void> {
|
|
463
|
+
await this.refreshTokenService.createToken(uid, tokenHash, expiresAt, userAgent, ipAddress);
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
async findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null> {
|
|
@@ -471,20 +471,20 @@ export class MongoTokenRepository implements TokenRepository {
|
|
|
471
471
|
await this.refreshTokenService.deleteByHash(tokenHash);
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
async deleteAllRefreshTokensForUser(
|
|
475
|
-
await this.refreshTokenService.deleteAllForUser(
|
|
474
|
+
async deleteAllRefreshTokensForUser(uid: string): Promise<void> {
|
|
475
|
+
await this.refreshTokenService.deleteAllForUser(uid);
|
|
476
476
|
}
|
|
477
477
|
|
|
478
|
-
async listRefreshTokensForUser(
|
|
479
|
-
return this.refreshTokenService.listForUser(
|
|
478
|
+
async listRefreshTokensForUser(uid: string): Promise<RefreshTokenInfo[]> {
|
|
479
|
+
return this.refreshTokenService.listForUser(uid);
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
-
async deleteRefreshTokenById(id: string,
|
|
483
|
-
await this.refreshTokenService.deleteById(id,
|
|
482
|
+
async deleteRefreshTokenById(id: string, uid: string): Promise<void> {
|
|
483
|
+
await this.refreshTokenService.deleteById(id, uid);
|
|
484
484
|
}
|
|
485
485
|
|
|
486
|
-
async createPasswordResetToken(
|
|
487
|
-
await this.passwordResetTokenService.createToken(
|
|
486
|
+
async createPasswordResetToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {
|
|
487
|
+
await this.passwordResetTokenService.createToken(uid, tokenHash, expiresAt);
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
async findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null> {
|
|
@@ -495,8 +495,8 @@ export class MongoTokenRepository implements TokenRepository {
|
|
|
495
495
|
await this.passwordResetTokenService.markAsUsed(tokenHash);
|
|
496
496
|
}
|
|
497
497
|
|
|
498
|
-
async deleteAllPasswordResetTokensForUser(
|
|
499
|
-
await this.passwordResetTokenService.deleteAllForUser(
|
|
498
|
+
async deleteAllPasswordResetTokensForUser(uid: string): Promise<void> {
|
|
499
|
+
await this.passwordResetTokenService.deleteAllForUser(uid);
|
|
500
500
|
}
|
|
501
501
|
|
|
502
502
|
async deleteExpiredTokens(): Promise<void> {
|
|
@@ -505,17 +505,17 @@ export class MongoTokenRepository implements TokenRepository {
|
|
|
505
505
|
|
|
506
506
|
// Magic link token operations
|
|
507
507
|
|
|
508
|
-
async createMagicLinkToken(
|
|
508
|
+
async createMagicLinkToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {
|
|
509
509
|
const col = this.db.collection("magic_link_tokens");
|
|
510
|
-
await col.deleteMany({
|
|
511
|
-
await col.insertOne({
|
|
510
|
+
await col.deleteMany({ uid, usedAt: null });
|
|
511
|
+
await col.insertOne({ uid, tokenHash, expiresAt, usedAt: null, createdAt: new Date() });
|
|
512
512
|
}
|
|
513
513
|
|
|
514
514
|
async findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null> {
|
|
515
515
|
const col = this.db.collection("magic_link_tokens");
|
|
516
516
|
const doc = await col.findOne({ tokenHash, usedAt: null, expiresAt: { $gt: new Date() } });
|
|
517
517
|
if (!doc) return null;
|
|
518
|
-
return {
|
|
518
|
+
return { uid: doc.uid as string, expiresAt: doc.expiresAt as Date };
|
|
519
519
|
}
|
|
520
520
|
|
|
521
521
|
async markMagicLinkTokenUsed(tokenHash: string): Promise<void> {
|
|
@@ -551,12 +551,12 @@ export class MongoAuthRepository implements AuthRepository {
|
|
|
551
551
|
return this.userService.getUserByIdentity(provider, providerId);
|
|
552
552
|
}
|
|
553
553
|
|
|
554
|
-
async getUserIdentities(
|
|
555
|
-
return this.userService.getUserIdentities(
|
|
554
|
+
async getUserIdentities(uid: string): Promise<UserIdentityData[]> {
|
|
555
|
+
return this.userService.getUserIdentities(uid);
|
|
556
556
|
}
|
|
557
557
|
|
|
558
|
-
async linkUserIdentity(
|
|
559
|
-
return this.userService.linkUserIdentity(
|
|
558
|
+
async linkUserIdentity(uid: string, provider: string, providerId: string, profileData?: Record<string, unknown>): Promise<void> {
|
|
559
|
+
return this.userService.linkUserIdentity(uid, provider, providerId, profileData);
|
|
560
560
|
}
|
|
561
561
|
|
|
562
562
|
async updateUser(id: string, data: Partial<Omit<CreateUserData, "id">>): Promise<UserData | null> {
|
|
@@ -591,24 +591,24 @@ export class MongoAuthRepository implements AuthRepository {
|
|
|
591
591
|
return this.userService.getUserByVerificationToken(token);
|
|
592
592
|
}
|
|
593
593
|
|
|
594
|
-
async getUserRoles(
|
|
595
|
-
return this.userService.getUserRoles(
|
|
594
|
+
async getUserRoles(uid: string): Promise<RoleData[]> {
|
|
595
|
+
return this.userService.getUserRoles(uid);
|
|
596
596
|
}
|
|
597
597
|
|
|
598
|
-
async getUserRoleIds(
|
|
599
|
-
return this.userService.getUserRoleIds(
|
|
598
|
+
async getUserRoleIds(uid: string): Promise<string[]> {
|
|
599
|
+
return this.userService.getUserRoleIds(uid);
|
|
600
600
|
}
|
|
601
601
|
|
|
602
|
-
async setUserRoles(
|
|
603
|
-
await this.userService.setUserRoles(
|
|
602
|
+
async setUserRoles(uid: string, roleIds: string[]): Promise<void> {
|
|
603
|
+
await this.userService.setUserRoles(uid, roleIds);
|
|
604
604
|
}
|
|
605
605
|
|
|
606
|
-
async assignDefaultRole(
|
|
607
|
-
await this.userService.assignDefaultRole(
|
|
606
|
+
async assignDefaultRole(uid: string, roleId: string): Promise<void> {
|
|
607
|
+
await this.userService.assignDefaultRole(uid, roleId);
|
|
608
608
|
}
|
|
609
609
|
|
|
610
|
-
async getUserWithRoles(
|
|
611
|
-
return this.userService.getUserWithRoles(
|
|
610
|
+
async getUserWithRoles(uid: string): Promise<{ user: UserData; roles: RoleData[] } | null> {
|
|
611
|
+
return this.userService.getUserWithRoles(uid);
|
|
612
612
|
}
|
|
613
613
|
|
|
614
614
|
async getRoleById(id: string): Promise<RoleData | null> {
|
|
@@ -631,8 +631,8 @@ export class MongoAuthRepository implements AuthRepository {
|
|
|
631
631
|
await this.roleService.deleteRole(id);
|
|
632
632
|
}
|
|
633
633
|
|
|
634
|
-
async createRefreshToken(
|
|
635
|
-
await this.tokenRepository.createRefreshToken(
|
|
634
|
+
async createRefreshToken(uid: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string): Promise<void> {
|
|
635
|
+
await this.tokenRepository.createRefreshToken(uid, tokenHash, expiresAt, userAgent, ipAddress);
|
|
636
636
|
}
|
|
637
637
|
|
|
638
638
|
async findRefreshTokenByHash(tokenHash: string): Promise<RefreshTokenInfo | null> {
|
|
@@ -643,20 +643,20 @@ export class MongoAuthRepository implements AuthRepository {
|
|
|
643
643
|
await this.tokenRepository.deleteRefreshToken(tokenHash);
|
|
644
644
|
}
|
|
645
645
|
|
|
646
|
-
async deleteAllRefreshTokensForUser(
|
|
647
|
-
await this.tokenRepository.deleteAllRefreshTokensForUser(
|
|
646
|
+
async deleteAllRefreshTokensForUser(uid: string): Promise<void> {
|
|
647
|
+
await this.tokenRepository.deleteAllRefreshTokensForUser(uid);
|
|
648
648
|
}
|
|
649
649
|
|
|
650
|
-
async listRefreshTokensForUser(
|
|
651
|
-
return this.tokenRepository.listRefreshTokensForUser(
|
|
650
|
+
async listRefreshTokensForUser(uid: string): Promise<RefreshTokenInfo[]> {
|
|
651
|
+
return this.tokenRepository.listRefreshTokensForUser(uid);
|
|
652
652
|
}
|
|
653
653
|
|
|
654
|
-
async deleteRefreshTokenById(id: string,
|
|
655
|
-
await this.tokenRepository.deleteRefreshTokenById(id,
|
|
654
|
+
async deleteRefreshTokenById(id: string, uid: string): Promise<void> {
|
|
655
|
+
await this.tokenRepository.deleteRefreshTokenById(id, uid);
|
|
656
656
|
}
|
|
657
657
|
|
|
658
|
-
async createPasswordResetToken(
|
|
659
|
-
await this.tokenRepository.createPasswordResetToken(
|
|
658
|
+
async createPasswordResetToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {
|
|
659
|
+
await this.tokenRepository.createPasswordResetToken(uid, tokenHash, expiresAt);
|
|
660
660
|
}
|
|
661
661
|
|
|
662
662
|
async findValidPasswordResetToken(tokenHash: string): Promise<PasswordResetTokenInfo | null> {
|
|
@@ -667,8 +667,8 @@ export class MongoAuthRepository implements AuthRepository {
|
|
|
667
667
|
await this.tokenRepository.markPasswordResetTokenUsed(tokenHash);
|
|
668
668
|
}
|
|
669
669
|
|
|
670
|
-
async deleteAllPasswordResetTokensForUser(
|
|
671
|
-
await this.tokenRepository.deleteAllPasswordResetTokensForUser(
|
|
670
|
+
async deleteAllPasswordResetTokensForUser(uid: string): Promise<void> {
|
|
671
|
+
await this.tokenRepository.deleteAllPasswordResetTokensForUser(uid);
|
|
672
672
|
}
|
|
673
673
|
|
|
674
674
|
async deleteExpiredTokens(): Promise<void> {
|
|
@@ -677,8 +677,8 @@ export class MongoAuthRepository implements AuthRepository {
|
|
|
677
677
|
|
|
678
678
|
// Magic link token operations
|
|
679
679
|
|
|
680
|
-
async createMagicLinkToken(
|
|
681
|
-
await this.tokenRepository.createMagicLinkToken(
|
|
680
|
+
async createMagicLinkToken(uid: string, tokenHash: string, expiresAt: Date): Promise<void> {
|
|
681
|
+
await this.tokenRepository.createMagicLinkToken(uid, tokenHash, expiresAt);
|
|
682
682
|
}
|
|
683
683
|
|
|
684
684
|
async findValidMagicLinkToken(tokenHash: string): Promise<MagicLinkTokenInfo | null> {
|
|
@@ -690,10 +690,10 @@ export class MongoAuthRepository implements AuthRepository {
|
|
|
690
690
|
}
|
|
691
691
|
|
|
692
692
|
// MFA Repository Stub
|
|
693
|
-
async createMfaFactor(
|
|
693
|
+
async createMfaFactor(uid: string, factorType: "totp", secretEncrypted: string, friendlyName?: string): Promise<MfaFactor> {
|
|
694
694
|
throw new Error("MFA is not implemented for MongoDB");
|
|
695
695
|
}
|
|
696
|
-
async getMfaFactors(
|
|
696
|
+
async getMfaFactors(uid: string): Promise<MfaFactor[]> {
|
|
697
697
|
return [];
|
|
698
698
|
}
|
|
699
699
|
async getMfaFactorById(factorId: string): Promise<(MfaFactor & { secretEncrypted: string }) | null> {
|
|
@@ -702,7 +702,7 @@ export class MongoAuthRepository implements AuthRepository {
|
|
|
702
702
|
async verifyMfaFactor(factorId: string): Promise<void> {
|
|
703
703
|
throw new Error("MFA is not implemented for MongoDB");
|
|
704
704
|
}
|
|
705
|
-
async deleteMfaFactor(factorId: string,
|
|
705
|
+
async deleteMfaFactor(factorId: string, uid: string): Promise<void> {
|
|
706
706
|
throw new Error("MFA is not implemented for MongoDB");
|
|
707
707
|
}
|
|
708
708
|
async createMfaChallenge(factorId: string, ipAddress?: string): Promise<MfaChallengeInfo> {
|
|
@@ -714,19 +714,19 @@ export class MongoAuthRepository implements AuthRepository {
|
|
|
714
714
|
async verifyMfaChallenge(challengeId: string): Promise<void> {
|
|
715
715
|
throw new Error("MFA is not implemented for MongoDB");
|
|
716
716
|
}
|
|
717
|
-
async createRecoveryCodes(
|
|
717
|
+
async createRecoveryCodes(uid: string, codeHashes: string[]): Promise<void> {
|
|
718
718
|
throw new Error("MFA is not implemented for MongoDB");
|
|
719
719
|
}
|
|
720
|
-
async useRecoveryCode(
|
|
720
|
+
async useRecoveryCode(uid: string, codeHash: string): Promise<boolean> {
|
|
721
721
|
return false;
|
|
722
722
|
}
|
|
723
|
-
async getUnusedRecoveryCodeCount(
|
|
723
|
+
async getUnusedRecoveryCodeCount(uid: string): Promise<number> {
|
|
724
724
|
return 0;
|
|
725
725
|
}
|
|
726
|
-
async deleteAllRecoveryCodes(
|
|
726
|
+
async deleteAllRecoveryCodes(uid: string): Promise<void> {
|
|
727
727
|
// No-op
|
|
728
728
|
}
|
|
729
|
-
async hasVerifiedMfaFactors(
|
|
729
|
+
async hasVerifiedMfaFactors(uid: string): Promise<boolean> {
|
|
730
730
|
return false;
|
|
731
731
|
}
|
|
732
732
|
}
|
|
@@ -781,7 +781,7 @@ export class AuthenticatedMongoDriver implements DataDriver {
|
|
|
781
781
|
|
|
782
782
|
listenCollection<M extends Record<string, any>>(props: ListenCollectionProps<M>): () => void {
|
|
783
783
|
const unsubscribe = this.delegate.listenCollection(props);
|
|
784
|
-
const authContext = {
|
|
784
|
+
const authContext = { uid: this.user.uid,
|
|
785
785
|
roles: this.user.roles ?? [] };
|
|
786
786
|
const subscriptions = this.delegate.getRealtimeService().getSubscriptions();
|
|
787
787
|
const lastEntry = Array.from(subscriptions.entries()).pop();
|
|
@@ -806,7 +806,7 @@ roles: this.user.roles ?? [] };
|
|
|
806
806
|
|
|
807
807
|
listenOne<M extends Record<string, any>>(props: ListenOneProps<M>): () => void {
|
|
808
808
|
const unsubscribe = this.delegate.listenOne(props);
|
|
809
|
-
const authContext = {
|
|
809
|
+
const authContext = { uid: this.user.uid,
|
|
810
810
|
roles: this.user.roles ?? [] };
|
|
811
811
|
const subscriptions = this.delegate.getRealtimeService().getSubscriptions();
|
|
812
812
|
const lastEntry = Array.from(subscriptions.entries()).pop();
|