Skip to content

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 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.

# 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.

# 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".

user = await auth.get_user(user_id)

print(user.email)        # "jane@example.com"
print(user.roles)        # ["admin", "editor"]
print(user.email_verified)  # True
user = await auth.get_user(user_id)

print(user.email)        # "jane@example.com"
print(user.roles)        # ["admin", "editor"]
print(user.email_verified)  # True

Returns 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).

# 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:

  1. Scrubs PII — name"Deleted user", avatar_url/phonenull, and email → a unique deleted+<id>@deleted.invalid placeholder (which frees the original email for future re-signup).
  2. Kills credentials — nulls the password hash, deletes MFA secret + backup codes.
  3. Revokes access — bumps token_version and deletes all refresh tokens/sessions.
  4. Deletes OAuth accounts, roles, verification tokens, and password history.
  5. 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 entirely

Pass 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.

# 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.

@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.

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.