Merge pull request 'fix(auth): Prioritize JWT token over trusted origin bypass' (#24) from fix/auth-token-priority into master

Reviewed-on: https://code.cannabrands.app/Creationshop/dispensary-scraper/pulls/24
This commit is contained in:
kelly
2025-12-11 01:34:10 +00:00

View File

@@ -153,22 +153,10 @@ export async function authenticateUser(email: string, password: string): Promise
} }
export async function authMiddleware(req: AuthRequest, res: Response, next: NextFunction) { export async function authMiddleware(req: AuthRequest, res: Response, next: NextFunction) {
// Allow trusted origins/IPs to bypass auth (internal services, same-origin)
if (isTrustedRequest(req)) {
req.user = {
id: 0,
email: 'internal@system',
role: 'internal'
};
return next();
}
const authHeader = req.headers.authorization; const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) { // If a Bearer token is provided, always try to use it first (logged-in user)
return res.status(401).json({ error: 'No token provided' }); if (authHeader && authHeader.startsWith('Bearer ')) {
}
const token = authHeader.substring(7); const token = authHeader.substring(7);
// Try JWT first // Try JWT first
@@ -187,56 +175,44 @@ export async function authMiddleware(req: AuthRequest, res: Response, next: Next
WHERE token = $1 WHERE token = $1
`, [token]); `, [token]);
if (result.rows.length === 0) { if (result.rows.length > 0) {
const apiToken = result.rows[0];
if (!apiToken.active) {
return res.status(401).json({ error: 'API token is inactive' });
}
if (apiToken.expires_at && new Date(apiToken.expires_at) < new Date()) {
return res.status(401).json({ error: 'API token has expired' });
}
req.user = {
id: 0,
email: `api:${apiToken.name}`,
role: 'api_token'
};
req.apiToken = apiToken;
return next();
}
} catch (err) {
console.error('API token lookup error:', err);
}
// Token provided but invalid
return res.status(401).json({ error: 'Invalid token' }); return res.status(401).json({ error: 'Invalid token' });
} }
const apiToken = result.rows[0]; // No token provided - check trusted origins for API access (WordPress, etc.)
if (isTrustedRequest(req)) {
// Check if token is active
if (!apiToken.active) {
return res.status(401).json({ error: 'Token is disabled' });
}
// Check if token is expired
if (apiToken.expires_at && new Date(apiToken.expires_at) < new Date()) {
return res.status(401).json({ error: 'Token has expired' });
}
// Check allowed endpoints
if (apiToken.allowed_endpoints && apiToken.allowed_endpoints.length > 0) {
const isAllowed = apiToken.allowed_endpoints.some((pattern: string) => {
// Simple wildcard matching
const regex = new RegExp('^' + pattern.replace('*', '.*') + '$');
return regex.test(req.path);
});
if (!isAllowed) {
return res.status(403).json({ error: 'Endpoint not allowed for this token' });
}
}
// Set API token on request for tracking
req.apiToken = {
id: apiToken.id,
name: apiToken.name,
rate_limit: apiToken.rate_limit
};
// Set a generic user for compatibility with existing code
req.user = { req.user = {
id: apiToken.id, id: 0,
email: `api-token-${apiToken.id}@system`, email: 'internal@system',
role: 'api' role: 'internal'
}; };
return next();
next();
} catch (error) {
console.error('Error verifying API token:', error);
return res.status(500).json({ error: 'Authentication failed' });
} }
return res.status(401).json({ error: 'No token provided' });
} }
/** /**
* Require specific role(s) to access endpoint. * Require specific role(s) to access endpoint.
* *