mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: support multiple OIDC redirect URIs (#25408)
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 OIDC `redirect_uri` based on the incoming request's Host header (validated against the list) instead of always using the static URL derived from `CODER_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_URL` points to, because the `redirect_uri` sent 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).
This commit is contained in:
+35
-13
@@ -200,7 +200,16 @@ func createOIDCConfig(ctx context.Context, logger slog.Logger, vals *codersdk.De
|
||||
return nil, xerrors.Errorf("parse oidc redirect url %q", err)
|
||||
}
|
||||
logger.Warn(ctx, "custom OIDC redirect URL used instead of 'access_url', ensure this matches the value configured in your OIDC provider")
|
||||
if len(vals.OIDC.RedirectAllowedHosts.Value()) > 0 {
|
||||
// Static override takes precedence; keep the behavior explicit and
|
||||
// loud rather than silently mixing the two modes.
|
||||
logger.Warn(ctx, "ignoring CODER_OIDC_REDIRECT_ALLOWED_HOSTS because CODER_OIDC_REDIRECT_URL is set")
|
||||
}
|
||||
}
|
||||
// Capture the configured scheme for the dynamic-host code path so that
|
||||
// the dynamic redirect_uri uses the same scheme as the static one even
|
||||
// when upstream proxies report a misleading X-Forwarded-Proto.
|
||||
redirectDefaultScheme := redirectURL.Scheme
|
||||
|
||||
// If the scopes contain 'groups', we enable group support.
|
||||
// Do not override any custom value set by the user.
|
||||
@@ -259,6 +268,17 @@ func createOIDCConfig(ctx context.Context, logger slog.Logger, vals *codersdk.De
|
||||
return nil, xerrors.Errorf("pkce detect in claims: %w", err)
|
||||
}
|
||||
|
||||
// CODER_OIDC_REDIRECT_URL is a strict override: when set, the redirect_uri
|
||||
// is fixed at startup and dynamic-host selection is disabled. Otherwise,
|
||||
// surface the allowlist to the middleware.
|
||||
var redirectAllowedHosts []string
|
||||
if vals.OIDC.RedirectURL.String() == "" {
|
||||
redirectAllowedHosts = vals.OIDC.RedirectAllowedHosts.Value()
|
||||
} else {
|
||||
// Static-override mode does not need the dynamic default scheme.
|
||||
redirectDefaultScheme = ""
|
||||
}
|
||||
|
||||
return &coderd.OIDCConfig{
|
||||
OAuth2Config: useCfg,
|
||||
Provider: oidcProvider,
|
||||
@@ -268,19 +288,21 @@ func createOIDCConfig(ctx context.Context, logger slog.Logger, vals *codersdk.De
|
||||
// matches the issuer URL. This is not recommended.
|
||||
SkipIssuerCheck: vals.OIDC.SkipIssuerChecks.Value(),
|
||||
}),
|
||||
EmailDomain: vals.OIDC.EmailDomain,
|
||||
AllowSignups: vals.OIDC.AllowSignups.Value(),
|
||||
UsernameField: vals.OIDC.UsernameField.String(),
|
||||
NameField: vals.OIDC.NameField.String(),
|
||||
EmailField: vals.OIDC.EmailField.String(),
|
||||
AuthURLParams: vals.OIDC.AuthURLParams.Value,
|
||||
SecondaryClaims: secondaryClaimsSrc,
|
||||
SignInText: vals.OIDC.SignInText.String(),
|
||||
SignupsDisabledText: vals.OIDC.SignupsDisabledText.String(),
|
||||
IconURL: vals.OIDC.IconURL.String(),
|
||||
IgnoreEmailVerified: vals.OIDC.IgnoreEmailVerified.Value(),
|
||||
PKCEMethods: pkceSupport.CodeChallengeMethodsSupported,
|
||||
EmailFallback: vals.OIDC.EmailFallback.Value(),
|
||||
EmailDomain: vals.OIDC.EmailDomain,
|
||||
AllowSignups: vals.OIDC.AllowSignups.Value(),
|
||||
UsernameField: vals.OIDC.UsernameField.String(),
|
||||
NameField: vals.OIDC.NameField.String(),
|
||||
EmailField: vals.OIDC.EmailField.String(),
|
||||
AuthURLParams: vals.OIDC.AuthURLParams.Value,
|
||||
SecondaryClaims: secondaryClaimsSrc,
|
||||
SignInText: vals.OIDC.SignInText.String(),
|
||||
SignupsDisabledText: vals.OIDC.SignupsDisabledText.String(),
|
||||
IconURL: vals.OIDC.IconURL.String(),
|
||||
IgnoreEmailVerified: vals.OIDC.IgnoreEmailVerified.Value(),
|
||||
PKCEMethods: pkceSupport.CodeChallengeMethodsSupported,
|
||||
EmailFallback: vals.OIDC.EmailFallback.Value(),
|
||||
RedirectAllowedHosts: redirectAllowedHosts,
|
||||
RedirectDefaultScheme: redirectDefaultScheme,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user