7 KiB
nginx auth_request Learning Demo
A hands-on demonstration of nginx's auth_request module with JWT-based authentication. This project helps you understand how to implement secure authentication and authorization using nginx as a reverse proxy with external authentication services.
What This Project Demonstrates
- nginx auth_request module: External authentication for protected resources
- JWT-based security: Industry-standard token authentication (not forgeable cookies)
- Role-based access control: Different user roles with different permissions
- Clean URL structure: Directory-based routing without
.htmlextensions - Custom error pages: Professional 403/404 pages with no caching
- FastAPI backend: Modern Python API for authentication logic
Architecture Overview
┌─────────────┐ auth_request ┌─────────────────┐
│ nginx │ ──────────────────▶│ FastAPI │
│ :8000 │ │ :5115 │
│ │◀──────────────────▶│ │
│ - Routes │ JWT validation │ - Authentication│
│ - Static │ │ - Authorization │
│ - Errors │ │ - User roles │
└─────────────┘ └─────────────────┘
Quick Start
Prerequisites
- Docker (for nginx)
- Python with uv (for FastAPI)
- just (task runner)
- curl (for testing)
1. Start Services
Start the authentication service:
just start-auth
Start nginx in a separate terminal:
just start-nginx
2. Test Authentication
Run the full test suite to see JWT authentication in action:
just test-full-flow
3. Manual Testing
Visit the application in your browser:
- http://localhost:8000/ - Home page (redirects to login if not authenticated)
- http://localhost:8000/login/ - Login page
- http://localhost:8000/admin/ - Admin-only page
User Accounts
| Username | Password | Role | Access |
|---|---|---|---|
| admin | admin | admin | All pages |
| reader | reader | reader | Home only |
Available Commands
Service Management
Start the FastAPI authentication service:
just start-auth
Stop the authentication service:
just stop-auth
Start nginx in Docker:
just start-nginx
Stop nginx:
just stop-nginx
View nginx logs:
just logs-nginx
Authentication Testing
Login as admin user and save JWT cookie:
just test-login-admin
Login as reader user and save JWT cookie:
just test-login-reader
Test admin access to protected page:
just test-admin-access
Test reader blocked from admin page:
just test-reader-blocked
Check current user info (admin):
just test-whoami-admin
Check current user info (reader):
just test-whoami-reader
Test logout functionality:
just test-logout
Run complete authentication test suite:
just test-full-flow
Clean up cookie files:
just clean-cookies
How nginx auth_request Works
- User requests protected resource → nginx receives request
- nginx makes auth subrequest → Internal call to
/authzendpoint - FastAPI validates JWT token → Checks cookie and user permissions
- Authorization response → 200 (allow), 401 (login), or 403 (forbidden)
- nginx acts on response → Serves content or shows error page
Configuration Highlights
nginx.conf key sections:
location / {
auth_request /authz; # External auth check
error_page 401 = @login; # Redirect unauthorized users
error_page 403 = @forbidden; # Show 403 page for forbidden
}
location = /authz {
internal; # Only nginx can call this
proxy_pass http://127.0.0.1:5115/authz;
proxy_set_header X-Original-URI $request_uri;
}
FastAPI auth logic:
@app.get("/authz")
def authz(request: Request):
token = request.cookies.get("access_token")
username = verify_jwt_token(token)
# Role-based access control
if path.startswith("/admin") and user_role != 'admin':
return Response("Forbidden", status_code=403)
return Response("OK", status_code=200)
Security Features
- JWT tokens: Cryptographically signed, cannot be forged
- Token expiration: 30-minute automatic expiry
- HttpOnly cookies: Prevent XSS cookie theft
- Role-based access: Fine-grained permission control
- Cache prevention: No caching of sensitive pages during demos
File Structure
├── main_auth.py # FastAPI authentication service
├── nginx.conf # nginx configuration with auth_request
├── justfile # Task automation recipes
├── site/ # Static web content
│ ├── index.html # Home page
│ ├── admin/index.html # Protected admin page
│ ├── login/index.html # Login form
│ ├── 403/index.html # Access denied page
│ └── 404/index.html # Not found page
└── README.md # This documentation
Learning Exercises
1. Basic Flow
# Start services
just start-auth
just start-nginx
# Test unauthenticated access
curl -I http://localhost:8000/
# Should redirect to /login/
# Login and get JWT
just test-login-admin
# Access protected resource
just test-admin-access
2. Role-Based Access
# Login as different users
just test-login-admin
just test-login-reader
# Compare access levels
just test-admin-access # Should succeed
just test-reader-blocked # Should fail with 403
3. JWT Security
# View JWT token structure
cat admin_cookies.txt
# Check user info from token
just test-whoami-admin
# Try accessing with expired/invalid token
# (manually edit cookie file to test)
Troubleshooting
Services not starting?
# Check if ports are available
lsof -i :5115 # FastAPI
lsof -i :8000 # nginx
# Check service status
docker ps | grep nginx
Authentication not working?
# Check FastAPI logs
just stop-auth
./main_auth.py # Run in foreground to see errors
# Check nginx logs
just logs-nginx
Cookie issues?
# Clean up old cookies
just clean-cookies
# Check cookie contents
cat admin_cookies.txt
Next Steps
- Experiment with different user roles
- Modify JWT expiration times
- Add new protected endpoints
- Implement password hashing
- Add HTTPS with proper secure cookies
- Try different nginx auth_request patterns
References
- nginx auth_request module
- FastAPI Security
- JWT.io - JWT debugger