{"id":47469430,"url":"https://github.com/Neko1313/casbin-fastapi-decorator","last_synced_at":"2026-04-08T07:00:40.683Z","repository":{"id":339112300,"uuid":"1154161001","full_name":"Neko1313/casbin-fastapi-decorator","owner":"Neko1313","description":"Authorization decorator factory for FastAPI based on Casbin. No middleware, no extra dependencies in route signatures.","archived":false,"fork":false,"pushed_at":"2026-03-18T05:58:31.000Z","size":792,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-18T20:41:04.788Z","etag":null,"topics":["abac","access-control","authorization","casbin","casdoor","decorators","fastapi","fastapi-decorator","jwt","middleware","permissions","python","python3","rbac","sqlalchemy"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Neko1313.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-10T04:33:17.000Z","updated_at":"2026-03-18T05:58:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"c39c2f59-4a05-4cb9-becf-49ea49946905","html_url":"https://github.com/Neko1313/casbin-fastapi-decorator","commit_stats":null,"previous_names":["neko1313/casbin-fastapi-decorator"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/Neko1313/casbin-fastapi-decorator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neko1313%2Fcasbin-fastapi-decorator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neko1313%2Fcasbin-fastapi-decorator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neko1313%2Fcasbin-fastapi-decorator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neko1313%2Fcasbin-fastapi-decorator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Neko1313","download_url":"https://codeload.github.com/Neko1313/casbin-fastapi-decorator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Neko1313%2Fcasbin-fastapi-decorator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31544087,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T16:28:08.000Z","status":"online","status_checked_at":"2026-04-08T02:00:06.127Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["abac","access-control","authorization","casbin","casdoor","decorators","fastapi","fastapi-decorator","jwt","middleware","permissions","python","python3","rbac","sqlalchemy"],"created_at":"2026-03-24T21:00:34.021Z","updated_at":"2026-04-08T07:00:40.668Z","avatar_url":"https://github.com/Neko1313.png","language":"Python","funding_links":[],"categories":["Authorization"],"sub_categories":["\u003ca name=\"authZ-python\"\u003e\u003c/a\u003ePython"],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://neko1313.github.io/casbin-fastapi-decorator-docs/img/logo.png\" alt=\"casbin-fastapi-decorator logo\" width=\"120\"/\u003e\n\n  \u003ch1\u003ecasbin-fastapi-decorator\u003c/h1\u003e\n\n  \u003cp\u003eAuthorization decorator factory for FastAPI based on \u003ca href=\"https://casbin.org/\"\u003eCasbin\u003c/a\u003e and \u003ca href=\"https://pypi.org/project/fastapi-decorators/\"\u003efastapi-decorators\u003c/a\u003e.\u003c/p\u003e\n\n  [![PyPI](https://img.shields.io/pypi/v/casbin-fastapi-decorator?color=blue)](https://pypi.org/project/casbin-fastapi-decorator/)\n  [![Python](https://img.shields.io/pypi/pyversions/casbin-fastapi-decorator)](https://pypi.org/project/casbin-fastapi-decorator/)\n  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/casbin-fastapi-decorator?period=total\u0026units=INTERNATIONAL_SYSTEM\u0026left_color=lightgrey\u0026right_color=blue\u0026left_text=downloads)](https://pepy.tech/projects/casbin-fastapi-decorator)\n  [![License](https://img.shields.io/github/license/Neko1313/casbin-fastapi-decorator)](LICENSE)\n  [![CI](https://img.shields.io/github/actions/workflow/status/Neko1313/casbin-fastapi-decorator/ci.yml?label=CI)](https://github.com/Neko1313/casbin-fastapi-decorator/actions)\n  [![codecov](https://codecov.io/gh/Neko1313/casbin-fastapi-decorator/graph/badge.svg?token=05ZhOXGetg)](https://codecov.io/gh/Neko1313/casbin-fastapi-decorator)\n\n  [📚 Documentation](https://neko1313.github.io/casbin-fastapi-decorator-docs/) · [PyPI](https://pypi.org/project/casbin-fastapi-decorator/) · [Casbin Ecosystem](https://casbin.org/ecosystem/)\n\u003c/div\u003e\n\n---\n\nDecorators are applied directly to routes — no middleware, no extra parameters in your function signatures.\n\n## Why decorator, not middleware?\n\n| Feature | **casbin-fastapi-decorator** | fastapi-authz / fastapi-casbin-auth |\n|---|:---:|:---:|\n| Approach | Decorator per route | Global middleware |\n| Per-route permission config | ✅ | ❌ |\n| Dynamic objects from request | ✅ `AccessSubject` | ❌ |\n| No extra params in endpoint signature | ✅ | ❌ |\n| Native FastAPI DI integration | ✅ | ⚠️ partial |\n| JWT extras | ✅ | ❌ |\n| DB-backed policies (SQLAlchemy async) | ✅ | ❌ |\n| File policies with hot-reload | ✅ | ❌ |\n| Casdoor OAuth2 integration | ✅ | ❌ |\n| Works with `APIRouter` | ✅ | ✅ |\n\nMiddleware-based authorization checks every incoming request globally. With a decorator, you configure permissions exactly where the route is defined — no hidden side effects, no boilerplate dependencies in every function signature.\n\n## Installation\n\n```bash\npip install casbin-fastapi-decorator\n```\n\nOptional extras — install only what you need:\n\n```bash\npip install \"casbin-fastapi-decorator[file]\"     # File policies with hot-reload (recommended)\npip install \"casbin-fastapi-decorator[jwt]\"      # JWT authentication\npip install \"casbin-fastapi-decorator[db]\"       # Policies from DB (SQLAlchemy) with hot-reload\npip install \"casbin-fastapi-decorator[casdoor]\"  # Casdoor OAuth2\n```\n\n## Quick start\n\n```python\nfrom contextlib import asynccontextmanager\nfrom fastapi import FastAPI, HTTPException\nfrom casbin_fastapi_decorator import AccessSubject, PermissionGuard\nfrom casbin_fastapi_decorator_file import CachedFileEnforcerProvider\n\n# 1. Providers — regular FastAPI dependencies\nasync def get_current_user() -\u003e dict:\n    return {\"sub\": \"alice\", \"role\": \"admin\"}\n\n# CachedFileEnforcerProvider loads the enforcer once and hot-reloads\n# automatically when model.conf or policy.csv changes on disk.\nenforcer_provider = CachedFileEnforcerProvider(\n    model_path=\"model.conf\",\n    policy_path=\"policy.csv\",\n)\n\n# 2. Decorator factory\nguard = PermissionGuard(\n    user_provider=get_current_user,\n    enforcer_provider=enforcer_provider,\n    error_factory=lambda user, *rv: HTTPException(403, \"Forbidden\"),\n)\n\n# 3. Wire lifespan to start/stop the file watcher\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    async with enforcer_provider:\n        yield\n\napp = FastAPI(lifespan=lifespan)\n\n# 4. Authentication only\n@app.get(\"/me\")\n@guard.auth_required()\nasync def me():\n    return {\"ok\": True}\n\n# 5. Static permission check\n@app.get(\"/articles\")\n@guard.require_permission(\"articles\", \"read\")\nasync def list_articles():\n    return []\n\n# 6. Dynamic check — object resolved from request\nasync def get_article(article_id: int) -\u003e dict:\n    return {\"id\": article_id, \"owner\": \"alice\"}\n\n@app.get(\"/articles/{article_id}\")\n@guard.require_permission(\n    AccessSubject(val=get_article, selector=lambda a: a[\"owner\"]),\n    \"read\",\n)\nasync def read_article(article_id: int):\n    return {\"article_id\": article_id}\n```\n\nArguments of `require_permission` are passed to `enforcer.enforce(user, *args)` in the same order. `AccessSubject` is resolved via FastAPI DI, then transformed by the `selector`.\n\n## API\n\n### `PermissionGuard`\n\n```python\nPermissionGuard(\n    user_provider=...,       # FastAPI dependency that returns the current user\n    enforcer_provider=...,   # FastAPI dependency that returns a casbin.Enforcer\n    error_factory=...,       # callable(user, *rvals) -\u003e Exception\n)\n```\n\n| Method | Description |\n|---|---|\n| `auth_required()` | Decorator: authentication only (user_provider must not raise) |\n| `require_permission(*args)` | Decorator: permission check via `enforcer.enforce(user, *args)` |\n\n### `AccessSubject`\n\n```python\nAccessSubject(\n    val=get_item,                        # FastAPI dependency\n    selector=lambda item: item[\"name\"],  # transformation before enforce\n)\n```\n\nWraps a dependency whose value is resolved from the request and passed to the enforcer. By default, `selector` is identity (`lambda x: x`).\n\n## File provider\n\n[`casbin-fastapi-decorator-file`](packages/casbin-fastapi-decorator-file) — loads the Casbin enforcer once from `model.conf` + `policy.csv` and **hot-reloads automatically** when either file changes on disk (via [watchdog](https://github.com/gorakhargosh/watchdog)).\n\n```bash\npip install \"casbin-fastapi-decorator[file]\"\n```\n\n```python\nfrom casbin_fastapi_decorator_file import CachedFileEnforcerProvider\n\nenforcer_provider = CachedFileEnforcerProvider(\n    model_path=\"casbin/model.conf\",\n    policy_path=\"casbin/policy.csv\",\n)\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI) -\u003e AsyncGenerator[None, None]:\n    async with enforcer_provider:   # starts watchdog\n        yield                       # stops watchdog on shutdown\n\nguard = PermissionGuard(\n    user_provider=get_current_user,\n    enforcer_provider=enforcer_provider,\n    error_factory=lambda *_: HTTPException(403, \"Forbidden\"),\n)\n```\n\nEdit `policy.csv` while the app is running — the enforcer reloads on the next request with zero downtime. The same applies to `model.conf` changes.\n\n\u003e **Recommended for all file-based setups.** Compared to a plain `async def get_enforcer()` that returns `casbin.Enforcer(...)`, this provider avoids re-reading files on every request.\n\nSee [packages/casbin-fastapi-decorator-file/README.md](packages/casbin-fastapi-decorator-file/README.md) for full API and usage.\n\n## JWT provider\n\n[`casbin-fastapi-decorator-jwt`](packages/casbin-fastapi-decorator-jwt) — extracts and validates a JWT from the Bearer header and/or a cookie.\n\n```bash\npip install \"casbin-fastapi-decorator[jwt]\"\n```\n\nSee [packages/casbin-fastapi-decorator-jwt/README.md](packages/casbin-fastapi-decorator-jwt/README.md) for full API and usage.\n\n## DB provider\n\n[`casbin-fastapi-decorator-db`](packages/casbin-fastapi-decorator-db) — loads Casbin policies from a SQLAlchemy async session with caching and hot-reload.\n\n```bash\npip install \"casbin-fastapi-decorator[db]\"\n```\n\nThe enforcer is cached and reloaded automatically when:\n- `model.conf` changes on disk (watchdog)\n- DB policy rows change — detected by SHA-256 hash, polled every `poll_interval` seconds (default 30 s)\n\n```python\nfrom casbin_fastapi_decorator_db import DatabaseEnforcerProvider\n\nenforcer_provider = DatabaseEnforcerProvider(\n    model_path=\"casbin/model.conf\",\n    session_factory=async_session,\n    policy_model=Policy,\n    policy_mapper=lambda p: (p.sub, p.obj, p.act),\n    poll_interval=30.0,  # seconds between DB hash checks\n)\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI) -\u003e AsyncGenerator[None, None]:\n    async with enforcer_provider:   # starts watchdog + polling task\n        yield\n```\n\nSee [packages/casbin-fastapi-decorator-db/README.md](packages/casbin-fastapi-decorator-db/README.md) for full API and usage.\n\n## Casdoor provider\n\n[`casbin-fastapi-decorator-casdoor`](packages/casbin-fastapi-decorator-casdoor) — Casdoor OAuth2 authentication and remote Casbin policy enforcement.\n\n```bash\npip install \"casbin-fastapi-decorator[casdoor]\"\n```\n\n```python\nfrom casbin_fastapi_decorator_casdoor import CasdoorEnforceTarget, CasdoorIntegration\n\ncasdoor = CasdoorIntegration(\n    endpoint=\"http://localhost:8000\",\n    client_id=\"...\", client_secret=\"...\", certificate=cert,\n    org_name=\"my_org\", application_name=\"my_app\",\n    target=CasdoorEnforceTarget(\n        enforce_id=lambda parsed: f\"{parsed['owner']}/my_enforcer\",\n    ),\n)\napp.include_router(casdoor.router)   # GET /callback, POST /logout\nguard = casdoor.create_guard()\n```\n\n`CasdoorEnforceTarget` selects the Casdoor enforce mode — by enforcer, permission, model, resource, or owner. Values can be static strings or callables resolved from the JWT payload at request time.\n\nSee [packages/casbin-fastapi-decorator-casdoor/README.md](packages/casbin-fastapi-decorator-casdoor/README.md) for full API, compose pattern, and usage.\n\n## Examples\n\n| Example | Description |\n|---|---|\n| [`examples/core`](examples/core) | Bearer token auth, plain file-based policies |\n| [`examples/core-file`](examples/core-file) | Bearer token auth, file policies with hot-reload via `CachedFileEnforcerProvider` |\n| [`examples/core-jwt`](examples/core-jwt) | JWT auth via `JWTUserProvider`, file-based policies |\n| [`examples/core-db`](examples/core-db) | Bearer token auth, DB policies with hot-reload via `DatabaseEnforcerProvider` |\n| [`examples/core-casdoor`](examples/core-casdoor) | Casdoor OAuth2 auth + remote enforcement, facade and compose patterns |\n\n## Development\n\nRequires Python 3.10+, [uv](https://docs.astral.sh/uv/), [task](https://taskfile.dev/).\n\n```bash\ntask install           # uv sync --all-groups + install all packages\ntask lint              # ruff + ty + bandit for all packages\ntask tests             # all tests (core + jwt + db + casdoor + file)\n```\n\nIndividual package tasks:\n\n```bash\ntask core:lint         task core:test\ntask jwt:lint          task jwt:test\ntask db:lint           task db:test         # requires Docker (testcontainers)\ntask casdoor:lint      task casdoor:test\ntask file:lint         task file:test\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNeko1313%2Fcasbin-fastapi-decorator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FNeko1313%2Fcasbin-fastapi-decorator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FNeko1313%2Fcasbin-fastapi-decorator/lists"}