# Authentication Setup Guide This guide walks you through setting up API key authentication for AegisGitea MCP to ensure only your ChatGPT workspace can access your self-hosted Gitea instance. --- ## Overview AegisGitea MCP uses **Bearer Token authentication** to secure access: - **API Keys**: Cryptographically secure 64-character tokens - **Header-based**: Keys sent via `Authorization: Bearer ` header - **Multi-key support**: Multiple keys for rotation grace periods - **Rate limiting**: Failed auth attempts trigger IP-based rate limits - **Audit logging**: All auth attempts logged for security monitoring --- ## Quick Start (5 minutes) ### 1. Generate API Key ```bash # Using Make make generate-key # Or directly python3 scripts/generate_api_key.py ``` This will: - Generate a secure 64-character API key - Show you the key (save it immediately!) - Provide `.env` configuration snippet - Optionally save metadata (not the key itself) for tracking **Example output:** ``` ✓ API Key Generated Successfully! API KEY: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2 📋 Next Steps: 1. Add this key to your .env file 2. Restart the MCP server 3. Configure ChatGPT Business ``` ### 2. Add Key to .env ```bash # Edit .env nano .env # Add/update this line: MCP_API_KEYS=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2 ``` ### 3. Restart MCP Server ```bash docker-compose restart aegis-mcp # Verify authentication is enabled docker-compose logs aegis-mcp | grep "authentication" ``` Expected log output: ``` API key authentication ENABLED (1 key(s) configured) ``` ### 4. Test Authentication ```bash # Without key - should fail with 401 curl https://mcp.yourdomain.com/mcp/tools # With valid key - should succeed curl -H "Authorization: Bearer YOUR_KEY_HERE" \ https://mcp.yourdomain.com/mcp/tools ``` --- ## Configuration Options ### Environment Variables ```bash # Enable/disable authentication AUTH_ENABLED=true # Set to false ONLY for testing # API keys (comma-separated for multiple) MCP_API_KEYS=key1 # Single key MCP_API_KEYS=key1,key2,key3 # Multiple keys (rotation grace period) # Rate limiting MAX_AUTH_FAILURES=5 # Max failed attempts before blocking AUTH_FAILURE_WINDOW=300 # Time window in seconds (5 min) ``` ### Multiple Keys (Rotation Grace Period) During key rotation, you can temporarily allow multiple keys: ```bash # Old key MCP_API_KEYS=old-key-here # Add new key (both work) MCP_API_KEYS=old-key-here,new-key-here # After updating ChatGPT, remove old key MCP_API_KEYS=new-key-here ``` --- ## Security Features ### 1. Constant-Time Comparison Keys are compared using `hmac.compare_digest()` to prevent timing attacks. ### 2. Rate Limiting - **Threshold**: 5 failed attempts per IP - **Window**: 5 minutes (configurable) - **Action**: Reject all requests from that IP until window expires - **Logging**: High-severity security event logged ### 3. Audit Logging Every authentication attempt logs: ```json { "timestamp": "2026-01-29T12:34:56.789Z", "event": "api_authentication", "status": "success", "client_ip": "203.0.113.42", "user_agent": "ChatGPT-User/1.0", "key_hint": "a1b2c3d4...e1f2" } ``` Failed attempts: ```json { "timestamp": "2026-01-29T12:34:56.789Z", "event": "access_denied", "reason": "invalid_api_key", "client_ip": "203.0.113.42" } ``` ### 4. Key Format Validation - Minimum length: 32 characters - Recommended length: 64 characters - Format: Hexadecimal string (0-9, a-f) --- ## Troubleshooting ### Issue: "No API keys configured" error **Symptoms:** ``` docker-compose logs: "No API keys configured in environment" ``` **Solution:** 1. Check `.env` file exists and contains `MCP_API_KEYS` 2. Ensure no typos (`MCP_API_KEYS` not `MCP_API_KEY`) 3. Verify key is at least 32 characters 4. Restart container after updating `.env` ### Issue: "Invalid API key" on valid key **Possible causes:** 1. **Whitespace in .env**: Remove spaces around `=` ```bash # Wrong MCP_API_KEYS = key-here # Correct MCP_API_KEYS=key-here ``` 2. **Key truncated**: Ensure entire 64-char key is copied ```bash # Check key length echo -n "your-key-here" | wc -c # Should output: 64 ``` 3. **Container not restarted**: Always restart after changing `.env` ```bash docker-compose restart aegis-mcp ``` ### Issue: Rate limit blocking legitimate requests **Symptoms:** ``` "Too many failed authentication attempts" ``` **Solution:** 1. Check audit logs for failed attempts: ```bash docker-compose exec aegis-mcp cat /var/log/aegis-mcp/audit.log | grep "invalid_api_key" ``` 2. Wait 5 minutes for rate limit to reset 3. If accidentally blocked yourself: ```bash # Restart container (clears in-memory rate limits) docker-compose restart aegis-mcp ``` 4. Adjust rate limit settings in `.env` if needed: ```bash MAX_AUTH_FAILURES=10 AUTH_FAILURE_WINDOW=600 ``` ### Issue: ChatGPT can't connect after adding auth See [CHATGPT_SETUP.md](CHATGPT_SETUP.md) for detailed ChatGPT configuration. **Quick check:** 1. Verify key in ChatGPT settings matches `.env` 2. Check Authorization header format: `Bearer ` (with space) 3. Test manually with curl first --- ## Monitoring Authentication ### View All Auth Attempts ```bash # All auth events docker-compose exec aegis-mcp grep "api_authentication" /var/log/aegis-mcp/audit.log # Failed attempts only docker-compose exec aegis-mcp grep "access_denied" /var/log/aegis-mcp/audit.log # Rate limit triggers docker-compose exec aegis-mcp grep "auth_rate_limit_exceeded" /var/log/aegis-mcp/audit.log ``` ### Real-Time Monitoring ```bash # Follow auth events docker-compose exec aegis-mcp tail -f /var/log/aegis-mcp/audit.log | grep "auth" ``` ### Weekly Summary Script ```bash #!/bin/bash # Save as scripts/auth_summary.sh CONTAINER="aegis-gitea-mcp" LOG_PATH="/var/log/aegis-mcp/audit.log" echo "=== Weekly Auth Summary ===" echo "" echo "Total auth attempts:" docker exec $CONTAINER grep "api_authentication" $LOG_PATH | wc -l echo "" echo "Successful:" docker exec $CONTAINER grep "api_authentication.*success" $LOG_PATH | wc -l echo "" echo "Failed:" docker exec $CONTAINER grep "access_denied.*invalid_api_key" $LOG_PATH | wc -l echo "" echo "Rate limited IPs:" docker exec $CONTAINER grep "auth_rate_limit_exceeded" $LOG_PATH | wc -l ``` --- ## Best Practices ### 1. Key Storage - ✅ Store in `.env` file (never commit to git) - ✅ Use password manager for backup - ✅ Save key metadata (not key itself) for tracking - ❌ Never hardcode in application code - ❌ Never share via unencrypted channels ### 2. Key Rotation - **Schedule**: Every 90 days (automated check available) - **Method**: Use `make rotate-key` for guided rotation - **Grace period**: Temporarily allow both old and new keys - **Verification**: Test new key before removing old key See [KEY_ROTATION.md](KEY_ROTATION.md) for detailed rotation procedures. ### 3. Access Control - **Single workspace**: One key for your ChatGPT Business account - **Multiple users**: One key per user (track with metadata) - **Revocation**: Remove key from `.env` and restart ### 4. Monitoring - **Weekly**: Review auth logs for anomalies - **Monthly**: Check key age with `make check-key-age` - **Quarterly**: Rotate keys - **Alerts**: Set up notifications for rate limit events --- ## Advanced Configuration ### Disable Authentication (Testing Only) **⚠️ WARNING: Only for isolated test environments** ```bash # .env AUTH_ENABLED=false ``` Server will log critical warning: ``` API key authentication DISABLED - server is open to all requests! ``` ### Custom Rate Limiting ```bash # More lenient (for high-traffic) MAX_AUTH_FAILURES=20 AUTH_FAILURE_WINDOW=600 # 10 minutes # More strict (for high-security) MAX_AUTH_FAILURES=3 AUTH_FAILURE_WINDOW=1800 # 30 minutes ``` ### Key Hashing (Future Enhancement) Currently keys are stored in plaintext in `.env`. Future versions will support hashed keys in database. **Current security model:** - ✅ Keys never logged in full (only first 8 + last 4 chars) - ✅ `.env` file protected by filesystem permissions - ✅ Container runs as non-root user - ⚠️ Ensure `.env` has restrictive permissions: `chmod 600 .env` --- ## Security Checklist Before going to production: - [ ] API key generated with `make generate-key` (not manually typed) - [ ] Key is exactly 64 characters - [ ] `.env` file has restrictive permissions (`chmod 600 .env`) - [ ] `.env` is in `.gitignore` (verify: `git status` shows no .env) - [ ] Container restarted after adding key - [ ] Authentication enabled (check logs for "ENABLED" message) - [ ] Test curl command succeeds with key, fails without - [ ] ChatGPT configured with Authorization header - [ ] Rate limiting tested (try 6 failed attempts) - [ ] Audit logs capturing auth events - [ ] Key rotation reminder set (90 days) - [ ] Backup key stored securely (password manager) --- ## FAQ **Q: Can I use the same key for multiple ChatGPT workspaces?** A: Yes, but not recommended. Generate separate keys for auditability: ```bash MCP_API_KEYS=workspace1-key,workspace2-key ``` **Q: How do I revoke access immediately?** A: Remove key from `.env` and restart: ```bash # Edit .env (remove old key) docker-compose restart aegis-mcp ``` Access is revoked instantly after restart. **Q: Does rate limiting affect valid keys?** A: No. Rate limiting only applies to IPs that fail authentication. Valid keys are never rate limited. **Q: Can I see which key was used for each request?** A: Yes. Audit logs include `key_hint` (first 8 + last 4 characters): ```json {"key_hint": "a1b2c3d4...e1f2"} ``` **Q: What happens if I lose my API key?** A: Generate a new one with `make generate-key`, update `.env`, restart server, update ChatGPT config. The old key stops working immediately after restart. --- ## Next Steps - [ChatGPT Setup Guide](CHATGPT_SETUP.md) - Configure ChatGPT Business - [Key Rotation Guide](KEY_ROTATION.md) - Automated rotation procedures - [Security Policy](SECURITY.md) - Overall security best practices --- **Need help?** Open an issue in the Gitea repository.