From bd77731487f3a58f8fea6b6a6330b8f74cb8643a Mon Sep 17 00:00:00 2001 From: "Waylon S. Walker" Date: Fri, 21 Nov 2025 13:21:33 -0600 Subject: [PATCH] README --- .gitignore | 1 + README.md | 296 +++++++++++++++++++++++++++++++++++++++++++++ reader_cookies.txt | 5 - 3 files changed, 297 insertions(+), 5 deletions(-) create mode 100644 README.md delete mode 100644 reader_cookies.txt diff --git a/.gitignore b/.gitignore index 4c83b03..a0dc7a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ reader_cookies.txt admin_cookies.txt +reader_cookies.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..3178be3 --- /dev/null +++ b/README.md @@ -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 diff --git a/reader_cookies.txt b/reader_cookies.txt deleted file mode 100644 index 2e92996..0000000 --- a/reader_cookies.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Netscape HTTP Cookie File -# https://curl.se/docs/http-cookies.html -# This file was generated by libcurl! Edit at your own risk. - -#HttpOnly_localhost FALSE / FALSE 1763754196 access_token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJyZWFkZXIiLCJyb2xlIjoicmVhZGVyIiwiZXhwIjoxNzYzNzU0MTk2fQ.G4lmnqksaHqX-36N_HFpn_NtrTh7H6Fq2zXrCDktMgg