296 lines
7 KiB
Markdown
296 lines
7 KiB
Markdown
# 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
|