diff --git a/cli/server.go b/cli/server.go index 06d7feaaed..4aed11d5c2 100644 --- a/cli/server.go +++ b/cli/server.go @@ -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 == "" { diff --git a/cli/testdata/server-config.yaml.golden b/cli/testdata/server-config.yaml.golden index c7b1098a6c..39666df533 100644 --- a/cli/testdata/server-config.yaml.golden +++ b/cli/testdata/server-config.yaml.golden @@ -421,6 +421,11 @@ oidc: # an insecure OIDC configuration. It is not recommended to use this flag. # (default: , 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: , 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. diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index d08c0d3ea0..b76254f2fd 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -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": { diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index b8fbb24d47..a8b515d967 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -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": { diff --git a/codersdk/deployment.go b/codersdk/deployment.go index bd1f8c6063..6a5a68c102 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -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, { diff --git a/docs/reference/api/general.md b/docs/reference/api/general.md index f3499b3930..3a87dd8bfc 100644 --- a/docs/reference/api/general.md +++ b/docs/reference/api/general.md @@ -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" ], diff --git a/docs/reference/api/schemas.md b/docs/reference/api/schemas.md index db674a94f0..a7958f239d 100644 --- a/docs/reference/api/schemas.md +++ b/docs/reference/api/schemas.md @@ -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 | | | diff --git a/site/src/api/typesGenerated.ts b/site/src/api/typesGenerated.ts index 32a4876240..cee52f7fbc 100644 --- a/site/src/api/typesGenerated.ts +++ b/site/src/api/typesGenerated.ts @@ -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