vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
417 lines (416 loc) • 17.3 kB
JavaScript
import crypto from 'crypto';
import { logSecurityEvent } from './audit-logger.js';
import logger from '../../../logger.js';
const ROLE_PERMISSIONS = {
admin: [
'task:create', 'task:read', 'task:update', 'task:delete', 'task:execute',
'project:create', 'project:read', 'project:update', 'project:delete',
'agent:manage', 'agent:assign', 'agent:monitor',
'system:admin', 'system:config', 'system:audit',
'file:read', 'file:write', 'file:execute'
],
manager: [
'task:create', 'task:read', 'task:update', 'task:delete', 'task:execute',
'project:create', 'project:read', 'project:update', 'project:delete',
'agent:assign', 'agent:monitor',
'file:read', 'file:write'
],
developer: [
'task:create', 'task:read', 'task:update', 'task:execute',
'project:read', 'project:update',
'agent:monitor',
'file:read', 'file:write', 'file:execute'
],
viewer: [
'task:read',
'project:read',
'agent:monitor',
'file:read'
],
guest: [
'task:read',
'project:read'
]
};
export class AuthenticationIntegration {
static instance = null;
config;
activeSessions = new Map();
activeTokens = new Map();
sessionCleanupTimer = null;
constructor(config) {
this.config = {
enabled: true,
tokenSecret: process.env.VIBE_AUTH_SECRET || this.generateSecureSecret(),
tokenExpirationMs: 3600000,
refreshTokenExpirationMs: 86400000,
sessionTimeoutMs: 7200000,
maxConcurrentSessions: 5,
enableRoleBasedAccess: true,
enableAuditLogging: true,
requireSecureTokens: true,
allowGuestAccess: false,
...config
};
this.startSessionCleanup();
logger.info({
enabled: this.config.enabled,
roleBasedAccess: this.config.enableRoleBasedAccess,
auditLogging: this.config.enableAuditLogging
}, 'Authentication Integration initialized');
}
static getInstance(config) {
if (!AuthenticationIntegration.instance) {
AuthenticationIntegration.instance = new AuthenticationIntegration(config);
}
return AuthenticationIntegration.instance;
}
async authenticate(userId, role, context) {
if (!this.config.enabled) {
return { success: true };
}
try {
const userSessions = Array.from(this.activeSessions.values())
.filter(session => session.userId === userId && session.isActive);
if (userSessions.length >= this.config.maxConcurrentSessions) {
await logSecurityEvent('authentication', 'medium', 'auth-integration', 'session_limit_exceeded', 'blocked', `User ${userId} exceeded concurrent session limit`, {
actor: { userId, ipAddress: context?.ipAddress, userAgent: context?.userAgent },
metadata: { currentSessions: userSessions.length, limit: this.config.maxConcurrentSessions }
});
return {
success: false,
error: 'Maximum concurrent sessions exceeded'
};
}
const sessionId = this.generateSessionId();
const now = new Date();
const session = {
id: sessionId,
userId,
role,
permissions: this.getRolePermissions(role),
createdAt: now,
lastActivity: now,
expiresAt: new Date(now.getTime() + this.config.sessionTimeoutMs),
ipAddress: context?.ipAddress,
userAgent: context?.userAgent,
isActive: true,
metadata: context?.metadata
};
const token = await this.createAuthToken(userId, sessionId, role, context);
this.activeSessions.set(sessionId, session);
this.activeTokens.set(token.id, token);
await logSecurityEvent('authentication', 'info', 'auth-integration', 'login', 'success', `User ${userId} authenticated successfully`, {
actor: { userId, sessionId, ipAddress: context?.ipAddress, userAgent: context?.userAgent },
metadata: { role, permissions: session.permissions.length }
});
return {
success: true,
token,
session
};
}
catch (error) {
await logSecurityEvent('authentication', 'high', 'auth-integration', 'login', 'failure', `Authentication failed for user ${userId}`, {
actor: { userId, ipAddress: context?.ipAddress, userAgent: context?.userAgent },
metadata: { error: error instanceof Error ? error.message : String(error) }
});
return {
success: false,
error: error instanceof Error ? error.message : 'Authentication failed'
};
}
}
async validateToken(tokenString) {
if (!this.config.enabled) {
return { success: true };
}
try {
const token = Array.from(this.activeTokens.values())
.find(t => t.token === tokenString);
if (!token) {
await logSecurityEvent('authentication', 'medium', 'auth-integration', 'token_validation', 'failure', 'Invalid token provided', { metadata: { tokenPrefix: tokenString.substring(0, 8) } });
return {
success: false,
error: 'Invalid token'
};
}
if (Date.now() > token.expiresAt.getTime()) {
await logSecurityEvent('authentication', 'low', 'auth-integration', 'token_validation', 'failure', 'Expired token used', {
actor: { userId: token.userId, sessionId: token.sessionId },
metadata: { tokenId: token.id, expiredAt: token.expiresAt }
});
return {
success: false,
error: 'Token expired',
requiresRefresh: true
};
}
token.lastUsed = new Date();
const session = this.activeSessions.get(token.sessionId);
if (!session || !session.isActive) {
return {
success: false,
error: 'Session not found or inactive'
};
}
session.lastActivity = new Date();
return {
success: true,
token,
session
};
}
catch (error) {
await logSecurityEvent('authentication', 'high', 'auth-integration', 'token_validation', 'failure', 'Token validation error', { metadata: { error: error instanceof Error ? error.message : String(error) } });
return {
success: false,
error: 'Token validation failed'
};
}
}
async refreshToken(refreshTokenString) {
if (!this.config.enabled) {
return { success: true };
}
try {
const token = Array.from(this.activeTokens.values())
.find(t => t.refreshToken === refreshTokenString);
if (!token) {
await logSecurityEvent('authentication', 'medium', 'auth-integration', 'token_refresh', 'failure', 'Invalid refresh token provided');
return {
success: false,
error: 'Invalid refresh token'
};
}
const session = this.activeSessions.get(token.sessionId);
if (!session || !session.isActive) {
return {
success: false,
error: 'Session not found or inactive'
};
}
const newToken = await this.createAuthToken(token.userId, token.sessionId, session.role, {
ipAddress: token.ipAddress,
userAgent: token.userAgent,
metadata: token.metadata
});
this.activeTokens.delete(token.id);
this.activeTokens.set(newToken.id, newToken);
await logSecurityEvent('authentication', 'info', 'auth-integration', 'token_refresh', 'success', 'Token refreshed successfully', {
actor: { userId: token.userId, sessionId: token.sessionId },
metadata: { oldTokenId: token.id, newTokenId: newToken.id }
});
return {
success: true,
token: newToken,
session
};
}
catch (error) {
await logSecurityEvent('authentication', 'high', 'auth-integration', 'token_refresh', 'failure', 'Token refresh failed', { metadata: { error: error instanceof Error ? error.message : String(error) } });
return {
success: false,
error: 'Token refresh failed'
};
}
}
async authorize(sessionId, permission, resource) {
if (!this.config.enabled || !this.config.enableRoleBasedAccess) {
return { authorized: true };
}
try {
const session = this.activeSessions.get(sessionId);
if (!session || !session.isActive) {
await logSecurityEvent('authorization', 'medium', 'auth-integration', 'permission_check', 'blocked', 'Authorization attempted with invalid session', {
actor: { sessionId },
resource,
metadata: { requiredPermission: permission }
});
return {
authorized: false,
reason: 'Invalid or inactive session',
requiredPermission: permission
};
}
const hasPermission = session.permissions.includes(permission);
if (!hasPermission) {
await logSecurityEvent('authorization', 'medium', 'auth-integration', 'permission_check', 'blocked', `Access denied: insufficient permissions`, {
actor: { userId: session.userId, sessionId },
resource,
metadata: {
requiredPermission: permission,
userRole: session.role,
userPermissions: session.permissions
}
});
return {
authorized: false,
reason: 'Insufficient permissions',
requiredPermission: permission,
userRole: session.role,
userPermissions: session.permissions
};
}
session.lastActivity = new Date();
return {
authorized: true,
userRole: session.role,
userPermissions: session.permissions
};
}
catch (error) {
await logSecurityEvent('authorization', 'high', 'auth-integration', 'permission_check', 'failure', 'Authorization check failed', {
actor: { sessionId },
resource,
metadata: {
error: error instanceof Error ? error.message : String(error),
requiredPermission: permission
}
});
return {
authorized: false,
reason: 'Authorization check failed',
requiredPermission: permission
};
}
}
async logout(sessionId) {
try {
const session = this.activeSessions.get(sessionId);
if (!session) {
return false;
}
session.isActive = false;
const sessionTokens = Array.from(this.activeTokens.entries())
.filter(([, token]) => token.sessionId === sessionId);
for (const [tokenId] of sessionTokens) {
this.activeTokens.delete(tokenId);
}
await logSecurityEvent('authentication', 'info', 'auth-integration', 'logout', 'success', 'User logged out successfully', {
actor: { userId: session.userId, sessionId },
metadata: { tokensRemoved: sessionTokens.length }
});
return true;
}
catch (error) {
await logSecurityEvent('authentication', 'medium', 'auth-integration', 'logout', 'failure', 'Logout failed', {
actor: { sessionId },
metadata: { error: error instanceof Error ? error.message : String(error) }
});
return false;
}
}
async createAuthToken(userId, sessionId, role, context) {
const tokenId = this.generateTokenId();
const now = new Date();
const token = {
id: tokenId,
userId,
sessionId,
token: this.generateSecureToken(),
refreshToken: this.generateSecureToken(),
issuedAt: now,
expiresAt: new Date(now.getTime() + this.config.tokenExpirationMs),
lastUsed: now,
ipAddress: context?.ipAddress,
userAgent: context?.userAgent,
permissions: this.getRolePermissions(role),
role,
metadata: context?.metadata
};
return token;
}
getRolePermissions(role) {
return [...ROLE_PERMISSIONS[role]];
}
generateSessionId() {
return `session_${crypto.randomBytes(16).toString('hex')}_${Date.now()}`;
}
generateTokenId() {
return `token_${crypto.randomBytes(16).toString('hex')}_${Date.now()}`;
}
generateSecureToken() {
const payload = {
random: crypto.randomBytes(32).toString('hex'),
timestamp: Date.now()
};
const signature = crypto
.createHmac('sha256', this.config.tokenSecret)
.update(JSON.stringify(payload))
.digest('hex');
return `${Buffer.from(JSON.stringify(payload)).toString('base64')}.${signature}`;
}
generateSecureSecret() {
return crypto.randomBytes(64).toString('hex');
}
startSessionCleanup() {
this.sessionCleanupTimer = setInterval(() => {
this.cleanupExpiredSessions();
}, 300000);
}
async cleanupExpiredSessions() {
const now = Date.now();
let cleanedSessions = 0;
let cleanedTokens = 0;
for (const [sessionId, session] of this.activeSessions) {
if (now > session.expiresAt.getTime() ||
(session.isActive && now - session.lastActivity.getTime() > this.config.sessionTimeoutMs)) {
session.isActive = false;
this.activeSessions.delete(sessionId);
cleanedSessions++;
await logSecurityEvent('authentication', 'info', 'auth-integration', 'session_cleanup', 'success', 'Expired session cleaned up', {
actor: { userId: session.userId, sessionId },
metadata: { expiredAt: session.expiresAt, lastActivity: session.lastActivity }
});
}
}
for (const [tokenId, token] of this.activeTokens) {
if (now > token.expiresAt.getTime() || !this.activeSessions.has(token.sessionId)) {
this.activeTokens.delete(tokenId);
cleanedTokens++;
}
}
if (cleanedSessions > 0 || cleanedTokens > 0) {
logger.debug({
cleanedSessions,
cleanedTokens,
activeSessions: this.activeSessions.size,
activeTokens: this.activeTokens.size
}, 'Session and token cleanup completed');
}
}
getAuthenticationStatistics() {
const sessionsByRole = {};
let totalSessionDuration = 0;
let sessionCount = 0;
for (const session of this.activeSessions.values()) {
if (session.isActive) {
sessionsByRole[session.role] = (sessionsByRole[session.role] || 0) + 1;
totalSessionDuration += Date.now() - session.createdAt.getTime();
sessionCount++;
}
}
return {
activeSessions: this.activeSessions.size,
activeTokens: this.activeTokens.size,
sessionsByRole,
averageSessionDuration: sessionCount > 0 ? totalSessionDuration / sessionCount : 0
};
}
async shutdown() {
if (this.sessionCleanupTimer) {
clearInterval(this.sessionCleanupTimer);
}
const sessionIds = Array.from(this.activeSessions.keys());
for (const sessionId of sessionIds) {
await this.logout(sessionId);
}
this.activeSessions.clear();
this.activeTokens.clear();
logger.info('Authentication Integration shutdown');
}
}
export function getAuthenticationIntegration() {
return AuthenticationIntegration.getInstance();
}