feat: support multiple OIDC redirect URIs - #25408
Conversation
55a526d to
4817327
Compare
0679b40 to
349b505
Compare
|
Looks good from a functionality standpoint. There's some lint/fmt issues, and code-gen needs to be run (see |
@zedkipp should be done now. |
|
Formatting checks are still failing. Try |
@zedkipp fmt passes now, but I'm not sure about the rest. |
|
@zedkipp I don't merge permissions here. Could someone help me with this? Looks like we'll need a rebase now |
99b6c00 to
ddac0d2
Compare
… dynamic redirect_uri Some upstream reverse proxies set X-Forwarded-Proto to the inner-hop scheme (the proxy-to-coderd leg, often http) rather than the original client-facing scheme. Trusting that header in the dynamic-host code path produced redirect_uri values the IdP rejected. Thread the configured AccessURL scheme into the middleware as RedirectDefaultScheme and prefer it over r.TLS / X-Forwarded-Proto. This matches what the static OIDC path uses today (vals.AccessURL.Scheme) and makes the dynamic path consistent. Request-derived detection remains as the fallback for deployments without a configured AccessURL.
Address review feedback from coder#25408: * The four-case switch had three dead branches in practice: the only real caller (cli/server.go) always passes a non-empty defaultScheme. Cases that fell back to r.TLS, X-Forwarded-Proto, or a literal "http" string were unreachable in any production code path. Worse, the bare "http" default would have produced silently insecure redirect_uris in the unlikely event the function was ever called without a defaultScheme; the IdP would reject the request and the failure mode would be confusing. * Replace string concatenation with url.URL{}.String() so the helper uses the standard-library URL builder instead of hand-rolled formatting. Net effect on the only real-world deployment: byte-for-byte identical output. The two unit tests that previously exercised the removed X-Forwarded-Proto fallback now pass defaultScheme directly, matching how real callers always supply it.
* Drop the stale "fallback for local-dev contexts" sentence in the ExtractOAuth2 doc comment. The corresponding fallback chain in buildDynamicRedirectURI was removed in a previous commit; the doc no longer matched the code. * Use slices.Contains in place of a manual loop when matching the request Host against the allowlist. * On the OIDC token exchange path, validate the OAuth2RedirectURICookie against a freshly-recomputed expected redirect_uri rather than forwarding the cookie value unchecked. If the cookie is missing or does not match, return 4xx instead of silently falling back to the static config redirect_uri (which would mismatch the authorization request and produce a confusing IdP rejection). This is defense in depth on top of the IdP's own redirect_uri allowlist. * Annotate the per-request log entry with oidc_rejected_reason and related fields on both rejection paths (disallowed host, missing cookie, cookie mismatch) so operators can grep / alert on rejected OIDC logins without code changes. Uses the existing context-based loggermw.RequestLoggerFromContext pattern, no new function args. * Add a TestServer/OIDC/RedirectAllowedHosts test in cli/server_test.go that verifies the new flag flows from the CLI through to the running deployment config. * Add unit tests for the two new rejection paths (missing redirect_uri cookie and mismatched redirect_uri cookie).
* gofmt: align field column widths in cli/server.go after the new RedirectDefaultScheme field made one of the keys longer. * lint: drop the unused method receiver name on exchangeAssertingProvider.AuthCodeURL (revive: unused-receiver). * gen: add the OAuth2RedirectURICookie const to site/src/api/typesGenerated.ts; add the redirect_allowed_hosts entry to the example response in docs/reference/api/general.md. * lint-docs: reformat the codersdk.OIDCConfig properties table in docs/reference/api/schemas.md so every column is padded to the longest cell, which is what markdown-table-formatter expects after the new redirect_allowed_hosts row introduced a longer description.
ddac0d2 to
a68a343
Compare
The description column max width (442 chars from redirect_allowed_hosts) was not reflected in the separator (356 dashes). Reformat so all column separators match their per-column content width.
Head branch was pushed to by a user without write access
This PR adds a new opt-in setting,
CODER_OIDC_REDIRECT_ALLOWED_HOSTS, that lets a single Coder deployment complete OIDC login on more than one hostname. When the allowlist is non-empty, Coder picks the OIDCredirect_uribased on the incoming request's Host header (validated against the list) instead of always using the static URL derived fromCODER_ACCESS_URL. When unset, the (default) behavior is identical to today.The motivation is that a single Coder deployment is frequently reachable via multiple hostnames - for example, an internal hostname for users on a corporate VPN and a different hostname routed through a zero-trust gateway for users off-VPN - but OIDC login today only works on whichever single hostname
CODER_ACCESS_URLpoints to, because theredirect_urisent to the IdP is fixed at server startup. Users who reach the deployment on any other valid hostname can see the login page but fail the OIDC callback, since the IdP redirects them back to a hostname they can't reach (or whose cookies they don't have).