HNG Stage 7 - Task 3: Mini Authentication + API Key System for Service-to-Service Access
A production-ready FastAPI application implementing dual authentication with JWT tokens for user access and API keys for service-to-service communication.
- User Authentication: JWT-based signup/login system
- π API Key Management: Create, list, and revoke API keys
- π Dual Authentication: Support for both JWT tokens and API keys
- π‘οΈ Secure: Password hashing with bcrypt, JWT with expiration
- ποΈ PostgreSQL: Production-ready database
- β 100% Test Coverage: Comprehensive test suite
- π Auto-generated API Docs: Interactive Swagger UI at
/docs
- Python 3.10+
- PostgreSQL database
- pip (Python package manager)
cd path/to/your/project directory
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txtCreate a PostgreSQL database:
# Using psql
createdb <database name>
# Or using SQL
psql -U postgres
CREATE DATABASE <database name>;Create a .env file (copy from .env.example):
cp .env.example .envEdit .env with your settings:
DATABASE_URL=postgresql://<db_username>:<db_password>@localhost:5432/<database_name>
SECRET_KEY=your-super-secret-key-min-32-characters
ACCESS_TOKEN_EXPIRE_MINUTES= # set the access token expiration time in minutes
API_KEY_EXPIRE_DAYS= # set the API key expiration time in days# Development mode with auto-reload
uvicorn main:app --reload
# Production mode
uvicorn main:app --host 0.0.0.0 --port 8000The API will be available at http://localhost:8000
Interactive API documentation is available at:
- Swagger UI: http://localhost:8000/docs
- ReDoc: http://localhost:8000/redoc
Register a new user account.
curl -X POST http://localhost:8000/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"username": "myusername",
"password": "securepass123"
}'Login and receive a JWT access token.
curl -X POST http://localhost:8000/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "myusername",
"password": "securepass123"
}'Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIs...",
"token_type": "bearer"
}Create a new API key (requires JWT authentication).
curl -X POST http://localhost:8000/keys/create \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "My Service Key",
"expires_in_days": 365
}'Response:
{
"id": 1,
"key": "sk_Ab3D...xyz",
"name": "My Service Key",
"created_at": "2025-12-05T23:00:00",
"expires_at": "2026-12-05T23:00:00",
"is_revoked": false
}key value immediately - it won't be shown again!
List all your API keys.
curl -X GET http://localhost:8000/keys \
-H "Authorization: Bearer YOUR_JWT_TOKEN"Revoke an API key.
curl -X DELETE http://localhost:8000/keys/1 \
-H "Authorization: Bearer YOUR_JWT_TOKEN"Accessible only with JWT token.
curl -X GET http://localhost:8000/protected/user \
-H "Authorization: Bearer YOUR_JWT_TOKEN"Accessible only with API key.
curl -X GET http://localhost:8000/protected/service \
-H "x-api-key: YOUR_API_KEY"Accessible with either JWT token or API key.
# With JWT
curl -X GET http://localhost:8000/protected/any \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
# With API key
curl -X GET http://localhost:8000/protected/any \
-H "x-api-key: YOUR_API_KEY"# Run all tests
pytest -v
# Run with coverage
pytest --cov=app tests/
# Run specific test file
pytest tests/test_auth.py -vhng-be-s7/
βββ app/
β βββ config.py # Environment configuration
β βββ database.py # Database setup and session
β βββ models/
β β βββ auth.py # SQLAlchemy models
β βββ schemas/
β β βββ auth.py # Pydantic schemas
β βββ services/
β β βββ auth.py # Authentication business logic
β β βββ api_keys.py # API key management logic
β βββ dependencies/
β β βββ auth.py # Auth dependencies
β βββ routers/
β β βββ auth.py # Auth endpoints
β β βββ api_keys.py # API key endpoints
β β βββ protected.py # Protected demo routes
β βββ utils/
β βββ security.py # Security utilities
βββ tests/
β βββ conftest.py # Pytest fixtures
β βββ test_auth.py # Auth endpoint tests
β βββ test_api_keys.py # API key tests
β βββ test_protected.py # Protected route tests
βββ main.py # FastAPI application
βββ requirements.txt # Python dependencies
βββ .env.example # Environment template
βββ README.md # This file
- User signs up via
/auth/signup - User logs in via
/auth/loginand receives a JWT token - Include token in requests:
Authorization: Bearer <token> - Token expires after 30 minutes (configurable)
- User creates API key via
/keys/create(requires JWT) - Use API key in requests:
x-api-key: <key> - API key expires after 365 days (configurable)
- Keys can be revoked anytime via
/keys/{key_id}
- β Password hashing with bcrypt
- β JWT tokens with configurable expiration
- β API key expiration and revocation
- β Protected routes with flexible authentication
- β CORS middleware configured
- β Input validation with Pydantic
- β SQL injection protection via SQLAlchemy ORM
Key environment variables in .env:
| Variable | Description | Default |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | Required |
SECRET_KEY |
JWT signing key (min 32 chars) | Required |
ACCESS_TOKEN_EXPIRE_MINUTES |
JWT token lifetime | 30 |
API_KEY_EXPIRE_DAYS |
API key lifetime | 365 |
CORS_ORIGINS |
Allowed CORS origins | localhost |
MIT License - feel free to use this for your projects!
- I Muaz - Built for HNG Stage 7 Task 3
Happy Coding! π