Scope management utilities for AuthX.
This module provides functions for matching and validating scopes with support for: - Simple string scopes (e.g., "read", "write", "admin") - Hierarchical scopes with colon separator (e.g., "users:read", "posts:write") - Wildcard patterns (e.g., "admin:*" matches "admin:users", "admin:settings")
match_scope
match_scope(required, provided)
Check if a provided scope matches a required scope.
Supports wildcard matching where a scope ending with ":*" matches any scope under that namespace.
| PARAMETER | DESCRIPTION |
required | The scope that is required (e.g., "users:read"). TYPE: str |
provided | The scope that was provided in the token (e.g., "users:*"). TYPE: str |
| RETURNS | DESCRIPTION |
bool | True if the provided scope satisfies the required scope. |
Examples:
>>> match_scope("read", "read")
True
>>> match_scope("users:read", "users:*")
True
>>> match_scope("users:read", "admin:*")
False
>>> match_scope("admin", "admin:*")
True
>>> match_scope("admin:users:edit", "admin:*")
True
Source code in authx/_internal/_scopes.py
| def match_scope(required: str, provided: str) -> bool:
"""Check if a provided scope matches a required scope.
Supports wildcard matching where a scope ending with ":*" matches
any scope under that namespace.
Args:
required: The scope that is required (e.g., "users:read").
provided: The scope that was provided in the token (e.g., "users:*").
Returns:
True if the provided scope satisfies the required scope.
Examples:
>>> match_scope("read", "read")
True
>>> match_scope("users:read", "users:*")
True
>>> match_scope("users:read", "admin:*")
False
>>> match_scope("admin", "admin:*")
True
>>> match_scope("admin:users:edit", "admin:*")
True
"""
# Exact match
if required == provided:
return True
# Wildcard match: "admin:*" should match "admin", "admin:users", "admin:users:edit"
if provided.endswith(":*"):
prefix = provided[:-1] # Remove the "*" to get "admin:"
namespace = provided[:-2] # Remove ":*" to get "admin"
# Match if required starts with "admin:" or equals "admin"
return required.startswith(prefix) or required == namespace
return False
|
has_required_scopes
has_required_scopes(required, provided, all_required=True)
Check if the provided scopes satisfy the required scopes.
| PARAMETER | DESCRIPTION |
required | List of scopes that are required. TYPE: Sequence[str] |
provided | List of scopes that were provided in the token. TYPE: Optional[Sequence[str]] |
all_required | If True, all required scopes must be satisfied (AND logic). If False, at least one required scope must be satisfied (OR logic). TYPE: bool DEFAULT: True |
| RETURNS | DESCRIPTION |
bool | True if the scope requirements are satisfied. |
Examples:
>>> has_required_scopes(["read"], ["read", "write"], all_required=True)
True
>>> has_required_scopes(["read", "admin"], ["read"], all_required=True)
False
>>> has_required_scopes(["read", "admin"], ["read"], all_required=False)
True
>>> has_required_scopes(["users:read"], ["users:*"], all_required=True)
True
Source code in authx/_internal/_scopes.py
| def has_required_scopes(
required: Sequence[str],
provided: Optional[Sequence[str]],
all_required: bool = True,
) -> bool:
"""Check if the provided scopes satisfy the required scopes.
Args:
required: List of scopes that are required.
provided: List of scopes that were provided in the token.
all_required: If True, all required scopes must be satisfied (AND logic).
If False, at least one required scope must be satisfied (OR logic).
Returns:
True if the scope requirements are satisfied.
Examples:
>>> has_required_scopes(["read"], ["read", "write"], all_required=True)
True
>>> has_required_scopes(["read", "admin"], ["read"], all_required=True)
False
>>> has_required_scopes(["read", "admin"], ["read"], all_required=False)
True
>>> has_required_scopes(["users:read"], ["users:*"], all_required=True)
True
"""
if provided is None:
return len(required) == 0
def scope_satisfied(req: str) -> bool:
"""Check if a single required scope is satisfied by any provided scope."""
return any(match_scope(req, prov) for prov in provided)
if all_required:
# All required scopes must be satisfied
return all(scope_satisfied(req) for req in required)
else:
# At least one required scope must be satisfied
return any(scope_satisfied(req) for req in required)
|
normalize_scope
Normalize a scope string by stripping whitespace and converting to lowercase.
| PARAMETER | DESCRIPTION |
scope | The scope string to normalize. TYPE: str |
| RETURNS | DESCRIPTION |
str | The normalized scope string. |
Source code in authx/_internal/_scopes.py
| def normalize_scope(scope: str) -> str:
"""Normalize a scope string by stripping whitespace and converting to lowercase.
Args:
scope: The scope string to normalize.
Returns:
The normalized scope string.
"""
return scope.strip().lower()
|
parse_scope_string
parse_scope_string(scope_string, delimiter=' ')
Parse a space-separated scope string into a list of scopes.
This is useful for OAuth2 compatibility where scopes are often passed as a single space-separated string.
| PARAMETER | DESCRIPTION |
scope_string | A string containing scopes separated by the delimiter. TYPE: str |
delimiter | The delimiter used to separate scopes. Defaults to space. TYPE: str DEFAULT: ' ' |
| RETURNS | DESCRIPTION |
list[str] | A list of individual scope strings. |
Examples:
>>> parse_scope_string("read write admin")
["read", "write", "admin"]
>>> parse_scope_string("users:read users:write")
["users:read", "users:write"]
Source code in authx/_internal/_scopes.py
| def parse_scope_string(scope_string: str, delimiter: str = " ") -> list[str]:
"""Parse a space-separated scope string into a list of scopes.
This is useful for OAuth2 compatibility where scopes are often passed
as a single space-separated string.
Args:
scope_string: A string containing scopes separated by the delimiter.
delimiter: The delimiter used to separate scopes. Defaults to space.
Returns:
A list of individual scope strings.
Examples:
>>> parse_scope_string("read write admin")
["read", "write", "admin"]
>>> parse_scope_string("users:read users:write")
["users:read", "users:write"]
"""
if not scope_string:
return []
return [s.strip() for s in scope_string.split(delimiter) if s.strip()]
|