Documentation
¶
Overview ¶
Package oauthflow implements the client side of the OAuth 2.0 authorization-code flow used to authenticate against remote MCP servers: PKCE and state generation, the loopback callback server, dynamic client registration, and code/refresh token exchange.
It is deliberately separate from pkg/tools/mcp so that packages which only drive the flow (e.g. pkg/runtime's remote-runtime login) don't link the whole MCP toolset - and everything it drags in - into embedders' binaries.
Index ¶
- func BuildAuthorizationURL(authEndpoint, clientID, redirectURI, state, codeChallenge, resourceURL string, ...) string
- func DefaultHTTPClient() *http.Client
- func GeneratePKCEVerifier() string
- func GenerateState() (string, error)
- func HTTPClientForAllowPrivateIPs(allowPrivateIPs bool) *http.Client
- func RegisterClient(ctx context.Context, authMetadata *AuthorizationServerMetadata, ...) (clientID, clientSecret string, err error)
- func RegisterClientWithClient(ctx context.Context, client *http.Client, ...) (clientID, clientSecret string, err error)
- func RequestAuthorizationCode(ctx context.Context, authURL string, callbackServer *CallbackServer, ...) (string, string, error)
- func SetHTTPClientForTesting(c *http.Client) (restore func())
- type AuthorizationServerMetadata
- type CallbackServer
- func (cs *CallbackServer) GetRedirectURI() string
- func (cs *CallbackServer) Port() int
- func (cs *CallbackServer) ResolveRedirectURI(callbackRedirectURL string) string
- func (cs *CallbackServer) SetExpectedState(state string)
- func (cs *CallbackServer) Shutdown(ctx context.Context) error
- func (cs *CallbackServer) Start() error
- func (cs *CallbackServer) WaitForCallback(ctx context.Context) (code, state string, err error)
- type OAuthToken
- func ExchangeCodeForToken(ctx context.Context, ...) (*OAuthToken, error)
- func ExchangeCodeForTokenWithClient(ctx context.Context, client *http.Client, ...) (*OAuthToken, error)
- func ExchangeCodeForTokenWithResource(ctx context.Context, ...) (*OAuthToken, error)
- func RefreshAccessToken(ctx context.Context, ...) (*OAuthToken, error)
- func RefreshAccessTokenWithClient(ctx context.Context, client *http.Client, ...) (*OAuthToken, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BuildAuthorizationURL ¶
func BuildAuthorizationURL(authEndpoint, clientID, redirectURI, state, codeChallenge, resourceURL string, scopes []string) string
BuildAuthorizationURL builds the OAuth authorization URL with PKCE. It merges the OAuth parameters into any query string already present on authEndpoint (e.g. Grafana Cloud's grafana_url= selector) so the result always has exactly one '?' even when the endpoint carries pre-existing params.
func DefaultHTTPClient ¶
DefaultHTTPClient returns the shared SSRF-safe client used for OAuth requests that don't carry per-server header or private-IP policies.
func GeneratePKCEVerifier ¶
func GeneratePKCEVerifier() string
GeneratePKCEVerifier generates a PKCE code verifier using oauth2 library
func GenerateState ¶
GenerateState generates a random state parameter for OAuth CSRF protection
func HTTPClientForAllowPrivateIPs ¶
HTTPClientForAllowPrivateIPs returns the shared SSRF-safe client, or a variant that may reach private IPs when allowPrivateIPs is set (used by configurations that explicitly opt in to talking to a server on a private network).
func RegisterClient ¶
func RegisterClient(ctx context.Context, authMetadata *AuthorizationServerMetadata, redirectURI string, scopes []string) (clientID, clientSecret string, err error)
RegisterClient performs dynamic client registration
func RegisterClientWithClient ¶
func RegisterClientWithClient(ctx context.Context, client *http.Client, authMetadata *AuthorizationServerMetadata, redirectURI string, scopes []string) (clientID, clientSecret string, err error)
RegisterClientWithClient is RegisterClient with an explicit *http.Client.
func RequestAuthorizationCode ¶
func RequestAuthorizationCode(ctx context.Context, authURL string, callbackServer *CallbackServer, expectedState string) (string, string, error)
RequestAuthorizationCode requests the user to open the authorization URL and waits for the callback
func SetHTTPClientForTesting ¶
SetHTTPClientForTesting replaces the shared OAuth client and returns a function restoring the previous one. It exists only so tests can hit httptest servers, which bind to 127.0.0.1 — an address the SSRF-safe client refuses by design. Never call it in production code.
Types ¶
type AuthorizationServerMetadata ¶
type AuthorizationServerMetadata struct {
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
JwksURI string `json:"jwks_uri,omitempty"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
ResponseTypesSupported []string `json:"response_types_supported"`
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`
RevocationEndpointAuthMethodsSupported []string `json:"revocation_endpoint_auth_methods_supported,omitempty"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
}
AuthorizationServerMetadata represents OAuth 2.0 Authorization Server Metadata (RFC 8414)
type CallbackServer ¶
type CallbackServer struct {
// contains filtered or unexported fields
}
CallbackServer handles OAuth callback requests
func NewCallbackServer ¶
func NewCallbackServer(ctx context.Context) (*CallbackServer, error)
NewCallbackServer creates a new OAuth callback server on a random available port
func NewCallbackServerOnPort ¶
func NewCallbackServerOnPort(ctx context.Context, port int) (*CallbackServer, error)
NewCallbackServerOnPort creates a new OAuth callback server on a specific port. Use port 0 to let the OS pick a random available port.
func (*CallbackServer) GetRedirectURI ¶
func (cs *CallbackServer) GetRedirectURI() string
func (*CallbackServer) Port ¶
func (cs *CallbackServer) Port() int
Port returns the local TCP port the callback server is listening on. This is useful when a fixed port was not requested (i.e. port 0 was passed) and the caller needs to know which port the OS assigned.
func (*CallbackServer) ResolveRedirectURI ¶
func (cs *CallbackServer) ResolveRedirectURI(callbackRedirectURL string) string
ResolveRedirectURI returns the OAuth redirect URI to advertise to the authorization server.
When callbackRedirectURL is empty, the local callback server's URI is returned unchanged (http://127.0.0.1:{port}/callback).
When callbackRedirectURL is set, it is returned verbatim except that any occurrence of the literal placeholder ${callbackPort} is replaced with the actual port the local callback server is listening on. The external URL is expected to eventually redirect the browser back to the local callback server, preserving the OAuth query parameters.
func (*CallbackServer) SetExpectedState ¶
func (cs *CallbackServer) SetExpectedState(state string)
func (*CallbackServer) Start ¶
func (cs *CallbackServer) Start() error
func (*CallbackServer) WaitForCallback ¶
func (cs *CallbackServer) WaitForCallback(ctx context.Context) (code, state string, err error)
type OAuthToken ¶
type OAuthToken struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
Scope string `json:"scope,omitempty"`
ExpiresAt time.Time `json:"expires_at"`
ClientID string `json:"client_id,omitempty"`
ClientSecret string `json:"client_secret,omitempty"`
AuthServer string `json:"auth_server,omitempty"`
// RequestedScopes records the scope list the config asked for when this
// token was obtained. Unlike Scope (which is whatever the authorization
// server chose to return, sometimes empty, sometimes comma/space
// separated), RequestedScopes reflects our intent and is used to detect
// when the config has changed and a new OAuth flow is required.
RequestedScopes []string `json:"requested_scopes,omitempty"`
}
OAuthToken is an OAuth 2.0 access token together with the refresh token, client credentials and scope bookkeeping needed to refresh it later.
func ExchangeCodeForToken ¶
func ExchangeCodeForToken(ctx context.Context, tokenEndpoint, code, codeVerifier, clientID, clientSecret, redirectURI string) (*OAuthToken, error)
ExchangeCodeForToken exchanges an authorization code for an access token.
func ExchangeCodeForTokenWithClient ¶
func ExchangeCodeForTokenWithClient(ctx context.Context, client *http.Client, tokenEndpoint, code, codeVerifier, clientID, clientSecret, redirectURI, resourceURL string) (*OAuthToken, error)
ExchangeCodeForTokenWithClient is ExchangeCodeForTokenWithResource with an explicit *http.Client, for callers that layer per-server headers or private-IP policies on top of the default client.
func ExchangeCodeForTokenWithResource ¶
func ExchangeCodeForTokenWithResource(ctx context.Context, tokenEndpoint, code, codeVerifier, clientID, clientSecret, redirectURI, resourceURL string) (*OAuthToken, error)
ExchangeCodeForTokenWithResource exchanges an authorization code and sends the RFC 8707 resource indicator to token endpoints that require it.
func RefreshAccessToken ¶
func RefreshAccessToken(ctx context.Context, tokenEndpoint, refreshToken, clientID, clientSecret string) (*OAuthToken, error)
RefreshAccessToken uses a refresh token to obtain a new access token without user interaction.
func RefreshAccessTokenWithClient ¶
func RefreshAccessTokenWithClient(ctx context.Context, client *http.Client, tokenEndpoint, refreshToken, clientID, clientSecret string) (*OAuthToken, error)
RefreshAccessTokenWithClient is RefreshAccessToken with an explicit *http.Client.
func (*OAuthToken) IsExpired ¶
func (t *OAuthToken) IsExpired() bool
IsExpired checks if the token is expired