const { createClient } = require('@supabase/supabase-js'); const ws = require('ws'); const supabase = require('../db'); /** * Helper to generate a tenant-scoped Supabase client dynamically per request. * This injects the active user JWT token into the client request headers, * which triggers Row-Level Security (RLS) policies inside PostgreSQL natively. */ const getTenantDb = (req) => { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return supabase; // Fallback to global admin client } const token = authHeader.split(' ')[1]; return createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY, { auth: { persistSession: false }, realtime: { transport: ws }, global: { headers: { Authorization: `Bearer ${token}` } } }); }; /** * Express Middleware to validate Supabase JWT session and extract tenant/role metadata. */ const requireAuth = async (req, res, next) => { const authHeader = req.headers.authorization; if (!authHeader || !authHeader.startsWith('Bearer ')) { return res.status(401).json({ error: 'Authorization token is missing or malformed' }); } const token = authHeader.split(' ')[1]; try { const { data: { user }, error } = await supabase.auth.getUser(token); if (error || !user) { return res.status(401).json({ error: 'Invalid or expired auth session' }); } // Retrieve custom SaaS tenancy properties from user metadata claims const userMetadata = user.user_metadata || {}; req.user = { id: user.id, email: user.email, role: userMetadata.role || 'Counselor', organization_id: userMetadata.organization_id || '00000000-0000-0000-0000-000000000001', // Fallback to Default Org branch_id: userMetadata.branch_id || '00000000-0000-0000-0000-000000000002', // Fallback to Default Branch }; next(); } catch (err) { console.error('Authentication check failed:', err); return res.status(500).json({ error: 'Internal Auth Validation Server Error' }); } }; /** * Express Middleware to restrict endpoints based on counselor roles. */ const requireRole = (allowedRoles) => { return (req, res, next) => { if (!req.user) { return res.status(401).json({ error: 'User context is unauthenticated' }); } if (!allowedRoles.includes(req.user.role)) { return res.status(403).json({ error: 'Forbidden: Role privileges exceeded' }); } next(); }; }; module.exports = { getTenantDb, requireAuth, requireRole };