@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.
Files changed (162) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/auth/AuthProviderFactory.d.ts +71 -0
  3. package/dist/auth/AuthProviderFactory.js +111 -0
  4. package/dist/auth/AuthProviderRegistry.d.ts +33 -0
  5. package/dist/auth/AuthProviderRegistry.js +190 -0
  6. package/dist/auth/RequestContext.d.ts +23 -0
  7. package/dist/auth/RequestContext.js +78 -0
  8. package/dist/auth/authContext.d.ts +198 -0
  9. package/dist/auth/authContext.js +314 -0
  10. package/dist/auth/errors.d.ts +63 -0
  11. package/dist/auth/errors.js +39 -0
  12. package/dist/auth/index.d.ts +20 -8
  13. package/dist/auth/index.js +35 -7
  14. package/dist/auth/middleware/AuthMiddleware.d.ts +181 -0
  15. package/dist/auth/middleware/AuthMiddleware.js +519 -0
  16. package/dist/auth/middleware/rateLimitByUser.d.ts +282 -0
  17. package/dist/auth/middleware/rateLimitByUser.js +554 -0
  18. package/dist/auth/providers/BaseAuthProvider.d.ts +259 -0
  19. package/dist/auth/providers/BaseAuthProvider.js +723 -0
  20. package/dist/auth/providers/CognitoProvider.d.ts +61 -0
  21. package/dist/auth/providers/CognitoProvider.js +304 -0
  22. package/dist/auth/providers/KeycloakProvider.d.ts +61 -0
  23. package/dist/auth/providers/KeycloakProvider.js +393 -0
  24. package/dist/auth/providers/auth0.d.ts +59 -0
  25. package/dist/auth/providers/auth0.js +274 -0
  26. package/dist/auth/providers/betterAuth.d.ts +51 -0
  27. package/dist/auth/providers/betterAuth.js +182 -0
  28. package/dist/auth/providers/clerk.d.ts +65 -0
  29. package/dist/auth/providers/clerk.js +317 -0
  30. package/dist/auth/providers/custom.d.ts +64 -0
  31. package/dist/auth/providers/custom.js +112 -0
  32. package/dist/auth/providers/firebase.d.ts +63 -0
  33. package/dist/auth/providers/firebase.js +226 -0
  34. package/dist/auth/providers/jwt.d.ts +68 -0
  35. package/dist/auth/providers/jwt.js +212 -0
  36. package/dist/auth/providers/oauth2.d.ts +73 -0
  37. package/dist/auth/providers/oauth2.js +303 -0
  38. package/dist/auth/providers/supabase.d.ts +63 -0
  39. package/dist/auth/providers/supabase.js +259 -0
  40. package/dist/auth/providers/workos.d.ts +61 -0
  41. package/dist/auth/providers/workos.js +284 -0
  42. package/dist/auth/serverBridge.d.ts +14 -0
  43. package/dist/auth/serverBridge.js +25 -0
  44. package/dist/auth/sessionManager.d.ts +142 -0
  45. package/dist/auth/sessionManager.js +437 -0
  46. package/dist/cli/commands/authProviders.d.ts +43 -0
  47. package/dist/cli/commands/authProviders.js +399 -0
  48. package/dist/cli/commands/proxy.js +4 -2
  49. package/dist/cli/factories/authCommandFactory.d.ts +23 -5
  50. package/dist/cli/factories/authCommandFactory.js +108 -5
  51. package/dist/cli/parser.js +1 -1
  52. package/dist/client/auth/AuthProviderFactory.js +111 -0
  53. package/dist/client/auth/AuthProviderRegistry.js +190 -0
  54. package/dist/client/auth/RequestContext.js +78 -0
  55. package/dist/client/auth/accountPool.js +178 -0
  56. package/dist/client/auth/authContext.js +314 -0
  57. package/dist/client/auth/errors.js +39 -0
  58. package/dist/client/auth/index.js +61 -0
  59. package/dist/client/auth/middleware/AuthMiddleware.js +519 -0
  60. package/dist/client/auth/middleware/rateLimitByUser.js +554 -0
  61. package/dist/client/auth/providers/BaseAuthProvider.js +723 -0
  62. package/dist/client/auth/providers/CognitoProvider.js +304 -0
  63. package/dist/client/auth/providers/KeycloakProvider.js +393 -0
  64. package/dist/client/auth/providers/auth0.js +274 -0
  65. package/dist/client/auth/providers/betterAuth.js +182 -0
  66. package/dist/client/auth/providers/clerk.js +317 -0
  67. package/dist/client/auth/providers/custom.js +112 -0
  68. package/dist/client/auth/providers/firebase.js +226 -0
  69. package/dist/client/auth/providers/jwt.js +212 -0
  70. package/dist/client/auth/providers/oauth2.js +303 -0
  71. package/dist/client/auth/providers/supabase.js +259 -0
  72. package/dist/client/auth/providers/workos.js +284 -0
  73. package/dist/client/auth/serverBridge.js +25 -0
  74. package/dist/client/auth/sessionManager.js +437 -0
  75. package/dist/client/core/infrastructure/baseRegistry.js +5 -1
  76. package/dist/client/index.js +25 -0
  77. package/dist/client/mcp/toolRegistry.js +11 -1
  78. package/dist/client/neurolink.js +218 -0
  79. package/dist/client/rag/ChunkerRegistry.js +2 -2
  80. package/dist/client/rag/metadata/MetadataExtractorRegistry.js +2 -2
  81. package/dist/client/rag/reranker/RerankerRegistry.js +2 -2
  82. package/dist/client/server/routes/agentRoutes.js +20 -2
  83. package/dist/client/types/authTypes.js +2 -1
  84. package/dist/core/infrastructure/baseRegistry.d.ts +3 -1
  85. package/dist/core/infrastructure/baseRegistry.js +5 -1
  86. package/dist/index.d.ts +1 -0
  87. package/dist/index.js +25 -0
  88. package/dist/lib/auth/AuthProviderFactory.d.ts +71 -0
  89. package/dist/lib/auth/AuthProviderFactory.js +112 -0
  90. package/dist/lib/auth/AuthProviderRegistry.d.ts +33 -0
  91. package/dist/lib/auth/AuthProviderRegistry.js +191 -0
  92. package/dist/lib/auth/RequestContext.d.ts +23 -0
  93. package/dist/lib/auth/RequestContext.js +79 -0
  94. package/dist/lib/auth/authContext.d.ts +198 -0
  95. package/dist/lib/auth/authContext.js +315 -0
  96. package/dist/lib/auth/errors.d.ts +63 -0
  97. package/dist/lib/auth/errors.js +40 -0
  98. package/dist/lib/auth/index.d.ts +20 -8
  99. package/dist/lib/auth/index.js +35 -7
  100. package/dist/lib/auth/middleware/AuthMiddleware.d.ts +181 -0
  101. package/dist/lib/auth/middleware/AuthMiddleware.js +520 -0
  102. package/dist/lib/auth/middleware/rateLimitByUser.d.ts +282 -0
  103. package/dist/lib/auth/middleware/rateLimitByUser.js +555 -0
  104. package/dist/lib/auth/providers/BaseAuthProvider.d.ts +259 -0
  105. package/dist/lib/auth/providers/BaseAuthProvider.js +724 -0
  106. package/dist/lib/auth/providers/CognitoProvider.d.ts +61 -0
  107. package/dist/lib/auth/providers/CognitoProvider.js +305 -0
  108. package/dist/lib/auth/providers/KeycloakProvider.d.ts +61 -0
  109. package/dist/lib/auth/providers/KeycloakProvider.js +394 -0
  110. package/dist/lib/auth/providers/auth0.d.ts +59 -0
  111. package/dist/lib/auth/providers/auth0.js +275 -0
  112. package/dist/lib/auth/providers/betterAuth.d.ts +51 -0
  113. package/dist/lib/auth/providers/betterAuth.js +183 -0
  114. package/dist/lib/auth/providers/clerk.d.ts +65 -0
  115. package/dist/lib/auth/providers/clerk.js +318 -0
  116. package/dist/lib/auth/providers/custom.d.ts +64 -0
  117. package/dist/lib/auth/providers/custom.js +113 -0
  118. package/dist/lib/auth/providers/firebase.d.ts +63 -0
  119. package/dist/lib/auth/providers/firebase.js +227 -0
  120. package/dist/lib/auth/providers/jwt.d.ts +68 -0
  121. package/dist/lib/auth/providers/jwt.js +213 -0
  122. package/dist/lib/auth/providers/oauth2.d.ts +73 -0
  123. package/dist/lib/auth/providers/oauth2.js +304 -0
  124. package/dist/lib/auth/providers/supabase.d.ts +63 -0
  125. package/dist/lib/auth/providers/supabase.js +260 -0
  126. package/dist/lib/auth/providers/workos.d.ts +61 -0
  127. package/dist/lib/auth/providers/workos.js +285 -0
  128. package/dist/lib/auth/serverBridge.d.ts +14 -0
  129. package/dist/lib/auth/serverBridge.js +26 -0
  130. package/dist/lib/auth/sessionManager.d.ts +142 -0
  131. package/dist/lib/auth/sessionManager.js +438 -0
  132. package/dist/lib/core/infrastructure/baseRegistry.d.ts +3 -1
  133. package/dist/lib/core/infrastructure/baseRegistry.js +5 -1
  134. package/dist/lib/index.d.ts +1 -0
  135. package/dist/lib/index.js +25 -0
  136. package/dist/lib/mcp/toolRegistry.js +11 -1
  137. package/dist/lib/neurolink.d.ts +42 -1
  138. package/dist/lib/neurolink.js +218 -0
  139. package/dist/lib/rag/ChunkerRegistry.js +2 -2
  140. package/dist/lib/rag/metadata/MetadataExtractorRegistry.js +2 -2
  141. package/dist/lib/rag/reranker/RerankerRegistry.js +2 -2
  142. package/dist/lib/server/routes/agentRoutes.js +20 -2
  143. package/dist/lib/types/authTypes.d.ts +937 -1
  144. package/dist/lib/types/authTypes.js +2 -1
  145. package/dist/lib/types/configTypes.d.ts +46 -0
  146. package/dist/lib/types/generateTypes.d.ts +6 -0
  147. package/dist/lib/types/index.d.ts +1 -0
  148. package/dist/lib/types/streamTypes.d.ts +6 -0
  149. package/dist/mcp/toolRegistry.js +11 -1
  150. package/dist/neurolink.d.ts +42 -1
  151. package/dist/neurolink.js +218 -0
  152. package/dist/rag/ChunkerRegistry.js +2 -2
  153. package/dist/rag/metadata/MetadataExtractorRegistry.js +2 -2
  154. package/dist/rag/reranker/RerankerRegistry.js +2 -2
  155. package/dist/server/routes/agentRoutes.js +20 -2
  156. package/dist/types/authTypes.d.ts +937 -1
  157. package/dist/types/authTypes.js +2 -1
  158. package/dist/types/configTypes.d.ts +46 -0
  159. package/dist/types/generateTypes.d.ts +6 -0
  160. package/dist/types/index.d.ts +1 -0
  161. package/dist/types/streamTypes.d.ts +6 -0
  162. package/package.json +2 -1
@@ -0,0 +1,181 @@
1
+ /**
2
+ * AuthMiddleware - Authentication and authorization middleware
3
+ *
4
+ * Provides middleware factories for:
5
+ * - Token extraction and validation
6
+ * - User context propagation
7
+ * - RBAC enforcement
8
+ * - Public route handling
9
+ */
10
+ import type { AuthenticatedContext, AuthMiddlewareConfig, AuthRequestContext, AuthUser, RBACMiddlewareConfig, TokenExtractionConfig } from "../../types/authTypes.js";
11
+ /**
12
+ * Auth middleware error codes
13
+ */
14
+ export declare const AuthMiddlewareErrorCodes: {
15
+ readonly MISSING_TOKEN: "AUTH_MIDDLEWARE-001";
16
+ readonly INVALID_TOKEN: "AUTH_MIDDLEWARE-002";
17
+ readonly UNAUTHORIZED: "AUTH_MIDDLEWARE-003";
18
+ readonly FORBIDDEN: "AUTH_MIDDLEWARE-004";
19
+ readonly PROVIDER_ERROR: "AUTH_MIDDLEWARE-005";
20
+ readonly CONFIGURATION_ERROR: "AUTH_MIDDLEWARE-006";
21
+ };
22
+ /**
23
+ * Auth middleware error factory
24
+ */
25
+ export declare const AuthMiddlewareError: {
26
+ codes: {
27
+ readonly MISSING_TOKEN: "AUTH_MIDDLEWARE-001";
28
+ readonly INVALID_TOKEN: "AUTH_MIDDLEWARE-002";
29
+ readonly UNAUTHORIZED: "AUTH_MIDDLEWARE-003";
30
+ readonly FORBIDDEN: "AUTH_MIDDLEWARE-004";
31
+ readonly PROVIDER_ERROR: "AUTH_MIDDLEWARE-005";
32
+ readonly CONFIGURATION_ERROR: "AUTH_MIDDLEWARE-006";
33
+ };
34
+ create: (code: "INVALID_TOKEN" | "MISSING_TOKEN" | "PROVIDER_ERROR" | "CONFIGURATION_ERROR" | "UNAUTHORIZED" | "FORBIDDEN", message: string, options?: {
35
+ retryable?: boolean;
36
+ details?: Record<string, unknown>;
37
+ cause?: Error;
38
+ } | undefined) => import("../../core/infrastructure/baseError.js").NeuroLinkFeatureError;
39
+ };
40
+ /**
41
+ * Minimal request object accepted by {@link createRequestContext}.
42
+ *
43
+ * Avoids `any` for Express/Koa/Hono request objects while remaining
44
+ * compatible with any framework that exposes these standard fields.
45
+ */
46
+ export type IncomingRequest = {
47
+ method?: string;
48
+ url?: string;
49
+ path?: string;
50
+ headers?: Record<string, string | string[] | undefined>;
51
+ cookies?: Record<string, string>;
52
+ query?: Record<string, string | string[] | undefined>;
53
+ body?: unknown;
54
+ ip?: string;
55
+ /** Populated by auth middleware after successful authentication */
56
+ user?: AuthUser;
57
+ /** Populated by auth middleware after successful authentication */
58
+ authContext?: AuthenticatedContext;
59
+ };
60
+ /**
61
+ * Minimal response object for Express-style middleware.
62
+ */
63
+ export type OutgoingResponse = {
64
+ status(code: number): OutgoingResponse;
65
+ json(body: unknown): void;
66
+ };
67
+ /**
68
+ * Middleware handler function type
69
+ */
70
+ export type MiddlewareHandler<TContext = AuthRequestContext> = (context: TContext) => Promise<MiddlewareResult>;
71
+ /**
72
+ * Middleware result
73
+ */
74
+ export type MiddlewareResult = {
75
+ /** Whether to proceed to next handler */
76
+ proceed: boolean;
77
+ /** Updated context (if authenticated) */
78
+ context?: AuthenticatedContext;
79
+ /** Error response if not proceeding */
80
+ error?: {
81
+ statusCode: number;
82
+ message: string;
83
+ code?: string;
84
+ };
85
+ };
86
+ /**
87
+ * Next function for middleware chaining
88
+ */
89
+ export type NextFunction = () => Promise<void>;
90
+ /**
91
+ * Express-style middleware function
92
+ */
93
+ export type ExpressMiddleware = (req: IncomingRequest, res: OutgoingResponse, next: NextFunction) => Promise<void>;
94
+ /**
95
+ * Extract token from request context based on configuration
96
+ */
97
+ export declare function extractToken(context: AuthRequestContext, config?: TokenExtractionConfig): Promise<string | null>;
98
+ /**
99
+ * Create authentication middleware
100
+ *
101
+ * Validates tokens and attaches user context to requests.
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * const authMiddleware = await createAuthMiddleware({
106
+ * provider: 'auth0',
107
+ * providerConfig: {
108
+ * type: 'auth0',
109
+ * domain: 'your-tenant.auth0.com',
110
+ * clientId: 'your-client-id',
111
+ * },
112
+ * publicRoutes: ['/health', '/public/*'],
113
+ * });
114
+ *
115
+ * // Use in request handler
116
+ * const result = await authMiddleware(requestContext);
117
+ * if (result.proceed) {
118
+ * // Access authenticated context
119
+ * console.log('User:', result.context?.user);
120
+ * } else {
121
+ * // Return error response
122
+ * res.status(result.error.statusCode).json({ error: result.error.message });
123
+ * }
124
+ * ```
125
+ */
126
+ export declare function createAuthMiddleware(config: AuthMiddlewareConfig): Promise<MiddlewareHandler<AuthRequestContext>>;
127
+ /**
128
+ * Create RBAC (Role-Based Access Control) middleware
129
+ *
130
+ * Checks if authenticated user has required roles/permissions.
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * const rbacMiddleware = createRBACMiddleware({
135
+ * roles: ['admin', 'moderator'],
136
+ * permissions: ['read:users'],
137
+ * });
138
+ *
139
+ * // Use after auth middleware
140
+ * const authResult = await authMiddleware(context);
141
+ * if (authResult.proceed && authResult.context) {
142
+ * const rbacResult = await rbacMiddleware(authResult.context);
143
+ * if (!rbacResult.proceed) {
144
+ * res.status(403).json({ error: rbacResult.error.message });
145
+ * }
146
+ * }
147
+ * ```
148
+ */
149
+ export declare function createRBACMiddleware(config: RBACMiddlewareConfig): MiddlewareHandler<AuthenticatedContext>;
150
+ /**
151
+ * Create combined auth + RBAC middleware
152
+ *
153
+ * Convenience function that combines authentication and authorization.
154
+ *
155
+ * @example
156
+ * ```typescript
157
+ * const protectedMiddleware = await createProtectedMiddleware({
158
+ * auth: {
159
+ * provider: 'auth0',
160
+ * providerConfig: { type: 'auth0', domain: '...', clientId: '...' },
161
+ * },
162
+ * rbac: {
163
+ * roles: ['admin'],
164
+ * },
165
+ * });
166
+ *
167
+ * const result = await protectedMiddleware(context);
168
+ * ```
169
+ */
170
+ export declare function createProtectedMiddleware(config: {
171
+ auth: AuthMiddlewareConfig;
172
+ rbac?: RBACMiddlewareConfig;
173
+ }): Promise<MiddlewareHandler<AuthRequestContext>>;
174
+ /**
175
+ * Create request context from standard request object
176
+ */
177
+ export declare function createRequestContext(req: IncomingRequest): AuthRequestContext;
178
+ /**
179
+ * Create Express-compatible middleware
180
+ */
181
+ export declare function createExpressAuthMiddleware(config: AuthMiddlewareConfig): Promise<ExpressMiddleware>;
@@ -0,0 +1,520 @@
1
+ /**
2
+ * AuthMiddleware - Authentication and authorization middleware
3
+ *
4
+ * Provides middleware factories for:
5
+ * - Token extraction and validation
6
+ * - User context propagation
7
+ * - RBAC enforcement
8
+ * - Public route handling
9
+ */
10
+ import { createErrorFactory } from "../../core/infrastructure/baseError.js";
11
+ import { withTimeout } from "../../utils/async/withTimeout.js";
12
+ import { logger } from "../../utils/logger.js";
13
+ import { AuthProviderFactory } from "../AuthProviderFactory.js";
14
+ // =============================================================================
15
+ // ERROR FACTORY
16
+ // =============================================================================
17
+ /**
18
+ * Auth middleware error codes
19
+ */
20
+ export const AuthMiddlewareErrorCodes = {
21
+ MISSING_TOKEN: "AUTH_MIDDLEWARE-001",
22
+ INVALID_TOKEN: "AUTH_MIDDLEWARE-002",
23
+ UNAUTHORIZED: "AUTH_MIDDLEWARE-003",
24
+ FORBIDDEN: "AUTH_MIDDLEWARE-004",
25
+ PROVIDER_ERROR: "AUTH_MIDDLEWARE-005",
26
+ CONFIGURATION_ERROR: "AUTH_MIDDLEWARE-006",
27
+ };
28
+ /**
29
+ * Auth middleware error factory
30
+ */
31
+ export const AuthMiddlewareError = createErrorFactory("AuthMiddleware", AuthMiddlewareErrorCodes);
32
+ // =============================================================================
33
+ // HELPERS
34
+ // =============================================================================
35
+ /**
36
+ * Create an AuthErrorInfo object for the onError callback.
37
+ *
38
+ * Avoids `as any` by constructing a proper Error with the required `code` field.
39
+ */
40
+ function createAuthErrorInfo(message, code) {
41
+ const err = new Error(message);
42
+ err.code = code;
43
+ return err;
44
+ }
45
+ // =============================================================================
46
+ // TOKEN EXTRACTION
47
+ // =============================================================================
48
+ /**
49
+ * Extract token from request context based on configuration
50
+ */
51
+ export async function extractToken(context, config) {
52
+ // Default: extract from Authorization header
53
+ const headerConfig = config?.fromHeader ?? {
54
+ name: "authorization",
55
+ prefix: "Bearer",
56
+ };
57
+ // Try header extraction (case-insensitive header lookup)
58
+ const headerName = headerConfig.name?.toLowerCase() ?? "authorization";
59
+ // Find header value with case-insensitive lookup
60
+ let headerValue;
61
+ for (const [key, value] of Object.entries(context.headers)) {
62
+ if (key.toLowerCase() === headerName) {
63
+ headerValue = value;
64
+ break;
65
+ }
66
+ }
67
+ if (headerValue) {
68
+ const value = Array.isArray(headerValue) ? headerValue[0] : headerValue;
69
+ if (value) {
70
+ const prefix = headerConfig.prefix ?? "Bearer";
71
+ if (!prefix) {
72
+ // If no prefix required, return as-is
73
+ return value;
74
+ }
75
+ // Compare scheme case-insensitively per RFC 7235
76
+ const prefixWithSpace = `${prefix} `;
77
+ if (value.length > prefixWithSpace.length &&
78
+ value.slice(0, prefixWithSpace.length).toLowerCase() ===
79
+ prefixWithSpace.toLowerCase()) {
80
+ return value.slice(prefixWithSpace.length);
81
+ }
82
+ }
83
+ }
84
+ // Try cookie extraction
85
+ if (config?.fromCookie?.name && context.cookies) {
86
+ const cookieToken = context.cookies[config.fromCookie.name];
87
+ if (cookieToken) {
88
+ return cookieToken;
89
+ }
90
+ }
91
+ // Try query parameter extraction
92
+ if (config?.fromQuery?.name && context.query) {
93
+ const queryToken = context.query[config.fromQuery.name];
94
+ if (queryToken) {
95
+ return Array.isArray(queryToken) ? queryToken[0] : queryToken;
96
+ }
97
+ }
98
+ // Try custom extraction
99
+ if (config?.custom) {
100
+ const customToken = await config.custom(context);
101
+ if (customToken) {
102
+ return customToken;
103
+ }
104
+ }
105
+ return null;
106
+ }
107
+ // =============================================================================
108
+ // AUTH MIDDLEWARE FACTORY
109
+ // =============================================================================
110
+ /**
111
+ * Create authentication middleware
112
+ *
113
+ * Validates tokens and attaches user context to requests.
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * const authMiddleware = await createAuthMiddleware({
118
+ * provider: 'auth0',
119
+ * providerConfig: {
120
+ * type: 'auth0',
121
+ * domain: 'your-tenant.auth0.com',
122
+ * clientId: 'your-client-id',
123
+ * },
124
+ * publicRoutes: ['/health', '/public/*'],
125
+ * });
126
+ *
127
+ * // Use in request handler
128
+ * const result = await authMiddleware(requestContext);
129
+ * if (result.proceed) {
130
+ * // Access authenticated context
131
+ * console.log('User:', result.context?.user);
132
+ * } else {
133
+ * // Return error response
134
+ * res.status(result.error.statusCode).json({ error: result.error.message });
135
+ * }
136
+ * ```
137
+ */
138
+ export async function createAuthMiddleware(config) {
139
+ // Create provider instance
140
+ const provider = await AuthProviderFactory.createProvider(config.provider, config.providerConfig);
141
+ logger.debug(`[AuthMiddleware] Created middleware with ${config.provider} provider`);
142
+ return async (context) => {
143
+ try {
144
+ // Check if route is public
145
+ if (isPublicRoute(context.path ?? "", config.publicRoutes)) {
146
+ logger.debug(`[AuthMiddleware] Public route: ${context.path}`);
147
+ return { proceed: true };
148
+ }
149
+ // Extract token
150
+ const token = await extractToken(context, config.tokenExtraction);
151
+ if (!token) {
152
+ // If auth is optional, proceed without user
153
+ if (config.optional) {
154
+ return { proceed: true };
155
+ }
156
+ const error = {
157
+ statusCode: 401,
158
+ message: "Authentication required",
159
+ code: "AUTH-005",
160
+ };
161
+ if (config.onError) {
162
+ await config.onError(createAuthErrorInfo(error.message, error.code), context);
163
+ }
164
+ return { proceed: false, error };
165
+ }
166
+ // Validate token (with 5s timeout to prevent hanging on slow providers)
167
+ const validationResult = await withTimeout(provider.authenticateToken(token), 5000, "Token authentication timed out after 5000ms");
168
+ if (!validationResult.valid) {
169
+ // If auth is optional, proceed without user
170
+ if (config.optional) {
171
+ return { proceed: true };
172
+ }
173
+ const errorCode = validationResult.errorCode ?? "AUTH-001";
174
+ const error = {
175
+ statusCode: 401,
176
+ message: validationResult.error ?? "Invalid token",
177
+ code: errorCode,
178
+ };
179
+ if (config.onError) {
180
+ await config.onError(createAuthErrorInfo(error.message, error.code), context);
181
+ }
182
+ return { proceed: false, error };
183
+ }
184
+ // Fail closed: valid token without a user object is treated as failure
185
+ if (!validationResult.user) {
186
+ const error = {
187
+ statusCode: 401,
188
+ message: "Token valid but no user identity resolved",
189
+ code: "AUTH-001",
190
+ };
191
+ if (config.onError) {
192
+ await config.onError(createAuthErrorInfo(error.message, error.code), context);
193
+ }
194
+ return { proceed: false, error };
195
+ }
196
+ // Create authenticated context
197
+ // Providers populate `payload` (most) or `claims` (Cognito, Keycloak).
198
+ // Prefer `payload`, fall back to `claims` for compatibility.
199
+ const authenticatedContext = {
200
+ ...context,
201
+ user: validationResult.user,
202
+ token,
203
+ claims: validationResult.payload ??
204
+ validationResult.claims,
205
+ };
206
+ // Call success hook
207
+ if (config.onAuthenticated) {
208
+ await config.onAuthenticated(authenticatedContext);
209
+ }
210
+ logger.debug(`[AuthMiddleware] Authenticated user: ${validationResult.user?.id}`);
211
+ return { proceed: true, context: authenticatedContext };
212
+ }
213
+ catch (error) {
214
+ logger.error(`[AuthMiddleware] Error:`, error);
215
+ const errorResult = {
216
+ statusCode: 500,
217
+ message: error instanceof Error ? error.message : "Authentication error",
218
+ code: "AUTH-014",
219
+ };
220
+ if (config.onError) {
221
+ await config.onError(createAuthErrorInfo(errorResult.message, errorResult.code), context);
222
+ }
223
+ return { proceed: false, error: errorResult };
224
+ }
225
+ };
226
+ }
227
+ // =============================================================================
228
+ // RBAC MIDDLEWARE FACTORY
229
+ // =============================================================================
230
+ /**
231
+ * Create RBAC (Role-Based Access Control) middleware
232
+ *
233
+ * Checks if authenticated user has required roles/permissions.
234
+ *
235
+ * @example
236
+ * ```typescript
237
+ * const rbacMiddleware = createRBACMiddleware({
238
+ * roles: ['admin', 'moderator'],
239
+ * permissions: ['read:users'],
240
+ * });
241
+ *
242
+ * // Use after auth middleware
243
+ * const authResult = await authMiddleware(context);
244
+ * if (authResult.proceed && authResult.context) {
245
+ * const rbacResult = await rbacMiddleware(authResult.context);
246
+ * if (!rbacResult.proceed) {
247
+ * res.status(403).json({ error: rbacResult.error.message });
248
+ * }
249
+ * }
250
+ * ```
251
+ */
252
+ export function createRBACMiddleware(config) {
253
+ return async (context) => {
254
+ try {
255
+ const user = context.user;
256
+ if (!user) {
257
+ return {
258
+ proceed: false,
259
+ error: {
260
+ statusCode: 401,
261
+ message: "User not authenticated",
262
+ code: "AUTH-005",
263
+ },
264
+ };
265
+ }
266
+ // Super admin roles bypass all role/permission checks
267
+ const superAdminRoles = config.superAdminRoles ?? [];
268
+ if (superAdminRoles.length > 0 &&
269
+ user.roles.some((r) => superAdminRoles.includes(r))) {
270
+ logger.debug(`[RBACMiddleware] Super admin bypass for user: ${user.id}`);
271
+ return { proceed: true, context };
272
+ }
273
+ // Build effective permissions from rolePermissions mapping and
274
+ // role hierarchy so that checks below consider inherited grants.
275
+ const effectivePermissions = new Set(user.permissions);
276
+ const rolePermissions = config.rolePermissions ?? {};
277
+ const roleHierarchy = config.roleHierarchy ?? {};
278
+ const expandRoles = (roles) => {
279
+ const expanded = new Set();
280
+ const queue = [...roles];
281
+ while (queue.length > 0) {
282
+ const role = queue.pop();
283
+ if (role === undefined || expanded.has(role)) {
284
+ continue;
285
+ }
286
+ expanded.add(role);
287
+ const children = roleHierarchy[role];
288
+ if (children) {
289
+ queue.push(...children);
290
+ }
291
+ }
292
+ return [...expanded];
293
+ };
294
+ const allRoles = expandRoles(user.roles);
295
+ for (const role of allRoles) {
296
+ const perms = rolePermissions[role];
297
+ if (perms) {
298
+ for (const p of perms) {
299
+ effectivePermissions.add(p);
300
+ }
301
+ }
302
+ }
303
+ // Check custom authorization first
304
+ if (config.custom) {
305
+ const customResult = await config.custom(user, context);
306
+ if (!customResult) {
307
+ const result = {
308
+ authorized: false,
309
+ user,
310
+ reason: "Custom authorization denied",
311
+ };
312
+ if (config.onDenied) {
313
+ await config.onDenied(result, context);
314
+ }
315
+ return {
316
+ proceed: false,
317
+ error: {
318
+ statusCode: 403,
319
+ message: "Access denied",
320
+ code: "AUTH-013",
321
+ },
322
+ };
323
+ }
324
+ }
325
+ // Check roles (includes inherited roles from hierarchy)
326
+ if (config.roles && config.roles.length > 0) {
327
+ const userRolesSet = new Set(allRoles);
328
+ const hasRequiredRoles = config.requireAllRoles
329
+ ? config.roles.every((r) => userRolesSet.has(r))
330
+ : config.roles.some((r) => userRolesSet.has(r));
331
+ if (!hasRequiredRoles) {
332
+ const missingRoles = config.roles.filter((r) => !userRolesSet.has(r));
333
+ const result = {
334
+ authorized: false,
335
+ user,
336
+ requiredRoles: config.roles,
337
+ missingRoles,
338
+ reason: `Missing roles: ${missingRoles.join(", ")}`,
339
+ };
340
+ if (config.onDenied) {
341
+ await config.onDenied(result, context);
342
+ }
343
+ return {
344
+ proceed: false,
345
+ error: {
346
+ statusCode: 403,
347
+ message: `Insufficient roles. Required: ${config.roles.join(", ")}`,
348
+ code: "AUTH-012",
349
+ },
350
+ };
351
+ }
352
+ }
353
+ // Check permissions (all required; uses effective permissions from role mapping)
354
+ if (config.permissions && config.permissions.length > 0) {
355
+ const missingPermissions = config.permissions.filter((p) => !effectivePermissions.has(p));
356
+ if (missingPermissions.length > 0) {
357
+ const result = {
358
+ authorized: false,
359
+ user,
360
+ requiredPermissions: config.permissions,
361
+ missingPermissions,
362
+ reason: `Missing permissions: ${missingPermissions.join(", ")}`,
363
+ };
364
+ if (config.onDenied) {
365
+ await config.onDenied(result, context);
366
+ }
367
+ return {
368
+ proceed: false,
369
+ error: {
370
+ statusCode: 403,
371
+ message: `Insufficient permissions. Required: ${config.permissions.join(", ")}`,
372
+ code: "AUTH-011",
373
+ },
374
+ };
375
+ }
376
+ }
377
+ logger.debug(`[RBACMiddleware] Authorized user: ${user.id}`);
378
+ return { proceed: true, context };
379
+ }
380
+ catch (error) {
381
+ logger.error(`[RBACMiddleware] Error:`, error);
382
+ return {
383
+ proceed: false,
384
+ error: {
385
+ statusCode: 500,
386
+ message: error instanceof Error ? error.message : "Authorization error",
387
+ code: "AUTH-014",
388
+ },
389
+ };
390
+ }
391
+ };
392
+ }
393
+ // =============================================================================
394
+ // COMBINED MIDDLEWARE
395
+ // =============================================================================
396
+ /**
397
+ * Create combined auth + RBAC middleware
398
+ *
399
+ * Convenience function that combines authentication and authorization.
400
+ *
401
+ * @example
402
+ * ```typescript
403
+ * const protectedMiddleware = await createProtectedMiddleware({
404
+ * auth: {
405
+ * provider: 'auth0',
406
+ * providerConfig: { type: 'auth0', domain: '...', clientId: '...' },
407
+ * },
408
+ * rbac: {
409
+ * roles: ['admin'],
410
+ * },
411
+ * });
412
+ *
413
+ * const result = await protectedMiddleware(context);
414
+ * ```
415
+ */
416
+ export async function createProtectedMiddleware(config) {
417
+ const authMiddleware = await createAuthMiddleware(config.auth);
418
+ const rbacMiddleware = config.rbac ? createRBACMiddleware(config.rbac) : null;
419
+ return async (context) => {
420
+ // Run auth middleware
421
+ const authResult = await authMiddleware(context);
422
+ if (!authResult.proceed) {
423
+ return authResult;
424
+ }
425
+ // If no RBAC configured, return auth result as-is
426
+ if (!rbacMiddleware) {
427
+ return authResult;
428
+ }
429
+ // Build the context for RBAC. When auth is optional and no token was
430
+ // provided, authResult.context is undefined. We still need to run RBAC
431
+ // so that role/permission checks are not silently bypassed. Pass a
432
+ // context without a user — the RBAC middleware already handles the
433
+ // missing-user case and returns a 401.
434
+ const rbacContext = authResult.context ?? context;
435
+ // Run RBAC middleware
436
+ return rbacMiddleware(rbacContext);
437
+ };
438
+ }
439
+ // =============================================================================
440
+ // UTILITY FUNCTIONS
441
+ // =============================================================================
442
+ /**
443
+ * Check if a path matches public routes
444
+ */
445
+ function isPublicRoute(path, publicRoutes) {
446
+ if (!publicRoutes || publicRoutes.length === 0) {
447
+ return false;
448
+ }
449
+ // Strip query string before matching
450
+ const pathWithoutQuery = path.split("?")[0];
451
+ const normalizedPath = pathWithoutQuery.replace(/\/$/, "") || "/";
452
+ for (const route of publicRoutes) {
453
+ // Exact match
454
+ if (route === normalizedPath) {
455
+ return true;
456
+ }
457
+ // Wildcard match (e.g., '/public/*')
458
+ if (route.endsWith("*")) {
459
+ const prefix = route.slice(0, -1);
460
+ if (normalizedPath.startsWith(prefix)) {
461
+ return true;
462
+ }
463
+ }
464
+ // Pattern match with path segments
465
+ if (route.includes(":")) {
466
+ const routeParts = route.split("/");
467
+ const pathParts = normalizedPath.split("/");
468
+ if (routeParts.length === pathParts.length) {
469
+ const matches = routeParts.every((part, i) => {
470
+ return part.startsWith(":") || part === pathParts[i];
471
+ });
472
+ if (matches) {
473
+ return true;
474
+ }
475
+ }
476
+ }
477
+ }
478
+ return false;
479
+ }
480
+ /**
481
+ * Create request context from standard request object
482
+ */
483
+ export function createRequestContext(req) {
484
+ return {
485
+ method: req.method ?? "GET",
486
+ path: req.path ?? req.url ?? "/",
487
+ headers: req.headers ?? {},
488
+ cookies: req.cookies,
489
+ query: req.query,
490
+ body: req.body,
491
+ ip: req.ip,
492
+ ipAddress: req.ip,
493
+ userAgent: req.headers?.["user-agent"],
494
+ };
495
+ }
496
+ /**
497
+ * Create Express-compatible middleware
498
+ */
499
+ export async function createExpressAuthMiddleware(config) {
500
+ const middleware = await createAuthMiddleware(config);
501
+ return async (req, res, next) => {
502
+ const context = createRequestContext(req);
503
+ const result = await middleware(context);
504
+ if (result.proceed) {
505
+ // Attach user to request
506
+ if (result.context) {
507
+ req.user = result.context.user;
508
+ req.authContext = result.context;
509
+ }
510
+ next();
511
+ }
512
+ else {
513
+ res.status(result.error?.statusCode ?? 401).json({
514
+ error: result.error?.message ?? "Unauthorized",
515
+ code: result.error?.code,
516
+ });
517
+ }
518
+ };
519
+ }
520
+ //# sourceMappingURL=AuthMiddleware.js.map