FastAPI authentication package using JWT access tokens, Redis refresh tokens, and PostgreSQL.
jwt_auth/
├── __init__.py
├── auth_routes.py
├── controllers/
│ └── auth_controller.py
├── services/
│ └── auth_services.py
├── schemas/
│ └── auth_schema.py
└── db/
├── db.py
└── redis.py
example_app.py shows minimal mounting/import usage.
pip install git+https://github.com/agabrielcorujo/jwt-auth.gitor in requirements.txt:
jwt-auth @ git+https://github.com/agabrielcorujo/jwt-auth.gitfrom fastapi import FastAPI
from jwt_auth.auth_routes import router as auth_router
app = FastAPI()
app.include_router(auth_router)Bearer decode helper:
from jwt_auth.services.auth_services import decode_access_tokenBase prefix: /auth
POST /auth/registerPOST /auth/loginPOST /auth/refreshPOST /auth/logoutPATCH /auth/password-reset-requestPATCH /auth/validate-password-reset-request
POST /auth/login
{
"email": "user@example.com",
"password": "plain-text-password"
}POST /auth/register
{
"email": "user@example.com",
"password": "plain-text-password",
"phone": "optional, defaults to n/a",
"first_name": "optional, defaults to n/a",
"last_name": "optional, defaults to n/a",
"city": "optional, defaults to n/a",
"street": "optional, defaults to n/a",
"state": "optional, defaults to n/a",
"zip_code": "optional, defaults to n/a"
}POST /auth/refresh and POST /auth/logout
- No JSON body required for token input.
- They read
refresh_tokenfrom cookie.
PATCH /auth/password-reset-request
{
"email": "user@example.com"
}PATCH /auth/validate-password-reset-request
{
"email": "user@example.com",
"code": "1234",
"password": "new-password"
}- Login returns:
access_tokenrolefirst_namelast_namestatus
- Register returns:
createduser_id
- Refresh returns:
access_tokentoken_type
- Logout returns:
status
On login, refresh cookie is set with:
key="refresh_token"httponly=Truesecure=Truesamesite="lax"path="/auth"domain=os.getenv("DOMAIN")
On logout, cookie is deleted with matching path="/auth" and domain=os.getenv("DOMAIN").
Required for app startup:
DB_HOST=...
DB_PORT=...
DB_NAME=...
DB_USER=...
DB_PASSWORD=...
JWT_KEY=...Redis:
REDIS_URL={redis url from whatever hosted provider you are using (not docker)}If REDIS_URL is missing (eg you are using docker), code defaults to redis://redis:6379.
Cookie domain (optional):
DOMAIN=.{your domain}.com or whateverTwilio (optional, password reset SMS endpoints):
TWILIO_ACCOUNT_SID=...
TWILIO_AUTH_TOKEN=...
TWILIO_PHONE=...- DB pool is initialized at import time in
jwt_auth/db/db.py. - Redis client is initialized at import time in
jwt_auth/db/redis.py. - If DB env vars are missing, DB connection fails, or Redis is unreachable, imports that depend on these modules will fail early.
Your PostgreSQL database must contain a users table with the following schema:
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
phone TEXT NOT NULL,
role TEXT NOT NULL,
city TEXT NOT NULL,
street TEXT NOT NULL,
state TEXT NOT NULL,
zip_code TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
);Required columns (in this order):
- id - Primary key (UUID, auto-generated with gen_random_uuid())
- email - Unique user email
- password_hash - Hashed password (never plaintext)
- first_name - User's first name
- last_name - User's last name
- phone - User's phone number
- role - Application role (e.g. user, admin)
- city - City
- street - Street address
- state - State
- zip_code - ZIP / postal code
- created_at - Account creation timestamp