Skip to content
Merged
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
2 changes: 1 addition & 1 deletion auth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
"net/http"
"sync"

"github.com/modelcontextprotocol/go-sdk/internal/oauthex"
"github.com/modelcontextprotocol/go-sdk/oauthex"
"golang.org/x/oauth2"
)

Expand Down
147 changes: 147 additions & 0 deletions oauthex/auth_meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// This file implements Authorization Server Metadata.
// See https://www.rfc-editor.org/rfc/rfc8414.html.

//go:build mcp_go_client_oauth

package oauthex

import (
"context"
"errors"
"fmt"
"net/http"
)

// AuthServerMeta represents the metadata for an OAuth 2.0 authorization server,
// as defined in [RFC 8414].
//
// Not supported:
// - signed metadata
//
// [RFC 8414]: https://tools.ietf.org/html/rfc8414)
type AuthServerMeta struct {
// GENERATED BY GEMINI 2.5.

// Issuer is the REQUIRED URL identifying the authorization server.
Issuer string `json:"issuer"`

// AuthorizationEndpoint is the REQUIRED URL of the server's OAuth 2.0 authorization endpoint.
AuthorizationEndpoint string `json:"authorization_endpoint"`

// TokenEndpoint is the REQUIRED URL of the server's OAuth 2.0 token endpoint.
TokenEndpoint string `json:"token_endpoint"`

// JWKSURI is the REQUIRED URL of the server's JSON Web Key Set [JWK] document.
JWKSURI string `json:"jwks_uri"`

// RegistrationEndpoint is the RECOMMENDED URL of the server's OAuth 2.0 Dynamic Client Registration endpoint.
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`

// ScopesSupported is a RECOMMENDED JSON array of strings containing a list of the OAuth 2.0
// "scope" values that this server supports.
ScopesSupported []string `json:"scopes_supported,omitempty"`

// ResponseTypesSupported is a REQUIRED JSON array of strings containing a list of the OAuth 2.0
// "response_type" values that this server supports.
ResponseTypesSupported []string `json:"response_types_supported"`

// ResponseModesSupported is a RECOMMENDED JSON array of strings containing a list of the OAuth 2.0
// "response_mode" values that this server supports.
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`

// GrantTypesSupported is a RECOMMENDED JSON array of strings containing a list of the OAuth 2.0
// grant type values that this server supports.
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`

// TokenEndpointAuthMethodsSupported is a RECOMMENDED JSON array of strings containing a list of
// client authentication methods supported by this token endpoint.
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`

// TokenEndpointAuthSigningAlgValuesSupported is a RECOMMENDED JSON array of strings containing
// a list of the JWS signing algorithms ("alg" values) supported by the token endpoint for
// the signature on the JWT used to authenticate the client.
TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"`

// ServiceDocumentation is a RECOMMENDED URL of a page containing human-readable documentation
// for the service.
ServiceDocumentation string `json:"service_documentation,omitempty"`

// UILocalesSupported is a RECOMMENDED JSON array of strings representing supported
// BCP47 [RFC5646] language tag values for display in the user interface.
UILocalesSupported []string `json:"ui_locales_supported,omitempty"`

// OpPolicyURI is a RECOMMENDED URL that the server provides to the person registering
// the client to read about the server's operator policies.
OpPolicyURI string `json:"op_policy_uri,omitempty"`

// OpTOSURI is a RECOMMENDED URL that the server provides to the person registering the
// client to read about the server's terms of service.
OpTOSURI string `json:"op_tos_uri,omitempty"`

// RevocationEndpoint is a RECOMMENDED URL of the server's OAuth 2.0 revocation endpoint.
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`

// RevocationEndpointAuthMethodsSupported is a RECOMMENDED JSON array of strings containing
// a list of client authentication methods supported by this revocation endpoint.
RevocationEndpointAuthMethodsSupported []string `json:"revocation_endpoint_auth_methods_supported,omitempty"`

// RevocationEndpointAuthSigningAlgValuesSupported is a RECOMMENDED JSON array of strings
// containing a list of the JWS signing algorithms ("alg" values) supported by the revocation
// endpoint for the signature on the JWT used to authenticate the client.
RevocationEndpointAuthSigningAlgValuesSupported []string `json:"revocation_endpoint_auth_signing_alg_values_supported,omitempty"`

// IntrospectionEndpoint is a RECOMMENDED URL of the server's OAuth 2.0 introspection endpoint.
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`

// IntrospectionEndpointAuthMethodsSupported is a RECOMMENDED JSON array of strings containing
// a list of client authentication methods supported by this introspection endpoint.
IntrospectionEndpointAuthMethodsSupported []string `json:"introspection_endpoint_auth_methods_supported,omitempty"`

// IntrospectionEndpointAuthSigningAlgValuesSupported is a RECOMMENDED JSON array of strings
// containing a list of the JWS signing algorithms ("alg" values) supported by the introspection
// endpoint for the signature on the JWT used to authenticate the client.
IntrospectionEndpointAuthSigningAlgValuesSupported []string `json:"introspection_endpoint_auth_signing_alg_values_supported,omitempty"`

// CodeChallengeMethodsSupported is a RECOMMENDED JSON array of strings containing a list of
// PKCE code challenge methods supported by this authorization server.
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
}

var wellKnownPaths = []string{
"/.well-known/oauth-authorization-server",
"/.well-known/openid-configuration",
}

// GetAuthServerMeta issues a GET request to retrieve authorization server metadata
// from an OAuth authorization server with the given issuerURL.
//
// It follows [RFC 8414]:
// - The well-known paths specified there are inserted into the URL's path, one at time.
// The first to succeed is used.
// - The Issuer field is checked against issuerURL.
//
// [RFC 8414]: https://tools.ietf.org/html/rfc8414
func GetAuthServerMeta(ctx context.Context, issuerURL string, c *http.Client) (*AuthServerMeta, error) {
var errs []error
for _, p := range wellKnownPaths {
u, err := prependToPath(issuerURL, p)
if err != nil {
// issuerURL is bad; no point in continuing.
return nil, err
}
asm, err := getJSON[AuthServerMeta](ctx, c, u, 1<<20)
if err == nil {
if asm.Issuer != issuerURL { // section 3.3
// Security violation; don't keep trying.
return nil, fmt.Errorf("metadata issuer %q does not match issuer URL %q", asm.Issuer, issuerURL)
}
return asm, nil
}
errs = append(errs, err)
}
return nil, fmt.Errorf("failed to get auth server metadata from %q: %w", issuerURL, errors.Join(errs...))
}
30 changes: 30 additions & 0 deletions oauthex/auth_meta_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

//go:build mcp_go_client_oauth

package oauthex

import (
"encoding/json"
"os"
"path/filepath"
"testing"
)

func TestAuthMetaParse(t *testing.T) {
// Verify that we parse Google's auth server metadata.
data, err := os.ReadFile(filepath.FromSlash("testdata/google-auth-meta.json"))
if err != nil {
t.Fatal(err)
}
var a AuthServerMeta
if err := json.Unmarshal(data, &a); err != nil {
t.Fatal(err)
}
// Spot check.
if g, w := a.Issuer, "https://accounts.google.com"; g != w {
t.Errorf("got %q, want %q", g, w)
}
}
133 changes: 2 additions & 131 deletions internal/oauthex/auth_meta.go → oauthex/dcr.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,114 +5,20 @@
// This file implements Authorization Server Metadata.
// See https://www.rfc-editor.org/rfc/rfc8414.html.

//go:build mcp_go_client_oauth

package oauthex

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"time"
)

// AuthServerMeta represents the metadata for an OAuth 2.0 authorization server,
// as defined in [RFC 8414].
//
// Not supported:
// - signed metadata
//
// [RFC 8414]: https://tools.ietf.org/html/rfc8414)
type AuthServerMeta struct {
// GENERATED BY GEMINI 2.5.

// Issuer is the REQUIRED URL identifying the authorization server.
Issuer string `json:"issuer"`

// AuthorizationEndpoint is the REQUIRED URL of the server's OAuth 2.0 authorization endpoint.
AuthorizationEndpoint string `json:"authorization_endpoint"`

// TokenEndpoint is the REQUIRED URL of the server's OAuth 2.0 token endpoint.
TokenEndpoint string `json:"token_endpoint"`

// JWKSURI is the REQUIRED URL of the server's JSON Web Key Set [JWK] document.
JWKSURI string `json:"jwks_uri"`

// RegistrationEndpoint is the RECOMMENDED URL of the server's OAuth 2.0 Dynamic Client Registration endpoint.
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`

// ScopesSupported is a RECOMMENDED JSON array of strings containing a list of the OAuth 2.0
// "scope" values that this server supports.
ScopesSupported []string `json:"scopes_supported,omitempty"`

// ResponseTypesSupported is a REQUIRED JSON array of strings containing a list of the OAuth 2.0
// "response_type" values that this server supports.
ResponseTypesSupported []string `json:"response_types_supported"`

// ResponseModesSupported is a RECOMMENDED JSON array of strings containing a list of the OAuth 2.0
// "response_mode" values that this server supports.
ResponseModesSupported []string `json:"response_modes_supported,omitempty"`

// GrantTypesSupported is a RECOMMENDED JSON array of strings containing a list of the OAuth 2.0
// grant type values that this server supports.
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`

// TokenEndpointAuthMethodsSupported is a RECOMMENDED JSON array of strings containing a list of
// client authentication methods supported by this token endpoint.
TokenEndpointAuthMethodsSupported []string `json:"token_endpoint_auth_methods_supported,omitempty"`

// TokenEndpointAuthSigningAlgValuesSupported is a RECOMMENDED JSON array of strings containing
// a list of the JWS signing algorithms ("alg" values) supported by the token endpoint for
// the signature on the JWT used to authenticate the client.
TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"`

// ServiceDocumentation is a RECOMMENDED URL of a page containing human-readable documentation
// for the service.
ServiceDocumentation string `json:"service_documentation,omitempty"`

// UILocalesSupported is a RECOMMENDED JSON array of strings representing supported
// BCP47 [RFC5646] language tag values for display in the user interface.
UILocalesSupported []string `json:"ui_locales_supported,omitempty"`

// OpPolicyURI is a RECOMMENDED URL that the server provides to the person registering
// the client to read about the server's operator policies.
OpPolicyURI string `json:"op_policy_uri,omitempty"`

// OpTOSURI is a RECOMMENDED URL that the server provides to the person registering the
// client to read about the server's terms of service.
OpTOSURI string `json:"op_tos_uri,omitempty"`

// RevocationEndpoint is a RECOMMENDED URL of the server's OAuth 2.0 revocation endpoint.
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`

// RevocationEndpointAuthMethodsSupported is a RECOMMENDED JSON array of strings containing
// a list of client authentication methods supported by this revocation endpoint.
RevocationEndpointAuthMethodsSupported []string `json:"revocation_endpoint_auth_methods_supported,omitempty"`

// RevocationEndpointAuthSigningAlgValuesSupported is a RECOMMENDED JSON array of strings
// containing a list of the JWS signing algorithms ("alg" values) supported by the revocation
// endpoint for the signature on the JWT used to authenticate the client.
RevocationEndpointAuthSigningAlgValuesSupported []string `json:"revocation_endpoint_auth_signing_alg_values_supported,omitempty"`

// IntrospectionEndpoint is a RECOMMENDED URL of the server's OAuth 2.0 introspection endpoint.
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`

// IntrospectionEndpointAuthMethodsSupported is a RECOMMENDED JSON array of strings containing
// a list of client authentication methods supported by this introspection endpoint.
IntrospectionEndpointAuthMethodsSupported []string `json:"introspection_endpoint_auth_methods_supported,omitempty"`

// IntrospectionEndpointAuthSigningAlgValuesSupported is a RECOMMENDED JSON array of strings
// containing a list of the JWS signing algorithms ("alg" values) supported by the introspection
// endpoint for the signature on the JWT used to authenticate the client.
IntrospectionEndpointAuthSigningAlgValuesSupported []string `json:"introspection_endpoint_auth_signing_alg_values_supported,omitempty"`

// CodeChallengeMethodsSupported is a RECOMMENDED JSON array of strings containing a list of
// PKCE code challenge methods supported by this authorization server.
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
}

// ClientRegistrationMetadata represents the client metadata fields for the DCR POST request (RFC 7591).
type ClientRegistrationMetadata struct {
// RedirectURIs is a REQUIRED JSON array of redirection URI strings for use in
Expand Down Expand Up @@ -260,41 +166,6 @@ func (e *ClientRegistrationError) Error() string {
return fmt.Sprintf("registration failed: %s (%s)", e.ErrorCode, e.ErrorDescription)
}

var wellKnownPaths = []string{
"/.well-known/oauth-authorization-server",
"/.well-known/openid-configuration",
}

// GetAuthServerMeta issues a GET request to retrieve authorization server metadata
// from an OAuth authorization server with the given issuerURL.
//
// It follows [RFC 8414]:
// - The well-known paths specified there are inserted into the URL's path, one at time.
// The first to succeed is used.
// - The Issuer field is checked against issuerURL.
//
// [RFC 8414]: https://tools.ietf.org/html/rfc8414
func GetAuthServerMeta(ctx context.Context, issuerURL string, c *http.Client) (*AuthServerMeta, error) {
var errs []error
for _, p := range wellKnownPaths {
u, err := prependToPath(issuerURL, p)
if err != nil {
// issuerURL is bad; no point in continuing.
return nil, err
}
asm, err := getJSON[AuthServerMeta](ctx, c, u, 1<<20)
if err == nil {
if asm.Issuer != issuerURL { // section 3.3
// Security violation; don't keep trying.
return nil, fmt.Errorf("metadata issuer %q does not match issuer URL %q", asm.Issuer, issuerURL)
}
return asm, nil
}
errs = append(errs, err)
}
return nil, fmt.Errorf("failed to get auth server metadata from %q: %w", issuerURL, errors.Join(errs...))
}

// RegisterClient performs Dynamic Client Registration according to RFC 7591.
func RegisterClient(ctx context.Context, registrationEndpoint string, clientMeta *ClientRegistrationMetadata, c *http.Client) (*ClientRegistrationResponse, error) {
if registrationEndpoint == "" {
Expand Down
18 changes: 2 additions & 16 deletions internal/oauthex/auth_meta_test.go → oauthex/dcr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

//go:build mcp_go_client_oauth

package oauthex

import (
Expand All @@ -19,22 +21,6 @@ import (
"github.com/google/go-cmp/cmp"
)

func TestAuthMetaParse(t *testing.T) {
// Verify that we parse Google's auth server metadata.
data, err := os.ReadFile(filepath.FromSlash("testdata/google-auth-meta.json"))
if err != nil {
t.Fatal(err)
}
var a AuthServerMeta
if err := json.Unmarshal(data, &a); err != nil {
t.Fatal(err)
}
// Spot check.
if g, w := a.Issuer, "https://accounts.google.com"; g != w {
t.Errorf("got %q, want %q", g, w)
}
}

func TestClientRegistrationMetadataParse(t *testing.T) {
// Verify that we can parse a typical client metadata JSON.
data, err := os.ReadFile(filepath.FromSlash("testdata/client-auth-meta.json"))
Expand Down
3 changes: 3 additions & 0 deletions internal/oauthex/oauth2.go → oauthex/oauth2.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
// license that can be found in the LICENSE file.

// Package oauthex implements extensions to OAuth2.

//go:build mcp_go_client_oauth

package oauthex

import (
Expand Down
2 changes: 2 additions & 0 deletions internal/oauthex/oauth2_test.go → oauthex/oauth2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

//go:build mcp_go_client_oauth

package oauthex

import (
Expand Down
Loading