flow-nexus
Version:
š AI-Powered Swarm Intelligence Platform - Gamified MCP Development with 70+ Tools
242 lines (215 loc) ⢠7.62 kB
text/typescript
/**
* Authentication Middleware
* Checks if user is authenticated before allowing tool access
*/
import { secureConfig } from '../config/secure-config';
import { createClient } from '@supabase/supabase-js';
// Tools that don't require authentication
const PUBLIC_TOOLS = [
'auth.init',
'auth.register',
'auth.login',
'auth.checkAuth',
'system.info',
'system.help'
];
export interface AuthCheckResult {
allowed: boolean;
message?: string;
requiresAuth?: boolean;
}
export class AuthMiddleware {
/**
* Check if a tool requires authentication
*/
public static requiresAuth(toolName: string): boolean {
// Check if tool is in public list
if (PUBLIC_TOOLS.includes(toolName)) {
return false;
}
// All other tools require authentication
return true;
}
/**
* Validate authentication for tool access
*/
public static validateAccess(toolName: string): AuthCheckResult {
// Check if tool requires auth
if (!this.requiresAuth(toolName)) {
return { allowed: true };
}
// Check if user is authenticated
if (!secureConfig.isAuthenticated()) {
return {
allowed: false,
requiresAuth: true,
message: `
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā š AUTHENTICATION REQUIRED ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā£
ā ā
ā The tool "${toolName}" requires authentication. ā
ā ā
ā Please authenticate first: ā
ā $ mcp-flow init (first-time setup) ā
ā $ mcp-flow login (existing users) ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
`
};
}
// Check credits for paid tools
const userConfig = secureConfig.getUserConfig();
if (this.isPaidTool(toolName)) {
const creditCost = this.getToolCreditCost(toolName);
if ((userConfig.credits || 0) < creditCost) {
return {
allowed: false,
message: `
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā š³ INSUFFICIENT CREDITS ā
ā āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā£
ā ā
ā Tool: ${toolName.padEnd(51)}ā
ā Cost: ${creditCost.toString().padEnd(51)}ā
ā Your Balance: ${(userConfig.credits || 0).toString().padEnd(43)}ā
ā ā
ā Please purchase more credits to continue. ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
`
};
}
}
return { allowed: true };
}
/**
* Check if tool requires credits
*/
private static isPaidTool(toolName: string): boolean {
const paidTools = [
'swarm_init',
'agent_spawn',
'task_orchestrate',
'neural_train',
'neural_predict',
'neural_cluster_init',
'neural_node_deploy',
'daa_agent_create',
'github_pr_manage',
'workflow_execute',
'workflow_create',
'sandbox_create',
'sandbox_execute',
'sandbox_stop',
'sandbox_configure'
];
return paidTools.some(tool => toolName.includes(tool));
}
/**
* Get credit cost for a tool
*/
private static getToolCreditCost(toolName: string): number {
const costs: { [key: string]: number } = {
'swarm_init': 10,
'agent_spawn': 5,
'task_orchestrate': 8,
'neural_train': 15,
'daa_agent_create': 12,
'github_pr_manage': 3,
'workflow_execute': 20
};
for (const [tool, cost] of Object.entries(costs)) {
if (toolName.includes(tool)) {
return cost;
}
}
return 1; // Default cost
}
/**
* Deduct credits after successful tool execution using meter_events
* This triggers automatic credit deduction via database trigger
*/
public static async deductCredits(toolName: string, metadata?: any): Promise<void> {
try {
// Get user config for user ID
const userConfig = secureConfig.getUserConfig();
if (!userConfig.userId) {
console.error('No user ID found for credit deduction');
return;
}
// Get Supabase config
const supabaseConfig = secureConfig.getSupabaseConfig();
if (!supabaseConfig) {
console.error('No Supabase config available');
return;
}
// Create Supabase client
const supabase = createClient(
supabaseConfig.url,
supabaseConfig.anonKey
);
// Insert into meter_events - this triggers automatic credit deduction
const { error } = await supabase
.from('meter_events')
.insert({
user_id: userConfig.userId,
event_name: toolName, // Must match tool_costs.tool_name
value: 1, // Quantity
metadata: metadata || {}
});
if (error) {
console.error('Failed to record meter event:', error);
} else {
console.log(`Credit deduction triggered for ${toolName}`);
}
} catch (error) {
console.error('Error in deductCredits:', error);
}
}
/**
* Get authentication status summary
*/
public static getAuthStatus(): string {
if (!secureConfig.isAuthenticated()) {
return 'Not authenticated - Run: mcp-flow init';
}
const userConfig = secureConfig.getUserConfig();
return `Authenticated as: ${userConfig.email} | Credits: ${userConfig.credits || 0}`;
}
}
/**
* Middleware wrapper for tool execution
*/
export function withAuth(toolName: string, handler: Function) {
return async (...args: any[]) => {
// Check authentication
const authCheck = AuthMiddleware.validateAccess(toolName);
if (!authCheck.allowed) {
return {
success: false,
error: 'Authentication required',
message: authCheck.message
};
}
try {
// Execute the tool
const result = await handler(...args);
// Deduct credits if successful
if (result.success !== false) {
// Pass the tool parameters as metadata for logging
const metadata = {
parameters: args[0] || {},
timestamp: new Date().toISOString()
};
await AuthMiddleware.deductCredits(toolName, metadata);
}
return result;
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Tool execution failed'
};
}
};
}