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.1.tar.gz (158.6 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.1-py3-none-any.whl (19.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: fastapi_jwt_authkit-1.0.1.tar.gz
  • Upload date:
  • Size: 158.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","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.1.tar.gz
Algorithm Hash digest
SHA256 ed5f036cc0bf698ae89ec0ee92efeeca2b98f9c96bee30361415617e5f2cf205
MD5 5cdb0a5829d3407f95757c9a5ade072e
BLAKE2b-256 4df88ad1fa4f9723b55ac7ef857f3c46f4006c19e546869aa6779bd3894862a8

See more details on using hashes here.

File details

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

File metadata

  • Download URL: fastapi_jwt_authkit-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 19.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.15 {"installer":{"name":"uv","version":"0.9.15","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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 bf94bc1b588ed69e563b398552534f847ca0c87bedc2d019655c3eaf1456f2dd
MD5 3ddebe4800ddecca47c7a1766c4bdd47
BLAKE2b-256 c0762d28fb6fa0660f5ece01706fe6326ffdb4a401aaf813cdad2e7b96a988a4

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