chore: add oidc redirect url to override access url (#21521)

If a deployment has 2 domains, overriding the oidc url allows the oidc
redirect to differ from the access_url

response to https://github.com/coder/coder/discussions/21500

**This config setting is hidden by default**
This commit is contained in:
Steven Masley
2026-02-20 09:11:01 -06:00
committed by GitHub
parent e5f64eb21d
commit efdaaa2c8f
8 changed files with 109 additions and 0 deletions
+9
View File
@@ -137,6 +137,15 @@ func createOIDCConfig(ctx context.Context, logger slog.Logger, vals *codersdk.De
if err != nil {
return nil, xerrors.Errorf("parse oidc oauth callback url: %w", err)
}
if vals.OIDC.RedirectURL.String() != "" {
redirectURL, err = vals.OIDC.RedirectURL.Value().Parse("/api/v2/users/oidc/callback")
if err != nil {
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 the scopes contain 'groups', we enable group support.
// Do not override any custom value set by the user.
if slice.Contains(vals.OIDC.Scopes, "groups") && vals.OIDC.GroupField == "" {
+5
View File
@@ -421,6 +421,11 @@ oidc:
# an insecure OIDC configuration. It is not recommended to use this flag.
# (default: <unset>, type: bool)
dangerousSkipIssuerChecks: false
# Optional override of the default redirect url which uses the deployment's access
# url. Useful in situations where a deployment has more than 1 domain. Using this
# setting can also break OIDC, so use with caution.
# (default: <unset>, type: url)
oidc-redirect-url:
# Telemetry is critical to our ability to improve Coder. We strip all personal
# information before sending data to our servers. Please only disable telemetry
# when required by your organization's security policy.
+8
View File
@@ -16784,6 +16784,14 @@ const docTemplate = `{
"organization_mapping": {
"type": "object"
},
"redirect_url": {
"description": "RedirectURL is optional, defaulting to 'ACCESS_URL'. Only useful in niche\nsituations where the OIDC callback domain is different from the ACCESS_URL\ndomain.",
"allOf": [
{
"$ref": "#/definitions/serpent.URL"
}
]
},
"scopes": {
"type": "array",
"items": {
+8
View File
@@ -15248,6 +15248,14 @@
"organization_mapping": {
"type": "object"
},
"redirect_url": {
"description": "RedirectURL is optional, defaulting to 'ACCESS_URL'. Only useful in niche\nsituations where the OIDC callback domain is different from the ACCESS_URL\ndomain.",
"allOf": [
{
"$ref": "#/definitions/serpent.URL"
}
]
},
"scopes": {
"type": "array",
"items": {
+20
View File
@@ -818,6 +818,11 @@ type OIDCConfig struct {
IconURL serpent.URL `json:"icon_url" typescript:",notnull"`
SignupsDisabledText serpent.String `json:"signups_disabled_text" typescript:",notnull"`
SkipIssuerChecks serpent.Bool `json:"skip_issuer_checks" typescript:",notnull"`
// RedirectURL is optional, defaulting to 'ACCESS_URL'. Only useful in niche
// situations where the OIDC callback domain is different from the ACCESS_URL
// domain.
RedirectURL serpent.URL `json:"redirect_url" typescript:",notnull"`
}
type TelemetryConfig struct {
@@ -2435,6 +2440,21 @@ func (c *DeploymentValues) Options() serpent.OptionSet {
Group: &deploymentGroupOIDC,
YAML: "dangerousSkipIssuerChecks",
},
{
Name: "OIDC Redirect URL",
Description: "Optional override of the default redirect url which uses the deployment's access url. " +
"Useful in situations where a deployment has more than 1 domain. Using this setting can also break OIDC, so use with caution.",
Required: false,
Flag: "oidc-redirect-url",
Env: "CODER_OIDC_REDIRECT_URL",
YAML: "oidc-redirect-url",
Value: &c.OIDC.RedirectURL,
Group: &deploymentGroupOIDC,
UseInstead: nil,
// In most deployments, this setting can only complicate and break OIDC.
// So hide it, and only surface it to the small number of users that need it.
Hidden: true,
},
// Telemetry settings
telemetryEnable,
{
+13
View File
@@ -432,6 +432,19 @@ curl -X GET http://coder-server:8080/api/v2/deployment/config \
"organization_assign_default": true,
"organization_field": "string",
"organization_mapping": {},
"redirect_url": {
"forceQuery": true,
"fragment": "string",
"host": "string",
"omitHost": true,
"opaque": "string",
"path": "string",
"rawFragment": "string",
"rawPath": "string",
"rawQuery": "string",
"scheme": "string",
"user": {}
},
"scopes": [
"string"
],
+40
View File
@@ -2933,6 +2933,19 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
"organization_assign_default": true,
"organization_field": "string",
"organization_mapping": {},
"redirect_url": {
"forceQuery": true,
"fragment": "string",
"host": "string",
"omitHost": true,
"opaque": "string",
"path": "string",
"rawFragment": "string",
"rawPath": "string",
"rawQuery": "string",
"scheme": "string",
"user": {}
},
"scopes": [
"string"
],
@@ -3488,6 +3501,19 @@ CreateWorkspaceRequest provides options for creating a new workspace. Only one o
"organization_assign_default": true,
"organization_field": "string",
"organization_mapping": {},
"redirect_url": {
"forceQuery": true,
"fragment": "string",
"host": "string",
"omitHost": true,
"opaque": "string",
"path": "string",
"rawFragment": "string",
"rawPath": "string",
"rawQuery": "string",
"scheme": "string",
"user": {}
},
"scopes": [
"string"
],
@@ -5731,6 +5757,19 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
"organization_assign_default": true,
"organization_field": "string",
"organization_mapping": {},
"redirect_url": {
"forceQuery": true,
"fragment": "string",
"host": "string",
"omitHost": true,
"opaque": "string",
"path": "string",
"rawFragment": "string",
"rawPath": "string",
"rawQuery": "string",
"scheme": "string",
"user": {}
},
"scopes": [
"string"
],
@@ -5772,6 +5811,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
| `organization_assign_default` | boolean | false | | |
| `organization_field` | string | false | | |
| `organization_mapping` | object | false | | |
| `redirect_url` | [serpent.URL](#serpenturl) | false | | Redirect URL is optional, defaulting to 'ACCESS_URL'. Only useful in niche situations where the OIDC callback domain is different from the ACCESS_URL domain. |
| `scopes` | array of string | false | | |
| `sign_in_text` | string | false | | |
| `signups_disabled_text` | string | false | | |
+6
View File
@@ -3361,6 +3361,12 @@ export interface OIDCConfig {
readonly icon_url: string;
readonly signups_disabled_text: string;
readonly skip_issuer_checks: boolean;
/**
* RedirectURL is optional, defaulting to 'ACCESS_URL'. Only useful in niche
* situations where the OIDC callback domain is different from the ACCESS_URL
* domain.
*/
readonly redirect_url: string;
}
// From codersdk/parameters.go