diff --git a/cmd/thv-operator/api/v1beta1/mcpexternalauthconfig_types.go b/cmd/thv-operator/api/v1beta1/mcpexternalauthconfig_types.go
index caa123ddb4..46d422a736 100644
--- a/cmd/thv-operator/api/v1beta1/mcpexternalauthconfig_types.go
+++ b/cmd/thv-operator/api/v1beta1/mcpexternalauthconfig_types.go
@@ -341,7 +341,8 @@ type BearerTokenConfig struct {
type EmbeddedAuthServerConfig struct {
// Issuer is the issuer identifier for this authorization server.
// This will be included in the "iss" claim of issued tokens.
- // Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414).
+ // Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts when
+ // insecureAllowHTTP is true) without query, fragment, or trailing slash (per RFC 8414).
// +kubebuilder:validation:Required
// +kubebuilder:validation:Pattern=`^https?://[^\s?#]+[^/\s?#]$`
Issuer string `json:"issuer"`
@@ -352,7 +353,8 @@ type EmbeddedAuthServerConfig struct {
// All other endpoints (token, registration, JWKS) remain derived from the issuer.
// This is useful when the browser-facing authorization endpoint needs to be on a
// different host than the issuer used for backend-to-backend calls.
- // Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.
+ // Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts
+ // when insecureAllowHTTP is true) without query, fragment, or trailing slash.
// +kubebuilder:validation:Pattern=`^https?://[^\s?#]+[^/\s?#]$`
// +optional
AuthorizationEndpointBaseURL string `json:"authorizationEndpointBaseUrl,omitempty"`
@@ -430,6 +432,22 @@ type EmbeddedAuthServerConfig struct {
// +optional
DisableUpstreamTokenInjection bool `json:"disableUpstreamTokenInjection,omitempty"`
+ // InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ // Only set this for in-cluster Kubernetes deployments where traffic between
+ // pods traverses a trusted network (e.g. the in-cluster service mesh).
+ // Production deployments reachable outside the cluster MUST use https://.
+ //
+ // On VirtualMCPServer: when false (the default), http:// issuers for non-localhost
+ // hosts are rejected at reconcile time with an AuthServerConfigValidated=False condition.
+ //
+ // On MCPServer and MCPRemoteProxy (via MCPExternalAuthConfig): this field is
+ // structurally present but enforcement is deferred to pod startup via Config.Validate();
+ // a misconfigured issuer will cause the pod to crash at startup rather than surface
+ // as an operator condition.
+ // +kubebuilder:default=false
+ // +optional
+ InsecureAllowHTTP bool `json:"insecureAllowHTTP,omitempty"`
+
// BaselineClientScopes is a baseline set of OAuth 2.0 scopes guaranteed to be
// included in every client registration. The embedded auth server unions these
// scopes into the registered set returned by RFC 7591 Dynamic Client
diff --git a/cmd/thv-operator/controllers/virtualmcpserver_controller.go b/cmd/thv-operator/controllers/virtualmcpserver_controller.go
index ea94504456..9080096b65 100644
--- a/cmd/thv-operator/controllers/virtualmcpserver_controller.go
+++ b/cmd/thv-operator/controllers/virtualmcpserver_controller.go
@@ -12,6 +12,7 @@ import (
stderrors "errors"
"fmt"
"maps"
+ "net/url"
"reflect"
"slices"
"strings"
@@ -42,6 +43,7 @@ import (
"github.com/stacklok/toolhive/cmd/thv-operator/pkg/virtualmcpserverstatus"
operatorvmcpconfig "github.com/stacklok/toolhive/cmd/thv-operator/pkg/vmcpconfig"
"github.com/stacklok/toolhive/pkg/authserver"
+ "github.com/stacklok/toolhive/pkg/networking"
vmcptypes "github.com/stacklok/toolhive/pkg/vmcp"
"github.com/stacklok/toolhive/pkg/vmcp/auth/converters"
authtypes "github.com/stacklok/toolhive/pkg/vmcp/auth/types"
@@ -488,7 +490,33 @@ func (*VirtualMCPServerReconciler) validateAuthServerConfig(
metav1.ConditionFalse,
)
statusManager.SetObservedGeneration(vmcp.Generation)
- return fmt.Errorf("%s", message)
+ return stderrors.New(message)
+ }
+
+ // Admission-time check: http:// issuers for non-localhost hosts require
+ // insecureAllowHTTP to be set explicitly. Without it the proxyrunner pod
+ // will crash at startup with a validateIssuerURL failure.
+ if strings.HasPrefix(cfg.Issuer, "http://") {
+ // url.Parse succeeds for any URL that passes the CRD regex; the
+ // parsed.Host != "" guard defends against the degenerate empty-host case.
+ parsed, err := url.Parse(cfg.Issuer)
+ if err == nil && parsed.Host != "" && !networking.IsLocalhost(parsed.Host) && !cfg.InsecureAllowHTTP {
+ message := fmt.Sprintf(
+ "spec.authServerConfig.issuer %q uses http:// with a non-localhost host; "+
+ "set spec.authServerConfig.insecureAllowHTTP: true to allow this for trusted "+
+ "in-cluster deployments, or use https:// for production deployments",
+ cfg.Issuer,
+ )
+ statusManager.SetPhase(mcpv1beta1.VirtualMCPServerPhaseFailed)
+ statusManager.SetMessage(message)
+ statusManager.SetAuthServerConfigValidatedCondition(
+ mcpv1beta1.ConditionReasonAuthServerConfigInvalid,
+ message,
+ metav1.ConditionFalse,
+ )
+ statusManager.SetObservedGeneration(vmcp.Generation)
+ return stderrors.New(message)
+ }
}
if len(cfg.UpstreamProviders) == 0 {
@@ -501,7 +529,7 @@ func (*VirtualMCPServerReconciler) validateAuthServerConfig(
metav1.ConditionFalse,
)
statusManager.SetObservedGeneration(vmcp.Generation)
- return fmt.Errorf("%s", message)
+ return stderrors.New(message)
}
// Validate additionalAuthorizationParams on each upstream provider
@@ -518,7 +546,7 @@ func (*VirtualMCPServerReconciler) validateAuthServerConfig(
metav1.ConditionFalse,
)
statusManager.SetObservedGeneration(vmcp.Generation)
- return fmt.Errorf("%s", message)
+ return stderrors.New(message)
}
}
@@ -1579,8 +1607,8 @@ func (*VirtualMCPServerReconciler) ensureServiceURL(
statusManager virtualmcpserverstatus.StatusManager,
) {
if vmcp.Status.URL == "" {
- url := createVmcpServiceURL(vmcp.Name, vmcp.Namespace, vmcpDefaultPort)
- statusManager.SetURL(url)
+ serviceURL := createVmcpServiceURL(vmcp.Name, vmcp.Namespace, vmcpDefaultPort)
+ statusManager.SetURL(serviceURL)
}
}
diff --git a/cmd/thv-operator/controllers/virtualmcpserver_controller_test.go b/cmd/thv-operator/controllers/virtualmcpserver_controller_test.go
index ccf61555d4..400f01472f 100644
--- a/cmd/thv-operator/controllers/virtualmcpserver_controller_test.go
+++ b/cmd/thv-operator/controllers/virtualmcpserver_controller_test.go
@@ -3848,3 +3848,103 @@ func TestVirtualMCPServerReconciler_IdentitySynthesizedTransitionsOnValidationFa
assert.NotContains(t, cond.Message, "atlassian",
"stale message naming the now-removed upstream must not survive the broken edit")
}
+
+// TestVirtualMCPServerValidateAuthServerConfig_InsecureAllowHTTP exercises the
+// admission-time check that rejects http:// issuers for non-localhost hosts
+// unless insecureAllowHTTP is explicitly set.
+func TestVirtualMCPServerValidateAuthServerConfig_InsecureAllowHTTP(t *testing.T) {
+ t.Parallel()
+
+ validUpstreams := []mcpv1beta1.UpstreamProviderConfig{
+ {
+ Name: "dex",
+ Type: mcpv1beta1.UpstreamProviderTypeOIDC,
+ OIDCConfig: &mcpv1beta1.OIDCUpstreamConfig{
+ IssuerURL: "https://dex.example.com",
+ ClientID: "test-client",
+ },
+ },
+ }
+
+ tests := []struct {
+ name string
+ issuer string
+ insecureAllowHTTP bool
+ wantErr bool
+ wantCondition metav1.ConditionStatus
+ }{
+ {
+ name: "https issuer: always valid",
+ issuer: "https://authserver.example.com",
+ wantCondition: metav1.ConditionTrue,
+ },
+ {
+ name: "http localhost issuer: valid without flag",
+ issuer: "http://localhost:4483",
+ wantCondition: metav1.ConditionTrue,
+ },
+ {
+ name: "http in-cluster issuer without flag: rejected",
+ issuer: "http://vmcp-test.default.svc.cluster.local:4483",
+ wantErr: true,
+ wantCondition: metav1.ConditionFalse,
+ },
+ {
+ name: "http in-cluster issuer with flag: accepted",
+ issuer: "http://vmcp-test.default.svc.cluster.local:4483",
+ insecureAllowHTTP: true,
+ wantCondition: metav1.ConditionTrue,
+ },
+ {
+ name: "http non-localhost issuer without flag: rejected",
+ issuer: "http://authserver.example.com",
+ wantErr: true,
+ wantCondition: metav1.ConditionFalse,
+ },
+ {
+ name: "http non-localhost issuer with flag: accepted",
+ issuer: "http://authserver.example.com",
+ insecureAllowHTTP: true,
+ wantCondition: metav1.ConditionTrue,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+
+ vmcp := v1beta1test.NewVirtualMCPServer(testVmcpName, "default",
+ v1beta1test.WithVMCPGroupRef("test-group"),
+ v1beta1test.WithVMCPAuthServerConfig(&mcpv1beta1.EmbeddedAuthServerConfig{
+ Issuer: tt.issuer,
+ InsecureAllowHTTP: tt.insecureAllowHTTP,
+ UpstreamProviders: validUpstreams,
+ }),
+ v1beta1test.MutateVMCP(func(v *mcpv1beta1.VirtualMCPServer) {
+ v.Generation = 1
+ }),
+ )
+
+ r := &VirtualMCPServerReconciler{}
+ statusManager := virtualmcpserverstatus.NewStatusManager(vmcp)
+ err := r.validateAuthServerConfig(vmcp, statusManager)
+ statusManager.UpdateStatus(t.Context(), &vmcp.Status)
+
+ if tt.wantErr {
+ require.Error(t, err)
+ } else {
+ require.NoError(t, err)
+ }
+
+ cond := findCondition(vmcp.Status.Conditions, mcpv1beta1.ConditionTypeAuthServerConfigValidated)
+ require.NotNil(t, cond, "AuthServerConfigValidated condition must be set")
+ assert.Equal(t, tt.wantCondition, cond.Status)
+
+ if tt.wantErr {
+ assert.Equal(t, mcpv1beta1.ConditionReasonAuthServerConfigInvalid, cond.Reason)
+ assert.Contains(t, cond.Message, "insecureAllowHTTP",
+ "rejection message must guide the user to the fix")
+ }
+ })
+ }
+}
diff --git a/cmd/thv-operator/pkg/controllerutil/authserver.go b/cmd/thv-operator/pkg/controllerutil/authserver.go
index baf32c853b..97a3692b96 100644
--- a/cmd/thv-operator/pkg/controllerutil/authserver.go
+++ b/cmd/thv-operator/pkg/controllerutil/authserver.go
@@ -585,6 +585,10 @@ func BuildAuthServerRunConfig(
// Wire through upstream token injection flag
config.DisableUpstreamTokenInjection = authConfig.DisableUpstreamTokenInjection
+ // Wire through the insecure HTTP issuer flag from the CRD field.
+ // This replaces any auto-inference and moves control to the deployer.
+ config.InsecureAllowHTTP = authConfig.InsecureAllowHTTP
+
// Build CIMD configuration. CacheFallbackTTL is passed as-is (string);
// resolveCIMDConfig in the runner parses it to time.Duration at startup.
if authConfig.CIMD != nil && authConfig.CIMD.Enabled {
diff --git a/cmd/thv-operator/pkg/controllerutil/authserver_test.go b/cmd/thv-operator/pkg/controllerutil/authserver_test.go
index 2a6b795bc0..b79d01584b 100644
--- a/cmd/thv-operator/pkg/controllerutil/authserver_test.go
+++ b/cmd/thv-operator/pkg/controllerutil/authserver_test.go
@@ -1610,6 +1610,40 @@ func TestBuildAuthServerRunConfig(t *testing.T) {
"DisableUpstreamTokenInjection should default to false")
},
},
+ {
+ name: "insecureAllowHTTP true is propagated to RunConfig",
+ authConfig: &mcpv1beta1.EmbeddedAuthServerConfig{
+ Issuer: "http://vmcp-test.default.svc.cluster.local:4483",
+ InsecureAllowHTTP: true,
+ HMACSecretRefs: []mcpv1beta1.SecretKeyRef{
+ {Name: "hmac-secret", Key: "hmac"},
+ },
+ },
+ allowedAudiences: defaultAudiences,
+ scopesSupported: defaultScopes,
+ checkFunc: func(t *testing.T, config *authserver.RunConfig) {
+ t.Helper()
+ assert.True(t, config.InsecureAllowHTTP,
+ "InsecureAllowHTTP must propagate from CRD field to RunConfig")
+ },
+ },
+ {
+ name: "insecureAllowHTTP false is propagated to RunConfig",
+ authConfig: &mcpv1beta1.EmbeddedAuthServerConfig{
+ Issuer: "https://authserver.example.com",
+ InsecureAllowHTTP: false,
+ HMACSecretRefs: []mcpv1beta1.SecretKeyRef{
+ {Name: "hmac-secret", Key: "hmac"},
+ },
+ },
+ allowedAudiences: defaultAudiences,
+ scopesSupported: defaultScopes,
+ checkFunc: func(t *testing.T, config *authserver.RunConfig) {
+ t.Helper()
+ assert.False(t, config.InsecureAllowHTTP,
+ "InsecureAllowHTTP false must propagate from CRD field to RunConfig")
+ },
+ },
}
for _, tt := range tests {
diff --git a/deploy/charts/operator-crds/files/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml b/deploy/charts/operator-crds/files/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml
index d88668e8dd..6a825d9800 100644
--- a/deploy/charts/operator-crds/files/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml
+++ b/deploy/charts/operator-crds/files/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml
@@ -213,7 +213,8 @@ spec:
All other endpoints (token, registration, JWKS) remain derived from the issuer.
This is useful when the browser-facing authorization endpoint needs to be on a
different host than the issuer used for backend-to-backend calls.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts
+ when insecureAllowHTTP is true) without query, fragment, or trailing slash.
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
baselineClientScopes:
@@ -307,11 +308,28 @@ spec:
type: object
type: array
x-kubernetes-list-type: atomic
+ insecureAllowHTTP:
+ default: false
+ description: |-
+ InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ Only set this for in-cluster Kubernetes deployments where traffic between
+ pods traverses a trusted network (e.g. the in-cluster service mesh).
+ Production deployments reachable outside the cluster MUST use https://.
+
+ On VirtualMCPServer: when false (the default), http:// issuers for non-localhost
+ hosts are rejected at reconcile time with an AuthServerConfigValidated=False condition.
+
+ On MCPServer and MCPRemoteProxy (via MCPExternalAuthConfig): this field is
+ structurally present but enforcement is deferred to pod startup via Config.Validate();
+ a misconfigured issuer will cause the pod to crash at startup rather than surface
+ as an operator condition.
+ type: boolean
issuer:
description: |-
Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414).
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts when
+ insecureAllowHTTP is true) without query, fragment, or trailing slash (per RFC 8414).
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
primaryUpstreamProvider:
@@ -1693,7 +1711,8 @@ spec:
All other endpoints (token, registration, JWKS) remain derived from the issuer.
This is useful when the browser-facing authorization endpoint needs to be on a
different host than the issuer used for backend-to-backend calls.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts
+ when insecureAllowHTTP is true) without query, fragment, or trailing slash.
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
baselineClientScopes:
@@ -1787,11 +1806,28 @@ spec:
type: object
type: array
x-kubernetes-list-type: atomic
+ insecureAllowHTTP:
+ default: false
+ description: |-
+ InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ Only set this for in-cluster Kubernetes deployments where traffic between
+ pods traverses a trusted network (e.g. the in-cluster service mesh).
+ Production deployments reachable outside the cluster MUST use https://.
+
+ On VirtualMCPServer: when false (the default), http:// issuers for non-localhost
+ hosts are rejected at reconcile time with an AuthServerConfigValidated=False condition.
+
+ On MCPServer and MCPRemoteProxy (via MCPExternalAuthConfig): this field is
+ structurally present but enforcement is deferred to pod startup via Config.Validate();
+ a misconfigured issuer will cause the pod to crash at startup rather than surface
+ as an operator condition.
+ type: boolean
issuer:
description: |-
Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414).
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts when
+ insecureAllowHTTP is true) without query, fragment, or trailing slash (per RFC 8414).
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
primaryUpstreamProvider:
diff --git a/deploy/charts/operator-crds/files/crds/toolhive.stacklok.dev_virtualmcpservers.yaml b/deploy/charts/operator-crds/files/crds/toolhive.stacklok.dev_virtualmcpservers.yaml
index 98d5e23f31..1ef661ca40 100644
--- a/deploy/charts/operator-crds/files/crds/toolhive.stacklok.dev_virtualmcpservers.yaml
+++ b/deploy/charts/operator-crds/files/crds/toolhive.stacklok.dev_virtualmcpservers.yaml
@@ -86,7 +86,8 @@ spec:
All other endpoints (token, registration, JWKS) remain derived from the issuer.
This is useful when the browser-facing authorization endpoint needs to be on a
different host than the issuer used for backend-to-backend calls.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts
+ when insecureAllowHTTP is true) without query, fragment, or trailing slash.
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
baselineClientScopes:
@@ -180,11 +181,28 @@ spec:
type: object
type: array
x-kubernetes-list-type: atomic
+ insecureAllowHTTP:
+ default: false
+ description: |-
+ InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ Only set this for in-cluster Kubernetes deployments where traffic between
+ pods traverses a trusted network (e.g. the in-cluster service mesh).
+ Production deployments reachable outside the cluster MUST use https://.
+
+ On VirtualMCPServer: when false (the default), http:// issuers for non-localhost
+ hosts are rejected at reconcile time with an AuthServerConfigValidated=False condition.
+
+ On MCPServer and MCPRemoteProxy (via MCPExternalAuthConfig): this field is
+ structurally present but enforcement is deferred to pod startup via Config.Validate();
+ a misconfigured issuer will cause the pod to crash at startup rather than surface
+ as an operator condition.
+ type: boolean
issuer:
description: |-
Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414).
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts when
+ insecureAllowHTTP is true) without query, fragment, or trailing slash (per RFC 8414).
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
primaryUpstreamProvider:
@@ -3318,7 +3336,8 @@ spec:
All other endpoints (token, registration, JWKS) remain derived from the issuer.
This is useful when the browser-facing authorization endpoint needs to be on a
different host than the issuer used for backend-to-backend calls.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts
+ when insecureAllowHTTP is true) without query, fragment, or trailing slash.
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
baselineClientScopes:
@@ -3412,11 +3431,28 @@ spec:
type: object
type: array
x-kubernetes-list-type: atomic
+ insecureAllowHTTP:
+ default: false
+ description: |-
+ InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ Only set this for in-cluster Kubernetes deployments where traffic between
+ pods traverses a trusted network (e.g. the in-cluster service mesh).
+ Production deployments reachable outside the cluster MUST use https://.
+
+ On VirtualMCPServer: when false (the default), http:// issuers for non-localhost
+ hosts are rejected at reconcile time with an AuthServerConfigValidated=False condition.
+
+ On MCPServer and MCPRemoteProxy (via MCPExternalAuthConfig): this field is
+ structurally present but enforcement is deferred to pod startup via Config.Validate();
+ a misconfigured issuer will cause the pod to crash at startup rather than surface
+ as an operator condition.
+ type: boolean
issuer:
description: |-
Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414).
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts when
+ insecureAllowHTTP is true) without query, fragment, or trailing slash (per RFC 8414).
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
primaryUpstreamProvider:
diff --git a/deploy/charts/operator-crds/templates/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml b/deploy/charts/operator-crds/templates/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml
index 27ec4c33c4..3d21527444 100644
--- a/deploy/charts/operator-crds/templates/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml
+++ b/deploy/charts/operator-crds/templates/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml
@@ -216,7 +216,8 @@ spec:
All other endpoints (token, registration, JWKS) remain derived from the issuer.
This is useful when the browser-facing authorization endpoint needs to be on a
different host than the issuer used for backend-to-backend calls.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts
+ when insecureAllowHTTP is true) without query, fragment, or trailing slash.
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
baselineClientScopes:
@@ -310,11 +311,28 @@ spec:
type: object
type: array
x-kubernetes-list-type: atomic
+ insecureAllowHTTP:
+ default: false
+ description: |-
+ InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ Only set this for in-cluster Kubernetes deployments where traffic between
+ pods traverses a trusted network (e.g. the in-cluster service mesh).
+ Production deployments reachable outside the cluster MUST use https://.
+
+ On VirtualMCPServer: when false (the default), http:// issuers for non-localhost
+ hosts are rejected at reconcile time with an AuthServerConfigValidated=False condition.
+
+ On MCPServer and MCPRemoteProxy (via MCPExternalAuthConfig): this field is
+ structurally present but enforcement is deferred to pod startup via Config.Validate();
+ a misconfigured issuer will cause the pod to crash at startup rather than surface
+ as an operator condition.
+ type: boolean
issuer:
description: |-
Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414).
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts when
+ insecureAllowHTTP is true) without query, fragment, or trailing slash (per RFC 8414).
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
primaryUpstreamProvider:
@@ -1696,7 +1714,8 @@ spec:
All other endpoints (token, registration, JWKS) remain derived from the issuer.
This is useful when the browser-facing authorization endpoint needs to be on a
different host than the issuer used for backend-to-backend calls.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts
+ when insecureAllowHTTP is true) without query, fragment, or trailing slash.
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
baselineClientScopes:
@@ -1790,11 +1809,28 @@ spec:
type: object
type: array
x-kubernetes-list-type: atomic
+ insecureAllowHTTP:
+ default: false
+ description: |-
+ InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ Only set this for in-cluster Kubernetes deployments where traffic between
+ pods traverses a trusted network (e.g. the in-cluster service mesh).
+ Production deployments reachable outside the cluster MUST use https://.
+
+ On VirtualMCPServer: when false (the default), http:// issuers for non-localhost
+ hosts are rejected at reconcile time with an AuthServerConfigValidated=False condition.
+
+ On MCPServer and MCPRemoteProxy (via MCPExternalAuthConfig): this field is
+ structurally present but enforcement is deferred to pod startup via Config.Validate();
+ a misconfigured issuer will cause the pod to crash at startup rather than surface
+ as an operator condition.
+ type: boolean
issuer:
description: |-
Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414).
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts when
+ insecureAllowHTTP is true) without query, fragment, or trailing slash (per RFC 8414).
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
primaryUpstreamProvider:
diff --git a/deploy/charts/operator-crds/templates/toolhive.stacklok.dev_virtualmcpservers.yaml b/deploy/charts/operator-crds/templates/toolhive.stacklok.dev_virtualmcpservers.yaml
index 131ea6de6c..0b4e98d12c 100644
--- a/deploy/charts/operator-crds/templates/toolhive.stacklok.dev_virtualmcpservers.yaml
+++ b/deploy/charts/operator-crds/templates/toolhive.stacklok.dev_virtualmcpservers.yaml
@@ -89,7 +89,8 @@ spec:
All other endpoints (token, registration, JWKS) remain derived from the issuer.
This is useful when the browser-facing authorization endpoint needs to be on a
different host than the issuer used for backend-to-backend calls.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts
+ when insecureAllowHTTP is true) without query, fragment, or trailing slash.
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
baselineClientScopes:
@@ -183,11 +184,28 @@ spec:
type: object
type: array
x-kubernetes-list-type: atomic
+ insecureAllowHTTP:
+ default: false
+ description: |-
+ InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ Only set this for in-cluster Kubernetes deployments where traffic between
+ pods traverses a trusted network (e.g. the in-cluster service mesh).
+ Production deployments reachable outside the cluster MUST use https://.
+
+ On VirtualMCPServer: when false (the default), http:// issuers for non-localhost
+ hosts are rejected at reconcile time with an AuthServerConfigValidated=False condition.
+
+ On MCPServer and MCPRemoteProxy (via MCPExternalAuthConfig): this field is
+ structurally present but enforcement is deferred to pod startup via Config.Validate();
+ a misconfigured issuer will cause the pod to crash at startup rather than surface
+ as an operator condition.
+ type: boolean
issuer:
description: |-
Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414).
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts when
+ insecureAllowHTTP is true) without query, fragment, or trailing slash (per RFC 8414).
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
primaryUpstreamProvider:
@@ -3321,7 +3339,8 @@ spec:
All other endpoints (token, registration, JWKS) remain derived from the issuer.
This is useful when the browser-facing authorization endpoint needs to be on a
different host than the issuer used for backend-to-backend calls.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts
+ when insecureAllowHTTP is true) without query, fragment, or trailing slash.
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
baselineClientScopes:
@@ -3415,11 +3434,28 @@ spec:
type: object
type: array
x-kubernetes-list-type: atomic
+ insecureAllowHTTP:
+ default: false
+ description: |-
+ InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ Only set this for in-cluster Kubernetes deployments where traffic between
+ pods traverses a trusted network (e.g. the in-cluster service mesh).
+ Production deployments reachable outside the cluster MUST use https://.
+
+ On VirtualMCPServer: when false (the default), http:// issuers for non-localhost
+ hosts are rejected at reconcile time with an AuthServerConfigValidated=False condition.
+
+ On MCPServer and MCPRemoteProxy (via MCPExternalAuthConfig): this field is
+ structurally present but enforcement is deferred to pod startup via Config.Validate();
+ a misconfigured issuer will cause the pod to crash at startup rather than surface
+ as an operator condition.
+ type: boolean
issuer:
description: |-
Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
- Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414).
+ Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts when
+ insecureAllowHTTP is true) without query, fragment, or trailing slash (per RFC 8414).
pattern: ^https?://[^\s?#]+[^/\s?#]$
type: string
primaryUpstreamProvider:
diff --git a/docs/operator/crd-api.md b/docs/operator/crd-api.md
index a71e09a627..00a989b2fe 100644
--- a/docs/operator/crd-api.md
+++ b/docs/operator/crd-api.md
@@ -1283,8 +1283,8 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
-| `issuer` _string_ | Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414). | | Pattern: `^https?://[^\s?#]+[^/\s?#]$`
Required: \{\}
|
-| `authorizationEndpointBaseUrl` _string_ | AuthorizationEndpointBaseURL overrides the base URL used for the authorization_endpoint
in the OAuth discovery document. When set, the discovery document will advertise
`\{authorizationEndpointBaseUrl\}/oauth/authorize` instead of `\{issuer\}/oauth/authorize`.
All other endpoints (token, registration, JWKS) remain derived from the issuer.
This is useful when the browser-facing authorization endpoint needs to be on a
different host than the issuer used for backend-to-backend calls.
Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash. | | Pattern: `^https?://[^\s?#]+[^/\s?#]$`
Optional: \{\}
|
+| `issuer` _string_ | Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts when
insecureAllowHTTP is true) without query, fragment, or trailing slash (per RFC 8414). | | Pattern: `^https?://[^\s?#]+[^/\s?#]$`
Required: \{\}
|
+| `authorizationEndpointBaseUrl` _string_ | AuthorizationEndpointBaseURL overrides the base URL used for the authorization_endpoint
in the OAuth discovery document. When set, the discovery document will advertise
`\{authorizationEndpointBaseUrl\}/oauth/authorize` instead of `\{issuer\}/oauth/authorize`.
All other endpoints (token, registration, JWKS) remain derived from the issuer.
This is useful when the browser-facing authorization endpoint needs to be on a
different host than the issuer used for backend-to-backend calls.
Must be a valid HTTPS URL (or HTTP for localhost, or HTTP for trusted in-cluster hosts
when insecureAllowHTTP is true) without query, fragment, or trailing slash. | | Pattern: `^https?://[^\s?#]+[^/\s?#]$`
Optional: \{\}
|
| `signingKeySecretRefs` _[api.v1beta1.SecretKeyRef](#apiv1beta1secretkeyref) array_ | SigningKeySecretRefs references Kubernetes Secrets containing signing keys for JWT operations.
Supports key rotation by allowing multiple keys (oldest keys are used for verification only).
If not specified, an ephemeral signing key will be auto-generated (development only -
JWTs will be invalid after restart). | | MaxItems: 5
Optional: \{\}
|
| `hmacSecretRefs` _[api.v1beta1.SecretKeyRef](#apiv1beta1secretkeyref) array_ | HMACSecretRefs references Kubernetes Secrets containing symmetric secrets for signing
authorization codes and refresh tokens (opaque tokens).
Current secret must be at least 32 bytes and cryptographically random.
Supports secret rotation via multiple entries (first is current, rest are for verification).
If not specified, an ephemeral secret will be auto-generated (development only -
auth codes and refresh tokens will be invalid after restart). | | Optional: \{\}
|
| `tokenLifespans` _[api.v1beta1.TokenLifespanConfig](#apiv1beta1tokenlifespanconfig)_ | TokenLifespans configures the duration that various tokens are valid.
If not specified, defaults are applied (access: 1h, refresh: 7d, authCode: 10m). | | Optional: \{\}
|
@@ -1292,6 +1292,7 @@ _Appears in:_
| `primaryUpstreamProvider` _string_ | PrimaryUpstreamProvider names the upstream IDP whose access token Cedar
should read claims from when authorising a request. Must match the name
of one of the entries in UpstreamProviders. When empty, the controller
auto-selects the first entry of UpstreamProviders.
Only meaningful on VirtualMCPServer, where multiple upstream providers
can be configured and Cedar needs to pick which token's claims to
evaluate. The VirtualMCPServer controller validates this field against
UpstreamProviders at admission and rejects unresolvable values.
On MCPServer and MCPRemoteProxy this field is structurally present (the
EmbeddedAuthServerConfig struct is shared) but has no runtime effect:
those CRDs are restricted to a single upstream so there is no choice to
make. Setting it on those CRDs is silently ignored. | | MaxLength: 63
MinLength: 1
Pattern: `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`
Optional: \{\}
|
| `storage` _[api.v1beta1.AuthServerStorageConfig](#apiv1beta1authserverstorageconfig)_ | Storage configures the storage backend for the embedded auth server.
If not specified, defaults to in-memory storage. | | Optional: \{\}
|
| `disableUpstreamTokenInjection` _boolean_ | DisableUpstreamTokenInjection prevents the embedded auth server from injecting
upstream IdP tokens into requests forwarded to the backend MCP server.
When true, the embedded auth server still handles OAuth flows for clients,
but instead of swapping ToolHive JWTs for upstream tokens the proxy STRIPS
the client's credential headers (Authorization, Cookie, Proxy-Authorization)
after validating the JWT — the backend receives an unauthenticated request.
Use headerForward to attach static credentials (e.g. an API key) if the
backend needs them. Cannot be combined with token exchange or AWS STS,
which would re-add credentials after the strip.
This is useful when the backend MCP server does not require authentication
(e.g., public documentation servers) but you still want client authentication. | false | Optional: \{\}
|
+| `insecureAllowHTTP` _boolean_ | InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
Only set this for in-cluster Kubernetes deployments where traffic between
pods traverses a trusted network (e.g. the in-cluster service mesh).
Production deployments reachable outside the cluster MUST use https://.
On VirtualMCPServer: when false (the default), http:// issuers for non-localhost
hosts are rejected at reconcile time with an AuthServerConfigValidated=False condition.
On MCPServer and MCPRemoteProxy (via MCPExternalAuthConfig): this field is
structurally present but enforcement is deferred to pod startup via Config.Validate();
a misconfigured issuer will cause the pod to crash at startup rather than surface
as an operator condition. | false | Optional: \{\}
|
| `baselineClientScopes` _string array_ | BaselineClientScopes is a baseline set of OAuth 2.0 scopes guaranteed to be
included in every client registration. The embedded auth server unions these
scopes into the registered set returned by RFC 7591 Dynamic Client
Registration, so a client that narrows the `scope` field at /oauth/register
can still request the baseline scopes at /oauth/authorize. All values must
be present in the upstream-derived scopesSupported set; the auth server
fails to start if any value is missing.
Security: every client registered via /oauth/register will gain the
ability to request these scopes at /oauth/authorize, regardless of what
the client itself requested. Keep the baseline narrow (typically
"openid" and "offline_access"). Adding a privileged scope here — e.g.
"admin:read" — would grant it to every DCR-registered client, including
public clients like Claude Code, Cursor, and VS Code.
When cimd.enabled is true, every dynamically resolved CIMD client will
also gain the ability to request these scopes, including third-party
clients resolved from arbitrary HTTPS URLs. | | MaxItems: 10
items:MinLength: 1
items:Pattern: `^[\x21\x23-\x5B\x5D-\x7E]+$`
Optional: \{\}
|
| `cimd` _[api.v1beta1.EmbeddedAuthServerCIMDConfig](#apiv1beta1embeddedauthservercimdconfig)_ | CIMD configures Client ID Metadata Document support. When omitted, CIMD is disabled. | | Optional: \{\}
|
diff --git a/docs/server/docs.go b/docs/server/docs.go
index 8a5e866faa..94a2b15384 100644
--- a/docs/server/docs.go
+++ b/docs/server/docs.go
@@ -563,6 +563,10 @@ const docTemplate = `{
"type": "array",
"uniqueItems": false
},
+ "insecure_allow_http": {
+ "description": "InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.\nOnly set this for in-cluster Kubernetes deployments on a trusted network.\nProduction deployments reachable outside the cluster MUST use https://.",
+ "type": "boolean"
+ },
"issuer": {
"description": "Issuer is the issuer identifier for this authorization server.\nThis will be included in the \"iss\" claim of issued tokens.\nMust be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.",
"type": "string"
diff --git a/docs/server/swagger.json b/docs/server/swagger.json
index 8dc3869b85..cc7d7b56aa 100644
--- a/docs/server/swagger.json
+++ b/docs/server/swagger.json
@@ -556,6 +556,10 @@
"type": "array",
"uniqueItems": false
},
+ "insecure_allow_http": {
+ "description": "InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.\nOnly set this for in-cluster Kubernetes deployments on a trusted network.\nProduction deployments reachable outside the cluster MUST use https://.",
+ "type": "boolean"
+ },
"issuer": {
"description": "Issuer is the issuer identifier for this authorization server.\nThis will be included in the \"iss\" claim of issued tokens.\nMust be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash.",
"type": "string"
diff --git a/docs/server/swagger.yaml b/docs/server/swagger.yaml
index 63d3dd713f..a790f89ccf 100644
--- a/docs/server/swagger.yaml
+++ b/docs/server/swagger.yaml
@@ -640,6 +640,12 @@ components:
type: string
type: array
uniqueItems: false
+ insecure_allow_http:
+ description: |-
+ InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ Only set this for in-cluster Kubernetes deployments on a trusted network.
+ Production deployments reachable outside the cluster MUST use https://.
+ type: boolean
issuer:
description: |-
Issuer is the issuer identifier for this authorization server.
diff --git a/pkg/authserver/config.go b/pkg/authserver/config.go
index ee3be3d965..bd9e6811f9 100644
--- a/pkg/authserver/config.go
+++ b/pkg/authserver/config.go
@@ -104,6 +104,12 @@ type RunConfig struct {
// embedded authorization server accepts HTTPS URLs as client_id values
// and resolves them via the CIMD protocol instead of requiring DCR.
CIMD *CIMDRunConfig `json:"cimd,omitempty" yaml:"cimd,omitempty"`
+
+ // InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ // Only set this for in-cluster Kubernetes deployments on a trusted network.
+ // Production deployments reachable outside the cluster MUST use https://.
+ //nolint:lll // field tags require full JSON+YAML names
+ InsecureAllowHTTP bool `json:"insecure_allow_http,omitempty" yaml:"insecure_allow_http,omitempty"`
}
// Validate checks that the on-disk RunConfig is internally consistent. Called
@@ -625,18 +631,23 @@ type Config struct {
// (Cache-Control header parsing is not yet implemented). Zero is replaced by
// a default (5 minutes) in applyDefaults when CIMDEnabled is true.
CIMDCacheFallbackTTL time.Duration
+
+ // InsecureAllowHTTP permits an http:// issuer URL for non-localhost hosts.
+ // Only set this for in-cluster Kubernetes deployments on a trusted network.
+ // Production deployments reachable outside the cluster MUST use https://.
+ InsecureAllowHTTP bool
}
// Validate checks that the Config is valid.
func (c *Config) Validate() error {
slog.Debug("validating authserver config", "issuer", c.Issuer)
- if err := validateIssuerURL(c.Issuer); err != nil {
+ if err := validateIssuerURL(c.Issuer, c.InsecureAllowHTTP); err != nil {
return fmt.Errorf("issuer: %w", err)
}
if c.AuthorizationEndpointBaseURL != "" {
- if err := validateIssuerURL(c.AuthorizationEndpointBaseURL); err != nil {
+ if err := validateIssuerURL(c.AuthorizationEndpointBaseURL, c.InsecureAllowHTTP); err != nil {
return fmt.Errorf("authorization_endpoint_base_url: %w", err)
}
}
@@ -928,7 +939,9 @@ func (c *Config) applyDefaults() error {
// validateIssuerURL validates that the issuer is a valid URL.
// Per OIDC Core Section 3.1.2.1 and RFC 8414 Section 2, the issuer
// MUST use the "https" scheme, except for localhost during development.
-func validateIssuerURL(issuer string) error {
+// When insecureAllowHTTP is true, http:// is also permitted for non-localhost
+// hosts (for in-cluster Kubernetes deployments on trusted networks).
+func validateIssuerURL(issuer string, insecureAllowHTTP bool) error {
if issuer == "" {
return fmt.Errorf("issuer is required")
}
@@ -954,12 +967,13 @@ func validateIssuerURL(issuer string) error {
return fmt.Errorf("must not contain fragment component")
}
- // HTTPS is required unless it's a loopback address (for development)
+ // HTTPS is required unless it's a loopback address (for development) or
+ // insecureAllowHTTP is explicitly set for trusted in-cluster deployments.
if parsed.Scheme != "https" {
if parsed.Scheme != "http" {
return fmt.Errorf("scheme must be https (or http for localhost)")
}
- if !networking.IsLocalhost(parsed.Host) {
+ if !networking.IsLocalhost(parsed.Host) && !insecureAllowHTTP {
return fmt.Errorf("http scheme is only allowed for localhost, use https for %s", parsed.Hostname())
}
}
diff --git a/pkg/authserver/config_test.go b/pkg/authserver/config_test.go
index 3c270771a4..ffb8e666e8 100644
--- a/pkg/authserver/config_test.go
+++ b/pkg/authserver/config_test.go
@@ -21,12 +21,13 @@ func TestValidateIssuerURL(t *testing.T) {
t.Parallel()
tests := []struct {
- name string
- issuer string
- wantErr bool
- errMsg string
+ name string
+ issuer string
+ insecureAllowHTTP bool
+ wantErr bool
+ errMsg string
}{
- // Valid
+ // Valid — strict mode (insecureAllowHTTP=false)
{name: "https", issuer: "https://example.com"},
{name: "https with port", issuer: "https://example.com:8443"},
{name: "https with path", issuer: "https://example.com/auth"},
@@ -35,7 +36,7 @@ func TestValidateIssuerURL(t *testing.T) {
{name: "http 127.0.0.1", issuer: "http://127.0.0.1:8080"},
{name: "http IPv6 loopback", issuer: "http://[::1]:8080"},
- // Invalid
+ // Invalid — strict mode
{name: "empty", issuer: "", wantErr: true, errMsg: "issuer is required"},
{name: "missing scheme", issuer: "example.com", wantErr: true, errMsg: "scheme is required"},
{name: "missing host", issuer: "https://", wantErr: true, errMsg: "host is required"},
@@ -44,12 +45,23 @@ func TestValidateIssuerURL(t *testing.T) {
{name: "http non-localhost", issuer: "http://example.com", wantErr: true, errMsg: "http scheme is only allowed for localhost"},
{name: "ftp scheme", issuer: "ftp://example.com", wantErr: true, errMsg: "scheme must be https"},
{name: "trailing slash", issuer: "https://example.com/", wantErr: true, errMsg: "must not have trailing slash"},
+
+ // Valid — insecureAllowHTTP=true permits http for non-localhost
+ {name: "http in-cluster insecure allowed", issuer: "http://vmcp-test.default.svc.cluster.local:4483", insecureAllowHTTP: true},
+ {name: "http non-localhost insecure allowed", issuer: "http://example.com", insecureAllowHTTP: true},
+ {name: "https still valid with insecure flag", issuer: "https://example.com", insecureAllowHTTP: true},
+ {name: "http localhost still valid with insecure flag", issuer: "http://localhost:8080", insecureAllowHTTP: true},
+
+ // Invalid — insecureAllowHTTP=true still enforces other rules
+ {name: "trailing slash insecure", issuer: "http://example.com/", insecureAllowHTTP: true, wantErr: true, errMsg: "must not have trailing slash"},
+ {name: "ftp scheme insecure", issuer: "ftp://example.com", insecureAllowHTTP: true, wantErr: true, errMsg: "scheme must be https"},
+ {name: "empty insecure", issuer: "", insecureAllowHTTP: true, wantErr: true, errMsg: "issuer is required"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
- err := validateIssuerURL(tt.issuer)
+ err := validateIssuerURL(tt.issuer, tt.insecureAllowHTTP)
assertError(t, err, tt.wantErr, tt.errMsg)
})
}
diff --git a/pkg/authserver/runner/embeddedauthserver.go b/pkg/authserver/runner/embeddedauthserver.go
index f5ee27a0a4..6d2145e1b0 100644
--- a/pkg/authserver/runner/embeddedauthserver.go
+++ b/pkg/authserver/runner/embeddedauthserver.go
@@ -228,6 +228,7 @@ func NewEmbeddedAuthServerWithStorage(
CIMDEnabled: cimdEnabled,
CIMDCacheMaxSize: cimdCacheMaxSize,
CIMDCacheFallbackTTL: cimdCacheFallbackTTL,
+ InsecureAllowHTTP: cfg.InsecureAllowHTTP,
}
// 7. Create the auth server. authserver.New also asserts the DCR
diff --git a/pkg/vmcp/auth/types/zz_generated.deepcopy.go b/pkg/vmcp/auth/types/zz_generated.deepcopy.go
index cff8a0cdc5..8a38c5b871 100644
--- a/pkg/vmcp/auth/types/zz_generated.deepcopy.go
+++ b/pkg/vmcp/auth/types/zz_generated.deepcopy.go
@@ -22,31 +22,6 @@ package types
import ()
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *OBOConfig) DeepCopyInto(out *OBOConfig) {
- *out = *in
- if in.Scopes != nil {
- in, out := &in.Scopes, &out.Scopes
- *out = make([]string, len(*in))
- copy(*out, *in)
- }
- if in.CacheSkewSeconds != nil {
- in, out := &in.CacheSkewSeconds, &out.CacheSkewSeconds
- *out = new(int32)
- **out = **in
- }
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OBOConfig.
-func (in *OBOConfig) DeepCopy() *OBOConfig {
- if in == nil {
- return nil
- }
- out := new(OBOConfig)
- in.DeepCopyInto(out)
- return out
-}
-
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AwsStsConfig) DeepCopyInto(out *AwsStsConfig) {
*out = *in
@@ -129,6 +104,31 @@ func (in *HeaderInjectionConfig) DeepCopy() *HeaderInjectionConfig {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *OBOConfig) DeepCopyInto(out *OBOConfig) {
+ *out = *in
+ if in.Scopes != nil {
+ in, out := &in.Scopes, &out.Scopes
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ if in.CacheSkewSeconds != nil {
+ in, out := &in.CacheSkewSeconds, &out.CacheSkewSeconds
+ *out = new(int32)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OBOConfig.
+func (in *OBOConfig) DeepCopy() *OBOConfig {
+ if in == nil {
+ return nil
+ }
+ out := new(OBOConfig)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RoleMapping) DeepCopyInto(out *RoleMapping) {
*out = *in