User Management
AuthFort provides programmatic methods for user management — no HTTP endpoints are exposed. Build your own admin panel or CLI on top of these.
List Users
Section titled “List Users”# List first 50 users (default)
result = await auth.list_users()
for user in result.users:
print(user.email, user.roles)
print(f"Page {result.offset // result.limit + 1}, total: {result.total}")# List first 50 users (default)
result = await auth.list_users()
for user in result.users:
print(user.email, user.roles)
print(f"Page {result.offset // result.limit + 1}, total: {result.total}")Returns a ListUsersResponse with users, total, limit, and offset.
Filtering
Section titled “Filtering”# Search by email or name
result = await auth.list_users(query="john")
# Only banned users
result = await auth.list_users(banned=True)
# Users with "admin" role
result = await auth.list_users(role="admin")
# Combine filters
result = await auth.list_users(query="john", banned=False, role="editor")# Search by email or name
result = await auth.list_users(query="john")
# Only banned users
result = await auth.list_users(banned=True)
# Users with "admin" role
result = await auth.list_users(role="admin")
# Combine filters
result = await auth.list_users(query="john", banned=False, role="editor")The query parameter does a case-insensitive partial match on both email and name.
Pagination & Sorting
Section titled “Pagination & Sorting”# Page 1
page1 = await auth.list_users(limit=20, offset=0)
# Page 2
page2 = await auth.list_users(limit=20, offset=20)
# Sort by email ascending
result = await auth.list_users(sort_by="email", sort_order="asc")# Page 1
page1 = await auth.list_users(limit=20, offset=0)
# Page 2
page2 = await auth.list_users(limit=20, offset=20)
# Sort by email ascending
result = await auth.list_users(sort_by="email", sort_order="asc")sort_by accepts "created_at" (default), "email", or "name".
Get a Single User
Section titled “Get a Single User”user = await auth.get_user(user_id)
print(user.email) # "jane@example.com"
print(user.roles) # ["admin", "editor"]
print(user.email_verified) # Trueuser = await auth.get_user(user_id)
print(user.email) # "jane@example.com"
print(user.roles) # ["admin", "editor"]
print(user.email_verified) # TrueReturns a UserResponse with the user’s roles. Raises AuthError if not found.
A soft-deleted user is treated as not found unless you pass get_user(user_id, deleted=True).
Delete a User
Section titled “Delete a User”# Default: anonymize + soft-delete (keeps the row + id)
await auth.delete_user(user_id)
# Opt into the legacy hard delete (removes the row entirely)
await auth.delete_user(user_id, hard=True)# Default: anonymize + soft-delete (keeps the row + id)
await auth.delete_user(user_id)
# Opt into the legacy hard delete (removes the row entirely)
await auth.delete_user(user_id, hard=True)By default, delete_user() anonymizes + soft-deletes the account — the
“erase the person, not the row” pattern. The authfort_users row and its id
are retained, so any of your own tables that reference authfort_users.id
(workspace ownership, created_by, audit) keep their foreign keys valid. AuthFort:
- Scrubs PII —
name→"Deleted user",avatar_url/phone→null, andemail→ a uniquedeleted+<id>@deleted.invalidplaceholder (which frees the original email for future re-signup). - Kills credentials — nulls the password hash, deletes MFA secret + backup codes.
- Revokes access — bumps
token_versionand deletes all refresh tokens/sessions. - Deletes OAuth accounts, roles, verification tokens, and password history.
- Sets
is_deleted = True,deleted_at = now().
The account can no longer log in, refresh, or use any email-based flow (magic
link, OTP, password reset, email verification), and is excluded from list_users()
/ get_user_count() by default. Anonymizing is idempotent — deleting an
already-deleted user is a no-op. The independent banned flag is unaffected.
await auth.delete_user(user_id, hard=True) # legacy: remove the row entirelyPass hard=True for the original behavior that removes the row and all related
records. Note this can fail or break referential integrity if your tables
reference authfort_users.id without an ON DELETE rule — which is exactly why
the soft-delete default exists.
Count Users
Section titled “Count Users”# Total users
total = await auth.get_user_count()
# Count with filters
banned_count = await auth.get_user_count(banned=True)
admin_count = await auth.get_user_count(role="admin")# Total users
total = await auth.get_user_count()
# Count with filters
banned_count = await auth.get_user_count(banned=True)
admin_count = await auth.get_user_count(role="admin")Uses the same filters as list_users() — useful for dashboard stats without fetching full user objects.
Events
Section titled “Events”@auth.on("user_deleted")
async def on_user_deleted(event):
print(f"Deleted user {event.email} ({event.user_id})")
# Clean up external services, send notification, etc.@auth.on("user_deleted")
async def on_user_deleted(event):
print(f"Deleted user {event.email} ({event.user_id})")
# Clean up external services, send notification, etc.The user_deleted event fires after successful deletion with user_id and email.
Building an Admin Panel
Section titled “Building an Admin Panel”These methods are building blocks — AuthFort doesn’t ship admin endpoints. Here’s a minimal FastAPI example:
from fastapi import Depends
@app.get("/admin/users")async def admin_list_users( user=Depends(auth.require_role("admin")), query: str | None = None, limit: int = 20, offset: int = 0,): return await auth.list_users(query=query, limit=limit, offset=offset)
@app.delete("/admin/users/{user_id}")async def admin_delete_user( user_id: str, user=Depends(auth.require_role("admin")),): await auth.delete_user(user_id) return {"deleted": True}See Roles & Permissions for require_role usage.