Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions internal/config/auth_broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ type AuthBrokerConfig struct {
Mode string `json:"mode" mapstructure:"mode"`
// TokenEndpoint is the IdP token endpoint used to mint the upstream credential.
TokenEndpoint string `json:"token_endpoint" mapstructure:"token_endpoint"`
// AuthorizationEndpoint is the upstream AS authorize URL the user is
// redirected to for consent. Required for the oauth_connect mode (Path B,
// spec 074 FR-011); unused by token_exchange/entra_obo.
AuthorizationEndpoint string `json:"authorization_endpoint,omitempty" mapstructure:"authorization_endpoint"`
// Resource is the RFC 8707 audience the resulting token is scoped to.
Resource string `json:"resource,omitempty" mapstructure:"resource"`
// Scopes requested for the upstream credential.
Expand Down Expand Up @@ -77,6 +81,11 @@ func (a *AuthBrokerConfig) Validate() error {
if a.TokenEndpoint == "" {
return fmt.Errorf("auth_broker.token_endpoint is required")
}
// The connect flow (Path B) additionally needs the upstream authorize URL
// to redirect the user to for consent.
if a.Mode == AuthBrokerModeOAuthConnect && a.AuthorizationEndpoint == "" {
return fmt.Errorf("auth_broker.authorization_endpoint is required for mode %q", AuthBrokerModeOAuthConnect)
}
return nil
}

Expand Down
36 changes: 35 additions & 1 deletion internal/config/auth_broker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,35 @@ func TestAuthBrokerConfig_ApplyDefaults(t *testing.T) {
})
}

func TestAuthBroker_OAuthConnectRequiresAuthorizationEndpoint(t *testing.T) {
t.Run("missing authorization_endpoint is rejected", func(t *testing.T) {
b := &AuthBrokerConfig{
Mode: AuthBrokerModeOAuthConnect,
TokenEndpoint: "https://idp/token",
}
err := b.Validate()
require.Error(t, err)
assert.Contains(t, err.Error(), "authorization_endpoint")
})

t.Run("authorization_endpoint present is accepted", func(t *testing.T) {
b := &AuthBrokerConfig{
Mode: AuthBrokerModeOAuthConnect,
AuthorizationEndpoint: "https://idp/authorize",
TokenEndpoint: "https://idp/token",
}
require.NoError(t, b.Validate())
})

t.Run("authorization_endpoint is not required for token_exchange", func(t *testing.T) {
b := &AuthBrokerConfig{
Mode: AuthBrokerModeTokenExchange,
TokenEndpoint: "https://idp/token",
}
require.NoError(t, b.Validate())
})
}

func TestAuthBroker_ValidHTTPBroker(t *testing.T) {
server := &ServerConfig{
Name: "github",
Expand Down Expand Up @@ -134,9 +163,14 @@ func TestAuthBroker_MissingRequiredFields(t *testing.T) {
func TestAuthBroker_AllValidModes(t *testing.T) {
for _, mode := range []string{AuthBrokerModeTokenExchange, AuthBrokerModeEntraOBO, AuthBrokerModeOAuthConnect} {
t.Run(mode, func(t *testing.T) {
broker := &AuthBrokerConfig{Mode: mode, TokenEndpoint: "https://idp/token"}
// The connect flow additionally requires the authorize endpoint.
if mode == AuthBrokerModeOAuthConnect {
broker.AuthorizationEndpoint = "https://idp/authorize"
}
cfg := baseValidConfig(&ServerConfig{
Name: "s", Protocol: "streamable-http", URL: "https://x/mcp",
AuthBroker: &AuthBrokerConfig{Mode: mode, TokenEndpoint: "https://idp/token"},
AuthBroker: broker,
})
require.NoError(t, cfg.Validate())
})
Expand Down
Loading
Loading