@juspay/neurolink
Version:
Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applicatio
404 lines • 12.7 kB
JavaScript
/**
* Authentication Middleware
* Provides flexible authentication support for server adapters
*/
import { AuthenticationError, AuthorizationError, InvalidAuthenticationError, } from "../errors.js";
/**
* Check if request is from development playground.
* Detects playground requests via special headers for development mode.
*
* @param headers - Request headers to check
* @returns True if request is from dev playground
*
* @example
* ```typescript
* if (isDevPlayground(ctx.headers)) {
* // Skip authentication for playground
* }
* ```
*/
export function isDevPlayground(headers) {
const getHeader = (name) => {
const value = headers[name.toLowerCase()];
return Array.isArray(value) ? value[0] : value;
};
return (getHeader("x-neurolink-dev-playground") === "true" ||
getHeader("x-neurolink-playground") === "true");
}
/**
* Default dev user for playground requests
*/
export const DEV_PLAYGROUND_USER = {
id: "playground",
roles: ["developer"],
};
/**
* Authentication result
*/
/**
* Create authentication middleware
*
* @example
* ```typescript
* const authMiddleware = createAuthMiddleware({
* type: "bearer",
* validate: async (token) => {
* const user = await verifyJWT(token);
* return user ? { id: user.id, email: user.email } : null;
* },
* skipPaths: ["/api/health", "/api/ready"],
* });
*
* server.registerMiddleware(authMiddleware);
* ```
*/
export function createAuthMiddleware(config) {
const { type, validate, skipPaths = [], errorMessage = "Authentication required", skipDevPlayground = true, } = config;
// Determine header name based on auth type
const headerName = config.headerName ?? getDefaultHeaderName(type);
return {
name: "authentication",
order: 10, // Run early but after request ID
excludePaths: skipPaths,
handler: async (ctx, next) => {
// Skip auth for dev playground in non-production
if (skipDevPlayground &&
process.env.NODE_ENV !== "production" &&
isDevPlayground(ctx.headers)) {
ctx.user = {
id: DEV_PLAYGROUND_USER.id,
email: DEV_PLAYGROUND_USER.email,
roles: DEV_PLAYGROUND_USER.roles,
};
return next();
}
try {
// Extract token based on auth type
const token = extractToken(ctx, type, headerName, config.extractToken);
if (!token) {
throw new AuthenticationError(errorMessage);
}
// Validate token
const result = await validate(token, ctx);
if (!result) {
throw new InvalidAuthenticationError("Invalid authentication token");
}
// Set user information in context
ctx.user = {
id: result.id,
email: result.email,
roles: result.roles,
};
// Store additional metadata
if (result.metadata) {
ctx.metadata.auth = result.metadata;
}
return next();
}
catch (error) {
if (error instanceof AuthenticationError ||
error instanceof InvalidAuthenticationError) {
throw error;
}
throw new AuthenticationError(error instanceof Error ? error.message : "Authentication failed");
}
},
};
}
/**
* Get default header name for auth type
*/
function getDefaultHeaderName(type) {
switch (type) {
case "bearer":
case "basic":
return "authorization";
case "api-key":
return "x-api-key";
case "custom":
return "authorization";
default:
return "authorization";
}
}
/**
* Extract token from request based on auth type
*/
function extractToken(ctx, type, headerName, customExtractor) {
// Use custom extractor if provided
if (type === "custom" && customExtractor) {
return customExtractor(ctx);
}
const headerValue = ctx.headers[headerName.toLowerCase()];
if (!headerValue) {
return null;
}
switch (type) {
case "bearer": {
// Extract token from "Bearer <token>"
const match = headerValue.match(/^Bearer\s+(.+)$/i);
return match ? match[1] : null;
}
case "basic": {
// Extract credentials from "Basic <base64>"
const match = headerValue.match(/^Basic\s+(.+)$/i);
return match ? match[1] : null;
}
case "api-key":
// API key is the raw header value
return headerValue;
case "custom":
// For custom type without extractor, return raw header
return headerValue;
default:
return null;
}
}
/**
* Role-based access control middleware
* Use after authentication middleware
*
* @example
* ```typescript
* const adminOnly = createRoleMiddleware({
* requiredRoles: ["admin"],
* errorMessage: "Admin access required",
* });
* ```
*/
export function createRoleMiddleware(config) {
const { requiredRoles, requireAll = false, errorMessage = "Insufficient permissions", } = config;
return {
name: "role-check",
order: 11, // Run after authentication
handler: async (ctx, next) => {
if (!ctx.user) {
throw new AuthenticationError("Authentication required");
}
const userRoles = ctx.user.roles || [];
const hasAccess = requireAll
? requiredRoles.every((role) => userRoles.includes(role))
: requiredRoles.some((role) => userRoles.includes(role));
if (!hasAccess) {
throw new AuthorizationError(errorMessage, ctx.requestId, requiredRoles);
}
return next();
},
};
}
// ============================================
// API Key Store
// ============================================
/**
* In-memory API key store for managing API keys
*
* @example
* ```typescript
* const store = new ApiKeyStore();
*
* // Add an API key
* store.addKey("my-api-key", { id: "user_1", email: "user@example.com" });
*
* // Validate a key
* const user = store.validate("my-api-key");
* if (user) {
* console.log("Valid key for user:", user.id);
* }
*
* // Remove a key
* store.removeKey("my-api-key");
*
* // Clear all keys
* store.clear();
* ```
*/
export class ApiKeyStore {
keys = new Map();
/**
* Add an API key with associated user
*/
addKey(apiKey, user) {
this.keys.set(apiKey, user);
}
/**
* Validate an API key
* @returns The user associated with the key, or null if invalid
*/
validate(apiKey) {
return this.keys.get(apiKey) ?? null;
}
/**
* Remove an API key
*/
removeKey(apiKey) {
return this.keys.delete(apiKey);
}
/**
* Clear all API keys
*/
clear() {
this.keys.clear();
}
/**
* Get the number of stored keys
*/
get size() {
return this.keys.size;
}
}
// ============================================
// Bearer Token Authentication
// ============================================
/**
* Options for bearer auth middleware
*/
/**
* Create bearer token authentication middleware
*
* @example
* ```typescript
* const authMiddleware = createBearerAuthMiddleware(
* async (token) => {
* const user = await verifyJWT(token);
* return user ? { id: user.id, roles: user.roles } : null;
* },
* { required: true }
* );
* ```
*/
export function createBearerAuthMiddleware(validate, options = {}) {
const { required = true, headerName = "authorization", skipPaths = [], } = options;
return {
name: "bearer-auth",
order: 10,
excludePaths: skipPaths,
handler: async (ctx, next) => {
const authHeader = ctx.headers[headerName.toLowerCase()];
// Extract bearer token
let token = null;
if (authHeader) {
const match = authHeader.match(/^Bearer\s+(.+)$/i);
token = match ? match[1] : null;
}
// Handle missing token
if (!token) {
if (required) {
throw new AuthenticationError("Authentication required");
}
return next();
}
// Validate token
const user = await validate(token);
if (!user) {
throw new AuthenticationError("Invalid authentication token");
}
// Set user in context
ctx.user = user;
return next();
},
};
}
// ============================================
// API Key Authentication
// ============================================
/**
* Options for API key auth middleware
*/
/**
* Create API key authentication middleware
*
* @example
* ```typescript
* const store = new ApiKeyStore();
* store.addKey("my-key", { id: "user_1" });
*
* const authMiddleware = createApiKeyAuthMiddleware(store);
* ```
*/
export function createApiKeyAuthMiddleware(store, options = {}) {
const { headerName = "x-api-key", skipPaths = [] } = options;
return {
name: "api-key-auth",
order: 10,
excludePaths: skipPaths,
handler: async (ctx, next) => {
const apiKey = ctx.headers[headerName.toLowerCase()];
if (!apiKey) {
throw new AuthenticationError("API key required");
}
const user = store.validate(apiKey);
if (!user) {
throw new AuthenticationError("Invalid API key");
}
ctx.user = user;
return next();
},
};
}
// ============================================
// Role-based Authorization (Simple)
// ============================================
/**
* Create role-based authorization middleware (simple version)
*
* @example
* ```typescript
* const adminMiddleware = createRoleAuthMiddleware(["admin"]);
* const editorMiddleware = createRoleAuthMiddleware(["admin", "editor"]);
* ```
*/
export function createRoleAuthMiddleware(requiredRoles, options = {}) {
const { requireAll = false } = options;
return {
name: "role-auth",
order: 11,
handler: async (ctx, next) => {
if (!ctx.user) {
throw new AuthenticationError("Authentication required");
}
const userRoles = ctx.user.roles || [];
const hasAccess = requireAll
? requiredRoles.every((role) => userRoles.includes(role))
: requiredRoles.some((role) => userRoles.includes(role));
if (!hasAccess) {
throw new AuthorizationError(`Required roles: ${requiredRoles.join(", ")}. User has: ${userRoles.join(", ") || "none"}`, ctx.requestId, requiredRoles);
}
return next();
},
};
}
// ============================================
// Permission-based Authorization
// ============================================
/**
* Create permission-based authorization middleware
*
* @example
* ```typescript
* const canEditMiddleware = createPermissionAuthMiddleware(["posts:edit"]);
* const canManageMiddleware = createPermissionAuthMiddleware(["users:read", "users:write"]);
* ```
*/
export function createPermissionAuthMiddleware(requiredPermissions, options = {}) {
const { requireAll = false } = options;
return {
name: "permission-auth",
order: 11,
handler: async (ctx, next) => {
if (!ctx.user) {
throw new AuthenticationError("Authentication required");
}
// Get permissions from user metadata or roles
const userPermissions = ctx.user
.permissions || [];
const hasAccess = requireAll
? requiredPermissions.every((perm) => userPermissions.includes(perm))
: requiredPermissions.some((perm) => userPermissions.includes(perm));
if (!hasAccess) {
throw new AuthorizationError(`Required permissions: ${requiredPermissions.join(", ")}. User has: ${userPermissions.join(", ") || "none"}`, ctx.requestId, requiredPermissions);
}
return next();
},
};
}
//# sourceMappingURL=auth.js.map