Metadata-Version: 2.4
Name: fastapi-fullauth
Version: 0.2.0
Summary: Production-grade, async-native authentication and authorization library for FastAPI
Project-URL: Homepage, https://github.com/mdfarhankc/fastapi-fullauth
Project-URL: Documentation, https://github.com/mdfarhankc/fastapi-fullauth
Project-URL: Repository, https://github.com/mdfarhankc/fastapi-fullauth
License-Expression: MIT
License-File: LICENSE
Keywords: argon2,authentication,authorization,fastapi,jwt,security
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Security
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: argon2-cffi>=23.0
Requires-Dist: fastapi>=0.110
Requires-Dist: pydantic-settings>=2.0
Requires-Dist: pydantic[email]>=2.0
Requires-Dist: pyjwt>=2.8
Requires-Dist: python-multipart>=0.0.22
Requires-Dist: uuid-utils>=0.14.1
Provides-Extra: all
Requires-Dist: alembic>=1.13; extra == 'all'
Requires-Dist: beanie>=1.25; extra == 'all'
Requires-Dist: httpx-oauth>=0.13; extra == 'all'
Requires-Dist: itsdangerous>=2.1; extra == 'all'
Requires-Dist: pyotp>=2.9; extra == 'all'
Requires-Dist: qrcode>=7.4; extra == 'all'
Requires-Dist: redis>=5.0; extra == 'all'
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'all'
Requires-Dist: sqlmodel>=0.0.16; extra == 'all'
Requires-Dist: tortoise-orm>=0.21; extra == 'all'
Requires-Dist: webauthn>=2.0; extra == 'all'
Provides-Extra: audit
Provides-Extra: beanie
Requires-Dist: beanie>=1.25; extra == 'beanie'
Provides-Extra: magic-link
Requires-Dist: itsdangerous>=2.1; extra == 'magic-link'
Provides-Extra: oauth
Requires-Dist: httpx-oauth>=0.13; extra == 'oauth'
Provides-Extra: passkeys
Requires-Dist: webauthn>=2.0; extra == 'passkeys'
Provides-Extra: redis
Requires-Dist: redis>=5.0; extra == 'redis'
Provides-Extra: sqlalchemy
Requires-Dist: alembic>=1.13; extra == 'sqlalchemy'
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'sqlalchemy'
Provides-Extra: sqlmodel
Requires-Dist: alembic>=1.13; extra == 'sqlmodel'
Requires-Dist: sqlmodel>=0.0.16; extra == 'sqlmodel'
Provides-Extra: tenants
Provides-Extra: tortoise
Requires-Dist: tortoise-orm>=0.21; extra == 'tortoise'
Provides-Extra: totp
Requires-Dist: pyotp>=2.9; extra == 'totp'
Requires-Dist: qrcode>=7.4; extra == 'totp'
Description-Content-Type: text/markdown

# fastapi-fullauth

[![PyPI](https://img.shields.io/pypi/v/fastapi-fullauth)](https://pypi.org/project/fastapi-fullauth/)
[![Python](https://img.shields.io/pypi/pyversions/fastapi-fullauth)](https://pypi.org/project/fastapi-fullauth/)
[![CI](https://github.com/mdfarhankc/fastapi-fullauth/actions/workflows/ci.yml/badge.svg)](https://github.com/mdfarhankc/fastapi-fullauth/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://opensource.org/licenses/MIT)

Async auth library for FastAPI. Handles JWT tokens, refresh rotation, password hashing, email verification, and role-based access out of the box.

## Install

```bash
pip install fastapi-fullauth
# with an ORM adapter:
pip install fastapi-fullauth[sqlmodel]
pip install fastapi-fullauth[sqlalchemy]
# with redis for token blacklisting:
pip install fastapi-fullauth[sqlmodel,redis]
```

## Quick start

```python
from fastapi import FastAPI
from fastapi_fullauth import FullAuth
from fastapi_fullauth.adapters.memory import InMemoryAdapter

app = FastAPI()

fullauth = FullAuth(
    secret_key="your-secret-key",
    adapter=InMemoryAdapter(),
)
fullauth.init_app(app)
```

This gives you `/auth/me`, `/auth/register`, `/auth/login`, `/auth/logout`, `/auth/refresh`, `/auth/change-password`, `/auth/password-reset/*`, `/auth/verify-email/*`, and admin role management endpoints — all under `/api/v1` by default.

Omit `secret_key` in dev and a random one is generated (tokens won't survive restarts).

## Custom user fields

Just define your model — schemas are auto-derived:

```python
from fastapi_fullauth.adapters.sqlmodel import UserBase, Role, UserRoleLink, RefreshTokenRecord, SQLModelAdapter
from sqlmodel import Field, Relationship

class MyUser(UserBase, table=True):
    __tablename__ = "fullauth_users"

    display_name: str = Field(default="", max_length=100)
    phone: str = Field(default="", max_length=20)

    roles: list[Role] = Relationship(link_model=UserRoleLink)
    refresh_tokens: list[RefreshTokenRecord] = Relationship()

fullauth = FullAuth(
    secret_key="...",
    adapter=SQLModelAdapter(session_maker, user_model=MyUser),
)
```

No need to create separate schema classes or subclass the adapter. Registration and response schemas pick up `display_name` and `phone` automatically. You can still pass explicit `user_schema` / `create_user_schema` if you want full control.

## Protected routes

```python
from fastapi import Depends
from fastapi_fullauth.dependencies import current_user, require_role

@app.get("/profile")
async def profile(user=Depends(current_user)):
    return user

@app.delete("/admin/users/{id}")
async def delete_user(user=Depends(require_role("admin"))):
    ...
```

## Configuration

Pass inline kwargs or a full config object:

```python
# inline
fullauth = FullAuth(
    secret_key="...",
    adapter=adapter,
    api_prefix="/api/v2",
    access_token_expire_minutes=60,
)

# or use FullAuthConfig for everything
from fastapi_fullauth import FullAuthConfig
fullauth = FullAuth(config=FullAuthConfig(SECRET_KEY="..."), adapter=adapter)
```

Config also reads env vars with `FULLAUTH_` prefix.

## Redis blacklist

```python
fullauth = FullAuth(
    secret_key="...",
    adapter=adapter,
    blacklist_backend="redis",
    redis_url="redis://localhost:6379/0",
)
```

## Refresh token security

Refresh tokens are stored in DB with family tracking. If a revoked token is replayed (possible theft), the entire token family gets revoked. Disable rotation with `REFRESH_TOKEN_ROTATION=False`.

## Event hooks

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

fullauth.hooks.on("after_register", welcome)
```

Events: `after_register`, `after_login`, `after_logout`, `after_password_change`, `after_password_reset`, `after_email_verify`, `send_verification_email`, `send_password_reset_email`

## Route control

```python
from fastapi_fullauth import Route

fullauth = FullAuth(
    secret_key="...",
    adapter=adapter,
    enabled_routes=[Route.LOGIN, Route.LOGOUT, Route.REFRESH],
)
```

## Middleware

SecurityHeaders, CSRF, and rate limiting are auto-wired from config flags. Pass `auto_middleware=False` to `init_app()` to handle it yourself.

## Auth rate limiting

Login, register, and password-reset have per-IP rate limits enabled by default (5/3/3 per minute). Configure via `AUTH_RATE_LIMIT_*` settings.

## Login field

By default, login uses `email`. Change it to any field on your user model:

```python
# username login: POST /login {"username": "john", "password": "..."}
fullauth = FullAuth(secret_key="...", adapter=adapter, login_field="username")

# phone login: POST /login {"phone": "+1234567890", "password": "..."}
fullauth = FullAuth(secret_key="...", adapter=adapter, login_field="phone")
```

The Swagger UI and request body update automatically. The adapter looks up users by that field.

## Development

```bash
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.memory_app.main:app --reload
uv run uvicorn examples.sqlmodel_app.main:app --reload
```

## License

MIT
