Skip to content

Commit fc259c3

Browse files
authored
Merge branch 'main' into feat/disable-workload-rbac
2 parents 08bfe6d + bc9b534 commit fc259c3

32 files changed

Lines changed: 1083 additions & 46 deletions

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,40 @@ type OAuth2UpstreamConfig struct {
331331
// Scopes are the OAuth scopes to request from the upstream IDP.
332332
// +optional
333333
Scopes []string `json:"scopes,omitempty"`
334+
335+
// TokenResponseMapping configures custom field extraction from non-standard token responses.
336+
// Some OAuth providers (e.g., GovSlack) nest token fields under non-standard paths
337+
// instead of returning them at the top level. When set, ToolHive performs the token
338+
// exchange HTTP call directly and extracts fields using the configured dot-notation paths.
339+
// If nil, standard OAuth 2.0 token response parsing is used.
340+
// +optional
341+
TokenResponseMapping *TokenResponseMapping `json:"tokenResponseMapping,omitempty"`
342+
}
343+
344+
// TokenResponseMapping maps non-standard token response fields to standard OAuth 2.0 fields
345+
// using dot-notation JSON paths. This supports upstream providers like GovSlack that nest
346+
// the access token under paths like "authed_user.access_token".
347+
type TokenResponseMapping struct {
348+
// AccessTokenPath is the dot-notation path to the access token in the response.
349+
// Example: "authed_user.access_token"
350+
// +kubebuilder:validation:Required
351+
// +kubebuilder:validation:MinLength=1
352+
AccessTokenPath string `json:"accessTokenPath"`
353+
354+
// ScopePath is the dot-notation path to the scope string in the response.
355+
// If not specified, defaults to "scope".
356+
// +optional
357+
ScopePath string `json:"scopePath,omitempty"`
358+
359+
// RefreshTokenPath is the dot-notation path to the refresh token in the response.
360+
// If not specified, defaults to "refresh_token".
361+
// +optional
362+
RefreshTokenPath string `json:"refreshTokenPath,omitempty"`
363+
364+
// ExpiresInPath is the dot-notation path to the expires_in value (in seconds).
365+
// If not specified, defaults to "expires_in".
366+
// +optional
367+
ExpiresInPath string `json:"expiresInPath,omitempty"`
334368
}
335369

336370
// UserInfoConfig contains configuration for fetching user information from an upstream provider.

cmd/thv-operator/api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,15 @@ func buildUpstreamRunConfig(
519519
if provider.OAuth2Config.UserInfo != nil {
520520
config.OAuth2Config.UserInfo = buildUserInfoRunConfig(provider.OAuth2Config.UserInfo)
521521
}
522+
if provider.OAuth2Config.TokenResponseMapping != nil {
523+
m := provider.OAuth2Config.TokenResponseMapping
524+
config.OAuth2Config.TokenResponseMapping = &authserver.TokenResponseMappingRunConfig{
525+
AccessTokenPath: m.AccessTokenPath,
526+
ScopePath: m.ScopePath,
527+
RefreshTokenPath: m.RefreshTokenPath,
528+
ExpiresInPath: m.ExpiresInPath,
529+
}
530+
}
522531
}
523532
}
524533

cmd/thv-proxyrunner/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ import (
1515
)
1616

1717
func main() {
18+
// Bind TOOLHIVE_DEBUG env var early, before logger initialization.
19+
// This must happen before viper.GetBool("debug") so the env var
20+
// is available when configuring the log level.
21+
if err := viper.BindEnv("debug", "TOOLHIVE_DEBUG"); err != nil {
22+
slog.Error("failed to bind TOOLHIVE_DEBUG env var", "error", err)
23+
}
24+
1825
// Initialize the logger
1926
var opts []logging.Option
2027
if viper.GetBool("debug") {

cmd/thv/main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@ import (
2323
)
2424

2525
func main() {
26+
// Bind TOOLHIVE_DEBUG env var early, before logger initialization.
27+
// This must happen before viper.GetBool("debug") so the env var
28+
// is available when configuring the log level.
29+
if err := viper.BindEnv("debug", "TOOLHIVE_DEBUG"); err != nil {
30+
slog.Error("failed to bind TOOLHIVE_DEBUG env var", "error", err)
31+
}
32+
2633
// Initialize the logger
2734
var opts []logging.Option
2835
if viper.GetBool("debug") {

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,38 @@ spec:
441441
token endpoint.
442442
pattern: ^https?://.*$
443443
type: string
444+
tokenResponseMapping:
445+
description: |-
446+
TokenResponseMapping configures custom field extraction from non-standard token responses.
447+
Some OAuth providers (e.g., GovSlack) nest token fields under non-standard paths
448+
instead of returning them at the top level. When set, ToolHive performs the token
449+
exchange HTTP call directly and extracts fields using the configured dot-notation paths.
450+
If nil, standard OAuth 2.0 token response parsing is used.
451+
properties:
452+
accessTokenPath:
453+
description: |-
454+
AccessTokenPath is the dot-notation path to the access token in the response.
455+
Example: "authed_user.access_token"
456+
minLength: 1
457+
type: string
458+
expiresInPath:
459+
description: |-
460+
ExpiresInPath is the dot-notation path to the expires_in value (in seconds).
461+
If not specified, defaults to "expires_in".
462+
type: string
463+
refreshTokenPath:
464+
description: |-
465+
RefreshTokenPath is the dot-notation path to the refresh token in the response.
466+
If not specified, defaults to "refresh_token".
467+
type: string
468+
scopePath:
469+
description: |-
470+
ScopePath is the dot-notation path to the scope string in the response.
471+
If not specified, defaults to "scope".
472+
type: string
473+
required:
474+
- accessTokenPath
475+
type: object
444476
userInfo:
445477
description: |-
446478
UserInfo contains configuration for fetching user information from the upstream provider.

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,38 @@ spec:
444444
token endpoint.
445445
pattern: ^https?://.*$
446446
type: string
447+
tokenResponseMapping:
448+
description: |-
449+
TokenResponseMapping configures custom field extraction from non-standard token responses.
450+
Some OAuth providers (e.g., GovSlack) nest token fields under non-standard paths
451+
instead of returning them at the top level. When set, ToolHive performs the token
452+
exchange HTTP call directly and extracts fields using the configured dot-notation paths.
453+
If nil, standard OAuth 2.0 token response parsing is used.
454+
properties:
455+
accessTokenPath:
456+
description: |-
457+
AccessTokenPath is the dot-notation path to the access token in the response.
458+
Example: "authed_user.access_token"
459+
minLength: 1
460+
type: string
461+
expiresInPath:
462+
description: |-
463+
ExpiresInPath is the dot-notation path to the expires_in value (in seconds).
464+
If not specified, defaults to "expires_in".
465+
type: string
466+
refreshTokenPath:
467+
description: |-
468+
RefreshTokenPath is the dot-notation path to the refresh token in the response.
469+
If not specified, defaults to "refresh_token".
470+
type: string
471+
scopePath:
472+
description: |-
473+
ScopePath is the dot-notation path to the scope string in the response.
474+
If not specified, defaults to "scope".
475+
type: string
476+
required:
477+
- accessTokenPath
478+
type: object
447479
userInfo:
448480
description: |-
449481
UserInfo contains configuration for fetching user information from the upstream provider.

docs/operator/crd-api.md

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/server/docs.go

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/server/swagger.json

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)