From 73b2ae2f0277304ae59d72d3b7ccbe8128ed4e40 Mon Sep 17 00:00:00 2001 From: Dan Milne Date: Tue, 4 Nov 2025 21:13:46 +1100 Subject: [PATCH] Add some docs --- .../api/forward_auth_controller.rb | 22 +- docs/oidc-key-setup.md | 214 ++++++++++++++++++ 2 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 docs/oidc-key-setup.md diff --git a/app/controllers/api/forward_auth_controller.rb b/app/controllers/api/forward_auth_controller.rb index bb29ecf..85d491b 100644 --- a/app/controllers/api/forward_auth_controller.rb +++ b/app/controllers/api/forward_auth_controller.rb @@ -137,7 +137,7 @@ module Api # Get the redirect URL from query params or construct default redirect_url = validate_redirect_url(params[:rd]) - base_url = redirect_url || "https://clinch.aapamilne.com" + base_url = determine_base_url(redirect_url) # Set the original URL that user was trying to access # This will be used after authentication @@ -214,5 +214,25 @@ module Api app.matches_domain?(domain.downcase) end end + + def determine_base_url(redirect_url) + # If we have a valid redirect URL, use it + return redirect_url if redirect_url.present? + + # Try CLINCH_HOST environment variable first + if ENV['CLINCH_HOST'].present? + "https://#{ENV['CLINCH_HOST']}" + else + # Fallback to the request host + request_host = request.host || request.headers['X-Forwarded-Host'] + if request_host.present? + Rails.logger.warn "ForwardAuth: CLINCH_HOST not set, using request host: #{request_host}" + "https://#{request_host}" + else + # No host information available - raise exception to force proper configuration + raise StandardError, "ForwardAuth: CLINCH_HOST environment variable not set and no request host available. Please configure CLINCH_HOST properly." + end + end + end end end diff --git a/docs/oidc-key-setup.md b/docs/oidc-key-setup.md new file mode 100644 index 0000000..34af249 --- /dev/null +++ b/docs/oidc-key-setup.md @@ -0,0 +1,214 @@ +# OIDC Private Key Setup + +Your OIDC provider needs an RSA private key to sign ID tokens. **This key must persist across deployments** or all existing tokens will become invalid. + +## Option 1: Environment Variable (Recommended for Docker/Kamal) + +### 1. Generate the key + +```bash +# Generate a 2048-bit RSA key +openssl genrsa -out oidc_private_key.pem 2048 + +# View the key (you'll copy this) +cat oidc_private_key.pem +``` + +### 2. Store in your `.env` file + +```bash +# .env (for local development) +OIDC_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAyZ0qaICMiLVWSFs+ef9Xok3fzy0p6k/7D5TQzmxf7C2vQG7s +2Odmi8iAHLoaUBaFj70qTbaconWyMr8s+ah+qZwrwolTLUe23VrceVXvInU57hBL +... +-----END RSA PRIVATE KEY-----" +``` + +**Important:** Keep the quotes and include the full key with `-----BEGIN` and `-----END` lines. + +### 3. For Kamal deployment + +Add to your Kamal secrets: + +```yaml +# config/deploy.yml +env: + secret: + - OIDC_PRIVATE_KEY +``` + +Then set it securely: + +```bash +# Generate key +bin/generate_oidc_key > oidc_private_key.pem + +# Option A: Using kamal env push (Kamal 2.0+) +kamal env push OIDC_PRIVATE_KEY="$(cat oidc_private_key.pem)" + +# Option B: Add to .kamal/secrets +echo "OIDC_PRIVATE_KEY=$(cat oidc_private_key.pem)" >> .kamal/secrets +``` + +### 4. Verify it's loaded + +```bash +# In Rails console +bin/rails runner "puts OidcJwtService.send(:private_key).present? ? 'Key loaded' : 'Key missing'" +``` + +--- + +## Option 2: Rails Credentials (Simpler but less flexible) + +### 1. Generate the key + +```bash +openssl genrsa -out oidc_private_key.pem 2048 +``` + +### 2. Add to Rails credentials + +```bash +EDITOR="nano" bin/rails credentials:edit +``` + +Add this section: + +```yaml +oidc_private_key: | + -----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEAyZ0qaICMiLVWSFs+ef9Xok3fzy0p6k/7D5TQzmxf7C2vQG7s + 2Odmi8iAHLoaUBaFj70qTbaconWyMr8s+ah+qZwrwolTLUe23VrceVXvInU57hBL + ... + -----END RSA PRIVATE KEY----- +``` + +**Important:** Use the `|` pipe character for multi-line, and indent the key content with 2 spaces. + +### 3. Save and verify + +```bash +# Verify credentials file +cat config/credentials.yml.enc # Should show encrypted data + +# Test in console +bin/rails runner "puts OidcJwtService.send(:private_key).present? ? 'Key loaded' : 'Key missing'" +``` + +### 4. For deployment + +The `config/credentials.yml.enc` file is committed to git. You need to: + +1. **Set RAILS_MASTER_KEY** env variable in production +2. Get the key from `config/master.key` (don't commit this!) + +```bash +# In Kamal +kamal env push RAILS_MASTER_KEY="$(cat config/master.key)" +``` + +--- + +## Comparison + +| Feature | ENV Variable | Rails Credentials | +|---------|-------------|-------------------| +| **Best for** | Docker, Kamal, 12-factor | Simple deployments | +| **Key rotation** | Easy (just update ENV) | Medium (re-encrypt) | +| **Per-environment** | Yes (dev/staging/prod can differ) | No (same key everywhere) | +| **Secrets manager** | Compatible (AWS Secrets, etc.) | Needs RAILS_MASTER_KEY | +| **Setup complexity** | Low | Medium | + +**Recommendation:** Use ENV variable (`OIDC_PRIVATE_KEY`) for production with Kamal. + +--- + +## Security Best Practices + +### DO: +- ✅ Generate the key once and keep it forever +- ✅ Store in secret manager (AWS Secrets Manager, 1Password, etc.) +- ✅ Use strong key (2048-bit RSA minimum) +- ✅ Backup the key securely +- ✅ Restrict access (only ops team) + +### DON'T: +- ❌ Commit the key to git (except encrypted credentials) +- ❌ Share the key in Slack/email +- ❌ Regenerate the key (invalidates all tokens) +- ❌ Store in `.env` if it's committed to git +- ❌ Use the same key for multiple environments + +--- + +## Key Rotation (Advanced) + +If you need to rotate keys (security incident, etc.): + +### 1. Generate new key + +```bash +openssl genrsa -out oidc_private_key_new.pem 2048 +``` + +### 2. Add NEW key alongside old (dual-key setup) + +This requires code changes to support multiple keys in JWKS. For now, rotation means: + +**Warning:** Rotating the key will **invalidate all existing OIDC sessions**. Users will need to log in again. + +### 3. Update OIDC_PRIVATE_KEY + +```bash +kamal env push OIDC_PRIVATE_KEY="$(cat oidc_private_key_new.pem)" +``` + +### 4. Restart application + +```bash +kamal deploy +``` + +--- + +## Troubleshooting + +### "No private key found" warning + +Check your setup: + +```bash +# Is ENV set? +echo $OIDC_PRIVATE_KEY + +# Can Rails load it? +bin/rails runner "puts ENV['OIDC_PRIVATE_KEY'].present? ? 'ENV set' : 'ENV missing'" + +# Does it load correctly? +bin/rails runner "puts OidcJwtService.send(:private_key).to_s[0..50]" +``` + +### "invalid RSA key" error + +- Make sure you include `-----BEGIN RSA PRIVATE KEY-----` header +- Ensure newlines are preserved (use quotes in ENV) +- Check for extra spaces or characters + +### Different JWKS key ID on each restart + +This means the key is being regenerated. You need to set `OIDC_PRIVATE_KEY` or add to credentials. + +### All tokens invalid after deployment + +The key changed. You either: +- Regenerated the key (don't do this!) +- Forgot to set ENV variable in production +- The key wasn't loaded correctly + +Check logs for warnings and verify key is loaded: + +```bash +kamal app logs --grep "OIDC" +```