Casdoor Example
A complete example using casbin-fastapi-decorator with the casdoor extra. It follows the current examples/core-casdoor application from the main repository and uses the high-level CasdoorIntegration facade.
Install
pip install "casbin-fastapi-decorator[casdoor]" uvicorn
Prerequisites
A running Casdoor instance with:
- an application configured for your callback URL
- a certificate used to validate JWTs
- a remote enforcement target such as an enforcer (
example-org/enforcer-example)
Project structure
my-app/
├── authz.py
├── model.py
└── main.py
authz.py
from casbin_fastapi_decorator_casdoor import (
CasdoorEnforceTarget,
CasdoorIntegration,
)
casdoor = CasdoorIntegration(
endpoint="http://localhost:8000",
client_id="example-client-id",
client_secret="example-client-secret",
certificate=open("casdoor.pem").read(),
org_name="example-org",
application_name="app-example",
target=CasdoorEnforceTarget(enforce_id="example-org/enforcer-example"),
redirect_after_login="/",
cookie_secure=False, # local HTTP only
)
guard = casdoor.create_guard()
main.py
from typing import Annotated
from authz import casdoor, guard
from casdoor import AsyncCasdoorSDK
from fastapi import Depends, FastAPI
app = FastAPI(title="Core + Casdoor Example")
app.include_router(casdoor.router) # GET /login, GET /callback, POST /logout, GET /me
@app.get("/")
async def index() -> dict:
return {
"message": "Log in via Casdoor to access protected endpoints.",
"login_url": "/login",
}
# The router already provides GET /me that returns the user profile
# This custom endpoint demonstrates how to use the casdoor.user_provider
@app.get("/profile")
@guard.auth_required()
async def profile(
token: Annotated[str, Depends(casdoor.user_provider)],
) -> dict:
sdk: AsyncCasdoorSDK = casdoor.sdk
return sdk.parse_jwt_token(token)
@app.get("/articles")
@guard.require_permission("articles", "read")
async def list_articles() -> dict:
return {"items": []}
@app.post("/articles")
@guard.require_permission("articles", "write")
async def create_article() -> dict:
return {"ok": True}
Flow
- Open
GET /loginin a browser. - The router issues an OAuth2
statevalue and redirects the browser to Casdoor. - After login, Casdoor redirects back to
GET /callback?code=...&state=.... - The integration validates
state, exchanges the code for tokens, and setsaccess_tokenandrefresh_tokencookies. - Protected routes call Casdoor's remote
/api/enforceendpoint using the configured target.
Try it
uvicorn main:app --reload
Then:
- Open
http://localhost:8000/login - Sign in through Casdoor
- Reuse the issued cookies against protected routes
Example:
curl http://localhost:8000/articles \
-H "Cookie: access_token=$ACCESS; refresh_token=$REFRESH"
Notes
GET /loginis now the canonical way to start the OAuth2 flow/callbackshould not be called directly; it expects thestateissued by/loginGET /mereturns the current user's profile by parsing the JWT access token (returns 401 if no valid token)POST /logoutcalls Casdoor's SSO logout endpoint when an access token is present, then clears both auth cookiesCasdoorUserProviderreturns the rawaccess_token; the enforcer parses it and uses the default subject format{owner}/{name}