JWT tokens, OAuth2 social login, RBAC, email verification, rate limiting — all wired together as a pure JSON API. One include_router call. 14 endpoints. Ready.
from contextlib import asynccontextmanager
from fastapi import FastAPI, Depends
from tortoise.contrib.fastapi import RegisterTortoise
from rapidauth import RapidAuth
from models import User
@asynccontextmanager
async def lifespan(app):
async with RegisterTortoise(
app,
db_url="sqlite://./app.db",
modules={"models": ["models"]},
generate_schemas=True,
):
yield
app = FastAPI(lifespan=lifespan)
auth = RapidAuth(
user_model=User,
jwt_secret="your-secret",
social_auth={
"google": {
"client_id": "...",
"client_secret": "...",
"redirect_uri": "...",
}
},
)
app.include_router(auth.router)
# 14 endpoints. Done.
Stop rewriting auth on every project. RapidAuth is a pure backend JSON API — your frontend handles the UI, RapidAuth handles the security.
One include_router call registers all 14 endpoints. No boilerplate, no config files to maintain.
Every endpoint returns JSON. No HTML rendering, no server-side forms. Your frontend owns the UX completely.
Per-token jti blacklisting, automatic rotation on refresh, and fully configurable expiry times.
Google, GitHub, Discord. Auto-create users from OAuth profiles on first login. One config dict.
Built-in role and permission system. Gate any route with a single dependency function.
Best-in-class password hashing. Timing-safe verify. Switch with one config value.
Auto-send on register. Dev-friendly raw token mode. Production: point links at your SPA.
Sliding-window brute-force protection on login. Configurable attempts and lockout window.
Tortoise ORM 1.x and SQLAlchemy async/sync. Auto-detected from your model class at runtime.
Choose your ORM and start building. RapidAuth auto-detects your model and wires everything automatically.
from contextlib import asynccontextmanager
from fastapi import FastAPI, Depends
from tortoise import fields
from tortoise.contrib.fastapi import RegisterTortoise
from tortoise.models import Model
from rapidauth import RapidAuth
class User(Model):
id = fields.IntField(pk=True)
username = fields.CharField(max_length=100, unique=True)
email = fields.CharField(max_length=254, unique=True)
password = fields.CharField(max_length=200)
is_active = fields.BooleanField(default=True)
is_verified = fields.BooleanField(default=True)
roles = fields.JSONField(default=list)
class Meta: table = "users"
@asynccontextmanager
async def lifespan(app):
async with RegisterTortoise(
app,
db_url="sqlite://./app.db",
modules={"models": ["__main__"]},
generate_schemas=True,
):
yield
app = FastAPI(lifespan=lifespan)
auth = RapidAuth(user_model=User, jwt_secret="change-in-production")
app.include_router(auth.router)
@app.get("/profile")
async def profile(user = Depends(auth.get_current_user())):
return {"username": user.username, "email": user.email}from contextlib import asynccontextmanager
from fastapi import FastAPI, Depends
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker
from sqlalchemy import Boolean, Integer, String, JSON
from rapidauth import RapidAuth
engine = create_async_engine("sqlite+aiosqlite:///./app.db")
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
class Base(DeclarativeBase): pass
class User(Base):
__tablename__ = "users"
id : Mapped[int] = mapped_column(Integer, primary_key=True)
username : Mapped[str] = mapped_column(String(100), unique=True)
email : Mapped[str] = mapped_column(String(254), unique=True)
password : Mapped[str] = mapped_column(String(200))
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
roles : Mapped[list] = mapped_column(JSON, default=list)
async def get_db():
async with AsyncSessionLocal() as s:
yield s
@asynccontextmanager
async def lifespan(app):
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield
app = FastAPI(lifespan=lifespan)
auth = RapidAuth(user_model=User, jwt_secret="your-secret", get_db=get_db)
app.include_router(auth.router)auth = RapidAuth(
user_model=User,
jwt_secret="your-long-secret",
jwt={"access_expire": 900, "refresh_expire": 604800},
refresh_token_mode="cookie",
cookie={"httponly": True, "secure": True, "samesite": "lax"},
password_hasher="argon2",
email={
"host": "smtp.gmail.com", "port": 587,
"username": "you@gmail.com", "password": "app-password",
"from_email": "you@gmail.com",
},
base_url="https://api.myapp.com",
verify_email_url="https://myapp.com/verify",
reset_password_url="https://myapp.com/reset-password",
social_auth={
"google": {"client_id": "...", "client_secret": "...", "redirect_uri": "..."},
"github": {"client_id": "...", "client_secret": "...", "redirect_uri": "..."},
},
rate_limit={"enabled": True, "max_login_attempts": 5, "lockout_seconds": 300},
email_verification_required=True,
)from fastapi import Depends
from rapidauth.exceptions import PermissionDeniedError
def require_role(*roles: str):
async def dep(user = Depends(auth.get_current_user())):
for role in roles:
if role not in (user.roles or []):
raise PermissionDeniedError(f"Role '{role}' required")
return user
return dep
@app.get("/admin")
async def admin_panel(user = Depends(require_role("admin"))):
return {"message": f"Hello, {user.username}"}
@app.get("/moderator")
async def mod_panel(user = Depends(require_role("admin", "moderator"))):
return {"ok": True}
# Seed admin user
await auth.create_user(
"admin", "admin@example.com", "AdminPass1!",
extra={"roles": ["admin"]},
)Full documentation, working examples, and a CLI scaffold — everything you need to ship auth fast.