@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
315 lines • 8 kB
JavaScript
// src/lib/auth/authContext.ts
import { AsyncLocalStorage } from "async_hooks";
import { AuthError } from "./errors.js";
/**
* Async local storage for auth context
*
* Enables access to authentication context throughout the request lifecycle
* without explicitly passing it through every function call.
*/
const authContextStorage = new AsyncLocalStorage();
/**
* Run a function with authentication context
*
* Sets up async local storage so getAuthContext() can be called
* from anywhere within the callback's execution.
*
* @param context - The authenticated context
* @param callback - Function to run with context available
* @returns Result of the callback
*
* @example
* ```typescript
* await runWithAuthContext(authContext, async () => {
* // Inside here, getAuthContext() returns the context
* const user = getCurrentUser();
* await processRequest();
* });
* ```
*/
export function runWithAuthContext(context, callback) {
return authContextStorage.run(context, callback);
}
/**
* Get the current authentication context
*
* Returns the authenticated context for the current request,
* or undefined if no context is set.
*
* @returns Current auth context or undefined
*
* @example
* ```typescript
* const context = getAuthContext();
* if (context) {
* console.log("Current user:", context.user.email);
* }
* ```
*/
export function getAuthContext() {
return authContextStorage.getStore() ?? globalAuthContext.get();
}
/**
* Get the current authenticated user
*
* Convenience function to get just the user from context.
*
* @returns Current user or undefined
*
* @example
* ```typescript
* const user = getCurrentUser();
* if (user) {
* console.log("Hello,", user.name);
* }
* ```
*/
export function getCurrentUser() {
return (authContextStorage.getStore() ?? globalAuthContext.get())?.user;
}
/**
* Get the current session
*
* Convenience function to get just the session from context.
*
* @returns Current session or undefined
*/
export function getCurrentSession() {
return (authContextStorage.getStore() ?? globalAuthContext.get())?.session;
}
/**
* Check if the current request is authenticated
*
* @returns True if authenticated, false otherwise
*/
export function isAuthenticated() {
return (authContextStorage.getStore() !== undefined ||
globalAuthContext.isAuthenticated());
}
/**
* Require authentication
*
* Throws if no auth context is available.
*
* @returns The authenticated context
* @throws Error if not authenticated
*
* @example
* ```typescript
* const context = requireAuth();
* // Safe to use context.user here
* ```
*/
export function requireAuth() {
const context = getAuthContext();
if (!context) {
throw AuthError.create("MISSING_TOKEN", "Authentication required");
}
return context;
}
/**
* Require a specific user
*
* Throws if no auth context or user doesn't match.
*
* @param userId - Expected user ID
* @returns The authenticated context
* @throws Error if not authenticated or wrong user
*/
export function requireUser(userId) {
const context = requireAuth();
if (context.user.id !== userId) {
throw AuthError.create("ACCESS_DENIED", "User mismatch");
}
return context;
}
/**
* Check if current user has a permission
*
* @param permission - Permission to check
* @returns True if user has permission
*/
export function hasPermission(permission) {
const user = getCurrentUser();
if (!user) {
return false;
}
// Check direct permission
if (user.permissions.includes(permission)) {
return true;
}
// Check wildcard
if (user.permissions.includes("*")) {
return true;
}
// Check permission hierarchy
const parts = permission.split(":");
for (let i = parts.length - 1; i > 0; i--) {
const wildcardPerm = [...parts.slice(0, i), "*"].join(":");
if (user.permissions.includes(wildcardPerm)) {
return true;
}
}
return false;
}
/**
* Check if current user has a role
*
* @param role - Role to check
* @returns True if user has role
*/
export function hasRole(role) {
const user = getCurrentUser();
return user?.roles.includes(role) ?? false;
}
/**
* Check if current user has any of the roles
*
* @param roles - Roles to check (any of)
* @returns True if user has at least one role
*/
export function hasAnyRole(roles) {
const user = getCurrentUser();
if (!user) {
return false;
}
return roles.some((role) => user.roles.includes(role));
}
/**
* Check if current user has all permissions
*
* @param permissions - Permissions to check (all of)
* @returns True if user has all permissions
*/
export function hasAllPermissions(permissions) {
return permissions.every((perm) => hasPermission(perm));
}
/**
* Require a permission
*
* Throws if user doesn't have the permission.
*
* @param permission - Required permission
* @throws Error if user lacks permission
*
* @example
* ```typescript
* requirePermission("admin:write");
* // Safe to proceed with admin write operation
* ```
*/
export function requirePermission(permission) {
if (!hasPermission(permission)) {
throw AuthError.create("INSUFFICIENT_PERMISSIONS", `Permission denied: ${permission}`);
}
}
/**
* Require a role
*
* Throws if user doesn't have the role.
*
* @param role - Required role
* @throws Error if user lacks role
*/
export function requireRole(role) {
if (!hasRole(role)) {
throw AuthError.create("INSUFFICIENT_ROLES", `Role required: ${role}`);
}
}
/**
* Create an authenticated context
*
* Helper to build an AuthenticatedContext object.
*
* @param user - The authenticated user
* @param session - The user's session
* @param request - The original request context
* @param provider - The auth provider type
* @returns Complete authenticated context
*/
export function createAuthenticatedContext(user, session, request, provider) {
return {
...request,
user,
session,
request,
authenticatedAt: new Date(),
provider,
};
}
/**
* Context holder for non-async-local-storage environments
*
* Use this when async local storage is not available.
*/
export class AuthContextHolder {
context;
/**
* Set the auth context
*/
set(context) {
this.context = context;
}
/**
* Get the auth context
*/
get() {
return this.context;
}
/**
* Clear the auth context
*/
clear() {
this.context = undefined;
}
/**
* Get the current user
*/
getUser() {
return this.context?.user;
}
/**
* Get the current session
*/
getSession() {
return this.context?.session;
}
/**
* Check if authenticated
*/
isAuthenticated() {
return this.context !== undefined;
}
/**
* Check if user has permission
*/
hasPermission(permission) {
const user = this.context?.user;
if (!user) {
return false;
}
if (user.permissions.includes(permission) ||
user.permissions.includes("*")) {
return true;
}
// Check hierarchy
const parts = permission.split(":");
for (let i = parts.length - 1; i > 0; i--) {
const wildcardPerm = [...parts.slice(0, i), "*"].join(":");
if (user.permissions.includes(wildcardPerm)) {
return true;
}
}
return false;
}
/**
* Check if user has role
*/
hasRole(role) {
return this.context?.user.roles.includes(role) ?? false;
}
}
// Internal: global context holder for non-async environments.
// Exported for use by neurolink.ts; not part of the public API contract.
export const globalAuthContext = new AuthContextHolder();
//# sourceMappingURL=authContext.js.map