✅ Backend Implementation: - Add Azure AD JWT token validation middleware - Create hybrid authentication system supporting both Azure AD and password auth - Implement auto-provisioning for new Azure AD users - Add admin controls to toggle password authentication - Update all API routes to use hybrid authentication - Add database fields for authentication (password, lastLoginAt) - Create comprehensive auth routes with validation endpoints ✅ Frontend Implementation: - Install and configure Azure MSAL browser library - Create Azure AD authentication service with popup/redirect support - Build hybrid authentication service managing both auth methods - Update Login.vue with modern dual-authentication UI - Implement dynamic password auth toggle based on admin settings - Update App.vue for proper session management and validation - Modify API service to handle both token types ✅ Security Features: - Azure AD tenant validation (Oliver Agency) - Role-based access control with auto-admin assignment - JWT token validation for both auth methods - Automatic user provisioning with proper defaults - Session validation and automatic logout on token expiry ✅ Admin Features: - Toggle password authentication on/off - Manage users from both authentication methods - Full role and agent access control - Azure AD user auto-provisioning as regular users ✅ Configuration: - Azure AD: Tenant e519c2e6-bc6d-4fdf-8d9c-923c2f002385 - Client ID: 9079054c-9620-4757-a256-23413042f1ef - Development redirect URI support - Fallback password authentication for testing 🔧 Technical Stack: - Azure MSAL Browser & Node libraries - JWT token validation and hybrid middleware - Database schema updates with migrations - Vue.js integration with MSAL - Express.js hybrid authentication routes 🚀 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
68 lines
No EOL
1.8 KiB
JavaScript
68 lines
No EOL
1.8 KiB
JavaScript
const jwt = require('jsonwebtoken');
|
|
const User = require('../models/User');
|
|
|
|
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
|
|
|
const authenticateToken = async (req, res, next) => {
|
|
try {
|
|
const authHeader = req.headers['authorization'];
|
|
const token = authHeader && authHeader.split(' ')[1];
|
|
|
|
if (!token) {
|
|
return res.status(401).json({ message: 'Access token required' });
|
|
}
|
|
|
|
const decoded = jwt.verify(token, JWT_SECRET);
|
|
|
|
// Get user from database to ensure they still exist and are active
|
|
const user = await User.findByPk(decoded.id);
|
|
|
|
if (!user || !user.isActive) {
|
|
return res.status(403).json({ message: 'User not found or inactive' });
|
|
}
|
|
|
|
req.user = {
|
|
id: user.id,
|
|
email: user.email,
|
|
name: user.name,
|
|
role: user.preferences?.role || 'user',
|
|
allowedAgents: user.preferences?.allowedAgents || null
|
|
};
|
|
|
|
next();
|
|
} catch (error) {
|
|
if (error.name === 'TokenExpiredError') {
|
|
return res.status(403).json({ message: 'Token expired' });
|
|
}
|
|
if (error.name === 'JsonWebTokenError') {
|
|
return res.status(403).json({ message: 'Invalid token' });
|
|
}
|
|
return res.status(500).json({ message: 'Token verification failed' });
|
|
}
|
|
};
|
|
|
|
const requireAdmin = (req, res, next) => {
|
|
if (!req.user || req.user.role !== 'admin') {
|
|
return res.status(403).json({ message: 'Admin privileges required' });
|
|
}
|
|
next();
|
|
};
|
|
|
|
const generateToken = (user) => {
|
|
return jwt.sign(
|
|
{
|
|
id: user.id,
|
|
email: user.email,
|
|
role: user.preferences?.role || 'user'
|
|
},
|
|
JWT_SECRET,
|
|
{ expiresIn: '24h' }
|
|
);
|
|
};
|
|
|
|
module.exports = {
|
|
authenticateToken,
|
|
requireAdmin,
|
|
generateToken,
|
|
JWT_SECRET
|
|
}; |