File size: 2,607 Bytes
57a1132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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
};