In this lesson, you will learn how to design a logic layer that restricts user access to specific resource IDs based on custom-defined authorization scopes.
While standard OAuth2 scopes like read or write are useful, they are often too broad. In complex applications, you frequently need to restrict a user to a specific subset of data, such as read:invoice:123. To implement this, we move beyond simple role checks and introduce a Scope Evaluator—a logic layer that parses requested scopes against the specific resource ID being accessed.
The Logic Layer Approach
Instead of hardcoding checks in your controllers, create a middleware or service that verifies if the user’s token contains a scope that matches both the action and the resource ID.
Consider this implementation pattern:
// A simple scope evaluator
const hasResourceAccess = (userScopes, action, resourceId) => {
// Check for granular scope: "read:invoice:123"
const granularScope = `${action}:invoice:${resourceId}`;
// Or check for a broad admin scope
return userScopes.includes(granularScope) || userScopes.includes('admin:all');
};
// Usage in a route
app.get('/invoices/:id', (req, res) => {
const { id } = req.params;
if (!hasResourceAccess(req.user.scopes, 'read', id)) {
return res.status(403).send('Forbidden: Insufficient Scope');
}
// Proceed to fetch the invoice...
});
Why This Matters
By building this layer, you decouple your business logic from security rules. When a request comes in, the logic layer acts as a gatekeeper. If the user only has permission for invoice:123, the system rejects any attempt to fetch invoice:456, even if the user is authenticated and has the generic read permission.
This approach prevents Insecure Direct Object Reference (IDOR) vulnerabilities by ensuring that identity-based scoping is enforced at the earliest possible stage of the request lifecycle. It allows your system to scale security policies dynamically without altering your underlying database queries.
Key Takeaway: Restrict access by mapping custom scopes to specific resource IDs within a dedicated logic layer to ensure fine-grained authorization.