Skip to main content

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

Project description

FastAPI FullAuth

FastAPI FullAuth

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

PyPI 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 or SQLAlchemy 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
  • Session management: list active sessions (device, IP, last used), revoke one device, or sign out everywhere else
  • Password hashing via Argon2id (default) or bcrypt, 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 and GitHub, with multi-redirect-URI support
  • Role-based access control: current_user, require_role(), require_permission()
  • 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, 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 ORM adapter + Alembic (pick one)
redis Redis backends for token blacklist, lockout, rate limiting, passkey challenges
oauth OAuth2 social login (Google, GitHub)
passkey Passkey / WebAuthn support
bcrypt bcrypt password hashing (Argon2id is the default and needs no extra)
sqlmodel-standard / sqlalchemy-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")
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

async def welcome(user):
    await send_email(user.email, "Welcome!")

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

fullauth.hooks.on("after_register", welcome)
fullauth.hooks.on("send_verification_email", send_verify)

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

Project details


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.14.1.tar.gz (362.0 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.14.1-py3-none-any.whl (148.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: fastapi_fullauth-0.14.1.tar.gz
  • Upload date:
  • Size: 362.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fastapi_fullauth-0.14.1.tar.gz
Algorithm Hash digest
SHA256 08675a3ef66fc984c252ff9f4fe3f94831df888d90c2034bd949ae161ef4a92b
MD5 3549a6da473fef612a73f4154fb7220a
BLAKE2b-256 0d82ed63c964198a96e71ada864910d8e863f69dffb2dce4382564448204a9b7

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_fullauth-0.14.1.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.14.1-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_fullauth-0.14.1-py3-none-any.whl
Algorithm Hash digest
SHA256 236ba4b5a5a5c5add214d4e3dfe0cd6ce09388f89dd8ea7263229f65570a4c47
MD5 ab150e3fa8bd95ad56bcc50078a097c4
BLAKE2b-256 6668ee6f2a73e4b59544ebb2b75654dcaba33dcba2ca28e64e6463305a19680a

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_fullauth-0.14.1-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.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page