OAuth2 Proxy Installation for Application Authentication
OAuth2 Proxy is a reverse proxy that adds OAuth2/OIDC authentication in front of any web application without modifying the application's code, supporting providers like Google, GitHub, Keycloak, and any OIDC-compliant identity provider. This guide covers deploying OAuth2 Proxy, configuring Google and GitHub providers, integrating with Nginx, and setting access policies.
Prerequisites
- A Linux server with an application to protect (Grafana, Jupyter, etc.)
- A domain name with SSL configured
- Nginx installed as a reverse proxy
- An OAuth2 provider account (Google Cloud Console, GitHub, or Keycloak)
- Access to configure DNS and SSL certificates
Installing OAuth2 Proxy
# Download the latest OAuth2 Proxy binary
OAUTH2_PROXY_VERSION=7.7.1
curl -L "https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v${OAUTH2_PROXY_VERSION}/oauth2-proxy-v${OAUTH2_PROXY_VERSION}.linux-amd64.tar.gz" \
-o /tmp/oauth2-proxy.tar.gz
tar -xzf /tmp/oauth2-proxy.tar.gz -C /tmp/
sudo mv /tmp/oauth2-proxy-v${OAUTH2_PROXY_VERSION}.linux-amd64/oauth2-proxy /usr/local/bin/
sudo chmod +x /usr/local/bin/oauth2-proxy
# Verify installation
oauth2-proxy --version
# Create a system user
sudo useradd -r -s /sbin/nologin oauth2proxy
sudo mkdir -p /etc/oauth2proxy
Configuring the Google Provider
Create OAuth2 Credentials in Google Cloud Console
- Go to Google Cloud Console > APIs & Services > Credentials
- Click Create Credentials > OAuth 2.0 Client IDs
- Application type: Web application
- Add authorized redirect URI:
https://your-app.example.com/oauth2/callback - Copy the Client ID and Client Secret
OAuth2 Proxy Configuration
# Generate a random cookie secret (32 bytes, base64 encoded)
COOKIE_SECRET=$(python3 -c "import secrets; print(secrets.token_urlsafe(24))")
sudo tee /etc/oauth2proxy/oauth2proxy.cfg << EOF
# Provider
provider = "google"
client_id = "YOUR_GOOGLE_CLIENT_ID.apps.googleusercontent.com"
client_secret = "YOUR_GOOGLE_CLIENT_SECRET"
# Callback URL (must match what you set in Google Console)
redirect_url = "https://app.example.com/oauth2/callback"
# Cookie security
cookie_secret = "${COOKIE_SECRET}"
cookie_secure = true
cookie_httponly = true
cookie_samesite = "lax"
cookie_expire = "168h" # 7 days
# Session
session_store_type = "cookie"
# Upstream (the app to protect)
upstreams = ["http://localhost:3000"]
# Bind address
http_address = "127.0.0.1:4180"
# Allowed domains (restrict to your org)
email_domains = ["example.com"]
# Or allow specific emails:
# authenticated_emails_file = "/etc/oauth2proxy/allowed-emails.txt"
EOF
sudo chown -R oauth2proxy:oauth2proxy /etc/oauth2proxy
sudo chmod 600 /etc/oauth2proxy/oauth2proxy.cfg
Configuring the GitHub Provider
Create a GitHub OAuth App
- Go to GitHub Settings > Developer settings > OAuth Apps > New OAuth App
- Set Authorization callback URL:
https://app.example.com/oauth2/callback - Copy the Client ID and generate a Client Secret
Configuration File
sudo tee /etc/oauth2proxy/oauth2proxy.cfg << EOF
# Provider
provider = "github"
client_id = "YOUR_GITHUB_CLIENT_ID"
client_secret = "YOUR_GITHUB_CLIENT_SECRET"
redirect_url = "https://app.example.com/oauth2/callback"
cookie_secret = "$(python3 -c "import secrets; print(secrets.token_urlsafe(24))")"
cookie_secure = true
cookie_httponly = true
cookie_expire = "168h"
upstreams = ["http://localhost:3000"]
http_address = "127.0.0.1:4180"
# Restrict by GitHub organization
github_org = "your-github-org"
# Or restrict by GitHub team
# github_org = "your-github-org"
# github_team = "your-team"
# Or allow specific users
# github_users = "user1,user2"
EOF
Configuring a Keycloak/OIDC Provider
# Get the OIDC discovery URL from Keycloak:
# https://auth.example.com/realms/myapp/.well-known/openid-configuration
sudo tee /etc/oauth2proxy/oauth2proxy.cfg << EOF
# Generic OIDC provider (works with Keycloak, Authentik, etc.)
provider = "oidc"
provider_display_name = "Company SSO"
oidc_issuer_url = "https://auth.example.com/realms/myapp"
client_id = "oauth2-proxy-client"
client_secret = "YOUR_KEYCLOAK_CLIENT_SECRET"
redirect_url = "https://app.example.com/oauth2/callback"
cookie_secret = "$(python3 -c "import secrets; print(secrets.token_urlsafe(24))")"
cookie_secure = true
cookie_expire = "24h"
upstreams = ["http://localhost:3000"]
http_address = "127.0.0.1:4180"
# Allow all authenticated users from the realm
email_domains = ["*"]
# Or restrict by email domain
# email_domains = ["example.com"]
# Request additional scopes
scope = "openid email profile groups"
# Pass user info to upstream as headers
set_xauthrequest = true
pass_access_token = true
EOF
Nginx Integration
The most common pattern is to use Nginx as the front-end SSL terminator with OAuth2 Proxy handling authentication:
sudo tee /etc/nginx/sites-available/protected-app << 'EOF'
server {
listen 80;
server_name app.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
# OAuth2 Proxy internal auth endpoint
location /oauth2/ {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Auth-Request-Redirect $request_uri;
}
# Auth verification endpoint
location = /oauth2/auth {
proxy_pass http://127.0.0.1:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Content-Length "";
proxy_pass_request_body off;
}
# Protected application
location / {
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
# Pass user information to the upstream app
auth_request_set $user $upstream_http_x_auth_request_user;
auth_request_set $email $upstream_http_x_auth_request_email;
proxy_set_header X-User $user;
proxy_set_header X-Email $email;
# Proxy to the actual application
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
EOF
sudo ln -s /etc/nginx/sites-available/protected-app /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Cookie Settings and Session Management
# Cookie configuration for security
cookie_secure = true # HTTPS only
cookie_httponly = true # No JavaScript access
cookie_samesite = "lax" # CSRF protection
cookie_expire = "168h" # 7-day sessions
cookie_refresh = "1h" # Refresh token every hour
# Cookie domain for subdomain sharing
cookie_domains = [".example.com"] # Allows *.example.com to share session
cookie_name = "_oauth2_proxy" # Custom cookie name
# Redis session store (for multi-server deployments)
session_store_type = "redis"
redis_connection_url = "redis://localhost:6379"
Access Control Policies
# Allow all email domains (any authenticated user)
email_domains = ["*"]
# Restrict to specific domains
email_domains = ["example.com", "partner.com"]
# Allow specific email addresses only
authenticated_emails_file = "/etc/oauth2proxy/allowed-emails.txt"
# File contains one email per line:
# [email protected]
# [email protected]
# Skip authentication for certain paths (health checks, public assets)
skip_auth_routes = [
"GET=^/health$",
"GET=^/public/.*"
]
# Allowed IP ranges (bypass auth for internal networks)
trusted_ips = ["10.0.0.0/8", "192.168.0.0/16"]
Running as a Systemd Service
sudo tee /etc/systemd/system/oauth2proxy.service << 'EOF'
[Unit]
Description=OAuth2 Proxy
After=network.target
[Service]
Type=simple
User=oauth2proxy
ExecStart=/usr/local/bin/oauth2-proxy --config=/etc/oauth2proxy/oauth2proxy.cfg
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable oauth2proxy
sudo systemctl start oauth2proxy
# Check status
sudo systemctl status oauth2proxy
sudo journalctl -u oauth2proxy -f
Troubleshooting
"Invalid state parameter" on callback
# This usually means the cookie was lost — check:
# 1. cookie_secret is consistent across restarts
# 2. redirect_url exactly matches what's registered in the provider
# 3. SSL is working (cookies require HTTPS in production)
"Access denied" after authentication
# Check the email_domains setting
# View oauth2-proxy logs to see which email authenticated
sudo journalctl -u oauth2proxy | grep "authenticated"
# Verify email is in the allowed list
grep [email protected] /etc/oauth2proxy/allowed-emails.txt
404 on /oauth2/callback
# Nginx must proxy /oauth2/ to oauth2-proxy
# Verify the /oauth2/ location block exists in Nginx config
# Test oauth2-proxy is running
curl http://127.0.0.1:4180/ping
"Token verification failed"
# For OIDC providers — verify the JWKS endpoint is accessible
curl "https://auth.example.com/realms/myapp/protocol/openid-connect/certs"
# Check time sync (JWT validation requires accurate time)
timedatectl
Conclusion
OAuth2 Proxy provides a universal authentication layer for any web application without requiring code changes, making it ideal for adding SSO to internal tools like Grafana, Jupyter, and custom dashboards. By integrating with Nginx's auth_request directive and connecting to any OIDC provider, you get a flexible, production-ready authentication gateway that handles all OAuth2 flows transparently.


