auth: allow passing custom http.Client to AuthorizationCodeHandler (#840)
This commit is contained in:
parent
724dd47aa3
commit
d836ceb87d
3 changed files with 173 additions and 51 deletions
|
|
@ -14,9 +14,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/internal/util"
|
||||
)
|
||||
|
||||
// AuthServerMeta represents the metadata for an OAuth 2.0 authorization server,
|
||||
|
|
@ -136,13 +133,9 @@ type AuthServerMeta struct {
|
|||
//
|
||||
// [RFC 8414]: https://tools.ietf.org/html/rfc8414
|
||||
func GetAuthServerMeta(ctx context.Context, metadataURL, issuer string, c *http.Client) (*AuthServerMeta, error) {
|
||||
u, err := url.Parse(metadataURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Only allow HTTP for local addresses (testing or development purposes).
|
||||
if !util.IsLoopback(u.Host) && u.Scheme != "https" {
|
||||
return nil, fmt.Errorf("metadataURL %q does not use HTTPS", metadataURL)
|
||||
if err := checkHTTPSOrLoopback(metadataURL); err != nil {
|
||||
return nil, fmt.Errorf("metadataURL: %v", err)
|
||||
}
|
||||
asm, err := getJSON[AuthServerMeta](ctx, c, metadataURL, 1<<20)
|
||||
if err != nil {
|
||||
|
|
@ -173,6 +166,8 @@ func GetAuthServerMeta(ctx context.Context, metadataURL, issuer string, c *http.
|
|||
|
||||
// validateAuthServerMetaURLs validates all URL fields in AuthServerMeta
|
||||
// to ensure they don't use dangerous schemes that could enable XSS attacks.
|
||||
// It also validates that URLs likely to be called by the client use
|
||||
// HTTPS or are loopback addresses.
|
||||
func validateAuthServerMetaURLs(asm *AuthServerMeta) error {
|
||||
urls := []struct {
|
||||
name string
|
||||
|
|
@ -194,5 +189,22 @@ func validateAuthServerMetaURLs(asm *AuthServerMeta) error {
|
|||
return fmt.Errorf("%s: %w", u.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
urls = []struct {
|
||||
name string
|
||||
value string
|
||||
}{
|
||||
{"authorization_endpoint", asm.AuthorizationEndpoint},
|
||||
{"token_endpoint", asm.TokenEndpoint},
|
||||
{"registration_endpoint", asm.RegistrationEndpoint},
|
||||
{"introspection_endpoint", asm.IntrospectionEndpoint},
|
||||
}
|
||||
|
||||
for _, u := range urls {
|
||||
if err := checkHTTPSOrLoopback(u.value); err != nil {
|
||||
return fmt.Errorf("%s: %w", u.name, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/internal/util"
|
||||
)
|
||||
|
||||
type httpStatusError struct {
|
||||
|
|
@ -78,3 +80,17 @@ func checkURLScheme(u string) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkHTTPSOrLoopback(addr string) error {
|
||||
if addr == "" {
|
||||
return nil
|
||||
}
|
||||
u, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !util.IsLoopback(u.Host) && u.Scheme != "https" {
|
||||
return fmt.Errorf("URL %q does not use HTTPS or is not a loopback address", addr)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,13 +98,9 @@ func resourceMetadataURL(cs []Challenge) string {
|
|||
// - The authorization_servers field of the resulting metadata is checked for dangerous URL schemes.
|
||||
func GetProtectedResourceMetadata(ctx context.Context, metadataURL, resourceURL string, c *http.Client) (_ *ProtectedResourceMetadata, err error) {
|
||||
defer util.Wrapf(&err, "GetProtectedResourceMetadata(%q)", metadataURL)
|
||||
u, err := url.Parse(metadataURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Only allow HTTP for local addresses (testing or development purposes).
|
||||
if !util.IsLoopback(u.Host) && u.Scheme != "https" {
|
||||
return nil, fmt.Errorf("metadataURL %q does not use HTTPS", metadataURL)
|
||||
if err := checkHTTPSOrLoopback(metadataURL); err != nil {
|
||||
return nil, fmt.Errorf("metadataURL: %v", err)
|
||||
}
|
||||
prm, err := getJSON[ProtectedResourceMetadata](ctx, c, metadataURL, 1<<20)
|
||||
if err != nil {
|
||||
|
|
@ -115,9 +111,12 @@ func GetProtectedResourceMetadata(ctx context.Context, metadataURL, resourceURL
|
|||
return nil, fmt.Errorf("got metadata resource %q, want %q", prm.Resource, resourceURL)
|
||||
}
|
||||
// Validate the authorization server URLs to prevent XSS attacks (see #526).
|
||||
for _, u := range prm.AuthorizationServers {
|
||||
for i, u := range prm.AuthorizationServers {
|
||||
if err := checkURLScheme(u); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("authorization_servers[%d]: %v", i, err)
|
||||
}
|
||||
if err := checkHTTPSOrLoopback(u); err != nil {
|
||||
return nil, fmt.Errorf("authorization_servers[%d]: %v", i, err)
|
||||
}
|
||||
}
|
||||
return prm, nil
|
||||
|
|
|
|||
Loading…
Reference in a new issue