oauthflow

package
v1.114.0 Latest Latest
Warning

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

Go to latest
Published: Jul 21, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

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

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

func DefaultHTTPClient() *http.Client

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

func GenerateState() (string, error)

GenerateState generates a random state parameter for OAuth CSRF protection

func HTTPClientForAllowPrivateIPs

func HTTPClientForAllowPrivateIPs(allowPrivateIPs bool) *http.Client

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

func SetHTTPClientForTesting(c *http.Client) (restore func())

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) Shutdown

func (cs *CallbackServer) Shutdown(ctx context.Context) error

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

Jump to

Keyboard shortcuts

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