Skip to content

Commit 6d6e1c2

Browse files
committed
Check PCKE Support
1 parent 8c42b69 commit 6d6e1c2

File tree

3 files changed

+89
-2
lines changed

3 files changed

+89
-2
lines changed

internal/testing/fake_auth_server.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"encoding/base64"
1010
"encoding/json"
1111
"fmt"
12+
"net"
1213
"net/http"
1314
"time"
1415

@@ -44,7 +45,19 @@ func NewFakeAuthMux() *http.ServeMux {
4445
}
4546

4647
func (s *state) handleMetadata(w http.ResponseWriter, r *http.Request) {
47-
issuer := "https://localhost:" + r.URL.Port()
48+
// Derive the port from the request Host; r.URL.Port() is empty on server side.
49+
hostPort := r.Host
50+
port := ""
51+
if hp := hostPort; hp != "" {
52+
if _, p, err := net.SplitHostPort(hp); err == nil {
53+
port = p
54+
}
55+
}
56+
if port == "" {
57+
// Fallback attempt; may still be empty depending on server/request.
58+
port = r.URL.Port()
59+
}
60+
issuer := "https://localhost:" + port
4861
metadata := map[string]any{
4962
"issuer": issuer,
5063
"authorization_endpoint": issuer + "/authorize",

mcp/client_example_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,14 @@ import (
88
"context"
99
"fmt"
1010
"log"
11+
"net/http"
12+
"net/http/httptest"
13+
"net/url"
1114

1215
"github.com/google/jsonschema-go/jsonschema"
16+
itesting "github.com/modelcontextprotocol/go-sdk/internal/testing"
1317
"github.com/modelcontextprotocol/go-sdk/mcp"
18+
"github.com/modelcontextprotocol/go-sdk/oauthex"
1419
)
1520

1621
// !+roots
@@ -137,3 +142,40 @@ func Example_elicitation() {
137142
}
138143

139144
// !-elicitation
145+
146+
// !+oauth_pkce
147+
148+
func Example_oauthPKCE() {
149+
ctx := context.Background()
150+
151+
// Start a fake OAuth 2.1 auth server that advertises PKCE (S256).
152+
orig := itesting.NewFakeAuthMux()
153+
wrapper := http.NewServeMux()
154+
wrapper.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
155+
orig.ServeHTTP(w, r)
156+
})
157+
ts := httptest.NewTLSServer(wrapper)
158+
defer ts.Close()
159+
160+
// Validate that the server supports PKCE per MCP auth requirements.
161+
// The fake server sets issuer to https://localhost:<port>, so compute that issuer.
162+
u, _ := url.Parse(ts.URL)
163+
issuer := "https://localhost:" + u.Port()
164+
165+
// The fake server presents a cert for example.com; set ServerName accordingly.
166+
httpClient := ts.Client()
167+
if tr, ok := httpClient.Transport.(*http.Transport); ok {
168+
clone := tr.Clone()
169+
clone.TLSClientConfig.ServerName = "example.com"
170+
httpClient.Transport = clone
171+
}
172+
173+
if err := oauthex.RequirePKCE(ctx, issuer, httpClient); err != nil {
174+
log.Fatal(err)
175+
}
176+
177+
fmt.Println("pkce ok")
178+
// Output: pkce ok
179+
}
180+
181+
// !-oauth_pkce

oauthex/oauthex.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,38 @@
55
// Package oauthex implements extensions to OAuth2.
66
package oauthex
77

8-
import "github.com/modelcontextprotocol/go-sdk/internal/oauthex"
8+
import (
9+
"context"
10+
"fmt"
11+
"net/http"
12+
13+
"github.com/modelcontextprotocol/go-sdk/internal/oauthex"
14+
)
915

1016
type ProtectedResourceMetadata = oauthex.ProtectedResourceMetadata
17+
18+
// AuthServerMeta is the authorization server metadata type.
19+
// It is re-exported here for users of the public oauthex package.
20+
type AuthServerMeta = oauthex.AuthServerMeta
21+
22+
// GetAuthServerMeta retrieves OAuth 2.0 Authorization Server Metadata for the given issuer URL.
23+
// This forwards to the internal implementation.
24+
func GetAuthServerMeta(ctx context.Context, issuerURL string, c *http.Client) (*AuthServerMeta, error) {
25+
return oauthex.GetAuthServerMeta(ctx, issuerURL, c)
26+
}
27+
28+
// RequirePKCE checks that the authorization server for issuerURL supports PKCE,
29+
// by verifying that CodeChallengeMethodsSupported is non-empty.
30+
// It returns an error if metadata cannot be fetched or PKCE is not advertised.
31+
func RequirePKCE(ctx context.Context, issuerURL string, c *http.Client) error {
32+
asm, err := oauthex.GetAuthServerMeta(ctx, issuerURL, c)
33+
if err != nil {
34+
return err
35+
}
36+
37+
if len(asm.CodeChallengeMethodsSupported) == 0 {
38+
return fmt.Errorf("authorization server at %s does not implement PKCE", issuerURL)
39+
}
40+
41+
return nil
42+
}

0 commit comments

Comments
 (0)