const jwt = require('jsonwebtoken'); const { jwtDecode } = require('jwt-decode'); const User = require('../models/User'); // Azure AD configuration const AZURE_TENANT_ID = 'e519c2e6-bc6d-4fdf-8d9c-923c2f002385'; const AZURE_CLIENT_ID = '9079054c-9620-4757-a256-23413042f1ef'; const validateAzureToken = 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' }); } // Decode the Azure AD token (without verification for now - in production should verify signature) let decoded; try { decoded = jwtDecode(token); } catch (error) { return res.status(403).json({ message: 'Invalid token format' }); } // Validate Azure AD token claims if (decoded.aud !== AZURE_CLIENT_ID) { return res.status(403).json({ message: 'Invalid token audience' }); } if (decoded.tid !== AZURE_TENANT_ID) { return res.status(403).json({ message: 'Invalid tenant' }); } if (decoded.exp * 1000 < Date.now()) { return res.status(403).json({ message: 'Token expired' }); } // Check if user exists in database, create if not let user = await User.findOne({ where: { email: decoded.email || decoded.upn } }); if (!user) { // Auto-provision new Azure AD user user = await User.create({ email: decoded.email || decoded.upn, name: decoded.name || decoded.given_name + ' ' + decoded.family_name, password: 'azure-ad-user', // Placeholder - not used for Azure users preferences: { theme: 'light', notifications: true, defaultAssistant: 'creator-bot-push-the-boundaries-of-technology', role: getAzureUserRole(decoded.email || decoded.upn), allowedAgents: getAzureUserRole(decoded.email || decoded.upn) === 'admin' ? null : [], authMethod: 'azure' }, isActive: true }); } else { // Update last login for existing user await user.update({ lastLoginAt: new Date(), preferences: { ...user.preferences, authMethod: 'azure' } }); } if (!user.isActive) { return res.status(403).json({ message: 'User account is disabled' }); } // Attach user info to request req.user = { id: user.id, email: user.email, name: user.name, role: user.preferences?.role || 'user', allowedAgents: user.preferences?.allowedAgents || null, authMethod: 'azure', azureId: decoded.oid || decoded.sub }; next(); } catch (error) { console.error('Azure token validation error:', error); return res.status(500).json({ message: 'Token validation failed' }); } }; // Determine user role based on email or Azure group membership const getAzureUserRole = (email) => { // Admin users - add more emails as needed const adminEmails = [ 'daveporter@oliver.agency', // Add other admin emails here ]; return adminEmails.includes(email.toLowerCase()) ? 'admin' : 'user'; }; // Hybrid authentication middleware that handles both Azure AD and JWT tokens const hybridAuthenticate = async (req, res, next) => { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) { return res.status(401).json({ message: 'Access token required' }); } try { // Try to decode as Azure AD token first const decoded = jwtDecode(token); // Check if it's an Azure AD token if (decoded.aud === AZURE_CLIENT_ID && decoded.tid === AZURE_TENANT_ID) { return validateAzureToken(req, res, next); } else { // Fall back to regular JWT validation const { authenticateToken } = require('./auth'); return authenticateToken(req, res, next); } } catch (error) { // If decode fails, try regular JWT validation const { authenticateToken } = require('./auth'); return authenticateToken(req, res, next); } }; module.exports = { validateAzureToken, hybridAuthenticate, getAzureUserRole };