Skip to main content

FastAPI authentication library with JWT access/refresh tokens, sync & async SQLAlchemy support, built-in auth routes (register/login/refresh/me), secure password hashing, and cookie/header token support.

Project description

FastAPI JWT Auth Kit

PyPI version Python License Downloads

FastAPI authentication library with JWT access/refresh tokens, sync & async SQLAlchemy support, built-in auth routes (register/login/refresh/me), secure password hashing, and cookie/header token support.

Complete authentication toolkit for FastAPI with JWT, designed for both sync and async SQLAlchemy backends. Provides batteries-included auth services, FastAPI routers, token utilities, and a clean protocol-driven repository interface.

Highlights

  • JWT access + refresh tokens with rotation
  • Sync and async SQLAlchemy adapters
  • Drop-in FastAPI routers for register, login, refresh, logout, and /me
  • Cookie and Authorization header support
  • Strong typing and protocol-based extensibility
  • Works with your own User model

Package Layout

packages/authkit/src/authkit/
  fastapi/      # FastAPI router builders + schemas
  ext/          # SQLAlchemy protocol adapters (sync/async)
  authenticator.py
  service.py
  tokens.py
  hashing.py
  settings.py
  extractors.py

Installation

From this repo

uv sync

In your project

pip install fastapi-jwt-authkit

The PyPI package name is fastapi-jwt-authkit, the import name is authkit.

Typing support

Type information (.pyi + py.typed) is bundled with the package for IDEs and static type checkers.

Quickstart (Async)

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

from authkit import AuthSettings
from authkit.fastapi.routers import build_auth_router_async

from your_app.models import Base, User

DATABASE_URL = "sqlite+aiosqlite:///./app.db"
engine = create_async_engine(DATABASE_URL, echo=False)
SessionLocal = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_session():
    async with SessionLocal() as session:
        try:
            yield session
            await session.commit()
        except Exception:
            await session.rollback()
            raise

settings = AuthSettings(secret_key="change-me", cookie_secure=False)

app = FastAPI()
auth_router = build_auth_router_async(
    settings=settings,
    get_session=get_session,
    user_model=User,
)
app.include_router(auth_router, prefix="/auth", tags=["auth"])

Quickstart (Sync)

from fastapi import FastAPI
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from authkit import AuthSettings
from authkit.fastapi.routers import build_auth_router_sync

from your_app.models import Base, User

DATABASE_URL = "sqlite:///./app.db"
engine = create_engine(DATABASE_URL, echo=False)
SessionLocal = sessionmaker(bind=engine, autocommit=False, autoflush=False)

def get_session():
    session = SessionLocal()
    try:
        yield session
        session.commit()
    except Exception:
        session.rollback()
        raise
    finally:
        session.close()

settings = AuthSettings(secret_key="change-me", cookie_secure=False)

app = FastAPI()
auth_router = build_auth_router_sync(
    settings=settings,
    get_session=get_session,
    user_models=User,
)
app.include_router(auth_router, prefix="/auth", tags=["auth"])

Core Concepts

AuthSettings

All auth behavior is configured via AuthSettings:

AuthSettings(
    secret_key="your-secret",
    algorithm="HS256",
    access_minutes=15,
    refresh_days=7,
    accept_header=True,
    accept_cookie=True,
    set_cookie_on_login=True,
    cookie_secure=True,
    cookie_samesite="lax",
)

AuthService / AsyncAuthService

Business logic for creating users, authenticating, and issuing tokens. Uses a repository protocol so you can plug in your own persistence layer.

BaseUser Model (Extendable)

Use BaseUser mixin to automatically get all required fields:

from authkit.fastapi.models import BaseUser
from sqlalchemy.orm import DeclarativeBase, Mapped
from sqlalchemy import String

class Base(DeclarativeBase):
    pass

class User(BaseUser, Base):
    __tablename__ = "users"
    
    # Required fields inherited from BaseUser:
    # id, email, username, password_hash, is_active, is_staff, is_superuser
    
    # Add your custom fields:
    phone_number: Mapped[str] = mapped_column(String(20), nullable=True)
    avatar_url: Mapped[str] = mapped_column(String(255), nullable=True)

SQLAlchemy Adapters

  • SQLAlchemySyncUserProtocol
  • SQLAlchemyAsyncUserProtocol

These adapters work with any model that implements the UserProtocol interface (including models extending BaseUser).

API Endpoints

When you include the auth router with prefix /auth, the following endpoints are available:

Method Path Description
POST /auth/register Create a new user
POST /auth/login Authenticate + issue tokens
POST /auth/refresh Refresh access token (accepts token from header, cookie, or body)
POST /auth/logout Clear auth cookies
GET /auth/me Get current user

Request/Response Schemas

Register

{ "email": "user@example.com", "username": "user", "password": "secret" }

Login

{ "username_or_email": "user", "password": "secret" }

Refresh (token can be in header, cookie, or body)

# Option 1: In request body
{ "refresh_token": "jwt" }

# Option 2: Authorization header
Authorization: Bearer <refresh_token>

# Option 3: Cookie (set automatically on login)
Cookie: refresh_token=<refresh_token>

Token Response

{ "access_token": "jwt", "refresh_token": "jwt" }

Cookies and Headers

AuthSettings controls where tokens are accepted and how they are stored:

  • accept_header: allow Authorization: Bearer <token>
  • accept_cookie: allow cookies
  • set_cookie_on_login: set cookies on successful login

Cookie names and TTL are customizable with: cookie_name_access, cookie_name_refresh, cookie_max_age_access, cookie_max_age_refresh.

Refresh Token Sources

The /auth/refresh endpoint accepts refresh tokens from multiple sources (checked in order):

  1. Authorization header: Authorization: Bearer <refresh_token>
  2. Cookie: refresh_token cookie
  3. Request body: {"refresh_token": "<token>"}

Example requests:

# Header
curl -X POST /auth/refresh -H "Authorization: Bearer <refresh_token>"

# Cookie (set automatically on login if enabled)
curl -X POST /auth/refresh --cookie "refresh_token=<token>"

# Body
curl -X POST /auth/refresh -d '{"refresh_token": "<token>"}'

Security Considerations

  • Use a strong secret_key and rotate regularly.
  • Set cookie_secure=True in production (HTTPS only).
  • Consider cookie_samesite="strict" for web apps with tight CSRF control.
  • Short access token TTLs with longer refresh TTLs are recommended.

Production Checklist

  • secret_key stored in a secure secret manager
  • HTTPS enforced
  • cookie_secure=True, cookie_samesite set per app policy
  • Rotate JWT secret or use KMS-backed signing
  • Enable logging around login and refresh flows
  • Implement account lockout or rate limiting at the API gateway
  • Configure backups for the user datastore

Compatibility

  • Python: 3.10+
  • FastAPI: 0.110+
  • SQLAlchemy: 2.x

Observability

This library raises standard HTTPException errors. For production:

  • Add structured logging around auth endpoints
  • Add tracing/metrics at the FastAPI middleware layer

Versioning

Follows semantic versioning: MAJOR.MINOR.PATCH.

Testing

Run the full test suite:

uv run pytest tests/ -v

All tests are under tests/ and cover tokens, hashing, services, and router integration (sync + async).

Examples

Working example apps are provided:

  • examples/async_app.py
  • examples/sync_app.py

Run:

uv run python -m examples.async_app
uv run python -m examples.sync_app

Extending the Repo Layer

Implement the repo protocol if you use a different persistence layer:

class MyRepo:
    def get_by_id(self, user_id: int): ...
    def get_by_email_or_username(self, value: str): ...
    def create_user(self, *, email: str, username: str, password: str,
                    is_staff: bool, is_active: bool, is_superuser: bool): ...

Async version must expose the same methods as async def.

Typing Notes

Type information is bundled with the package (.pyi + py.typed). If your IDE or type checker does not pick it up, ensure:

  • Your tooling supports PEP 561

Troubleshooting

Bcrypt errors on Windows

If you see bcrypt backend errors during hashing, ensure you have bcrypt<5 installed. This repo pins it accordingly in pyproject.toml.

SQLite in-memory tests

In-memory SQLite needs a StaticPool so all sessions share the same DB connection. The tests already handle this.

License

MIT (see LICENSE).

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_jwt_authkit-1.0.2.tar.gz (157.5 kB view details)

Uploaded Source

Built Distribution

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

fastapi_jwt_authkit-1.0.2-py3-none-any.whl (19.8 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_jwt_authkit-1.0.2.tar.gz.

File metadata

  • Download URL: fastapi_jwt_authkit-1.0.2.tar.gz
  • Upload date:
  • Size: 157.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for fastapi_jwt_authkit-1.0.2.tar.gz
Algorithm Hash digest
SHA256 265b069c40832d6e088bb486d52398403ef22d5dd5bb372d652400727e46a87e
MD5 7e3604fe72eb5c5bedb2e832b92323bc
BLAKE2b-256 668f13125237fad941223ca2942c366892de357054fe1370a26bdb9c22186374

See more details on using hashes here.

File details

Details for the file fastapi_jwt_authkit-1.0.2-py3-none-any.whl.

File metadata

  • Download URL: fastapi_jwt_authkit-1.0.2-py3-none-any.whl
  • Upload date:
  • Size: 19.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.0 {"installer":{"name":"uv","version":"0.10.0","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for fastapi_jwt_authkit-1.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 65d428021c944e170624e08dae8d81a47112be8af5e616691d93ba66b1eb307d
MD5 5a1791f772334eabb30b9455879ccdc5
BLAKE2b-256 21c72f7eef9500b36aefd4bafeabd7eaa9b2eae41858008639d6ef72d11afa9e

See more details on using hashes here.

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