Skip to content

Security: go-authgate/authgate

docs/SECURITY.md

Security Guide

This guide covers security best practices, threat model, and production hardening for AuthGate.

Table of Contents


Production Deployment Checklist

Before deploying AuthGate to production, complete this security checklist:

Secrets and Configuration

  • Change JWT_SECRET to strong random value (32+ characters, use openssl rand -hex 32)
  • Change SESSION_SECRET to strong random value (32+ characters)
  • Set secure DEFAULT_ADMIN_PASSWORD (or immediately change auto-generated password)
  • Never commit secrets or private keys to version control
  • Use environment variables or secrets management system
  • Rotate secrets regularly (JWT_SECRET: every 90 days, SESSION_SECRET: every 180 days)
  • If using RS256/ES256: set JWT_PRIVATE_KEY_PATH file permissions to 0400 (read-only by owner)
  • If using RS256/ES256: use at least 2048-bit RSA key or P-256 curve for ECDSA

Network Security

  • Use HTTPS exclusively (set BASE_URL to https://...)
  • Obtain valid SSL/TLS certificates (Let's Encrypt or commercial CA)
  • Configure firewall rules (only expose ports 80, 443)
  • Use reverse proxy (Nginx, Caddy) for additional security layers
  • Enable HTTP/2 for better performance
  • Configure proper CORS policies if needed

Token Configuration

  • Set appropriate DeviceCodeExpiration (default: 30 minutes)
  • Set appropriate JWTExpiration (default: 1 hour, max: 24 hours)
  • Set appropriate REFRESH_TOKEN_EXPIRATION (default: 30 days)
  • Consider enabling token rotation for high-security scenarios (ENABLE_TOKEN_ROTATION=true)
  • Review token scopes and ensure least privilege

Rate Limiting

  • Enable rate limiting (enabled by default)
  • Use Redis store for multi-pod deployments (RATE_LIMIT_STORE=redis)
  • Adjust limits based on traffic patterns
  • Monitor rate limit events in audit logs
  • Set up alerts for excessive rate limiting
  • If enabling dynamic client registration, set DYNAMIC_CLIENT_REGISTRATION_TOKEN
  • Review and approve pending client registrations promptly

Audit Logging

  • Enable audit logging (enabled by default)
  • Set appropriate retention period (AUDIT_LOG_RETENTION, default: 90 days)
  • Monitor audit log storage growth
  • Set up regular exports for compliance (CSV available)
  • Review audit logs regularly for security incidents
  • Set up alerts for critical audit events

Database Security

  • Use PostgreSQL for production (more secure than SQLite)
  • Enable database encryption at rest
  • Regularly backup database (automated daily backups)
  • Restrict database access (localhost only or private network)
  • Use strong database passwords
  • Enable database SSL/TLS connections

Application Security

  • Run as non-root user (Docker: UID 1000, Systemd: dedicated user)
  • Use Docker non-root user mode (already configured in Dockerfile)
  • Configure timeouts (ReadTimeout, WriteTimeout, IdleTimeout)
  • Set up automated cleanup for expired tokens and device codes
  • Implement secure session management (7-day expiry, encrypted cookies)
  • Enable CSRF protection (already enabled)

Monitoring and Operations

  • Monitor /health endpoint for service availability
  • Set up log aggregation (Loki, Papertrail, CloudWatch)
  • Configure alerts for critical errors
  • Monitor failed login attempts
  • Track unusual authentication patterns
  • Set up automated backup verification

User Education

  • Educate users to use /account/sessions to review active devices
  • Provide clear instructions for revoking suspicious sessions
  • Document incident response procedures
  • Train administrators on security best practices
  • Train administrators that POST /admin/users/:id/disable is the fastest containment action — it revokes every active and refresh token immediately, blocks future logins (local + OAuth callbacks return ErrAccountDisabled), and RequireAuth clears any live session on the next request. Guards prevent disabling your own account or the last active admin (disabled admins do not block this guard).

Threat Model

What AuthGate Protects Against

Client Secret Exposure in Distributed Apps

  • Device Authorization Flow doesn't require embedding secrets in CLI tools
  • Device codes are short-lived (30 minutes) and single-use

Phishing Attacks

  • User authorizes on trusted domain (your AuthGate instance)
  • Device codes displayed in CLI, not entered by user
  • Verification URI clearly shows your domain

Replay Attacks

  • Device codes are single-use (marked as used after authorization)
  • JWTs have expiration times and signature verification
  • CSRF tokens protect state-changing operations

Token Tampering

  • JWTs signed with HS256, RS256, or ES256 (configurable)
  • Signature verification on every request
  • Token claims cannot be modified without detection
  • RS256/ES256 public keys exposed via JWKS endpoint for decentralized verification

Brute Force Attacks

  • Rate limiting enabled by default (configurable per endpoint)
  • IP-based tracking prevents single attacker from overwhelming service
  • Failed login attempts logged in audit trail

Unauthorized Client Registration

  • Optional initial access token required for dynamic registration
  • Registered clients default to "pending" status (admin approval required)
  • All registration attempts are audit logged

Token Information Leakage

  • Introspection endpoint requires client authentication (Basic Auth or form credentials)
  • Rate limited to prevent client secret brute force

API Abuse and DoS Attempts

  • Per-endpoint rate limits protect critical paths
  • Memory or Redis store for distributed rate limiting
  • Graceful degradation when limits are exceeded

Session Hijacking

  • Encrypted session cookies (AES-256)
  • HttpOnly and Secure flags on cookies
  • Session fingerprinting support (configurable)
  • 7-day session expiration (configurable)

What You Must Secure

🔒 Server Host Security

  • OS hardening and security updates
  • Firewall configuration
  • SSH key management
  • Intrusion detection system (IDS)

🔒 Database Encryption at Rest

  • Use encrypted volumes (LUKS, AWS EBS encryption)
  • Secure database backups (encrypted storage)
  • Restrict database file permissions (0600)

🔒 TLS/HTTPS in Production

  • Valid SSL certificates (not self-signed)
  • Strong cipher suites (TLSv1.2+)
  • HSTS headers (Strict-Transport-Security)

🔒 Secret Rotation Policies

  • Regular rotation of JWT_SECRET (every 90 days)
  • Session secret rotation (every 180 days)
  • Admin password changes (every 90 days)
  • OAuth client secret rotation (as needed)
  • Asymmetric key rotation for RS256/ES256 (see below)

🔒 Asymmetric Key Management (RS256/ES256)

  • Store private key files with restricted permissions (chmod 0400)
  • Never commit private keys to version control; use secrets management or volume mounts
  • The JWKS endpoint caches responses with Cache-Control: public, max-age=3600 (1 hour); resource servers may not see the new key immediately after rotation
  • Rotation procedure: generate new key → update JWT_PRIVATE_KEY_PATH and JWT_KEY_ID → restart → allow up to 1 hour for JWKS cache expiry at resource servers
  • AuthGate serves a single active public key in JWKS; multi-key JWKS is not supported

🔒 Network Isolation

  • Place database on private network
  • Restrict external access to application port only
  • Use VPN for administrative access

Secrets Management

Generate Strong Secrets

# Generate 256-bit secrets
openssl rand -hex 32

# Or use /dev/urandom
head -c 32 /dev/urandom | base64

Store Secrets Securely

Option 1: Environment Variables (Docker, Systemd)

# .env file (set permissions to 0600)
JWT_SECRET=abc123...
SESSION_SECRET=def456...

# Restrict permissions
chmod 600 .env
chown authgate:authgate .env

Option 2: Secrets Management Systems

HashiCorp Vault:

# Store secret
vault kv put secret/authgate JWT_SECRET=abc123...

# Retrieve in startup script
export JWT_SECRET=$(vault kv get -field=JWT_SECRET secret/authgate)

AWS Secrets Manager:

# Store secret
aws secretsmanager create-secret --name authgate-jwt-secret --secret-string "abc123..."

# Retrieve in application
aws secretsmanager get-secret-value --secret-id authgate-jwt-secret

Kubernetes Secrets:

apiVersion: v1
kind: Secret
metadata:
  name: authgate-secrets
type: Opaque
data:
  jwt-secret: YWJjMTIzLi4u # base64 encoded
  session-secret: ZGVmNDU2Li4u

Secret Rotation

Rotating JWT_SECRET:

  1. Generate new secret: openssl rand -hex 32
  2. Schedule maintenance window (brief service interruption)
  3. Update JWT_SECRET in configuration
  4. Restart service
  5. All existing tokens become invalid (users must re-authenticate)
  6. Consider implementing dual-key verification during rotation

Rotating SESSION_SECRET:

  1. Generate new secret
  2. Update SESSION_SECRET in configuration
  3. Restart service
  4. All existing sessions become invalid (users must re-login)

TLS/HTTPS Configuration

Obtain SSL Certificate (Let's Encrypt)

# Install Certbot
sudo apt-get install certbot python3-certbot-nginx

# Obtain certificate
sudo certbot --nginx -d auth.yourdomain.com

# Auto-renewal (configured automatically)
sudo certbot renew --dry-run

Nginx SSL Configuration

server {
    listen 443 ssl http2;
    server_name auth.yourdomain.com;

    # SSL Configuration
    ssl_certificate /etc/letsencrypt/live/auth.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/auth.yourdomain.com/privkey.pem;

    # Modern SSL configuration (Mozilla SSL Configuration Generator)
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;

    # HSTS (15768000 seconds = 6 months)
    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains" always;

    # Additional security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;

    # ... proxy configuration
}

Test SSL Configuration

# Test with SSL Labs
# Visit: https://www.ssllabs.com/ssltest/analyze.html?d=auth.yourdomain.com

# Test locally
openssl s_client -connect auth.yourdomain.com:443 -tls1_2

Database Security

SQLite Security (Development/Small Deployments)

# Set proper file permissions
chmod 600 oauth.db
chown authgate:authgate oauth.db

# Enable WAL mode for better concurrency
sqlite3 oauth.db "PRAGMA journal_mode=WAL;"

# Regular backups
sqlite3 oauth.db ".backup /backup/oauth-$(date +%Y%m%d).db"

PostgreSQL Security (Production)

Connection Security:

# Use SSL connections (postgresql.conf)
ssl = on
ssl_cert_file = '/path/to/server.crt'
ssl_key_file = '/path/to/server.key'

# Require SSL for connections (pg_hba.conf)
hostssl all all 0.0.0.0/0 md5

Database Hardening:

-- Create dedicated user
CREATE USER authgate WITH PASSWORD 'strong-password';

-- Grant minimal permissions
GRANT CONNECT ON DATABASE authgate TO authgate;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO authgate;

-- Revoke public schema access
REVOKE ALL ON SCHEMA public FROM PUBLIC;

Encryption at Rest:

# Enable transparent data encryption (TDE)
# Implementation varies by PostgreSQL version and platform
# Consult PostgreSQL documentation for your version

Session Security

Session Configuration

# Session settings in config/config.go
SessionSecret:  os.Getenv("SESSION_SECRET")
SessionMaxAge:  7 * 24 * 60 * 60  # 7 days
SessionSecure:  true              # HTTPS only
SessionHttpOnly: true             # Prevent XSS
SessionSameSite: "Lax"            # CSRF protection

Session Fingerprinting (Optional)

Enable session fingerprinting to detect session hijacking:

# .env
ENABLE_SESSION_FINGERPRINTING=true

This creates a fingerprint from:

  • User-Agent header
  • IP address (optional)
  • Accept-Language header

Session Idle Timeout

Configure automatic session termination after inactivity:

# .env
SESSION_IDLE_TIMEOUT=1h  # Terminate after 1 hour of inactivity

Rate Limiting

See Configuration Guide - Rate Limiting for complete documentation.

Security Benefits

  • Prevents password brute force (5 req/min on /login)
  • Mitigates device code enumeration (10 req/min on /oauth/device/code)
  • Prevents user code guessing (10 req/min on /device/verify)
  • Protects against DoS (configurable limits per endpoint)
  • Per-IP tracking (prevents single attacker from overwhelming service)

Production Configuration

# .env
ENABLE_RATE_LIMIT=true
RATE_LIMIT_STORE=redis  # For multi-pod deployments
REDIS_ADDR=redis-service:6379
REDIS_PASSWORD=secure-password

# Adjust limits based on your traffic
LOGIN_RATE_LIMIT=5
DEVICE_CODE_RATE_LIMIT=10
TOKEN_RATE_LIMIT=20
DEVICE_VERIFY_RATE_LIMIT=10

Audit and Compliance

Enable Comprehensive Auditing

# .env
ENABLE_AUDIT_LOGGING=true
AUDIT_LOG_RETENTION=2160h  # 90 days (adjust for compliance)

Compliance Requirements

SOC 2:

  • Audit all authentication events
  • Track all token operations
  • Monitor failed login attempts
  • Export audit logs regularly

ISO 27001:

  • Retain audit logs for required period (90 days minimum)
  • Implement automated cleanup
  • Protect audit logs from tampering

GDPR:

  • Log data access events
  • Track consent (if applicable)
  • Provide audit trail for data subject requests
  • Implement data retention policies

Regular Security Reviews

  • Weekly: Review failed authentication attempts
  • Monthly: Analyze rate limit events and suspicious patterns
  • Quarterly: Export audit logs for compliance reporting
  • Annually: Full security audit and penetration testing

Incident Response

Security Incident Playbook

Step 1: Detection

  • Monitor for critical audit events
  • Review failed authentication patterns
  • Check rate limit exceeded alerts
  • Analyze unusual traffic patterns

Step 2: Containment

# Preferred (admin): disable the affected user — revokes every access/refresh
# token, blocks future logins (local + OAuth callbacks), and RequireAuth clears
# any live session on the user's next request. Re-enable later with /enable.
curl -X POST https://auth.yourdomain.com/admin/users/<user-id>/disable \
  -H "Cookie: admin-session=..." \
  -H "X-CSRF-Token: ..."

# Self-service alternative when the user is cooperating:
curl -X POST https://auth.yourdomain.com/account/sessions/revoke-all \
  -H "Cookie: session=..."

# Block attacker IP at firewall level
sudo iptables -A INPUT -s <attacker-ip> -j DROP

# Temporarily disable affected OAuth client
# (Update database or use admin interface)

Step 3: Investigation

# Export audit logs for analysis
curl "https://auth.yourdomain.com/admin/audit/export?since=24h" \
  -H "Cookie: admin-session=..." -o incident-logs.csv

# Review systemd logs
sudo journalctl -u authgate --since "24 hours ago" > authgate-incident.log

# Check database for anomalies
sqlite3 oauth.db "SELECT * FROM audit_logs WHERE severity='CRITICAL' AND event_time > datetime('now', '-24 hours');"

Step 4: Remediation

  • Rotate affected secrets (JWT_SECRET, SESSION_SECRET)
  • Force password reset for affected users
  • Patch vulnerabilities if applicable
  • Update firewall rules
  • Review and update rate limits

Step 5: Recovery

  • Restore from backup if data was compromised
  • Re-enable affected services
  • Communicate with affected users
  • Document incident in incident report

Step 6: Post-Incident Review

  • Analyze root cause
  • Update security procedures
  • Implement additional controls
  • Schedule follow-up review

Contact Information

Maintain an up-to-date incident response contact list:

  • Security Team: security@yourdomain.com
  • On-Call Engineer: [phone number]
  • Platform Provider: [support contact]
  • Legal/Compliance: [contact info]

Security Resources


Next Steps:

There aren’t any published security advisories