This commit is contained in:
Waylon Walker 2025-11-21 13:21:33 -06:00
parent 298d3223f0
commit bd77731487
3 changed files with 297 additions and 5 deletions

296
README.md Normal file
View file

@ -0,0 +1,296 @@
# 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 `.html` extensions
- **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:
```bash
just start-auth
```
Start nginx in a separate terminal:
```bash
just start-nginx
```
### 2. Test Authentication
Run the full test suite to see JWT authentication in action:
```bash
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:
```bash
just start-auth
```
Stop the authentication service:
```bash
just stop-auth
```
Start nginx in Docker:
```bash
just start-nginx
```
Stop nginx:
```bash
just stop-nginx
```
View nginx logs:
```bash
just logs-nginx
```
### Authentication Testing
Login as admin user and save JWT cookie:
```bash
just test-login-admin
```
Login as reader user and save JWT cookie:
```bash
just test-login-reader
```
Test admin access to protected page:
```bash
just test-admin-access
```
Test reader blocked from admin page:
```bash
just test-reader-blocked
```
Check current user info (admin):
```bash
just test-whoami-admin
```
Check current user info (reader):
```bash
just test-whoami-reader
```
Test logout functionality:
```bash
just test-logout
```
Run complete authentication test suite:
```bash
just test-full-flow
```
Clean up cookie files:
```bash
just clean-cookies
```
## How nginx auth_request Works
1. **User requests protected resource** → nginx receives request
2. **nginx makes auth subrequest** → Internal call to `/authz` endpoint
3. **FastAPI validates JWT token** → Checks cookie and user permissions
4. **Authorization response** → 200 (allow), 401 (login), or 403 (forbidden)
5. **nginx acts on response** → Serves content or shows error page
### Configuration Highlights
**nginx.conf key sections:**
```nginx
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:**
```python
@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
```bash
# 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
```bash
# 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
```bash
# 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?
```bash
# Check if ports are available
lsof -i :5115 # FastAPI
lsof -i :8000 # nginx
# Check service status
docker ps | grep nginx
```
### Authentication not working?
```bash
# Check FastAPI logs
just stop-auth
./main_auth.py # Run in foreground to see errors
# Check nginx logs
just logs-nginx
```
### Cookie issues?
```bash
# 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](http://nginx.org/en/docs/http/ngx_http_auth_request_module.html)
- [FastAPI Security](https://fastapi.tiangolo.com/tutorial/security/)
- [JWT.io](https://jwt.io/) - JWT debugger