@juspay/neurolink 9.31.1 → 9.32.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/CHANGELOG.md +12 -0
- package/dist/auth/AuthProviderFactory.d.ts +71 -0
- package/dist/auth/AuthProviderFactory.js +111 -0
- package/dist/auth/AuthProviderRegistry.d.ts +33 -0
- package/dist/auth/AuthProviderRegistry.js +190 -0
- package/dist/auth/RequestContext.d.ts +23 -0
- package/dist/auth/RequestContext.js +78 -0
- package/dist/auth/authContext.d.ts +198 -0
- package/dist/auth/authContext.js +314 -0
- package/dist/auth/errors.d.ts +63 -0
- package/dist/auth/errors.js +39 -0
- package/dist/auth/index.d.ts +20 -8
- package/dist/auth/index.js +35 -7
- package/dist/auth/middleware/AuthMiddleware.d.ts +181 -0
- package/dist/auth/middleware/AuthMiddleware.js +519 -0
- package/dist/auth/middleware/rateLimitByUser.d.ts +282 -0
- package/dist/auth/middleware/rateLimitByUser.js +554 -0
- package/dist/auth/providers/BaseAuthProvider.d.ts +259 -0
- package/dist/auth/providers/BaseAuthProvider.js +723 -0
- package/dist/auth/providers/CognitoProvider.d.ts +61 -0
- package/dist/auth/providers/CognitoProvider.js +304 -0
- package/dist/auth/providers/KeycloakProvider.d.ts +61 -0
- package/dist/auth/providers/KeycloakProvider.js +393 -0
- package/dist/auth/providers/auth0.d.ts +59 -0
- package/dist/auth/providers/auth0.js +274 -0
- package/dist/auth/providers/betterAuth.d.ts +51 -0
- package/dist/auth/providers/betterAuth.js +182 -0
- package/dist/auth/providers/clerk.d.ts +65 -0
- package/dist/auth/providers/clerk.js +317 -0
- package/dist/auth/providers/custom.d.ts +64 -0
- package/dist/auth/providers/custom.js +112 -0
- package/dist/auth/providers/firebase.d.ts +63 -0
- package/dist/auth/providers/firebase.js +226 -0
- package/dist/auth/providers/jwt.d.ts +68 -0
- package/dist/auth/providers/jwt.js +212 -0
- package/dist/auth/providers/oauth2.d.ts +73 -0
- package/dist/auth/providers/oauth2.js +303 -0
- package/dist/auth/providers/supabase.d.ts +63 -0
- package/dist/auth/providers/supabase.js +259 -0
- package/dist/auth/providers/workos.d.ts +61 -0
- package/dist/auth/providers/workos.js +284 -0
- package/dist/auth/serverBridge.d.ts +14 -0
- package/dist/auth/serverBridge.js +25 -0
- package/dist/auth/sessionManager.d.ts +142 -0
- package/dist/auth/sessionManager.js +437 -0
- package/dist/cli/commands/authProviders.d.ts +43 -0
- package/dist/cli/commands/authProviders.js +399 -0
- package/dist/cli/commands/proxy.js +4 -2
- package/dist/cli/factories/authCommandFactory.d.ts +23 -5
- package/dist/cli/factories/authCommandFactory.js +108 -5
- package/dist/cli/parser.js +1 -1
- package/dist/client/auth/AuthProviderFactory.js +111 -0
- package/dist/client/auth/AuthProviderRegistry.js +190 -0
- package/dist/client/auth/RequestContext.js +78 -0
- package/dist/client/auth/accountPool.js +178 -0
- package/dist/client/auth/authContext.js +314 -0
- package/dist/client/auth/errors.js +39 -0
- package/dist/client/auth/index.js +61 -0
- package/dist/client/auth/middleware/AuthMiddleware.js +519 -0
- package/dist/client/auth/middleware/rateLimitByUser.js +554 -0
- package/dist/client/auth/providers/BaseAuthProvider.js +723 -0
- package/dist/client/auth/providers/CognitoProvider.js +304 -0
- package/dist/client/auth/providers/KeycloakProvider.js +393 -0
- package/dist/client/auth/providers/auth0.js +274 -0
- package/dist/client/auth/providers/betterAuth.js +182 -0
- package/dist/client/auth/providers/clerk.js +317 -0
- package/dist/client/auth/providers/custom.js +112 -0
- package/dist/client/auth/providers/firebase.js +226 -0
- package/dist/client/auth/providers/jwt.js +212 -0
- package/dist/client/auth/providers/oauth2.js +303 -0
- package/dist/client/auth/providers/supabase.js +259 -0
- package/dist/client/auth/providers/workos.js +284 -0
- package/dist/client/auth/serverBridge.js +25 -0
- package/dist/client/auth/sessionManager.js +437 -0
- package/dist/client/core/infrastructure/baseRegistry.js +5 -1
- package/dist/client/index.js +25 -0
- package/dist/client/mcp/toolRegistry.js +11 -1
- package/dist/client/neurolink.js +218 -0
- package/dist/client/rag/ChunkerRegistry.js +2 -2
- package/dist/client/rag/metadata/MetadataExtractorRegistry.js +2 -2
- package/dist/client/rag/reranker/RerankerRegistry.js +2 -2
- package/dist/client/server/routes/agentRoutes.js +20 -2
- package/dist/client/types/authTypes.js +2 -1
- package/dist/core/infrastructure/baseRegistry.d.ts +3 -1
- package/dist/core/infrastructure/baseRegistry.js +5 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +25 -0
- package/dist/lib/auth/AuthProviderFactory.d.ts +71 -0
- package/dist/lib/auth/AuthProviderFactory.js +112 -0
- package/dist/lib/auth/AuthProviderRegistry.d.ts +33 -0
- package/dist/lib/auth/AuthProviderRegistry.js +191 -0
- package/dist/lib/auth/RequestContext.d.ts +23 -0
- package/dist/lib/auth/RequestContext.js +79 -0
- package/dist/lib/auth/authContext.d.ts +198 -0
- package/dist/lib/auth/authContext.js +315 -0
- package/dist/lib/auth/errors.d.ts +63 -0
- package/dist/lib/auth/errors.js +40 -0
- package/dist/lib/auth/index.d.ts +20 -8
- package/dist/lib/auth/index.js +35 -7
- package/dist/lib/auth/middleware/AuthMiddleware.d.ts +181 -0
- package/dist/lib/auth/middleware/AuthMiddleware.js +520 -0
- package/dist/lib/auth/middleware/rateLimitByUser.d.ts +282 -0
- package/dist/lib/auth/middleware/rateLimitByUser.js +555 -0
- package/dist/lib/auth/providers/BaseAuthProvider.d.ts +259 -0
- package/dist/lib/auth/providers/BaseAuthProvider.js +724 -0
- package/dist/lib/auth/providers/CognitoProvider.d.ts +61 -0
- package/dist/lib/auth/providers/CognitoProvider.js +305 -0
- package/dist/lib/auth/providers/KeycloakProvider.d.ts +61 -0
- package/dist/lib/auth/providers/KeycloakProvider.js +394 -0
- package/dist/lib/auth/providers/auth0.d.ts +59 -0
- package/dist/lib/auth/providers/auth0.js +275 -0
- package/dist/lib/auth/providers/betterAuth.d.ts +51 -0
- package/dist/lib/auth/providers/betterAuth.js +183 -0
- package/dist/lib/auth/providers/clerk.d.ts +65 -0
- package/dist/lib/auth/providers/clerk.js +318 -0
- package/dist/lib/auth/providers/custom.d.ts +64 -0
- package/dist/lib/auth/providers/custom.js +113 -0
- package/dist/lib/auth/providers/firebase.d.ts +63 -0
- package/dist/lib/auth/providers/firebase.js +227 -0
- package/dist/lib/auth/providers/jwt.d.ts +68 -0
- package/dist/lib/auth/providers/jwt.js +213 -0
- package/dist/lib/auth/providers/oauth2.d.ts +73 -0
- package/dist/lib/auth/providers/oauth2.js +304 -0
- package/dist/lib/auth/providers/supabase.d.ts +63 -0
- package/dist/lib/auth/providers/supabase.js +260 -0
- package/dist/lib/auth/providers/workos.d.ts +61 -0
- package/dist/lib/auth/providers/workos.js +285 -0
- package/dist/lib/auth/serverBridge.d.ts +14 -0
- package/dist/lib/auth/serverBridge.js +26 -0
- package/dist/lib/auth/sessionManager.d.ts +142 -0
- package/dist/lib/auth/sessionManager.js +438 -0
- package/dist/lib/core/infrastructure/baseRegistry.d.ts +3 -1
- package/dist/lib/core/infrastructure/baseRegistry.js +5 -1
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.js +25 -0
- package/dist/lib/mcp/toolRegistry.js +11 -1
- package/dist/lib/neurolink.d.ts +42 -1
- package/dist/lib/neurolink.js +218 -0
- package/dist/lib/rag/ChunkerRegistry.js +2 -2
- package/dist/lib/rag/metadata/MetadataExtractorRegistry.js +2 -2
- package/dist/lib/rag/reranker/RerankerRegistry.js +2 -2
- package/dist/lib/server/routes/agentRoutes.js +20 -2
- package/dist/lib/types/authTypes.d.ts +937 -1
- package/dist/lib/types/authTypes.js +2 -1
- package/dist/lib/types/configTypes.d.ts +46 -0
- package/dist/lib/types/generateTypes.d.ts +6 -0
- package/dist/lib/types/index.d.ts +1 -0
- package/dist/lib/types/streamTypes.d.ts +6 -0
- package/dist/mcp/toolRegistry.js +11 -1
- package/dist/neurolink.d.ts +42 -1
- package/dist/neurolink.js +218 -0
- package/dist/rag/ChunkerRegistry.js +2 -2
- package/dist/rag/metadata/MetadataExtractorRegistry.js +2 -2
- package/dist/rag/reranker/RerankerRegistry.js +2 -2
- package/dist/server/routes/agentRoutes.js +20 -2
- package/dist/types/authTypes.d.ts +937 -1
- package/dist/types/authTypes.js +2 -1
- package/dist/types/configTypes.d.ts +46 -0
- package/dist/types/generateTypes.d.ts +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/streamTypes.d.ts +6 -0
- package/package.json +2 -1
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
// src/lib/auth/sessionManager.ts
|
|
2
|
+
import { logger } from "../utils/logger.js";
|
|
3
|
+
/** Mask an identifier for safe logging: show first 4 chars + "***" */
|
|
4
|
+
function maskId(id) {
|
|
5
|
+
if (id.length <= 4) {
|
|
6
|
+
return "***";
|
|
7
|
+
}
|
|
8
|
+
return `${id.slice(0, 4)}***`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* In-memory session storage
|
|
12
|
+
*
|
|
13
|
+
* Simple session storage using Map. Suitable for single-instance deployments
|
|
14
|
+
* or development. Sessions are lost on restart.
|
|
15
|
+
*/
|
|
16
|
+
export class MemorySessionStorage {
|
|
17
|
+
sessions = new Map();
|
|
18
|
+
userSessions = new Map();
|
|
19
|
+
async get(sessionId) {
|
|
20
|
+
const session = this.sessions.get(sessionId);
|
|
21
|
+
if (!session) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
// Check expiration
|
|
25
|
+
if (session.expiresAt && new Date() > session.expiresAt) {
|
|
26
|
+
await this.delete(sessionId);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return session;
|
|
30
|
+
}
|
|
31
|
+
async set(session) {
|
|
32
|
+
this.sessions.set(session.id, session);
|
|
33
|
+
// Track user's sessions
|
|
34
|
+
if (!this.userSessions.has(session.user.id)) {
|
|
35
|
+
this.userSessions.set(session.user.id, new Set());
|
|
36
|
+
}
|
|
37
|
+
this.userSessions.get(session.user.id).add(session.id);
|
|
38
|
+
}
|
|
39
|
+
async delete(sessionId) {
|
|
40
|
+
const session = this.sessions.get(sessionId);
|
|
41
|
+
if (session) {
|
|
42
|
+
const userSessionSet = this.userSessions.get(session.user.id);
|
|
43
|
+
if (userSessionSet) {
|
|
44
|
+
userSessionSet.delete(sessionId);
|
|
45
|
+
if (userSessionSet.size === 0) {
|
|
46
|
+
this.userSessions.delete(session.user.id);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
this.sessions.delete(sessionId);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async getUserSessions(userId) {
|
|
53
|
+
const sessionIds = this.userSessions.get(userId);
|
|
54
|
+
if (!sessionIds) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const sessions = [];
|
|
58
|
+
for (const sessionId of sessionIds) {
|
|
59
|
+
const session = await this.get(sessionId);
|
|
60
|
+
if (session) {
|
|
61
|
+
sessions.push(session);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return sessions;
|
|
65
|
+
}
|
|
66
|
+
async deleteUserSessions(userId) {
|
|
67
|
+
const sessionIds = this.userSessions.get(userId);
|
|
68
|
+
if (sessionIds) {
|
|
69
|
+
for (const sessionId of sessionIds) {
|
|
70
|
+
this.sessions.delete(sessionId);
|
|
71
|
+
}
|
|
72
|
+
this.userSessions.delete(userId);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async clear() {
|
|
76
|
+
this.sessions.clear();
|
|
77
|
+
this.userSessions.clear();
|
|
78
|
+
}
|
|
79
|
+
async isHealthy() {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Redis session storage
|
|
85
|
+
*
|
|
86
|
+
* Distributed session storage using Redis. Suitable for multi-instance
|
|
87
|
+
* deployments. Requires ioredis or similar Redis client.
|
|
88
|
+
*
|
|
89
|
+
* Note: Redis client must be provided or configured via environment.
|
|
90
|
+
*/
|
|
91
|
+
export class RedisSessionStorage {
|
|
92
|
+
prefix;
|
|
93
|
+
ttl;
|
|
94
|
+
redisUrl;
|
|
95
|
+
client = null;
|
|
96
|
+
initPromise = null;
|
|
97
|
+
constructor(config) {
|
|
98
|
+
this.redisUrl = config.url;
|
|
99
|
+
this.prefix = config.prefix || "neurolink:sessions:";
|
|
100
|
+
this.ttl = config.ttl || 3600;
|
|
101
|
+
}
|
|
102
|
+
async getClient() {
|
|
103
|
+
if (this.client) {
|
|
104
|
+
return this.client;
|
|
105
|
+
}
|
|
106
|
+
if (!this.initPromise) {
|
|
107
|
+
this.initPromise = this.createClient();
|
|
108
|
+
}
|
|
109
|
+
return this.initPromise;
|
|
110
|
+
}
|
|
111
|
+
async createClient() {
|
|
112
|
+
try {
|
|
113
|
+
// Use variable indirection to prevent TypeScript from resolving the module at compile time
|
|
114
|
+
const moduleName = "ioredis";
|
|
115
|
+
const ioredis = await import(/* @vite-ignore */ moduleName);
|
|
116
|
+
const Redis = ioredis.default || ioredis.Redis;
|
|
117
|
+
this.client = new Redis(this.redisUrl);
|
|
118
|
+
return this.client;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
this.initPromise = null;
|
|
122
|
+
logger.error('Redis client (ioredis) not available. Install ioredis package and ensure Redis is reachable when using storage: "redis".');
|
|
123
|
+
throw new Error("Redis client not available");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
sessionKey(sessionId) {
|
|
127
|
+
return `${this.prefix}${sessionId}`;
|
|
128
|
+
}
|
|
129
|
+
userSessionsKey(userId) {
|
|
130
|
+
return `${this.prefix}user:${userId}`;
|
|
131
|
+
}
|
|
132
|
+
async get(sessionId) {
|
|
133
|
+
try {
|
|
134
|
+
const client = await this.getClient();
|
|
135
|
+
const data = await client.get(this.sessionKey(sessionId));
|
|
136
|
+
if (!data) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const session = JSON.parse(data);
|
|
140
|
+
// Parse dates
|
|
141
|
+
session.createdAt = new Date(session.createdAt);
|
|
142
|
+
if (session.expiresAt !== null && session.expiresAt !== undefined) {
|
|
143
|
+
session.expiresAt = new Date(session.expiresAt);
|
|
144
|
+
}
|
|
145
|
+
// Check expiration
|
|
146
|
+
if (new Date() > session.expiresAt) {
|
|
147
|
+
await this.delete(sessionId);
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
return session;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
logger.error("Redis session get error:", error);
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async set(session) {
|
|
158
|
+
try {
|
|
159
|
+
const client = await this.getClient();
|
|
160
|
+
// Calculate TTL from session expiration
|
|
161
|
+
const ttlSeconds = session.expiresAt
|
|
162
|
+
? Math.max(1, Math.floor((session.expiresAt.getTime() - Date.now()) / 1000))
|
|
163
|
+
: this.ttl;
|
|
164
|
+
// Store session
|
|
165
|
+
await client.setex(this.sessionKey(session.id), ttlSeconds, JSON.stringify(session));
|
|
166
|
+
// Track user's sessions
|
|
167
|
+
await client.sadd(this.userSessionsKey(session.user.id), session.id);
|
|
168
|
+
await client.expire(this.userSessionsKey(session.user.id), this.ttl);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
logger.error("Redis session set error:", error);
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
async delete(sessionId) {
|
|
176
|
+
try {
|
|
177
|
+
const client = await this.getClient();
|
|
178
|
+
// Read the raw session data directly instead of calling this.get(),
|
|
179
|
+
// which checks expiration and calls this.delete() — causing infinite
|
|
180
|
+
// recursion for expired sessions.
|
|
181
|
+
const data = await client.get(this.sessionKey(sessionId));
|
|
182
|
+
if (data) {
|
|
183
|
+
try {
|
|
184
|
+
const session = JSON.parse(data);
|
|
185
|
+
await client.srem(this.userSessionsKey(session.user.id), sessionId);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// If parsing fails, we still delete the key below
|
|
189
|
+
logger.warn(`Failed to parse session data for cleanup: ${maskId(sessionId)}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
await client.del(this.sessionKey(sessionId));
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
logger.error("Redis session delete error:", error);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async getUserSessions(userId) {
|
|
199
|
+
try {
|
|
200
|
+
const client = await this.getClient();
|
|
201
|
+
const sessionIds = await client.smembers(this.userSessionsKey(userId));
|
|
202
|
+
const sessions = [];
|
|
203
|
+
for (const sessionId of sessionIds) {
|
|
204
|
+
const session = await this.get(sessionId);
|
|
205
|
+
if (session) {
|
|
206
|
+
sessions.push(session);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return sessions;
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
logger.error("Redis getUserSessions error:", error);
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
async deleteUserSessions(userId) {
|
|
217
|
+
try {
|
|
218
|
+
const client = await this.getClient();
|
|
219
|
+
const sessionIds = await client.smembers(this.userSessionsKey(userId));
|
|
220
|
+
for (const sessionId of sessionIds) {
|
|
221
|
+
await client.del(this.sessionKey(sessionId));
|
|
222
|
+
}
|
|
223
|
+
await client.del(this.userSessionsKey(userId));
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
logger.error("Redis deleteUserSessions error:", error);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async clear() {
|
|
230
|
+
try {
|
|
231
|
+
const client = await this.getClient();
|
|
232
|
+
// Use SCAN instead of KEYS to avoid blocking Redis in production
|
|
233
|
+
let cursor = "0";
|
|
234
|
+
do {
|
|
235
|
+
const [nextCursor, keys] = await client.scan(cursor, "MATCH", `${this.prefix}*`, "COUNT", "100");
|
|
236
|
+
cursor = nextCursor;
|
|
237
|
+
if (keys.length > 0) {
|
|
238
|
+
await client.del(...keys);
|
|
239
|
+
}
|
|
240
|
+
} while (cursor !== "0");
|
|
241
|
+
}
|
|
242
|
+
catch (error) {
|
|
243
|
+
logger.error("Redis clear error:", error);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async isHealthy() {
|
|
247
|
+
try {
|
|
248
|
+
const client = await this.getClient();
|
|
249
|
+
const pong = await client.ping();
|
|
250
|
+
return pong === "PONG";
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
async disconnect() {
|
|
257
|
+
if (this.client) {
|
|
258
|
+
await this.client.quit();
|
|
259
|
+
this.client = null;
|
|
260
|
+
this.initPromise = null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Session Manager
|
|
266
|
+
*
|
|
267
|
+
* High-level session management that handles session lifecycle,
|
|
268
|
+
* automatic refresh, and storage abstraction.
|
|
269
|
+
*/
|
|
270
|
+
export class SessionManager {
|
|
271
|
+
storage;
|
|
272
|
+
config;
|
|
273
|
+
constructor(config = {}) {
|
|
274
|
+
this.config = {
|
|
275
|
+
...config,
|
|
276
|
+
duration: config.duration || 3600,
|
|
277
|
+
autoRefresh: config.autoRefresh ?? true,
|
|
278
|
+
refreshThreshold: config.refreshThreshold || 300,
|
|
279
|
+
storage: config.storage || "memory",
|
|
280
|
+
};
|
|
281
|
+
// Initialize storage based on config
|
|
282
|
+
this.storage = this.createStorage();
|
|
283
|
+
}
|
|
284
|
+
createStorage() {
|
|
285
|
+
switch (this.config.storage) {
|
|
286
|
+
case "redis":
|
|
287
|
+
if (!this.config.redis?.url) {
|
|
288
|
+
logger.warn("Redis URL not provided, falling back to memory storage");
|
|
289
|
+
return new MemorySessionStorage();
|
|
290
|
+
}
|
|
291
|
+
return new RedisSessionStorage({
|
|
292
|
+
url: this.config.redis.url,
|
|
293
|
+
prefix: this.config.redis.prefix,
|
|
294
|
+
ttl: this.config.redis.ttl || this.config.duration,
|
|
295
|
+
});
|
|
296
|
+
case "memory":
|
|
297
|
+
default:
|
|
298
|
+
return new MemorySessionStorage();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Create a new session
|
|
303
|
+
*/
|
|
304
|
+
async createSession(user, metadata) {
|
|
305
|
+
const sessionId = crypto.randomUUID();
|
|
306
|
+
const now = new Date();
|
|
307
|
+
const duration = this.config.duration || 3600;
|
|
308
|
+
const session = {
|
|
309
|
+
id: sessionId,
|
|
310
|
+
user,
|
|
311
|
+
createdAt: now,
|
|
312
|
+
expiresAt: new Date(now.getTime() + duration * 1000),
|
|
313
|
+
isValid: true,
|
|
314
|
+
ipAddress: metadata?.ipAddress,
|
|
315
|
+
userAgent: metadata?.userAgent,
|
|
316
|
+
deviceId: metadata?.deviceId,
|
|
317
|
+
};
|
|
318
|
+
await this.storage.set(session);
|
|
319
|
+
logger.debug(`Session created: ${maskId(sessionId)} for user: ${maskId(user.id)}`);
|
|
320
|
+
return session;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Get a session by ID
|
|
324
|
+
*
|
|
325
|
+
* Optionally auto-refreshes if close to expiration.
|
|
326
|
+
*/
|
|
327
|
+
async getSession(sessionId, autoRefresh = this.config.autoRefresh) {
|
|
328
|
+
const session = await this.storage.get(sessionId);
|
|
329
|
+
if (!session) {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
// Auto-refresh if close to expiration
|
|
333
|
+
if (autoRefresh && this.shouldRefresh(session)) {
|
|
334
|
+
return this.refreshSession(sessionId);
|
|
335
|
+
}
|
|
336
|
+
return session;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Check if session should be refreshed
|
|
340
|
+
*/
|
|
341
|
+
shouldRefresh(session) {
|
|
342
|
+
if (!session.expiresAt) {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
const threshold = this.config.refreshThreshold || 300;
|
|
346
|
+
const timeUntilExpiry = (session.expiresAt.getTime() - Date.now()) / 1000;
|
|
347
|
+
return timeUntilExpiry < threshold;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Refresh a session
|
|
351
|
+
*/
|
|
352
|
+
async refreshSession(sessionId) {
|
|
353
|
+
const session = await this.storage.get(sessionId);
|
|
354
|
+
if (!session) {
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
const duration = this.config.duration || 3600;
|
|
358
|
+
session.expiresAt = new Date(Date.now() + duration * 1000);
|
|
359
|
+
await this.storage.set(session);
|
|
360
|
+
logger.debug(`Session refreshed: ${maskId(sessionId)}`);
|
|
361
|
+
return session;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Destroy a session
|
|
365
|
+
*/
|
|
366
|
+
async destroySession(sessionId) {
|
|
367
|
+
await this.storage.delete(sessionId);
|
|
368
|
+
logger.debug(`Session destroyed: ${maskId(sessionId)}`);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Get all sessions for a user
|
|
372
|
+
*/
|
|
373
|
+
async getUserSessions(userId) {
|
|
374
|
+
return this.storage.getUserSessions(userId);
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Destroy all sessions for a user (global logout)
|
|
378
|
+
*/
|
|
379
|
+
async destroyAllUserSessions(userId) {
|
|
380
|
+
await this.storage.deleteUserSessions(userId);
|
|
381
|
+
logger.debug(`All sessions destroyed for user: ${maskId(userId)}`);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Validate a session is still active
|
|
385
|
+
*/
|
|
386
|
+
async validateSession(sessionId) {
|
|
387
|
+
const session = await this.storage.get(sessionId);
|
|
388
|
+
return session !== null && session.isValid;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Update session metadata
|
|
392
|
+
*/
|
|
393
|
+
async updateSessionMetadata(sessionId, metadata) {
|
|
394
|
+
const session = await this.storage.get(sessionId);
|
|
395
|
+
if (!session) {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
session.metadata = {
|
|
399
|
+
...session.metadata,
|
|
400
|
+
...metadata,
|
|
401
|
+
};
|
|
402
|
+
await this.storage.set(session);
|
|
403
|
+
return session;
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Health check
|
|
407
|
+
*/
|
|
408
|
+
async isHealthy() {
|
|
409
|
+
return this.storage.isHealthy();
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Clear all sessions (for testing/cleanup)
|
|
413
|
+
*/
|
|
414
|
+
async clear() {
|
|
415
|
+
await this.storage.clear();
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Create session storage based on configuration
|
|
420
|
+
*/
|
|
421
|
+
export function createSessionStorage(config) {
|
|
422
|
+
switch (config.storage) {
|
|
423
|
+
case "redis":
|
|
424
|
+
if (!config.redis?.url) {
|
|
425
|
+
logger.warn("Redis URL not provided, falling back to memory storage");
|
|
426
|
+
return new MemorySessionStorage();
|
|
427
|
+
}
|
|
428
|
+
return new RedisSessionStorage({
|
|
429
|
+
url: config.redis.url,
|
|
430
|
+
prefix: config.redis.prefix,
|
|
431
|
+
ttl: config.redis.ttl || config.duration,
|
|
432
|
+
});
|
|
433
|
+
case "memory":
|
|
434
|
+
default:
|
|
435
|
+
return new MemorySessionStorage();
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
//# sourceMappingURL=sessionManager.js.map
|
|
@@ -10,7 +10,9 @@ export declare abstract class BaseRegistry<TItem, TMetadata = unknown> {
|
|
|
10
10
|
protected initPromise: Promise<void> | null;
|
|
11
11
|
protected abstract registerAll(): Promise<void>;
|
|
12
12
|
ensureInitialized(): Promise<void>;
|
|
13
|
-
register(id: string, factory: () => Promise<TItem>,
|
|
13
|
+
register(id: string, factory: () => Promise<TItem>, aliases?: string[], options?: {
|
|
14
|
+
metadata: TMetadata;
|
|
15
|
+
}): void;
|
|
14
16
|
get(id: string): Promise<TItem | undefined>;
|
|
15
17
|
has(id: string): boolean;
|
|
16
18
|
list(): Array<{
|
|
@@ -14,8 +14,12 @@ export class BaseRegistry {
|
|
|
14
14
|
await this.initPromise;
|
|
15
15
|
this.initialized = true;
|
|
16
16
|
}
|
|
17
|
-
register(id, factory,
|
|
17
|
+
register(id, factory, aliases = [], options) {
|
|
18
|
+
const metadata = options?.metadata ?? {};
|
|
18
19
|
this.items.set(id, { factory, metadata });
|
|
20
|
+
for (const alias of aliases) {
|
|
21
|
+
this.items.set(alias.toLowerCase(), { factory, metadata });
|
|
22
|
+
}
|
|
19
23
|
logger.debug(`Registered ${id} in registry`);
|
|
20
24
|
}
|
|
21
25
|
async get(id) {
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -374,3 +374,4 @@ export { RAGASEvaluator } from "./evaluation/ragasEvaluator.js";
|
|
|
374
374
|
export { RetryManager } from "./evaluation/retryManager.js";
|
|
375
375
|
export { mapToEvaluationData } from "./evaluation/scoring.js";
|
|
376
376
|
export type { EnhancedConversationTurn, EnhancedEvaluationContext, EvaluationConfig, EvaluationResult, GetPromptFunction, QualityErrorDetails, QueryIntentAnalysis, } from "./types/evaluationTypes.js";
|
|
377
|
+
export { AuthProviderFactory, createAuthProvider, AuthProviderRegistry, AuthError as AuthErrorFactory, AuthErrorCodes, BaseAuthProvider, InMemorySessionStorage, AuthProviderError, createAuthMiddleware as createAuthProviderMiddleware, createRBACMiddleware, createProtectedMiddleware, createExpressAuthMiddleware, createRequestContext, extractToken, AuthMiddlewareError, AuthMiddlewareErrorCodes, type MiddlewareHandler as AuthMiddlewareHandler, type MiddlewareResult, type NextFunction, type ExpressMiddleware, UserRateLimiter, MemoryRateLimitStorage, RedisRateLimitStorage, createRateLimitByUserMiddleware, createAuthenticatedRateLimitMiddleware, createRateLimitStorage, type RateLimitConfig as AuthRateLimitConfig, type RateLimitResult, type RateLimitMiddlewareResult, type RateLimitStorage, SessionManager, MemorySessionStorage, RedisSessionStorage, createSessionStorage, type SessionStorageInterface, AuthContextHolder, globalAuthContext, getAuthContext, getCurrentUser, getCurrentSession, isAuthenticated, hasRole, hasAnyRole, hasPermission, hasAllPermissions, requireAuth, requireRole, requirePermission, requireUser, runWithAuthContext, createAuthenticatedContext, RequestContext, NEUROLINK_RESOURCE_ID_KEY, NEUROLINK_THREAD_ID_KEY, createAuthValidatorFromProvider, } from "./auth/index.js";
|
package/dist/lib/index.js
CHANGED
|
@@ -509,4 +509,29 @@ export { PromptBuilder } from "./evaluation/prompts.js";
|
|
|
509
509
|
export { RAGASEvaluator } from "./evaluation/ragasEvaluator.js";
|
|
510
510
|
export { RetryManager } from "./evaluation/retryManager.js";
|
|
511
511
|
export { mapToEvaluationData } from "./evaluation/scoring.js";
|
|
512
|
+
// ============================================================================
|
|
513
|
+
// AUTHENTICATION PROVIDERS - Multi-provider Auth Integration
|
|
514
|
+
// ============================================================================
|
|
515
|
+
// Single-sourced from ./auth/index.js. Only aliases that differ from the
|
|
516
|
+
// canonical export name are listed explicitly; everything else is re-exported
|
|
517
|
+
// as-is.
|
|
518
|
+
export {
|
|
519
|
+
// Factory & Registry
|
|
520
|
+
AuthProviderFactory, createAuthProvider, AuthProviderRegistry,
|
|
521
|
+
// Unified error factory
|
|
522
|
+
AuthError as AuthErrorFactory, AuthErrorCodes,
|
|
523
|
+
// Base Provider
|
|
524
|
+
BaseAuthProvider, InMemorySessionStorage, AuthProviderError,
|
|
525
|
+
// Auth Middleware (aliased to avoid conflict with server createAuthMiddleware)
|
|
526
|
+
createAuthMiddleware as createAuthProviderMiddleware, createRBACMiddleware, createProtectedMiddleware, createExpressAuthMiddleware, createRequestContext, extractToken, AuthMiddlewareError, AuthMiddlewareErrorCodes,
|
|
527
|
+
// Rate Limiting Middleware
|
|
528
|
+
UserRateLimiter, MemoryRateLimitStorage, RedisRateLimitStorage, createRateLimitByUserMiddleware, createAuthenticatedRateLimitMiddleware, createRateLimitStorage,
|
|
529
|
+
// Session Management
|
|
530
|
+
SessionManager, MemorySessionStorage, RedisSessionStorage, createSessionStorage,
|
|
531
|
+
// Auth Context
|
|
532
|
+
AuthContextHolder, globalAuthContext, getAuthContext, getCurrentUser, getCurrentSession, isAuthenticated, hasRole, hasAnyRole, hasPermission, hasAllPermissions, requireAuth, requireRole, requirePermission, requireUser, runWithAuthContext, createAuthenticatedContext,
|
|
533
|
+
// Request Context
|
|
534
|
+
RequestContext, NEUROLINK_RESOURCE_ID_KEY, NEUROLINK_THREAD_ID_KEY,
|
|
535
|
+
// Server Bridge
|
|
536
|
+
createAuthValidatorFromProvider, } from "./auth/index.js";
|
|
512
537
|
//# sourceMappingURL=index.js.map
|
|
@@ -11,6 +11,7 @@ import { detectCategory, createMCPServerInfo } from "../utils/mcpDefaults.js";
|
|
|
11
11
|
import { FlexibleToolValidator } from "./flexibleToolValidator.js";
|
|
12
12
|
import { HITLUserRejectedError, HITLTimeoutError } from "../hitl/hitlErrors.js";
|
|
13
13
|
import { withSpan, tracers, ATTR } from "../telemetry/index.js";
|
|
14
|
+
import { getAuthContext } from "../auth/authContext.js";
|
|
14
15
|
export class MCPToolRegistry extends MCPRegistry {
|
|
15
16
|
tools = new Map();
|
|
16
17
|
toolImplementations = new Map(); // Store actual tool implementations
|
|
@@ -290,11 +291,20 @@ export class MCPToolRegistry extends MCPRegistry {
|
|
|
290
291
|
: "mcp";
|
|
291
292
|
span.setAttribute("tool.type", toolType);
|
|
292
293
|
span.setAttribute(ATTR.MCP_SERVER_ID, serverId);
|
|
294
|
+
// Try to get auth context if available
|
|
295
|
+
let authUserId;
|
|
296
|
+
try {
|
|
297
|
+
const authCtx = getAuthContext();
|
|
298
|
+
authUserId = authCtx?.user?.id;
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// Auth context not available — that's fine
|
|
302
|
+
}
|
|
293
303
|
// Create execution context if not provided
|
|
294
304
|
const execContext = {
|
|
295
305
|
...context,
|
|
296
306
|
sessionId: context?.sessionId ?? randomUUID(),
|
|
297
|
-
userId: context?.userId,
|
|
307
|
+
userId: context?.userId ?? authUserId,
|
|
298
308
|
};
|
|
299
309
|
// Get the tool implementation using the resolved toolId
|
|
300
310
|
const toolImpl = this.toolImplementations.get(toolId);
|
package/dist/lib/neurolink.d.ts
CHANGED
|
@@ -14,7 +14,8 @@ import { MCPToolRegistry } from "./mcp/toolRegistry.js";
|
|
|
14
14
|
import type { MetricsSummary, TraceView } from "./observability/metricsAggregator.js";
|
|
15
15
|
import type { SpanData } from "./observability/types/spanTypes.js";
|
|
16
16
|
import type { JsonObject, NeuroLinkEvents, TypedEventEmitter } from "./types/common.js";
|
|
17
|
-
import type {
|
|
17
|
+
import type { AuthenticatedContext, MastraAuthProvider } from "./types/authTypes.js";
|
|
18
|
+
import type { MCPEnhancementsConfig, NeuroLinkAuthConfig, NeurolinkConstructorConfig } from "./types/configTypes.js";
|
|
18
19
|
import type { ChatMessage } from "./types/conversation.js";
|
|
19
20
|
import type { ExternalMCPOperationResult, ExternalMCPServerInstance, ExternalMCPToolInfo } from "./types/externalMcp.js";
|
|
20
21
|
import type { GenerateOptions, GenerateResult } from "./types/generateTypes.js";
|
|
@@ -64,6 +65,9 @@ export declare class NeuroLink {
|
|
|
64
65
|
private conversationMemoryNeedsInit;
|
|
65
66
|
private conversationMemoryConfig?;
|
|
66
67
|
private enableOrchestration;
|
|
68
|
+
private authProvider?;
|
|
69
|
+
private pendingAuthConfig?;
|
|
70
|
+
private authInitPromise?;
|
|
67
71
|
private hitlManager?;
|
|
68
72
|
private _sessionCostUsd;
|
|
69
73
|
private fileRegistry;
|
|
@@ -1664,6 +1668,43 @@ export declare class NeuroLink {
|
|
|
1664
1668
|
* Check if a session needs compaction.
|
|
1665
1669
|
*/
|
|
1666
1670
|
needsCompaction(sessionId: string, provider?: string, model?: string): boolean;
|
|
1671
|
+
/**
|
|
1672
|
+
* Set the authentication provider for the NeuroLink instance
|
|
1673
|
+
*
|
|
1674
|
+
* @param config - Auth provider or configuration to create one
|
|
1675
|
+
*/
|
|
1676
|
+
setAuthProvider(config: NeuroLinkAuthConfig): Promise<void>;
|
|
1677
|
+
/**
|
|
1678
|
+
* Get the currently configured authentication provider
|
|
1679
|
+
*/
|
|
1680
|
+
getAuthProvider(): MastraAuthProvider | undefined;
|
|
1681
|
+
/**
|
|
1682
|
+
* Lazily initialize the auth provider from pendingAuthConfig.
|
|
1683
|
+
* Called on first use (generate/stream with auth token) to avoid
|
|
1684
|
+
* async work in the synchronous constructor.
|
|
1685
|
+
*/
|
|
1686
|
+
private ensureAuthProvider;
|
|
1687
|
+
/**
|
|
1688
|
+
* Set the current authentication context for request handling.
|
|
1689
|
+
*
|
|
1690
|
+
* Delegates to the global AuthContextHolder so that auth state is NOT
|
|
1691
|
+
* stored as an instance field (which would leak between concurrent requests
|
|
1692
|
+
* sharing the same NeuroLink singleton). Prefer `runWithAuthContext()` from
|
|
1693
|
+
* `authContext.ts` for proper request-scoped context via AsyncLocalStorage.
|
|
1694
|
+
*
|
|
1695
|
+
* @param context - The authenticated user context
|
|
1696
|
+
*/
|
|
1697
|
+
setAuthContext(context: AuthenticatedContext): Promise<void>;
|
|
1698
|
+
/**
|
|
1699
|
+
* Get the current authentication context.
|
|
1700
|
+
*
|
|
1701
|
+
* Checks AsyncLocalStorage first, then falls back to the global holder.
|
|
1702
|
+
*/
|
|
1703
|
+
getAuthContext(): Promise<AuthenticatedContext | undefined>;
|
|
1704
|
+
/**
|
|
1705
|
+
* Clear the current authentication context
|
|
1706
|
+
*/
|
|
1707
|
+
clearAuthContext(): Promise<void>;
|
|
1667
1708
|
/**
|
|
1668
1709
|
* Get the external server manager instance
|
|
1669
1710
|
* Used internally by server adapters for external MCP server management
|