The Analytics Dashboard is now protected with JWT authentication, using the same auth system as the HazeBot Admin app. This provides:
- Secure access control (admin/mod roles only)
- Bitwarden password manager support
- Token-based authentication (no cookies)
- Seamless integration with existing HazeBot auth
User Request → NGINX (your-domain.com:443)
↓
auth_request → Flask Backend (/api/auth/verify-token)
↓ ↓
JWT Valid? 401/403 Error
↓
Proxy → Analytics Server (localhost:8089)
- Intercepts
/analytics/requests - Calls Flask JWT verification endpoint via
auth_request - Proxies to Analytics server if JWT is valid
- Returns 401 error if authentication fails
- Added to
api/auth_routes.py - Validates JWT token structure and signature
- Checks token expiry
- Verifies user has admin/mod permissions
- Returns HTTP 200 (valid) or 401/403 (invalid/forbidden)
- Already configured with redirect logic (
/analytics→/analytics/) - Serves dashboard at
/analytics/analytics_dashboard.html - No changes needed - authentication handled by NGINX
-
SSH into production server:
ssh root@YOUR_SERVER
-
Edit the NGINX config for your domain:
nano /etc/nginx/sites-available/your-domain
-
Add the contents of
nginx_analytics_jwt.confinside theserverblock (after existing location blocks). -
Test NGINX config:
nginx -t
-
Reload NGINX:
systemctl reload nginx
-
On production server, pull latest code:
cd /path/to/HazeBot git pull origin main -
Restart Flask backend (if running in Docker):
docker restart <backend-container-name>
Or if running with systemd:
systemctl restart hazebot-api
-
Check if Analytics server is running:
ps aux | grep view_analytics.py -
If not running, start it:
cd /path/to/HazeBot/analytics python3 view_analytics.py &
-
Or create a systemd service for it (recommended).
curl -I https://your-domain.com/analytics/Expected: 401 (Unauthorized)
curl -X POST https://your-domain.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"your-username","password":"your-password"}'Expected: {"token":"eyJ...", "user":"...", "role":"admin", ...}
TOKEN="<token-from-step-2>"
curl -I https://your-domain.com/analytics/ \
-H "Authorization: Bearer $TOKEN"Expected: 200 OK (then redirects to /analytics/analytics_dashboard.html)
-
Open browser developer tools (F12)
-
Go to Console tab
-
Execute:
// Get token (replace with actual username/password) fetch('https://your-domain.com/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username: 'your-username', password: 'your-password' }) }) .then(r => r.json()) .then(data => { localStorage.setItem('jwt_token', data.token); console.log('Token saved:', data.token); });
-
Install a browser extension to add Authorization headers (e.g., "ModHeader")
-
Add header:
Authorization: Bearer <token> -
Navigate to
https://your-domain.com/analytics/
-
In Bitwarden, create a new Login item:
- Name: "HazeBot Analytics"
- Username:
<your-username> - Password:
<your-password> - URI:
https://your-domain.com/analytics
-
Create a login page (optional) or use the API directly with a tool that supports Bitwarden.
- Algorithm: HS256 (HMAC-SHA256)
- Secret Key:
SECRET_KEYfrom Flask app config (set in .env) - Expiry: 7 days (for legacy auth), varies for Discord OAuth
{
"user": "admin",
"discord_id": "legacy_user",
"exp": 1234567890,
"role": "admin",
"permissions": ["all"],
"auth_type": "legacy",
"session_id": "abc123..."
}Only users with admin or mod role can access Analytics.
Lootlings (regular Discord members) are denied with 403 Forbidden.
- HazeBot Admin App: Stored in Flutter secure storage
- Browser Access: Store in
localStorageor use an extension - Bitwarden: Use username/password, get token via
/api/auth/login
- Check if token is expired (7 days for legacy auth)
- Verify token format:
Authorization: Bearer <token> - Check Flask logs for JWT decode errors
- User doesn't have admin/mod role
- Check Discord roles (ADMIN_ROLE_ID, MODERATOR_ROLE_ID in .env)
- Analytics server (8089) not running
- Flask backend not responding
- Check Docker container status
- NGINX auth_request timeout (default 60s)
- Increase timeout in NGINX config:
proxy_read_timeout 90s; proxy_connect_timeout 90s;
Required in .env file:
SECRET_KEY: Flask JWT signing keyAPI_ADMIN_USER: Admin usernameAPI_ADMIN_PASS: Admin passwordAPI_EXTRA_USERS: Additional users (format:user1:pass1,user2:pass2)DISCORD_CLIENT_ID: Discord OAuth2 client ID (for Discord login)DISCORD_CLIENT_SECRET: Discord OAuth2 client secret
If you want to use Discord login instead of username/password:
-
Initiate OAuth2 flow:
curl https://your-domain.com/api/discord/auth
-
Visit the returned
auth_urlin browser -
Authorize the app
-
Get JWT token from callback
This requires Discord app setup (see FIREBASE_SETUP.md for details).
api/auth_routes.py: Added/api/auth/verify-tokenendpointanalytics/nginx_analytics_jwt.conf: NGINX config for JWT authanalytics/ANALYTICS_JWT_SETUP.md: This documentation
- Deploy NGINX config to production
- Test JWT authentication
- Create a login page (optional, for browser access)
- Update Analytics dashboard UI to show logged-in user
- Add logout functionality
- Consider implementing token refresh mechanism
- Analytics Overview:
README.md- Complete analytics system documentation - Deployment Summary:
DEPLOYMENT_SUMMARY.md- Quick deployment reference - API Documentation:
../api/README.md- REST API endpoints and authentication - Main README:
../README.md- HazeBot overview and setup