Skip to content

API Authorization — Complete Guide to Access Control

DodaTech Updated 2026-06-28 4 min read

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

  1. What is the difference between RBAC and ABAC?
  2. Why should authorization be checked before business logic?
  3. What is the principle of Least Privilege?
  4. How do OAuth 2.0 scopes relate to authorization?
  5. Why should authN and authZ be separate middleware?

Answers:

  1. RBAC grants permissions based on role; ABAC evaluates user, resource, and environment attributes.
  2. Checking after processing wastes resources and may expose sensitive data before the denial.
  3. Users should have the minimum permissions needed to perform their tasks, nothing more.
  4. Scopes define specific permissions a client requests, and the token grants only those scopes.
  5. 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

What is the principle of least privilege?

: Grant users the minimum permissions required to do their work, reducing the Blast Radius of compromised accounts.

How do you handle authorization for multi-tenant APIs?

: Combine role checks with tenant isolation: users can only access resources within their tenant.

Can authorization be implemented at the API Gateway?

: Yes, gateways can enforce coarse authorization; fine-grained authorization belongs in the service.

What is the difference between 401 and 403?

: 401 Unauthenticated (not logged in); 403 Forbidden (logged in but not permitted).

How do you test authorization logic?

: Write tests for each role against each endpoint, verifying allowed and denied access.

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