pkg-auth
Clean-architecture identity + ACL for multi-framework Python services. Handles JWT authentication (via Keycloak) and database-backed authorization (users, organizations, roles, permissions, memberships) in a single package with first-class support for FastAPI, Django, and Strawberry GraphQL.
v3.0.0 is a breaking change. The
permissions.is_platformflag is replaced by a tri-statevisibilityenum (platform_only/shared/tenant_only); permissiondescriptionis now a localized JSONB map; and a first-class Service model with a default-deny service guard is added. Seedocs/Upgrade-Service-Guard.mdfor the 3.0.0 upgrade guide. Earlier migrations:docs/MIGRATION_v1.md,docs/MIGRATION_v2.md.
Install
# Core (identity only — no DB deps)
pip install pkg-auth
# With ACL + FastAPI (most common for fritill services)
pip install "pkg-auth[acl-sqlalchemy,fastapi]"
# With ACL + Django
pip install "pkg-auth[acl-django,django]"
# With optional Redis cache
pip install "pkg-auth[cache-redis]"
# Everything
pip install "pkg-auth[all]"
Available extras: acl-sqlalchemy, acl-django, cache-redis, fastapi, django, strawberry, all.
Quickstart (FastAPI)
from fastapi import Depends, FastAPI
from pkg_auth.authentication import IdentityContext
from pkg_auth.authorization import AuthContext
from pkg_auth.integrations.fastapi import (
create_authentication,
make_get_auth_context,
require_permission,
)
# --- Wire authentication + authorization ---
auth = create_authentication(
keycloak_base_url="https://auth.example.com",
realm="fritill",
audience="courses-service",
)
# Mode B (consumer — the common case): pass resolve_user_use_case.
# Mode A (source-of-truth): pass sync_user_use_case instead. Exactly
# one of the two is required; passing both raises ValueError.
get_auth_context = make_get_auth_context(
get_identity=auth.get_identity,
resolve_user_use_case=resolve_user, # or: sync_user_use_case=sync_user (Mode A)
resolve_use_case=resolve,
organization_repo=org_repo,
)
app = FastAPI()
# --- Use in routes ---
@app.get("/courses/{id}")
async def get_course(
id: str,
bundle: tuple[IdentityContext, AuthContext] = Depends(
require_permission("course:view", get_auth_context=get_auth_context)
),
):
identity, auth_ctx = bundle
return {"course_id": id, "roles": sorted(auth_ctx.role_names)}
See examples/itqadem_courses_app for a complete working example.
Features
- Authentication — Keycloak JWT validation producing an
IdentityContext. - Authorization (ACL) — database-backed users, organizations, roles, permissions, and memberships, resolved into a hot-path
AuthContext(role_names,perms). - Permission visibility — tri-state catalog visibility (
platform_only/shared/tenant_only). - Localized descriptions — permission descriptions stored as localized JSONB (
LocalizedText,ACL_DEFAULT_LOCALE). - Services & service guard — first-class
Service/OrganizationServicemodel with a default-deny service guard; the platform org bypasses it. - Mode A / Mode B — be the source of truth for the
userstable, or consume a shared ACL read-only. - Integrations — FastAPI, Django, and Strawberry GraphQL.
- CLIs —
keycloak-init-client,pkg-auth-sync-catalog,pkg-auth-sync-services.
Architecture
pkg_auth/
authentication/ JWT validation → IdentityContext (identity only)
authorization/ Full ACL (users, orgs, roles, perms, memberships)
domain/ Pure entities, ports (Protocol), exceptions
application/use_cases/ Business logic (use cases)
adapters/
sqlalchemy/ Canonical schema + Alembic migrations (0001–0005) + repos
django_orm/ Mirror models (managed=False) + repos
cache/ InMemoryTTLCache / RedisCache + decorator
integrations/
fastapi/ Deps + require_permission + exception handlers
django/ Middleware + decorators
strawberry/ Context getter + permission classes
admin/ Keycloak admin client (user provisioning)
Layering rules: domain has zero external imports; application imports only domain; adapters import their framework; integrations import everything.
Documentation
- Authorization model — schema, permission catalog, roles, memberships, service guard
- Upgrade to the Service Guard (3.0.0)
- Caching — InMemoryTTLCache, RedisCache, invalidation contract
- FastAPI Integration
- Django Integration
- Strawberry Integration
- Keycloak Admin
- Migration v1 · Migration v2
Metadata
Release files for pkg-auth 3.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pkg_auth-3.1.0.tar.gz | 70.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pkg_auth-3.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 185.2 kB
Release files / pkg_auth-3.1.0.tar.gz
| Download URL | pkg_auth-3.1.0.tar.gz |
|---|---|
| Size | 70.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
45098ecee2328ad05bbfeee249bbe83f1d0b06e63df06a89a30fcac5dc1094cf
|
|
BLAKE2b-256 checksum How to use checksums |
b3dfe6c15a3f20923cb2cfd38e64b8f66fe7e0b4f791a2e559601b509240a50d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Release files / pkg_auth-3.1.0-py3-none-any.whl
| Download URL | pkg_auth-3.1.0-py3-none-any.whl |
|---|---|
| Size | 115.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9987b478219075c3af5b52d0dba3cc127294e9aae21708c0448f45eacdce4ea6
|
|
BLAKE2b-256 checksum How to use checksums |
1ba496d9e95748470c8c25391d3470a02b61ed804fc49cb4e97fc662a7f5d70d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|