Skip to content
Conduit

skill

fastapi-fullauth

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

mdfarhankc/fastapi-fullauthcommunitylistedunreviewed

Set up

Skills are installed by placing their folder where your agent loads skills.

The download comes from github.com/mdfarhankc/fastapi-fullauth itself, not from our server, so what you get is exactly what its author published.

Install instructions come from the README

Skills do not use an MCP config. Follow the steps in the README below.

README

synced Oct 25, 2026

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
  • Any primary key type: UUID by default, or UserSchema[int] for integer and sequence keys, UserSchema[str] for string keys
  • 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]"
ExtraAdds
sqlmodel / sqlalchemySQL ORM adapter + Alembic (pick one)
tortoiseTortoise ORM adapter (async SQL)
beanieBeanie adapter (MongoDB)
redisRedis backends for token blacklist, lockout, rate limiting, passkey challenges
oauthOAuth2 social login (Google, GitHub, Discord, GitLab)
passkeyPasskey / WebAuthn support
bcryptbcrypt password hashing (Argon2id is the default and needs no extra)
sqlmodel-standard / sqlalchemy-standard / tortoise-standard / beanie-standardOne 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.

RouterRoutes
auth_routerregister, login, logout, refresh
profile_routerme, verified-me, update profile, delete account, change password
verify_routeremail verification, password reset
admin_routerassign/remove roles and permissions (superuser)
oauth_routerOAuth provider routes (only if configured)
passkey_routerPasskey register, authenticate, list, delete (only if enabled)
sessions_routerlist 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

MethodPathDescription
POST/auth/registerCreate a new user
POST/auth/loginAuthenticate, get tokens
POST/auth/logoutBlacklist token
POST/auth/refreshRotate token pair
GET/auth/sessionsList active sessions
DELETE/auth/sessions/{family_id}Revoke one session
POST/auth/sessions/revoke-othersSign out other sessions
GET/auth/meGet current user
GET/auth/me/verifiedVerified users only
PATCH/auth/meUpdate profile
DELETE/auth/meDelete account
POST/auth/change-passwordChange password
POST/auth/verify-email/requestRequest verification email
POST/auth/verify-email/confirmConfirm email
POST/auth/password-reset/requestRequest password reset
POST/auth/password-reset/confirmReset password
POST/auth/admin/assign-roleAssign role (superuser)
POST/auth/admin/remove-roleRemove role (superuser)
POST/auth/admin/assign-permissionAssign permission to role (superuser)
POST/auth/admin/remove-permissionRemove 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.

Coming from fastapi-users?

fastapi-users is in maintenance mode. The migration guide maps every concept and route, and your existing password hashes keep working, so nobody has to reset a password.

AI-friendly docs

Using an AI coding assistant? Install the fastapi-fullauth Agent Skill so it knows the library's APIs, contracts, and pitfalls:

npx skills add mdfarhankc/fastapi-fullauth

It works with any agent that supports the Agent Skills standard, including Claude Code, Codex, Cursor, GitHub Copilot, and Gemini CLI.

For tools that take a docs URL instead:

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

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

↑↓ navigate openesc close