oauth

package
v1.3.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 19, 2025 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package oauth provides OAuth discovery and authorization URL handling for MCP servers.

OAuth Discovery Implementation Gap in MCP Ecosystem:

While the MCP Specification technically describes using /.well-known/oauth-protected-resource for discovery (RFC 9728, see https://www.speakeasy.com/mcp/building-servers/state-of-oauth-in-mcp), most current MCP servers actually look for /.well-known/oauth-authorization-server directly at the MCP server's domain (RFC 8414). This is how the OAuth dance works in practice today. The gap between specification and implementation is prevalent in the MCP ecosystem.

The mcp-go oauthHandler.GetAuthorizationURL doesn't handle this correctly and follows only the strict RFC 9728 path, which fails with most real-world MCP servers. While waiting for this PR https://github.com/mark3labs/mcp-go/pull/581 to fix the upstream discovery logic, we need a workaround that implements the correct discovery sequence that actually works with existing MCP servers.

This implementation bridges the gap by:

  1. First trying the RFC 9728 approach (/.well-known/oauth-protected-resource)
  2. Falling back to the RFC 8414 approach (/.well-known/oauth-authorization-server) which is what most MCP servers actually implement
  3. Providing sensible defaults as a final fallback

This ensures compatibility with both specification-compliant servers and the majority of existing MCP server implementations that follow the more common OAuth patterns.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecodeSessionIDFromState

func DecodeSessionIDFromState(state string) (string, error)

DecodeSessionIDFromState extracts the session ID from an OAuth state parameter.

This function is used by OAuth callback handlers to decode session IDs. When the OAuth provider redirects back to our callback endpoint, they include the state parameter we originally sent. This function reverses the encoding done by GenerateStateWithSessionID to extract the session ID, allowing us to route the authorization code back to the correct agent session that initiated the OAuth flow.

This is the critical piece that bridges the browser-based OAuth callback back to the specific runtime session that needs the authorization.

func GenerateStateWithSessionID

func GenerateStateWithSessionID(sessionID string) (string, error)

GenerateStateWithSessionID generates an OAuth state parameter that encodes the session ID.

OAuth State Parameter Design:

When an agent needs OAuth authorization, the flow works like this: 1. Agent runtime detects OAuth is needed and pauses execution 2. We generate authorization URL with state parameter containing the session ID 3. User's browser is redirected to the OAuth provider for authorization 4. OAuth provider redirects back to our callback URL with the authorization code AND the state 5. Our callback handler receives the state, extracts the session ID from it 6. We can then resume the correct agent session with the authorization code

Without encoding session ID in state, we couldn't match the OAuth callback to the specific agent session that requested authorization, especially in multi-session scenarios.

The state parameter combines: - Session ID: To route the callback back to the correct session - Random bytes: For CSRF protection (traditional OAuth security requirement)

func GetAuthorizationURL

func GetAuthorizationURL(ctx context.Context, oauthHandler *transport.OAuthHandler, state, codeChallenge string) (string, error)

GetAuthorizationURL gets the OAuth authorization URL using the correct discovery logic.

This function works around the mcp-go library's incomplete OAuth discovery implementation. The upstream GetAuthorizationURL method fails with most MCP servers because it only tries the RFC 9728 discovery path, while most servers implement RFC 8414 directly.

Our approach: 1. Use our custom discovery logic to find the correct authorization endpoint 2. Leverage the existing mcp-go logic for parameter construction (client_id, scope, etc.) 3. Combine the correct endpoint with the correct parameters

This ensures we get properly formatted OAuth URLs that work with real MCP servers.

func MayBeOAuthError added in v1.3.6

func MayBeOAuthError(err error) (error, bool)

MayBeOAuthError checks if the given error is an OAuth authorization error and wraps it with server information if available.

This function examines error chains to identify OAuth authorization errors wrapped within agent ToolSetError (or any other future error that might contain a possible OAuth error). When found, it extracts server information and returns a properly wrapped AuthorizationRequiredError.

Returns: - wrapped error and true if this is an OAuth authorization error - original error and false otherwise

func OpenBrowser

func OpenBrowser(url string) error

OpenBrowser opens the default browser to the specified URL

Types

type AuthServerMetadata

type AuthServerMetadata struct {
	Issuer                            string   `json:"issuer"`
	AuthorizationEndpoint             string   `json:"authorization_endpoint"`
	TokenEndpoint                     string   `json:"token_endpoint"`
	RegistrationEndpoint              string   `json:"registration_endpoint,omitempty"`
	JwksURI                           string   `json:"jwks_uri,omitempty"`
	ScopesSupported                   []string `json:"scopes_supported,omitempty"`
	ResponseTypesSupported            []string `json:"response_types_supported"`
	GrantTypesSupported               []string `json:"grant_types_supported,omitempty"`
	TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`
}

AuthServerMetadata represents the OAuth 2.0 Authorization Server Metadata This is a copy from the mcp-go library to avoid importing internal types

type AuthorizationRequiredError

type AuthorizationRequiredError struct {
	Err        error
	ServerURL  string
	ServerType string
}

AuthorizationRequiredError wraps an OAuth authorization error with server information

func (*AuthorizationRequiredError) Error

func (*AuthorizationRequiredError) Unwrap

func (e *AuthorizationRequiredError) Unwrap() error

type Manager

type Manager interface {
	// HandleAuthorizationFlow handles a single OAuth authorization flow
	HandleAuthorizationFlow(ctx context.Context, sessionID string, oauthErr *AuthorizationRequiredError) error

	// StartAuthorizationFlow signals that user confirmation has been given to start the OAuth flow
	StartAuthorizationFlow(confirmation bool)

	// SendAuthorizationCode sends the OAuth authorization code after user has completed the OAuth flow
	SendAuthorizationCode(code string) error
}

Manager defines the contract for OAuth flow management

func NewManager

func NewManager(emitAuthRequired func(serverURL, serverType, status string)) Manager

NewManager creates a new OAuth manager

type ProtectedResource

type ProtectedResource struct {
	AuthorizationServers []string `json:"authorization_servers"`
	Resource             string   `json:"resource"`
	ResourceName         string   `json:"resource_name,omitempty"`
}

ProtectedResource represents the response from /.well-known/oauth-protected-resource

type ServerInfoProvider

type ServerInfoProvider interface {
	GetServerInfo() (serverURL, serverType string)
}

ServerInfoProvider interface for toolsets that can provide server information

type StateData

type StateData struct {
	SessionID string `json:"session_id"` // The session ID that initiated the OAuth flow
	Random    string `json:"random"`     // Random component for CSRF protection
}

StateData represents the data encoded in the OAuth state parameter.

In OAuth flows, the state parameter serves dual purposes:

  1. Security: CSRF protection by including random data
  2. Session tracking: When the browser returns from authorization, we need to know which session triggered the OAuth flow to route the callback correctly.

Since OAuth authorization happens in a browser (different context from our runtime), we embed the session ID in the state parameter so we can retrieve it when the authorization server redirects back to us with the authorization code.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL