Skip to content

Consumer Integration

This tutorial adds AuthLoom to an existing FastAPI app and gets to the first successful signup, current-user request, signout, and signin.

It uses SQLite with aiosqlite for one clear path. If your app uses PostgreSQL, install asyncpg instead and use a postgresql+asyncpg://... URL.

1. Install

AuthLoom requires Python >=3.12, FastAPI, and SQLAlchemy async sessions.

Using uv:

uv add authloom aiosqlite

Using pip:

pip install authloom aiosqlite

2. Create The Engine And Session Factory

AuthLoom uses the async SQLAlchemy session factory created by your app.

from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine

database_url = "sqlite+aiosqlite:///./app.db"

engine = create_async_engine(
    database_url,
    echo=False,
    connect_args={"check_same_thread": False},
)

AsyncSessionLocal = async_sessionmaker(
    engine,
    class_=AsyncSession,
    expire_on_commit=False,
)

Use async SQLAlchemy URLs. For example, use sqlite+aiosqlite:///..., not sqlite:///....

You may also reuse the same factory in application dependencies:

from collections.abc import AsyncGenerator

async def get_db() -> AsyncGenerator[AsyncSession, None]:
    async with AsyncSessionLocal() as session:
        yield session

3. Create AuthLoom

Only session_factory is required.

from authloom import AuthLoom, AuthLoomConfig

auth = AuthLoom(
    config=AuthLoomConfig(
        session_factory=AsyncSessionLocal,
    )
)

4. Add The Minimum Migration Setup

Before signup can work, the database must contain AuthLoom's tables.

AuthLoom does not install Alembic or run migrations for you. Add AuthLoom metadata to your application-owned Alembic target_metadata:

from authloom.db import metadata as authloom_metadata

target_metadata = authloom_metadata

If your app also has its own models, include both metadata objects:

from authloom.db import metadata as authloom_metadata
from myapp.db import Base

target_metadata = [authloom_metadata, Base.metadata]

Then generate, review, and apply a migration:

alembic revision --autogenerate -m "add authloom tables"
alembic upgrade head

See Database and Migrations for the complete beginner migration guide.

5. Mount The Router Once

For the localhost-only curl smoke test below, mount AuthLoom's router once:

from fastapi import FastAPI

from authloom import create_auth_router

app = FastAPI()
app.include_router(create_auth_router(auth))

The router defaults to /auth. To use another prefix, pass prefix:

app.include_router(create_auth_router(auth, prefix="/accounts"))

Then use /accounts/signup, /accounts/signin, and the same prefix for the other built-in routes. If your delivery hooks build password-reset or email-verification links, use the same prefix in those URLs.

6. Localhost-Only Curl Smoke Test

This section is only for command-line testing on localhost. Do not use this router setup in a browser or deployed app until you add CSRF protection in the next section.

Start your app, then use a cookie jar so curl keeps the session cookie:

rm -f cookies.txt

Create an account. The password is 15 characters to satisfy the default minimum length.

curl -i -c cookies.txt \
  -X POST http://127.0.0.1:8000/auth/signup \
  -H 'content-type: application/json' \
  -d '{
    "name": "Example User",
    "email": "user@example.com",
    "password": "abcdefghijklmno",
    "password_confirm": "abcdefghijklmno"
  }'

Read the current user with the cookie saved by signup:

curl -i -b cookies.txt http://127.0.0.1:8000/auth/me

Sign out and update the cookie jar:

curl -i -b cookies.txt -c cookies.txt \
  -X POST http://127.0.0.1:8000/auth/signout

Sign in again and save the new session cookie:

curl -i -b cookies.txt -c cookies.txt \
  -X POST http://127.0.0.1:8000/auth/signin \
  -H 'content-type: application/json' \
  -d '{
    "email": "user@example.com",
    "password": "abcdefghijklmno"
  }'

Confirm the new session works:

curl -i -b cookies.txt http://127.0.0.1:8000/auth/me

Mandatory CSRF For Browser Or Deployment Use

AuthLoom uses cookies for browser authentication. Before using AuthLoom from a browser or deploying the app, add consumer-owned CSRF protection to every browser-facing state-changing route.

For AuthLoom's built-in unsafe routes, pass your CSRF dependency when you create the router. Replace the single include_router call above; do not mount the router a second time.

from fastapi import Depends

from authloom import create_auth_router

csrf_dependency = Depends(verify_csrf_request)

app.include_router(
    create_auth_router(
        auth,
        prefix="/auth",
        unsafe_route_dependencies=(csrf_dependency,),
    )
)

This applies to built-in state-changing routes such as signup, signin, signout, password reset, password change, and token-request routes. Add the same kind of protection separately to your application-owned HTML or JSON mutation routes.

AuthLoom does not require a specific CSRF library. The credentials-auth example shows one complete implementation.

Built-In Routes

Method Path Purpose
POST /auth/signup Create a user, create a session, and set the session cookie.
POST /auth/signin Verify credentials, create a session, and set the session cookie.
POST /auth/signout Revoke the current session and delete the session cookie.
GET /auth/me Return the current authenticated user.
POST /auth/request-password-reset Request a password-reset token and invoke the configured hook.
POST /auth/password-reset?token=... Complete password reset with a valid token.
POST /auth/password-change Change the authenticated user's password.
POST /auth/request-email-verification Request an email-verification token and invoke the configured hook.
GET /auth/email-verification?token=... Consume the submitted verification token and mark the email verified.

GET /auth/email-verification?token=... is a clickable bearer-token link from email. Use HTTPS, avoid logging full URLs, and do not require a browser CSRF token for that route.

If you configured a custom router prefix, replace /auth with that prefix in the paths above.

Use Current-User Dependencies

Use auth.require_current_user when a route must have a valid session cookie. It returns the AuthLoom User model or raises 401 Unauthorized.

from typing import Annotated

from fastapi import Depends

from authloom.db import User

@app.get("/me")
async def get_me(user: Annotated[User, Depends(auth.require_current_user)]):
    return {"id": user.id, "email": user.email, "name": user.name}

Use auth.optional_current_user when a route should work for both anonymous and authenticated users. It returns User | None.

@app.get("/homepage")
async def homepage(
    user: Annotated[User | None, Depends(auth.optional_current_user)],
):
    return {"authenticated": user is not None}

Operations

Clean Up Stale Sessions

AuthLoom checks session validity during authentication. Cleanup is separate: it removes expired and revoked session rows to control database growth.

deleted_count = await auth.delete_stale_sessions()

For deterministic cleanup, pass a timezone-aware cutoff:

from datetime import UTC, datetime

deleted_count = await auth.delete_stale_sessions(
    before=datetime(2026, 1, 1, tzinfo=UTC)
)

Your application decides when to run cleanup. AuthLoom does not include cron, a worker, a CLI, or a scheduler.

Production Notes

  • Use HTTPS and production cookie settings. See Configuration.
  • Keep migrations reviewed and application-owned. See Database and Migrations.
  • Read Account Security Flows before enabling password reset, password change, or email verification.
  • Route-level authorization for your own resources remains application-owned.