Skip to content

Commit dc11d30

Browse files
Add constants for auth strategy types (#2477)
* Add constants for auth strategy types Replace magic strings with constants in vMCP auth system to improve type safety and prevent typos. This makes the code more maintainable and refactoring easier. Strategy type constants (StrategyTypeHeaderInjection, StrategyTypeUnauthenticated) and metadata key constants (MetadataHeaderName, MetadataHeaderValue) are now defined in pkg/vmcp/auth/strategies/constants.go. Fixes #2466 * Replace remaining magic strings with constants in auth system Address review feedback by replacing magic strings in: - pkg/vmcp/auth/factory/outgoing.go: Use strategy type constants - pkg/vmcp/config/validator.go: Use strategy type and metadata constants Co-authored-by: Juan Antonio Osorio <JAORMX@users.noreply.github.com> --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Juan Antonio Osorio <JAORMX@users.noreply.github.com>
1 parent 2cc3bfd commit dc11d30

File tree

6 files changed

+47
-20
lines changed

6 files changed

+47
-20
lines changed

pkg/vmcp/auth/factory/outgoing.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func NewOutgoingAuthRegistry(_ context.Context, cfg *config.OutgoingAuthConfig)
7676
// registerUnauthenticatedStrategy registers the default unauthenticated strategy.
7777
func registerUnauthenticatedStrategy(registry auth.OutgoingAuthRegistry) error {
7878
unauthStrategy := strategies.NewUnauthenticatedStrategy()
79-
if err := registry.RegisterStrategy("unauthenticated", unauthStrategy); err != nil {
79+
if err := registry.RegisterStrategy(strategies.StrategyTypeUnauthenticated, unauthStrategy); err != nil {
8080
return fmt.Errorf("failed to register default unauthenticated strategy: %w", err)
8181
}
8282
return nil
@@ -120,7 +120,7 @@ func collectStrategyTypes(cfg *config.OutgoingAuthConfig) map[string]struct{} {
120120
func registerStrategies(registry auth.OutgoingAuthRegistry, strategyTypes map[string]struct{}) error {
121121
for strategyType := range strategyTypes {
122122
// Skip "unauthenticated" - already registered
123-
if strategyType == "unauthenticated" {
123+
if strategyType == strategies.StrategyTypeUnauthenticated {
124124
continue
125125
}
126126

@@ -156,9 +156,9 @@ func createStrategy(strategyType string) (auth.Strategy, error) {
156156
}
157157

158158
switch strategyType {
159-
case "header_injection":
159+
case strategies.StrategyTypeHeaderInjection:
160160
return strategies.NewHeaderInjectionStrategy(), nil
161-
case "unauthenticated":
161+
case strategies.StrategyTypeUnauthenticated:
162162
return strategies.NewUnauthenticatedStrategy(), nil
163163
default:
164164
return nil, fmt.Errorf("unknown strategy type: %s", strategyType)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Package strategies provides authentication strategy implementations for Virtual MCP Server.
2+
package strategies
3+
4+
// Strategy type identifiers used to identify authentication strategies.
5+
const (
6+
// StrategyTypeUnauthenticated identifies the unauthenticated strategy.
7+
// This strategy performs no authentication and is used when a backend
8+
// requires no authentication.
9+
StrategyTypeUnauthenticated = "unauthenticated"
10+
11+
// StrategyTypeHeaderInjection identifies the header injection strategy.
12+
// This strategy injects a static header value into request headers.
13+
StrategyTypeHeaderInjection = "header_injection"
14+
)
15+
16+
// Metadata key names used in strategy configurations.
17+
const (
18+
// MetadataHeaderName is the key for the HTTP header name in metadata.
19+
// Used by HeaderInjectionStrategy to identify which header to inject.
20+
MetadataHeaderName = "header_name"
21+
22+
// MetadataHeaderValue is the key for the HTTP header value in metadata.
23+
// Used by HeaderInjectionStrategy to identify the value to inject.
24+
MetadataHeaderValue = "header_value"
25+
)

pkg/vmcp/auth/strategies/header_injection.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func NewHeaderInjectionStrategy() *HeaderInjectionStrategy {
3838

3939
// Name returns the strategy identifier.
4040
func (*HeaderInjectionStrategy) Name() string {
41-
return "header_injection"
41+
return StrategyTypeHeaderInjection
4242
}
4343

4444
// Authenticate injects the header value from metadata into the request header.
@@ -56,12 +56,12 @@ func (*HeaderInjectionStrategy) Name() string {
5656
// - header_name is missing or empty
5757
// - header_value is missing or empty
5858
func (*HeaderInjectionStrategy) Authenticate(_ context.Context, req *http.Request, metadata map[string]any) error {
59-
headerName, ok := metadata["header_name"].(string)
59+
headerName, ok := metadata[MetadataHeaderName].(string)
6060
if !ok || headerName == "" {
6161
return fmt.Errorf("header_name required in metadata")
6262
}
6363

64-
headerValue, ok := metadata["header_value"].(string)
64+
headerValue, ok := metadata[MetadataHeaderValue].(string)
6565
if !ok || headerValue == "" {
6666
return fmt.Errorf("header_value required in metadata")
6767
}
@@ -89,12 +89,12 @@ func (*HeaderInjectionStrategy) Authenticate(_ context.Context, req *http.Reques
8989
// This validation is typically called during configuration parsing to fail fast
9090
// if the strategy is misconfigured.
9191
func (*HeaderInjectionStrategy) Validate(metadata map[string]any) error {
92-
headerName, ok := metadata["header_name"].(string)
92+
headerName, ok := metadata[MetadataHeaderName].(string)
9393
if !ok || headerName == "" {
9494
return fmt.Errorf("header_name required in metadata")
9595
}
9696

97-
headerValue, ok := metadata["header_value"].(string)
97+
headerValue, ok := metadata[MetadataHeaderValue].(string)
9898
if !ok || headerValue == "" {
9999
return fmt.Errorf("header_value required in metadata")
100100
}

pkg/vmcp/auth/strategies/unauthenticated.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func NewUnauthenticatedStrategy() *UnauthenticatedStrategy {
3838

3939
// Name returns the strategy identifier.
4040
func (*UnauthenticatedStrategy) Name() string {
41-
return "unauthenticated"
41+
return StrategyTypeUnauthenticated
4242
}
4343

4444
// Authenticate performs no authentication and returns immediately.

pkg/vmcp/config/validator.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"strings"
66

77
"github.com/stacklok/toolhive/pkg/vmcp"
8+
"github.com/stacklok/toolhive/pkg/vmcp/auth/strategies"
89
)
910

1011
// DefaultValidator implements comprehensive configuration validation.
@@ -169,7 +170,7 @@ func (*DefaultValidator) validateBackendAuthStrategy(_ string, strategy *Backend
169170
}
170171

171172
validTypes := []string{
172-
"unauthenticated", "header_injection",
173+
strategies.StrategyTypeUnauthenticated, strategies.StrategyTypeHeaderInjection,
173174
// TODO: Add more as strategies are implemented:
174175
// "pass_through", "token_exchange", "client_credentials",
175176
// "service_account", "oauth_proxy",
@@ -195,13 +196,13 @@ func (*DefaultValidator) validateBackendAuthStrategy(_ string, strategy *Backend
195196
return fmt.Errorf("service_account requires metadata field: credentials_env")
196197
}
197198

198-
case "header_injection":
199+
case strategies.StrategyTypeHeaderInjection:
199200
// Header injection requires header name and value
200-
if _, ok := strategy.Metadata["header_name"]; !ok {
201-
return fmt.Errorf("header_injection requires metadata field: header_name")
201+
if _, ok := strategy.Metadata[strategies.MetadataHeaderName]; !ok {
202+
return fmt.Errorf("header_injection requires metadata field: %s", strategies.MetadataHeaderName)
202203
}
203-
if _, ok := strategy.Metadata["header_value"]; !ok {
204-
return fmt.Errorf("header_injection requires metadata field: header_value")
204+
if _, ok := strategy.Metadata[strategies.MetadataHeaderValue]; !ok {
205+
return fmt.Errorf("header_injection requires metadata field: %s", strategies.MetadataHeaderValue)
205206
}
206207
}
207208

pkg/vmcp/config/yaml_loader.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"gopkg.in/yaml.v3"
99

1010
"github.com/stacklok/toolhive/pkg/vmcp"
11+
"github.com/stacklok/toolhive/pkg/vmcp/auth/strategies"
1112
)
1213

1314
// YAMLLoader loads configuration from a YAML file.
@@ -310,17 +311,17 @@ func (*YAMLLoader) transformBackendAuthStrategy(raw *rawBackendAuthStrategy) (*B
310311
}
311312

312313
switch raw.Type {
313-
case "header_injection":
314+
case strategies.StrategyTypeHeaderInjection:
314315
if raw.HeaderInjection == nil {
315316
return nil, fmt.Errorf("header_injection configuration is required")
316317
}
317318

318319
strategy.Metadata = map[string]any{
319-
"header_name": raw.HeaderInjection.HeaderName,
320-
"header_value": raw.HeaderInjection.HeaderValue,
320+
strategies.MetadataHeaderName: raw.HeaderInjection.HeaderName,
321+
strategies.MetadataHeaderValue: raw.HeaderInjection.HeaderValue,
321322
}
322323

323-
case "unauthenticated":
324+
case strategies.StrategyTypeUnauthenticated:
324325
// No metadata required for unauthenticated strategy
325326

326327
case "token_exchange":

0 commit comments

Comments
 (0)