FastAPI
casbin-fastapi-decorator is a community library that protects FastAPI routes with decorators. Its Casdoor package, casbin-fastapi-decorator-casdoor, signs users in through Casdoor (OAuth2) and checks permissions by calling Casdoor's /api/enforce API, so the policies live in Casdoor instead of your app.
This library is maintained by the community, not by the Casdoor team. Report bugs in its GitHub repository.
The following are some of the names in the configuration:
CASDOOR_HOSTNAME: The domain name or IP where Casdoor server is deployed, e.g. http://localhost:8000.
FASTAPI_HOSTNAME: The domain name or IP where your FastAPI app is deployed, e.g. http://localhost:8080.
Step 1: Deploy Casdoor
Deploy Casdoor and make sure you can sign in to it.
Step 2: Configure Casdoor
- Create a new application or use an existing one.
- Add
FASTAPI_HOSTNAME/callbackto Redirect URLs of the application. - Copy the Client ID, Client Secret, the organization name and the application name.
- Open the certificate used by the application (Certs page) and copy its public key.
- Create the permission rules that the app should check. You can use an enforcer, a permission, a model, a resource or a whole organization as the target; you will pass its ID in Step 4.
Step 3: Install the library
pip install "casbin-fastapi-decorator[casdoor]"
Python 3.10 or later is required.
Step 4: Protect your routes
CasdoorIntegration creates the Casdoor SDK client, the sign-in routes and a PermissionGuard in one call:
from fastapi import FastAPI
from casbin_fastapi_decorator_casdoor import CasdoorEnforceTarget, CasdoorIntegration
casdoor = CasdoorIntegration(
endpoint="http://localhost:8000",
client_id="<client-id>",
client_secret="<client-secret>",
certificate="-----BEGIN CERTIFICATE-----\n...",
org_name="built-in",
application_name="app-built-in",
target=CasdoorEnforceTarget(
# the enforcer ID is built from the signed-in user's organization
enforce_id=lambda parsed: f"{parsed['owner']}/my-enforcer",
),
cookie_secure=False, # only for local HTTP testing; keep the default True in production
)
app = FastAPI()
app.include_router(casdoor.router) # GET /login, GET /callback, POST /logout, GET /me
guard = casdoor.create_guard()
@app.get("/articles")
@guard.require_permission("articles", "read")
async def list_articles():
return []
@app.get("/profile")
@guard.auth_required()
async def profile():
return {"ok": True}
How it works:
- Send the user to
FASTAPI_HOSTNAME/login. The app redirects to Casdoor with a randomstatevalue. - After sign-in, Casdoor redirects back to
/callback. The app checksstate, exchanges the code for tokens and stores them in theaccess_tokenandrefresh_tokencookies. - On each protected request, the app verifies the token with the certificate and calls Casdoor's
/api/enforcewith["<owner>/<name>", "articles", "read"]. Requests that are denied get403 Forbidden. POST /logoutsigns the user out of Casdoor and clears the cookies.
Step 5: Choose the enforce target
CasdoorEnforceTarget decides which Casdoor object holds the rules. Set exactly one field, either to a fixed ID or to a function that receives the parsed JWT:
| Field | Casdoor object |
|---|---|
enforce_id | Enforcer, e.g. built-in/my-enforcer |
permission_id | Permission, e.g. built-in/can-read-articles |
model_id | Model, e.g. built-in/rbac-model |
resource_id | Resource |
owner | All permissions of an organization, e.g. built-in |
CasdoorEnforceTarget(permission_id="built-in/can-read-articles")
Advanced usage
To use your own user ID format, a different target per guard, or your own error responses, build the parts yourself:
from casdoor import AsyncCasdoorSDK
from fastapi import FastAPI, HTTPException
from casbin_fastapi_decorator import PermissionGuard
from casbin_fastapi_decorator_casdoor import (
CasdoorEnforcerProvider,
CasdoorEnforceTarget,
CasdoorUserProvider,
make_casdoor_router,
)
sdk = AsyncCasdoorSDK(
endpoint="http://localhost:8000",
client_id="<client-id>",
client_secret="<client-secret>",
certificate="<certificate>",
org_name="built-in",
application_name="app-built-in",
)
guard = PermissionGuard(
user_provider=CasdoorUserProvider(sdk=sdk),
enforcer_provider=CasdoorEnforcerProvider(
sdk=sdk,
target=CasdoorEnforceTarget(enforce_id="built-in/my-enforcer"),
user_factory=lambda parsed: parsed["email"], # default is "owner/name"
),
error_factory=lambda user, *rvals: HTTPException(403, "Forbidden"),
)
app = FastAPI()
app.include_router(make_casdoor_router(sdk, redirect_after_login="/docs"))
See the library README for all options, such as cookie names, cookie domain and a custom state store.