Skip to main content

FastAPI FullAuth

FastAPI FullAuth

Production-grade, async-native authentication and authorization for FastAPI.

PyPI Downloads Python CI License Docs

Documentation: https://mdfarhankc.github.io/fastapi-fullauth
Source Code: https://github.com/mdfarhankc/fastapi-fullauth


A complete, async-native authentication and authorization system for FastAPI - production-ready and pluggable. JWT access/refresh tokens with rotation, Argon2 password hashing, email verification, OAuth2 social login, passkeys, session management, and role-based access control, all out of the box. Bring your own database with the SQLModel, SQLAlchemy, Tortoise ORM, or Beanie (MongoDB) adapter, and opt into only the features you need.

Features

  • JWT access + refresh tokens with configurable expiry
  • Refresh token rotation with reuse detection; revokes entire session family on replay. Tokens are stored as sha256 digests, so a leaked database exposes no usable sessions
  • Secure by default: registration anti-enumeration, login timing-attack defense, account lockout, and spoof-resistant client IP resolution are all on out of the box
  • Session management: list active sessions (device, IP, last used), revoke one device, or sign out everywhere else
  • Password hashing via Argon2id (default) or bcrypt, offloaded to a worker thread, with transparent rehashing
  • Email verification and password reset flows with event hooks
  • Passkey (WebAuthn): passwordless login with fingerprint, Face ID, security keys
  • OAuth2 social login: Google, GitHub, Discord, and GitLab built in; add your own provider by subclassing StandardOAuthProvider
  • Role-based access control: current_user, require_role(), require_permission(), and typed dependencies for custom user schemas
  • Rate limiting: per-route auth limits + global middleware (memory or Redis)
  • CSRF protection and security headers middleware
  • Bearer or cookie transport: opt into HttpOnly cookies that carry both access and refresh tokens, out of JavaScript's reach; bearer is the default
  • Pluggable adapters: SQLModel, SQLAlchemy, Tortoise ORM, Beanie (MongoDB), or write your own for any data store
  • Generic type parameters: define your own schemas with full IDE support and type safety
  • Composable routers: include only the route groups you need
  • Event hooks: after_register, after_login, send_verification_email, etc.
  • Custom JWT claims: embed app-specific data in tokens
  • Structured logging: all auth events, security violations, and failures logged
  • Redis support: token blacklist and rate limiter backends
  • Python 3.10 - 3.14 supported

Installation

The fastest way to start: one adapter plus every optional feature in a single extra.

# Recommended: SQLModel adapter + Redis, OAuth, passkeys, bcrypt
pip install "fastapi-fullauth[sqlmodel-standard]"

# Same, on the SQLAlchemy adapter
pip install "fastapi-fullauth[sqlalchemy-standard]"

Or stay minimal and add only what you use:

# Core + one ORM adapter (pick one)
pip install "fastapi-fullauth[sqlmodel]"

# Mix and match any extras
pip install "fastapi-fullauth[sqlalchemy,oauth,redis]"
Extra Adds
sqlmodel / sqlalchemy SQL ORM adapter + Alembic (pick one)
tortoise Tortoise ORM adapter (async SQL)
beanie Beanie adapter (MongoDB)
redis Redis backends for token blacklist, lockout, rate limiting, passkey challenges
oauth OAuth2 social login (Google, GitHub, Discord, GitLab)
passkey Passkey / WebAuthn support
bcrypt bcrypt password hashing (Argon2id is the default and needs no extra)
sqlmodel-standard / sqlalchemy-standard / tortoise-standard / beanie-standard One adapter plus all of the above

Quotes around the package spec keep shells like zsh from globbing the [extras].

Quick start

from fastapi import FastAPI
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from sqlmodel import Relationship

from fastapi_fullauth import FullAuth, FullAuthConfig
from fastapi_fullauth.adapters import SQLModelAdapter
from fastapi_fullauth.models.sqlmodel import RefreshTokenMixin, UserMixin


class RefreshToken(RefreshTokenMixin, table=True):
    pass


class User(UserMixin, table=True):
    refresh_tokens: list[RefreshToken] = Relationship()


engine = create_async_engine("sqlite+aiosqlite:///./app.db")
session_maker = async_sessionmaker(engine, expire_on_commit=False)

app = FastAPI()
fullauth = FullAuth(
    adapter=SQLModelAdapter(
        session_maker=session_maker,
        user_model=User,
        refresh_token_model=RefreshToken,
    ),
    config=FullAuthConfig(SECRET_KEY="your-secret-key"),
)
fullauth.init_app(app)

That's it; all auth routes are registered under /api/v1/auth/ automatically. Create the tables with Alembic (see the migrations guide) or SQLModel.metadata.create_all for a quick local start.

The SQLite quick-start needs an async driver: pip install aiosqlite.

Omit config in dev and a random secret key is generated (tokens won't survive restarts).

Composable routers

Opt in to a subset of routers:

fullauth.init_app(app, include_routers=["auth", "profile"])

include_routers=None (default) registers every available router. Or wire routers manually for full control:

app = FastAPI()
fullauth.bind(app)  # required for dependencies to work

app.include_router(fullauth.auth_router, prefix="/api/v1/auth")
app.include_router(fullauth.profile_router, prefix="/api/v1/auth")

bind() registers no shutdown cleanup (only init_app() wraps the lifespan), so on this path call await fullauth.aclose() in your shutdown to release pooled Redis connections and OAuth HTTP clients.

Router Routes
auth_router register, login, logout, refresh
profile_router me, verified-me, update profile, delete account, change password
verify_router email verification, password reset
admin_router assign/remove roles and permissions (superuser)
oauth_router OAuth provider routes (only if configured)
passkey_router Passkey register, authenticate, list, delete (only if enabled)
sessions_router list active sessions, revoke one device, sign out others

Middleware

init_app() does not wire any middleware automatically. Import what you want and add it yourself:

from fastapi_fullauth.middleware import (
    SecurityHeadersMiddleware,
    CSRFMiddleware,
    RateLimitMiddleware,
)

app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(CSRFMiddleware, secret=fullauth.config.SECRET_KEY)
app.add_middleware(RateLimitMiddleware, max_requests=60, window_seconds=60)

Token transport

By default, tokens are returned in the response body for Authorization: Bearer use. Opt into cookie transport to carry both the access and refresh tokens in separate HttpOnly cookies, out of JavaScript's reach:

from fastapi_fullauth.backends import CookieBackend

fullauth = FullAuth(
    adapter=adapter,
    config=config,
    backends=[CookieBackend(config)],
)

/refresh and /logout then read the tokens from the cookies, so the browser never stores them. Wire CSRFMiddleware whenever you use cookie transport.

Routes

Method Path Description
POST /auth/register Create a new user
POST /auth/login Authenticate, get tokens
POST /auth/logout Blacklist token
POST /auth/refresh Rotate token pair
GET /auth/sessions List active sessions
DELETE /auth/sessions/{family_id} Revoke one session
POST /auth/sessions/revoke-others Sign out other sessions
GET /auth/me Get current user
GET /auth/me/verified Verified users only
PATCH /auth/me Update profile
DELETE /auth/me Delete account
POST /auth/change-password Change password
POST /auth/verify-email/request Request verification email
POST /auth/verify-email/confirm Confirm email
POST /auth/password-reset/request Request password reset
POST /auth/password-reset/confirm Reset password
POST /auth/admin/assign-role Assign role (superuser)
POST /auth/admin/remove-role Remove role (superuser)
POST /auth/admin/assign-permission Assign permission to role (superuser)
POST /auth/admin/remove-permission Remove permission from role (superuser)
GET /auth/admin/role-permissions/{role_name} List role's permissions (superuser)

With OAuth enabled, additional routes are registered under /auth/oauth/. All routes are prefixed with /api/v1 by default.

Custom user schemas

Combine each mixin with table=True (or your DeclarativeBase for the SQLAlchemy adapter), then pass everything to the adapter:

from sqlmodel import Field, Relationship
from fastapi_fullauth import FullAuth, FullAuthConfig, UserSchema, CreateUserSchema
from fastapi_fullauth.adapters import SQLModelAdapter
from fastapi_fullauth.models.sqlmodel import (
    RefreshTokenMixin, RoleMixin, UserMixin, UserRoleMixin,
)


class RefreshToken(RefreshTokenMixin, table=True): pass
class Role(RoleMixin, table=True): pass
class UserRole(UserRoleMixin, table=True): pass


class User(UserMixin, table=True):
    display_name: str = Field(default="", max_length=100)
    phone: str = Field(default="", max_length=20)
    roles: list[Role] = Relationship(link_model=UserRole)
    refresh_tokens: list[RefreshToken] = Relationship()


class MyUserSchema(UserSchema):
    display_name: str = ""
    phone: str = ""


class MyCreateSchema(CreateUserSchema):
    display_name: str = ""


fullauth = FullAuth(
    adapter=SQLModelAdapter(
        session_maker,
        user_model=User,
        refresh_token_model=RefreshToken,
        role_model=Role,
        user_role_model=UserRole,
        user_schema=MyUserSchema,
        create_user_schema=MyCreateSchema,
    ),
    config=FullAuthConfig(SECRET_KEY="..."),
)

Full IDE autocompletion and type checking on custom fields:

from typing import Annotated
from fastapi import Depends
from fastapi_fullauth.dependencies import current_user, current_active_verified_user

CurrentUser = Annotated[MyUserSchema, Depends(current_user)]
VerifiedUser = Annotated[MyUserSchema, Depends(current_active_verified_user)]

@app.get("/profile")
async def profile(user: CurrentUser):
    return {"name": user.display_name}  # IDE knows this field exists

Protected routes

from typing import Annotated
from fastapi import Depends
from fastapi_fullauth.dependencies import current_user, current_active_verified_user, current_superuser, require_role

CurrentUser = Annotated[UserSchema, Depends(current_user)]
VerifiedUser = Annotated[UserSchema, Depends(current_active_verified_user)]
SuperUser = Annotated[UserSchema, Depends(current_superuser)]

@app.get("/profile")
async def profile(user: CurrentUser):
    return user

@app.get("/dashboard")
async def dashboard(user: VerifiedUser):
    return {"email": user.email}

@app.delete("/admin/users/{id}")
async def delete_user(user: SuperUser):
    ...

@app.get("/editor")
async def editor_panel(user=Depends(require_role("editor"))):
    ...

OAuth2 social login

from fastapi_fullauth import FullAuth, FullAuthConfig
from fastapi_fullauth.oauth.google import GoogleOAuthProvider
from fastapi_fullauth.oauth.github import GitHubOAuthProvider

fullauth = FullAuth(
    adapter=adapter,
    config=FullAuthConfig(SECRET_KEY="..."),
    providers=[
        GoogleOAuthProvider(
            client_id="your-google-client-id",
            client_secret="your-google-secret",
            redirect_uris=[
                "http://localhost:3000/auth/callback",
                "https://myapp.com/auth/callback",
            ],
        ),
        GitHubOAuthProvider(
            client_id="your-github-client-id",
            client_secret="your-github-secret",
            redirect_uris=["http://localhost:3000/auth/callback"],
        ),
    ],
)

Requires httpx: pip install "fastapi-fullauth[oauth]"

Security note: the OAuth state token is signed and carries the PKCE challenge, but it is not bound to the browser session. For defense against login-CSRF, also bind state to the initiating browser (e.g. a short-lived cookie you set before redirecting and verify on callback).

Event hooks

@fullauth.hooks.on("after_register")
async def welcome(user):
    await send_email(user.email, "Welcome!")

@fullauth.hooks.on("send_verification_email")
async def send_verify(email, token):
    await send_email(email, f"Verify: https://myapp.com/verify?token={token}")

Events: after_register, after_login, after_logout, after_password_change, after_password_reset, after_email_verify, send_verification_email, send_password_reset_email, after_oauth_login, after_oauth_register

Configuration

Pass a FullAuthConfig object or set env vars with FULLAUTH_ prefix.

fullauth = FullAuth(
    adapter=adapter,
    config=FullAuthConfig(
        SECRET_KEY="...",
        ACCESS_TOKEN_EXPIRE_MINUTES=60,
        API_PREFIX="/api/v2",
        LOGIN_FIELD="username",
        PASSWORD_HASH_ALGORITHM="bcrypt",
        BLACKLIST_BACKEND="redis",
        REDIS_URL="redis://localhost:6379/0",
        AUTH_RATE_LIMIT_ENABLED=True,
        TRUSTED_PROXY_HEADERS=["X-Forwarded-For"],
    ),
)

See Configuration docs for all options.

AI-friendly docs

Using an AI coding assistant? Point it at our LLM-optimized docs:

  • llms.txt: concise overview with links to all doc pages
  • llms-full.txt: full documentation in a single file

Works with Claude, Cursor, Copilot, and any tool that accepts a docs URL.

Development

git clone https://github.com/mdfarhankc/fastapi-fullauth.git
cd fastapi-fullauth
uv sync --dev --extra sqlalchemy --extra sqlmodel
uv run pytest tests/ -v

# run examples
uv run uvicorn examples.sqlmodel_app.main:app --reload

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapi_fullauth-0.15.0.tar.gz (420.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_fullauth-0.15.0-py3-none-any.whl (183.6 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_fullauth-0.15.0.tar.gz.

File metadata

  • Download URL: fastapi_fullauth-0.15.0.tar.gz
  • Upload date:
  • Size: 420.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for fastapi_fullauth-0.15.0.tar.gz
Algorithm Hash digest
SHA256 c142f192a967da3b4ac276a20fb4741550ac551d073d468e5aaa8c2b773ff347
MD5 69dbc904eae8d7549f72f21b371f2147
BLAKE2b-256 4dad6ba4f7c842c5463882418826ce0f4bcb99d8722ef261443fd9a8c8366ff9

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_fullauth-0.15.0.tar.gz:

Publisher: publish.yml on mdfarhankc/fastapi-fullauth

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastapi_fullauth-0.15.0-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_fullauth-0.15.0-py3-none-any.whl
Algorithm Hash digest
SHA256 74ce959b764ea315f25aeadbc2844363b6f0aedb76fd7004ddf16dc36fda0f44
MD5 abf0a58c5a7efe5b1c9dc3d2895bf2ed
BLAKE2b-256 27d6b827f24026a0d215158cf27e35554d0ee50db9f8e82ca57312f21599002a

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_fullauth-0.15.0-py3-none-any.whl:

Publisher: publish.yml on mdfarhankc/fastapi-fullauth

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.15.0 This release

2 files

0.14.1

2 files

0.14.0

2 files

0.13.0

2 files

0.12.0

2 files

0.11.0

2 files

0.10.0

2 files

0.9.1

2 files

0.9.0

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.0

2 files

0.4.0

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page