Fix OmniAuth redirect_uri to resolve dynamically from request host (Closes #246) #247
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/246-omniauth-redirect-uri"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Replace the hardcoded
APP_URL-basedredirect_uriin the OmniAuth initializer with a dynamic setup lambda that computes the callback URL from the incoming request's scheme and host at request time. This fixes Keycloak login when accessing the app vialandscaping-assistant.app-- previously the redirect_uri always pointed to the Tailscale hostname fallback.Changes
config/initializers/omniauth.rb-- Removed staticredirect_urifromclient_optionsand added an OmniAuthsetuplambda that setsredirect_uridynamically usingRack::Request#schemeand#host_with_portfrom the current requestTest Plan
landscaping-assistant.appproduces aredirect_uripointing tolandscaping-assistant.app/auth/keycloak/callbacklandscaping-assistant.tail5b443a.ts.netstill works with the Tailscale hostname in the callbackReview Checklist
Related
Closes #246
Related Notes
None
PR #247 Review
DOMAIN REVIEW
Stack: Ruby on Rails, OmniAuth (OpenID Connect), Rack, Keycloak
This PR replaces a hardcoded
redirect_uri(built fromENV['APP_URL']with a Tailscale fallback) with a dynamicsetuplambda that derives the callback URL fromRack::Request#schemeand#host_with_portat request time. This is the correct OmniAuth pattern for multi-host environments.Host Header Injection (OWASP):
Constructing
redirect_urifrom the request's Host header is a well-known attack surface. An attacker could send a forged Host header to redirect the OAuth callback to an attacker-controlled domain. However, this PR is safe because of two independent mitigations already in place:config.hostsallowlist -- PRs #230, #232, and #239 confirm thatHostAuthorizationis configured. Rails rejects requests with unrecognized Host headers with a 403 before the OmniAuth middleware fires.redirect_uriagainst its configured allowed redirect URIs. Even if a forged Host somehow bypassed Rails, Keycloak would reject the authorization request.These two layers make the dynamic approach safe for this deployment.
OmniAuth
setuplambda: The implementation follows the documented OmniAuth setup-phase pattern correctly. Accessingenv["omniauth.strategy"].options[:client_options][:redirect_uri]is the standard way to dynamically configure options per-request.BLOCKERS
None.
Test coverage assessment: This is a config-only change (+13/-3) to an OmniAuth initializer. The 9 existing session request specs verify the auth flow end-to-end. Writing a unit test for the setup lambda in isolation would require mocking Rack env and OmniAuth internals with low marginal value. The real verification is the post-deploy check described in the Test Plan. No blocker here.
NITS
Comment precision: The new comment block (lines 10-13) says "without a hardcoded APP_URL fallback" -- the old code did not just fall back to APP_URL, it always used APP_URL (with a Tailscale default if APP_URL was unset). Minor wording nit, not blocking.
Trailing comma consistency: The diff removes the comma after
secret:and the oldredirect_uriline. The new structure placescallback_pathandsetupas sibling keys toclient_options. Formatting looks correct; just noting the structural change is clean.SOP COMPLIANCE
fix/246-omniauth-redirect-uri)PROCESS OBSERVATIONS
VERDICT: APPROVED
PR #247 Review
DOMAIN REVIEW
Tech stack: Ruby on Rails, OmniAuth (OpenID Connect strategy), Keycloak IdP.
What this PR does: Replaces a static
redirect_uri(built fromENV['APP_URL']with a hardcoded Tailscale fallback) with an OmniAuthsetuplambda that resolves the callback URL dynamically fromRack::Request#schemeand#host_with_port. This is the canonical OmniAuth pattern for multi-host deployments and correctly solves the problem described in #246.Security analysis -- open redirect via Host header?
The setup lambda trusts the incoming request's
Hostheader to build theredirect_uri. In isolation, this would be an open-redirect risk: an attacker could forge aHost: evil.comheader and the callback would redirect toevil.com. However, Railsconfig.hosts(added in PR #230, hardened in PR #239) rejects requests with unrecognizedHostheaders before they reach OmniAuth middleware. This makes the approach safe --HostAuthorizationis the gate, and the setup lambda only runs for allowed hosts. No BLOCKER here, but this dependency is worth documenting (see nit below).OmniAuth pattern correctness: The
setuplambda receives the Rack env, constructs aRack::Request, and setsstrategy.options[:client_options][:redirect_uri]. This is the documented OmniAuth setup phase pattern. The lambda fires before the authorization request is built, so theredirect_uriis correct for both the authorize redirect and the token exchange.APP_URLenv var: The PR removes the only consumer ofAPP_URLin this file. IfAPP_URLis no longer used elsewhere, it can be removed from deployment manifests / env configuration to avoid confusion. Not a blocker.BLOCKERS
None.
The setup lambda is not directly unit-testable because
OmniAuth.config.test_mode = truebypasses the setup phase entirely. This is an OmniAuth framework limitation, not a coverage gap in this PR. The Test Plan correctly identifies that post-deploy verification against bothlandscaping-assistant.appand the Tailscale hostname is the real validation path. The 9 existing session request specs continue to pass and cover callback handling, session creation, role extraction, logout, and failure handling.NITS
Document the
config.hostsdependency: The security of this approach depends onHostAuthorizationrejecting spoofedHostheaders upstream. Consider adding a one-line comment in the setup lambda noting this dependency, e.g.:APP_URLcleanup: IfAPP_URLis no longer referenced anywhere else, consider removing it from deployment env config (k8s manifests,.env.example, etc.) to avoid confusion about whether it is still needed.Trailing comma consistency: The
callback_pathline now ends without a trailing comma beforesetup:, which is fine for the last-before-block position. No action needed, just noting the style is consistent.SOP COMPLIANCE
.envfiles, or credentials committedPROCESS OBSERVATIONS
redirect_uri).VERDICT: APPROVED