API Authorization — Complete Guide to Access Control
In this tutorial, you will learn about API Authorization. We cover key concepts, practical examples, and best practices to help you master this topic.
API authorization controls what authenticated users can do, using role-based access control (RBAC), attribute-based access control (ABAC), and permission scopes to enforce granular access policies.
What You'll Learn
- RBAC vs ABAC authorization models
- Implementing permission checks in API middleware
- OAuth 2.0 scopes for delegated authorization
Why It Matters
Authentication only confirms identity; without authorization, any authenticated user can access any resource. Authorization ensures users can only perform actions they are permitted to.
Real-World Use
Durga Antivirus Pro API has three roles: admin (full access), analyst (read threats, write reports), and viewer (read-only). A middleware checks the user role on every request before allowing the operation.
flowchart LR
R["Request"] --> A["Authenticated?"]
A -->|"Yes"| P["Check Permissions"]
A -->|"No"| 401["401 Unauthorized"]
P -->|"Allowed"| H["Handle Request"]
P -->|"Denied"| 403["403 Forbidden"]
style P fill:#dbeafe,stroke:#2563eb
Code Examples
# RBAC middleware
from flask import Flask, request, jsonify
from functools import wraps
app = Flask(__name__)
ROLES = {
'admin': ['read', 'write', 'delete', 'manage'],
'analyst': ['read', 'write'],
'viewer': ['read'],
}
def require_permission(permission):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
user = request.user # Set by auth middleware
if not user:
return jsonify({'error': 'Not authenticated'}), 401
user_permissions = ROLES.get(user['role'], [])
if permission not in user_permissions:
return jsonify({'error': 'Forbidden'}), 403
return f(*args, **kwargs)
return wrapper
return decorator
@app.route('/api/threats', methods=['GET'])
@require_permission('read')
def list_threats():
return jsonify({'threats': []})
@app.route('/api/threats', methods=['POST'])
@require_permission('write')
def create_threat():
return jsonify({'created': True})
Expected output: GET requires 'read' permission; POST requires 'write' permission; unauthorized users get 403.
// Scope-based authorization with OAuth 2.0 scopes
const express = require('express');
const app = express();
const SCOPE_HIERARCHY = {
'threats:read': ['threats:read'],
'threats:write': ['threats:read', 'threats:write'],
'admin': ['threats:read', 'threats:write', 'users:manage'],
};
function requireScope(requiredScope) {
return (req, res, next) => {
const tokenScopes = req.token.scopes || [];
const userScopes = SCOPE_HIERARCHY[requiredScope] || [];
// Check if user has at least the required scope
const authorized = userScopes.some(s => tokenScopes.includes(s));
if (!authorized) {
return res.status(403).json({ error: 'Insufficient scope' });
}
next();
};
}
app.get('/api/threats', requireScope('threats:read'), (req, res) => {
res.json({ threats: [] });
});
app.post('/api/threats', requireScope('threats:write'), (req, res) => {
res.status(201).json({ id: 1 });
});
Expected output: Token with threats:read scope can read but not write; threats:write scope can do both.
# Attribute-based access control (ABAC)
from flask import Flask, request, jsonify
app = Flask(__name__)
def check_abac(user, resource, action):
# Rule: Users can only access their own resources
if resource.get('owner_id') == user['id']:
return True
# Rule: Admins can access any resource
if user['role'] == 'admin':
return True
# Rule: Analysts can read any resource but only write their own
if user['role'] == 'analyst' and action == 'read':
return True
return False
@app.route('/api/orders/<order_id>', methods=['GET'])
def get_order(order_id):
order = {'id': order_id, 'owner_id': 'user_123'}
if not check_abac(request.user, order, 'read'):
return jsonify({'error': 'Forbidden'}), 403
return jsonify(order)
Expected output: ABAC evaluates user attributes, resource attributes, and action to make access decisions.
Common Mistakes
1. Authorization After Business Logic
Checking permissions after processing the request wastes resources. Check authorization first.
2. Hardcoding User IDs in Permissions
Role-based permissions are maintainable; user-specific permissions become unmanageable at scale.
3. Not Checking Authorization on Every Request
Assuming authenticated users have full access is a common security gap. Check permissions per-endpoint.
4. Exposing Permission Errors in Production
Detailed permission error messages reveal access control structure. Return generic 403.
5. Mixing AuthN and AuthZ Logic
Authentication and authorization should be separate middleware layers for maintainability.
Practice Questions
- What is the difference between RBAC and ABAC?
- Why should authorization be checked before business logic?
- What is the principle of Least Privilege?
- How do OAuth 2.0 scopes relate to authorization?
- Why should authN and authZ be separate middleware?
Answers:
- RBAC grants permissions based on role; ABAC evaluates user, resource, and environment attributes.
- Checking after processing wastes resources and may expose sensitive data before the denial.
- Users should have the minimum permissions needed to perform their tasks, nothing more.
- Scopes define specific permissions a client requests, and the token grants only those scopes.
- Separation allows each concern to be tested, modified, and reused independently.
Challenge: Implement RBAC for a document management API with roles: admin (all), editor (create/edit, delete own), viewer (read). Include permission middleware, test each role against all endpoints.
FAQ
Mini Project
Build an authorization system for a project management API with three roles: Admin (manage all), Manager (create/edit projects, manage team), Member (view and comment). Implement RBAC middleware and write tests verifying each endpoint's access rules.
What's Next
Learn about API authentication methods to pair with authorization, or explore API security patterns for comprehensive protection.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro