deckhearth/lib/permission-middleware.js
Randall Stillwell 85131240e5 🔍 Add Authentication Debugging
- Added debug logging to getUserFromRequest function
- Log when no auth header is found vs when token verification fails
- Log token verification attempts and decoded results
- This will help identify why 'All My Cards' collection lookup is failing

The issue appears to be that the development fallback always returns admin user,
but users are trying to access collections belonging to other users (like Bob).
This debugging will help us see if tokens are being sent properly.
2025-07-27 21:22:25 -05:00

162 lines
4.7 KiB
JavaScript

import { sql } from '@vercel/postgres';
import jwt from 'jsonwebtoken';
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
/**
* Get user ID from request headers
*/
export async function getUserFromRequest(req) {
try {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
// For development, return user ID 1 if no token (should be removed in production)
console.warn('⚠️ Development mode: Using fallback user authentication');
console.warn('⚠️ No auth header found:', authHeader);
return { userId: 1, email: 'admin@tcgvault.com', role: 'admin' };
}
const token = authHeader.substring(7);
console.log('🔍 Attempting to verify token:', token.substring(0, 20) + '...');
const decoded = jwt.verify(token, JWT_SECRET);
console.log('✅ Token decoded successfully:', decoded);
// Get user data from database
const result = await sql`
SELECT id, email, role
FROM users
WHERE id = ${decoded.userId}
`;
if (result.rows.length === 0) {
return null;
}
return {
userId: result.rows[0].id,
email: result.rows[0].email,
role: result.rows[0].role
};
} catch (error) {
console.error('Error getting user from request:', error);
return null;
}
}
/**
* Check if user has permission to access a collection
*/
export async function checkCollectionPermission(collectionId, userId, requiredPermission = 'viewer') {
try {
// Get collection and user permission
const result = await sql`
SELECT
c.id,
c.user_id as owner_id,
c.is_public,
cp.role,
cp.status
FROM collections c
LEFT JOIN collection_permissions cp ON c.id = cp.collection_id AND cp.user_id = ${userId}
WHERE c.id = ${collectionId}
`;
if (result.rows.length === 0) {
return { hasAccess: false, reason: 'Collection not found' };
}
const collection = result.rows[0];
// Owner always has access
if (collection.owner_id === userId) {
return { hasAccess: true, role: 'owner', collection };
}
// Public collections - everyone can view (but not edit)
if (collection.is_public && requiredPermission === 'viewer') {
return { hasAccess: true, role: 'viewer', collection };
}
// Check explicit permissions
if (collection.role && collection.status === 'active') {
const hasRequiredPermission = checkRolePermission(collection.role, requiredPermission);
if (hasRequiredPermission) {
return { hasAccess: true, role: collection.role, collection };
}
}
// Collections without explicit permission
return { hasAccess: false, reason: 'Access denied' };
} catch (error) {
console.error('Error checking collection permission:', error);
return { hasAccess: false, reason: 'Internal error' };
}
}
/**
* Check if a role has the required permission level
*/
function checkRolePermission(userRole, requiredPermission) {
const roleHierarchy = {
viewer: 1,
editor: 2,
owner: 3
};
const userLevel = roleHierarchy[userRole] || 0;
const requiredLevel = roleHierarchy[requiredPermission] || 0;
return userLevel >= requiredLevel;
}
/**
* Middleware to protect collection routes
*/
export function withCollectionPermission(requiredPermission = 'viewer') {
return function(handler) {
return async function(req, res) {
try {
const { id: collectionId } = req.query;
if (!collectionId) {
return res.status(400).json({ error: 'Collection ID is required' });
}
const user = await getUserFromRequest(req);
if (!user) {
return res.status(401).json({ error: 'Authentication required' });
}
const permission = await checkCollectionPermission(collectionId, user.userId, requiredPermission);
if (!permission.hasAccess) {
return res.status(403).json({ error: permission.reason || 'Access denied' });
}
// Add user and permission info to request
req.user = user;
req.permission = permission;
return handler(req, res);
} catch (error) {
console.error('Permission middleware error:', error);
return res.status(500).json({ error: 'Internal server error' });
}
};
};
}
/**
* Log collection activity
*/
export async function logCollectionActivity(collectionId, userId, action, details = {}) {
try {
await sql`
INSERT INTO collection_activity (collection_id, user_id, action, details)
VALUES (${collectionId}, ${userId}, ${action}, ${JSON.stringify(details)})
`;
} catch (error) {
console.error('Error logging collection activity:', error);
}
}