diff --git a/cmd/thv/app/run_flags.go b/cmd/thv/app/run_flags.go index 31f703ef3c..f9fbb55e46 100644 --- a/cmd/thv/app/run_flags.go +++ b/cmd/thv/app/run_flags.go @@ -466,7 +466,8 @@ func setupOIDCConfiguration(cmd *cobra.Command, runFlags *RunFlags) (*auth.Token } return createOIDCConfig(oidcIssuer, oidcAudience, oidcJwksURL, oidcIntrospectionURL, - oidcClientID, oidcClientSecret, runFlags.ResourceURL, runFlags.JWKSAllowPrivateIP, oidcScopes), nil + oidcClientID, oidcClientSecret, runFlags.ResourceURL, runFlags.ThvCABundle, runFlags.JWKSAuthTokenFile, + runFlags.JWKSAllowPrivateIP, runFlags.InsecureAllowHTTP, oidcScopes), nil } // resolveMetricsOnTransportPort turns the bound bool into a tri-state. Only an @@ -1207,19 +1208,23 @@ func getTelemetryFromFlags(cmd *cobra.Command, config *cfg.Config, otelEndpoint // createOIDCConfig creates an OIDC configuration if any OIDC parameters are provided func createOIDCConfig(oidcIssuer, oidcAudience, oidcJwksURL, oidcIntrospectionURL, - oidcClientID, oidcClientSecret, resourceURL string, allowPrivateIP bool, scopes []string) *auth.TokenValidatorConfig { + oidcClientID, oidcClientSecret, resourceURL, caCertPath, authTokenFile string, + allowPrivateIP, insecureAllowHTTP bool, scopes []string) *auth.TokenValidatorConfig { if oidcIssuer != "" || oidcAudience != "" || oidcJwksURL != "" || oidcIntrospectionURL != "" || oidcClientID != "" || oidcClientSecret != "" || resourceURL != "" { return &auth.TokenValidatorConfig{ - Issuer: oidcIssuer, - Audience: oidcAudience, - JWKSURL: oidcJwksURL, - IntrospectionURL: oidcIntrospectionURL, - ClientID: oidcClientID, - ClientSecret: oidcClientSecret, - ResourceURL: resourceURL, - AllowPrivateIP: allowPrivateIP, - Scopes: scopes, + Issuer: oidcIssuer, + Audience: oidcAudience, + JWKSURL: oidcJwksURL, + IntrospectionURL: oidcIntrospectionURL, + ClientID: oidcClientID, + ClientSecret: oidcClientSecret, + ResourceURL: resourceURL, + CACertPath: caCertPath, + AuthTokenFile: authTokenFile, + AllowPrivateIP: allowPrivateIP, + InsecureAllowHTTP: insecureAllowHTTP, + Scopes: scopes, } } return nil diff --git a/cmd/thv/app/run_flags_test.go b/cmd/thv/app/run_flags_test.go index 4ffe0cc51f..228354f8d5 100644 --- a/cmd/thv/app/run_flags_test.go +++ b/cmd/thv/app/run_flags_test.go @@ -4,6 +4,7 @@ package app import ( + "encoding/json" "log/slog" "os" "path/filepath" @@ -17,6 +18,7 @@ import ( "github.com/stacklok/toolhive-core/logging" regtypes "github.com/stacklok/toolhive-core/registry/types" + "github.com/stacklok/toolhive/pkg/auth" "github.com/stacklok/toolhive/pkg/config" "github.com/stacklok/toolhive/pkg/runner" "github.com/stacklok/toolhive/pkg/webhook" @@ -916,3 +918,57 @@ func TestBuildRunnerConfig_MaxRequestBodySizeWiring(t *testing.T) { }) } } + +// TestSetupOIDCConfiguration_MiddlewareFlagWiring guards the JWKS/OIDC +// transport flags (--thv-ca-bundle, --jwks-auth-token-file, +// --jwks-allow-private-ip, --oidc-insecure-allow-http) from being dropped on +// the way into the auth middleware config. The runtime validator is built +// from the middleware config, not the deprecated top-level OIDCConfig, so a +// value that only reaches the latter is silently ignored. See #6522. +func TestSetupOIDCConfiguration_MiddlewareFlagWiring(t *testing.T) { + t.Parallel() + + runFlags := &RunFlags{} + cmd := &cobra.Command{} + AddRunFlags(cmd, runFlags) + AddOIDCFlags(cmd) + + for flag, value := range map[string]string{ + "permission-profile": "none", + "transport": "stdio", + "oidc-issuer": "http://localhost:8099", + "oidc-audience": "test", + "thv-ca-bundle": "/path/to/ca.pem", + "jwks-auth-token-file": "/path/to/token", + "jwks-allow-private-ip": "true", + "oidc-insecure-allow-http": "true", + } { + require.NoError(t, cmd.Flags().Set(flag, value)) + } + + oidcConfig, err := setupOIDCConfiguration(cmd, runFlags) + require.NoError(t, err) + require.NotNil(t, oidcConfig) + + cfg, err := buildRunnerConfig( + t.Context(), runFlags, nil, false, "127.0.0.1", nil, "test:latest", nil, + map[string]string{}, &runner.DetachedEnvVarValidator{}, oidcConfig, nil, &config.Config{}, + ) + require.NoError(t, err) + + var authParams auth.MiddlewareParams + for _, mw := range cfg.MiddlewareConfigs { + if mw.Type == auth.MiddlewareType { + require.NoError(t, json.Unmarshal(mw.Parameters, &authParams)) + break + } + } + got := authParams.OIDCConfig + require.NotNil(t, got, "auth middleware must be present") + + assert.Equal(t, "http://localhost:8099", got.Issuer) + assert.Equal(t, "/path/to/ca.pem", got.CACertPath) + assert.Equal(t, "/path/to/token", got.AuthTokenFile) + assert.True(t, got.AllowPrivateIP) + assert.True(t, got.InsecureAllowHTTP) +}