Scope-based authorization in Python

A simple Python class that checks user scopes against required permissions for a resource, returning an authorization decision.

Easy Python 3.9+ Aug 9, 2026 API design & gRPC 12 views 0 copies

Python code

27 lines
Python 3.9+
class ScopeAuthorization:
    def __init__(self):
        self.scopes = {
            "read": ["resource:read"],
            "write": ["resource:read", "resource:write"],
            "admin": ["resource:read", "resource:write", "resource:delete"]
        }

    def authorize(self, user_scopes, required_scope, resource):
        if not required_scope in self.scopes:
            return f"Unknown scope: {required_scope}"

        if not user_scopes or not isinstance(user_scopes, list):
            return f"User has no scopes. Required: {required_scope} for {resource}"

        if any(scope in self.scopes[required_scope] for scope in user_scopes):
            return f"Authorized: {resource} (scope: {required_scope})"
        else:
            return f"Denied: {resource} (required scope: {required_scope})"


if __name__ == "__main__":
    auth = ScopeAuthorization()
    print(auth.authorize(["resource:read", "resource:write"], "write", "orders/123"))
    print(auth.authorize(["resource:read"], "write", "orders/123"))
    print(auth.authorize(["resource:read"], "read", "invoices/456"))
    print(auth.authorize([], "read", "reports/789"))

Output

stdout
Authorized: orders/123 (scope: write)
Denied: orders/123 (required scope: write)
Authorized: invoices/456 (scope: read)
User has no scopes. Required: read for reports/789

How it works

The ScopeAuthorization class maps named scopes (like read, write, admin) to granular resource permissions. The authorize method first checks if the required scope is valid, then verifies the user has a non-empty list of scopes, and finally checks for any overlap between the user's scopes and the required scope's permissions. This pattern separates permission definitions from enforcement logic, making it easy to adjust access levels without touching business code. It returns descriptive messages for each case, which can be useful for debugging or logging in real applications.

Common mistakes

  • Using `required_scope in self.scopes` instead of `not required_scope in self.scopes` for the unknown scope check
  • Forgetting to validate that user_scopes is a list before iterating over it
  • Assuming admin automatically gets write access if write doesn't include all admin scopes

Variations

  1. Use a dictionary of dictionaries to define scopes with more granular permissions per resource type
  2. Implement an `is_authorized` method that returns True/False instead of a message

Real-world use cases

  • API gateways that validate JWT claims or OAuth scopes before routing requests to protected endpoints.
  • Dashboard applications that show or hide UI elements based on the logged-in user's role permissions.
  • Background job schedulers that check if a worker token has the required scope to trigger resource maintenance tasks.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from API design & gRPC

Related tutorials and quizzes for this topic.