https://github.com/mdfarhankc/fastapi-fullauth
Production-grade, async-native authentication and authorization library for FastAPI.
https://github.com/mdfarhankc/fastapi-fullauth
access-token argon2 async authentication authorization bcrypt csrf fastapi fullauth jwt oauth pydantic-v2 rbac redis refresh-token security sqlalchemy sqlmodel users
Last synced: 7 days ago
JSON representation
Production-grade, async-native authentication and authorization library for FastAPI.
- Host: GitHub
- URL: https://github.com/mdfarhankc/fastapi-fullauth
- Owner: mdfarhankc
- License: mit
- Created: 2026-03-28T11:09:37.000Z (2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-19T11:33:35.000Z (12 days ago)
- Last Synced: 2026-05-19T14:35:56.322Z (12 days ago)
- Topics: access-token, argon2, async, authentication, authorization, bcrypt, csrf, fastapi, fullauth, jwt, oauth, pydantic-v2, rbac, redis, refresh-token, security, sqlalchemy, sqlmodel, users
- Language: Python
- Homepage: https://mdfarhankc.github.io/fastapi-fullauth/
- Size: 1.54 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: docs/security/middleware.md
Awesome Lists containing this project
README
FastAPI FullAuth
Production-grade, async-native authentication and authorization for FastAPI.
Documentation: https://mdfarhankc.github.io/fastapi-fullauth
Source Code: https://github.com/mdfarhankc/fastapi-fullauth
---
Add a complete authentication and authorization system to your **FastAPI** project. FastAPI FullAuth is designed to be production-ready, async-native, and pluggable = handling JWT tokens, refresh rotation, password hashing, email verification, OAuth2 social login, and role-based access out of the box.
## Features
- **JWT access + refresh tokens** with configurable expiry
- **Refresh token rotation** with reuse detection = revokes entire session family on replay
- **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** = `CurrentUser`, `VerifiedUser`, `SuperUser`, `require_role()`
- **Rate limiting** = per-route auth limits + global middleware (memory or Redis)
- **CSRF protection** and **security headers** middleware, auto-wired
- **Pluggable adapters** = SQLModel or SQLAlchemy
- **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
```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]
# with OAuth2 social login
pip install fastapi-fullauth[sqlmodel,oauth]
# with passkey/WebAuthn
pip install fastapi-fullauth[sqlmodel,passkey]
# everything
pip install fastapi-fullauth[all]
```
## Quick start
```python
from fastapi import FastAPI
from sqlmodel import Field, 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()
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.
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:
```python
fullauth.init_app(app, include_routers=["auth", "profile"])
```
`include_routers=None` (default) registers every available router. Or wire routers manually for full control:
```python
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) |
### Middleware
`init_app()` does not wire any middleware automatically. Import what you want and add it yourself:
```python
from fastapi_fullauth.middleware import (
SecurityHeadersMiddleware,
CSRFMiddleware,
RateLimitMiddleware,
)
app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(CSRFMiddleware, secret=fullauth.config.CSRF_SECRET or fullauth.config.SECRET_KEY)
app.add_middleware(RateLimitMiddleware, max_requests=60, window_seconds=60)
```
## 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/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}` | 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:
```python
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. Use `get_current_user_dependency()` for typed dependencies:
```python
from typing import Annotated
from fastapi import Depends
from fastapi_fullauth.dependencies import get_current_user_dependency
MyCurrentUser = Annotated[MyUserSchema, Depends(get_current_user_dependency(MyUserSchema))]
@app.get("/profile")
async def profile(user: MyCurrentUser):
return {"name": user.display_name} # IDE knows this field exists
```
## Protected routes
```python
from fastapi import Depends
from fastapi_fullauth.dependencies import CurrentUser, VerifiedUser, SuperUser, require_role
@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
```python
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]`
## Event hooks
```python
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`
## Configuration
Pass a `FullAuthConfig` object or set env vars with `FULLAUTH_` prefix.
```python
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](https://mdfarhankc.github.io/fastapi-fullauth/configuration/) for all options.
## AI-friendly docs
Using an AI coding assistant? Point it at our LLM-optimized docs:
- **[llms.txt](https://mdfarhankc.github.io/fastapi-fullauth/llms.txt)** = concise overview with links to all doc pages
- **[llms-full.txt](https://mdfarhankc.github.io/fastapi-fullauth/llms-full.txt)** = full documentation in a single file
Works with Claude, Cursor, Copilot, and any tool that accepts a docs URL.
## 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.sqlmodel_app.main:app --reload
```
## License
MIT