A production-ready FastAPI authentication system with JWT tokens, role-based authorization, SQLAlchemy ORM, and PostgreSQL.
Author: Dip Roy
- 🔐 JWT Authentication - Access tokens with configurable expiration (default: 15 min)
- 👥 Role-Based Authorization - User and Admin roles with protected endpoints
- 🗄️ PostgreSQL + SQLAlchemy - Modern ORM with type hints
- 🔄 Alembic Migrations - Database version control
- 🔒 Secure Password Hashing - bcrypt via passlib
- ✅ 21 Pytest Tests - Full test coverage for auth flows
- 📝 Pydantic Validation - Request/response validation
- 🌐 CORS Enabled - Ready for frontend integration
| Category | Technology |
|---|---|
| Framework | FastAPI 0.123+ |
| Database | PostgreSQL + SQLAlchemy 2.0 |
| Auth | python-jose (JWT), passlib (bcrypt) |
| Migrations | Alembic |
| Validation | Pydantic 2.x |
| Testing | Pytest + HTTPX |
| Server | Uvicorn |
pyauth/
├── app/
│ ├── api/
│ │ ├── __init__.py
│ │ ├── auth.py # /auth/register & /auth/login endpoints
│ │ └── protected.py # /protected/user & /protected/admin routes
│ ├── core/
│ │ ├── __init__.py
│ │ ├── config.py # Pydantic settings (loads .env)
│ │ └── security.py # bcrypt hashing & python-jose JWT
│ ├── db/
│ │ ├── __init__.py
│ │ └── session.py # SQLAlchemy engine & session factory
│ ├── __init__.py
│ ├── crud.py # get_user_by_email, create_user, authenticate_user
│ ├── deps.py # get_current_user, require_role dependencies
│ ├── models.py # User model with UserRole enum (USER, ADMIN)
│ └── schemas.py # Pydantic request/response schemas
├── alembic/
│ ├── versions/
│ │ └── 001_create_users_table.py
│ ├── env.py
│ └── script.py.mako
├── tests/
│ ├── __init__.py
│ ├── conftest.py # Test fixtures & in-memory SQLite DB
│ └── test_auth.py # 21 comprehensive auth tests
├── .env # Your environment variables (git-ignored)
├── .gitignore
├── alembic.ini
├── docker-compose.yml # Optional: PostgreSQL container
├── Makefile # make run, make test, etc.
├── requirements.txt
└── README.md
cd pyauth
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txtCreate a .env file in the project root:
# Generate a secure secret key
openssl rand -hex 32DATABASE_URL=postgresql://postgres:postgres@localhost:5432/pyauth
SECRET_KEY=your-256-bit-secret-key-here
ACCESS_TOKEN_EXPIRE_MINUTES=15
⚠️ Important:DATABASE_URLandSECRET_KEYhave no defaults and must be set.
Option A: Local PostgreSQL
# Create database
psql -U postgres -c "CREATE DATABASE pyauth;"Option B: Docker Compose
docker-compose up -dalembic upgrade head# Using uvicorn directly
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# Or using make
make runThe API will be available at http://localhost:8000
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
| Method | Endpoint | Description | Auth Required |
|---|---|---|---|
| GET | /public |
Public endpoint | No |
| POST | /auth/register |
Register new user | No |
| POST | /auth/login |
Login and get token | No |
| GET | /protected/user |
User-only endpoint | Yes (any role) |
| GET | /protected/admin |
Admin-only endpoint | Yes (admin role) |
curl -X POST http://localhost:8000/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "secretpass123", "full_name": "John Doe"}'curl -X POST http://localhost:8000/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "secretpass123"}'Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}curl http://localhost:8000/protected/user \
-H "Authorization: Bearer <your_access_token>"# Run all tests
pytest
# Run with verbose output
pytest -v
# Run with coverage
pytest --cov=app| Variable | Description | Required |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string (e.g., postgresql://user:pass@localhost:5432/pyauth) |
✅ Yes |
SECRET_KEY |
JWT signing key (256-bit hex string recommended) | ✅ Yes |
ACCESS_TOKEN_EXPIRE_MINUTES |
Token expiration time in minutes | No (default: 15) |
Generate a secure secret key:
openssl rand -hex 32
make run # Start development server (uvicorn --reload)
make test # Run all 21 tests with pytest
make migrate # Run alembic upgrade head
make revision # Create new migration (edit message in Makefile)By default, users register with the user role. To promote a user to admin:
UPDATE users SET role = 'admin' WHERE email = 'admin@example.com';- Passwords are hashed with bcrypt (12 rounds)
- JWT tokens use HS256 algorithm
- Tokens contain user ID in the
subclaim (as string per RFC 7519) SECRET_KEYandDATABASE_URLmust be set in.env(no defaults).envis git-ignored to prevent accidental commits
All 21 tests cover:
- User registration (valid, duplicate email, weak password)
- User login (valid, wrong email/password)
- JWT token validation and expiration
- Protected endpoint access control
- Admin-only endpoint authorization
- Token format and error handling
Run tests:
pytest -v
# Output: 21 passed ✅MIT License - Dip Roy