Casdoor Setup
A step-by-step guide to integrating Casdoor with casbin-fastapi-decorator.
Prerequisites
- A running Casdoor instance (see Casdoor quick start)
- An application registered in Casdoor
- The Casdoor Python SDK: included when you install
casbin-fastapi-decorator[casdoor]
1. Register your application in Casdoor
In the Casdoor admin UI:
- Go to Applications → Add
- Set the redirect URL to your FastAPI callback endpoint (e.g.,
http://localhost:8000/auth/callback) - Note the Client ID and Client Secret
- Download or copy the Certificate (public key for JWT validation)
2. Configure the SDK
from casdoor import AsyncCasdoorSDK
sdk = AsyncCasdoorSDK(
endpoint="https://your-casdoor-instance.com",
client_id="your-client-id",
client_secret="your-client-secret",
certificate="""-----BEGIN CERTIFICATE-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END CERTIFICATE-----""",
org_name="your-org",
application_name="your-app",
)
3. Implement the OAuth2 flow
Login redirect
Send the user to Casdoor's authorization endpoint:
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
app = FastAPI()
@app.get("/auth/login")
async def login() -> RedirectResponse:
auth_url = sdk.get_auth_link(
redirect_uri="http://localhost:8000/auth/callback",
response_type="code",
scope="read",
)
return RedirectResponse(auth_url)
Callback handler
Exchange the authorization code for tokens and set them as cookies:
from fastapi import Response
@app.get("/auth/callback")
async def callback(code: str, state: str, response: Response) -> dict:
# Exchange code for tokens
token = await sdk.get_oauth_token(code=code)
response.set_cookie(
key="access_token",
value=token.access_token,
httponly=True,
secure=True,
samesite="lax",
)
response.set_cookie(
key="refresh_token",
value=token.refresh_token,
httponly=True,
secure=True,
samesite="lax",
)
return {"message": "Logged in successfully"}
4. Protect routes
from casbin_fastapi_decorator import PermissionGuard
from casbin_fastapi_decorator_casdoor import CasdoorUserProvider
import casbin
user_provider = CasdoorUserProvider(sdk=sdk)
guard = PermissionGuard(
user_provider=user_provider,
enforcer_provider=lambda: casbin.Enforcer("casbin/model.conf", "casbin/policy.csv"),
error_factory=lambda *_: HTTPException(403, "Forbidden"),
)
@app.get("/dashboard")
@guard.auth_required()
async def dashboard():
return {"message": "Welcome!"}
@app.get("/admin")
@guard.require_permission("admin", "access")
async def admin_panel():
return {"message": "Admin area"}
5. Logout
Clear the cookies on logout:
@app.post("/auth/logout")
async def logout(response: Response) -> dict:
response.delete_cookie("access_token")
response.delete_cookie("refresh_token")
return {"message": "Logged out"}
User identity
CasdoorUserProvider returns the raw access_token string as the user identity. This string is passed as the first argument to enforcer.enforce().
Make sure your Casbin policy uses Casdoor user identifiers as subjects. You can decode the token to inspect the claims:
import jwt
token = "eyJ..." # the access_token
payload = jwt.decode(token, options={"verify_signature": False})
print(payload) # {"sub": "user-id", "name": "Alice", ...}
Update your policy.csv or database policies to use the appropriate identifier (e.g., the sub claim or the username).