Skip to content
5 changes: 5 additions & 0 deletions oauthex/auth_meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,11 @@ func GetAuthServerMeta(ctx context.Context, issuerURL string, c *http.Client) (*
// Security violation; don't keep trying.
return nil, fmt.Errorf("metadata issuer %q does not match issuer URL %q", asm.Issuer, issuerURL)
}

if len(asm.CodeChallengeMethodsSupported) == 0 {
return nil, fmt.Errorf("authorization server at %s does not implement PKCE", issuerURL)
}

return asm, nil
}
errs = append(errs, err)
Expand Down
34 changes: 34 additions & 0 deletions oauthex/auth_meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
package oauthex

import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"testing"
Expand All @@ -28,3 +32,33 @@ func TestAuthMetaParse(t *testing.T) {
t.Errorf("got %q, want %q", g, w)
}
}

func TestGetAuthServerMetaRequirePKCE(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't actually test that we do the check. There needs to be a test for an auth server that doesn't do PKCE.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revised

ctx := context.Background()

// Start a fake OAuth 2.1 auth server that advertises PKCE (S256).
wrapper := http.NewServeMux()
wrapper.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
NewFakeMCPServerMux().ServeHTTP(w, r)
})
ts := httptest.NewTLSServer(wrapper)
defer ts.Close()

// Validate that the server supports PKCE per MCP auth requirements.
// The fake server sets issuer to https://localhost:<port>, so compute that issuer.
u, _ := url.Parse(ts.URL)
issuer := "https://localhost:" + u.Port()

// The fake server presents a cert for example.com; set ServerName accordingly.
httpClient := ts.Client()
if tr, ok := httpClient.Transport.(*http.Transport); ok {
clone := tr.Clone()
clone.TLSClientConfig.ServerName = "example.com"
httpClient.Transport = clone
}

if _, err := GetAuthServerMeta(ctx, issuer, httpClient); err != nil {
t.Fatal(err)
}

}