Skip to content

Commit bb4421a

Browse files
committed
properly set token exchange
1 parent 400cf7b commit bb4421a

File tree

5 files changed

+28
-18
lines changed

5 files changed

+28
-18
lines changed

cmd/thv-operator/api/v1alpha1/mcpexternalauthconfig_types.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@ type TokenExchangeConfig struct {
5353
Scopes []string `json:"scopes,omitempty"`
5454

5555
// SubjectTokenType is the type of the incoming subject token.
56-
// Valid values: "urn:ietf:params:oauth:token-type:jwt", "urn:ietf:params:oauth:token-type:access_token", "urn:ietf:params:oauth:token-type:id_token"
57-
// If not specified, defaults to "urn:ietf:params:oauth:token-type:access_token"
58-
// For OIDC/JWT tokens (like Okta), use "urn:ietf:params:oauth:token-type:jwt"
56+
// Accepts short forms: "access_token" (default), "id_token", "jwt"
57+
// Or full URNs: "urn:ietf:params:oauth:token-type:access_token",
58+
// "urn:ietf:params:oauth:token-type:id_token",
59+
// "urn:ietf:params:oauth:token-type:jwt"
60+
// For Google Workload Identity Federation with OIDC providers (like Okta), use "id_token"
61+
// +kubebuilder:validation:Pattern=`^(access_token|id_token|jwt|urn:ietf:params:oauth:token-type:(access_token|id_token|jwt))?$`
5962
// +optional
6063
SubjectTokenType string `json:"subjectTokenType,omitempty"`
6164

cmd/thv-operator/pkg/controllerutil/tokenexchange.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ func AddExternalAuthConfigOptions(
143143
headerStrategy = "custom"
144144
}
145145

146+
// Normalize SubjectTokenType to full URN (accepts both short forms and full URNs)
147+
normalizedTokenType, err := tokenexchange.NormalizeTokenType(tokenExchangeSpec.SubjectTokenType)
148+
if err != nil {
149+
return fmt.Errorf("invalid subject token type: %w", err)
150+
}
151+
146152
// Build token exchange configuration
147153
// Client secret is provided via TOOLHIVE_TOKEN_EXCHANGE_CLIENT_SECRET environment variable
148154
// to avoid embedding plaintext secrets in the ConfigMap
@@ -151,7 +157,7 @@ func AddExternalAuthConfigOptions(
151157
ClientID: tokenExchangeSpec.ClientID,
152158
Audience: tokenExchangeSpec.Audience,
153159
Scopes: tokenExchangeSpec.Scopes,
154-
SubjectTokenType: tokenExchangeSpec.SubjectTokenType,
160+
SubjectTokenType: normalizedTokenType,
155161
HeaderStrategy: headerStrategy,
156162
ExternalTokenHeaderName: tokenExchangeSpec.ExternalTokenHeaderName,
157163
}

deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,12 @@ spec:
100100
subjectTokenType:
101101
description: |-
102102
SubjectTokenType is the type of the incoming subject token.
103-
Valid values: "urn:ietf:params:oauth:token-type:jwt", "urn:ietf:params:oauth:token-type:access_token", "urn:ietf:params:oauth:token-type:id_token"
104-
If not specified, defaults to "urn:ietf:params:oauth:token-type:access_token"
105-
For OIDC/JWT tokens (like Okta), use "urn:ietf:params:oauth:token-type:jwt"
103+
Accepts short forms: "access_token" (default), "id_token", "jwt"
104+
Or full URNs: "urn:ietf:params:oauth:token-type:access_token",
105+
"urn:ietf:params:oauth:token-type:id_token",
106+
"urn:ietf:params:oauth:token-type:jwt"
107+
For Google Workload Identity Federation with OIDC providers (like Okta), use "id_token"
108+
pattern: ^(access_token|id_token|jwt|urn:ietf:params:oauth:token-type:(access_token|id_token|jwt))?$
106109
type: string
107110
tokenUrl:
108111
description: TokenURL is the OAuth 2.0 token endpoint URL for

pkg/auth/tokenexchange/exchange.go

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -243,19 +243,14 @@ func (c *ExchangeConfig) Validate() error {
243243
// don't require client credentials and rely on the trust relationship
244244
// configured in the identity provider (e.g., Workload Identity Federation)
245245

246-
// Validate SubjectTokenType if provided
246+
// Validate and normalize SubjectTokenType if provided
247247
if c.SubjectTokenType != "" {
248-
validTypes := []string{tokenTypeAccessToken, tokenTypeIDToken, tokenTypeJWT}
249-
isValid := false
250-
for _, validType := range validTypes {
251-
if c.SubjectTokenType == validType {
252-
isValid = true
253-
break
254-
}
255-
}
256-
if !isValid {
257-
return fmt.Errorf("invalid SubjectTokenType %q: must be one of %v", c.SubjectTokenType, validTypes)
248+
normalized, err := NormalizeTokenType(c.SubjectTokenType)
249+
if err != nil {
250+
return fmt.Errorf("invalid SubjectTokenType: %w", err)
258251
}
252+
// Update the config with the normalized value
253+
c.SubjectTokenType = normalized
259254
}
260255

261256
// Validate URL format

pkg/auth/tokenexchange/middleware.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,9 @@ func createTokenExchangeMiddleware(
345345
return
346346
}
347347

348+
// Token exchange succeeded
349+
logger.Debug("Token exchange successful")
350+
348351
// Inject the exchanged token into the request using the pre-selected strategy
349352
if err := injectToken(r, exchangedToken.AccessToken); err != nil {
350353
logger.Warnf("Failed to inject token: %v", err)

0 commit comments

Comments
 (0)