Function-Based Permissions¶
For simpler permission checks, you can use the @permission decorator instead of defining a class.
Basic Usage¶
from fastapi import Depends, FastAPI, Request
from fastapi_has_permissions import permission
@permission
async def has_authorization_header(request: Request) -> bool:
return "Authorization" in request.headers
app = FastAPI()
@app.get(
"/protected",
dependencies=[Depends(has_authorization_header())],
)
async def protected():
return {"message": "You have access!"}
The @permission decorator wraps your async function into a permission factory. Call it to create
a Permission instance. This means it supports all the same features as class-based permissions,
including boolean composition.
With FastAPI Dependency Injection¶
Just like class-based permissions, function-based permissions support FastAPI's DI:
from typing import Annotated
from fastapi import Depends, FastAPI, Header
from fastapi_has_permissions import permission
@permission
async def has_admin_role(role: Annotated[str, Header()]) -> bool:
return role == "admin"
app = FastAPI()
@app.get(
"/admin",
dependencies=[Depends(has_admin_role())],
)
async def admin_only():
return {"message": "Admin access granted"}
Custom Error Messages¶
You can pass message and status_code arguments to customize the error response:
from fastapi_has_permissions import permission
@permission(message="You must be an admin", status_code=401)
async def has_admin_role(role: Annotated[str, Header()]) -> bool:
return role == "admin"
When used with arguments, @permission(...) returns a decorator:
# Without arguments (direct decorator)
@permission
async def my_check(...) -> bool:
...
# With arguments (decorator factory)
@permission(message="Custom message", status_code=401)
async def my_check(...) -> bool:
...
In both cases, the result is a factory -- call it to create a permission instance:
Depends(my_check()) # call to create the permission instance
Dependencies with Dep¶
Function-based permissions can accept Dep arguments, similar to class-based permissions with Dep fields.
Declare parameters with Dep[T] type annotations -- these must come before any regular parameters in the
function signature:
from typing import Annotated
from fastapi import Depends, Header
from fastapi_has_permissions import Dep, permission
async def get_admin_role() -> str:
return "admin"
@permission
async def has_role(
admin_role: Dep[str],
role: Annotated[str, Header()],
) -> bool:
return role == admin_role
When creating the permission instance, pass the dependencies as positional arguments to the factory:
AdminRoleDep = Annotated[str, Depends(get_admin_role)]
has_admin_role = has_role(AdminRoleDep)
@app.get(
"/admin",
dependencies=[Depends(has_admin_role)],
)
async def admin_only():
return {"message": "Admin access granted"}
At request time, Annotated[str, Depends(get_admin_role)] is resolved by FastAPI and its value is
passed as the admin_role argument. The remaining parameters (role) are resolved via FastAPI's
standard DI.
Note
All Dep parameters must be declared before non-Dep parameters in the function signature.
The number of Dep parameters must match the number of arguments passed to the factory.
Boolean Composition¶
Function-based permissions support the same &, |, ~ operators as class-based permissions:
from fastapi import Depends, FastAPI, Request
from typing import Annotated
from fastapi import Header
from fastapi_has_permissions import permission
@permission
async def has_authorization(request: Request) -> bool:
return "Authorization" in request.headers
@permission
async def has_admin_role(role: Annotated[str, Header()]) -> bool:
return role == "admin"
app = FastAPI()
# Both must pass
@app.get(
"/admin",
dependencies=[Depends(has_authorization() & has_admin_role())],
)
async def admin_only():
return {"message": "Authenticated admin access"}
# Either must pass
@app.get(
"/flexible",
dependencies=[Depends(has_authorization() | has_admin_role())],
)
async def flexible():
return {"message": "Access granted"}
Tip
Function-based permissions work well for both simple checks and parameterized checks
using Dep. For permissions that need multiple configuration fields or complex
state, consider class-based permissions instead.