diff --git a/auth/authorization_code.go b/auth/authorization_code.go index 142bac12f..a49c100e2 100644 --- a/auth/authorization_code.go +++ b/auth/authorization_code.go @@ -402,14 +402,23 @@ func (h *AuthorizationCodeHandler) Authorize(ctx context.Context, req *http.Requ } // resourceMetadataURLFromChallenges returns a resource metadata URL from the given "WWW-Authenticate" header challenges, -// or the empty string if there is none. +// preferring a "Bearer" challenge and otherwise the first other scheme that carries one. It returns the empty string if +// there is none. func resourceMetadataURLFromChallenges(cs []oauthex.Challenge) string { + var fallback string for _, c := range cs { - if u := c.Params["resource_metadata"]; u != "" { + u := c.Params["resource_metadata"] + if u == "" { + continue + } + if strings.EqualFold(c.Scheme, "Bearer") { return u } + if fallback == "" { + fallback = u + } } - return "" + return fallback } // scopesFromChallenges returns the scopes from the given "WWW-Authenticate" header challenges. diff --git a/auth/authorization_code_test.go b/auth/authorization_code_test.go index 862514627..ee995e211 100644 --- a/auth/authorization_code_test.go +++ b/auth/authorization_code_test.go @@ -1606,3 +1606,24 @@ func TestInitialTokenSource(t *testing.T) { t.Errorf("expected access token 'set_token', got '%s'", tok.AccessToken) } } + +func TestResourceMetadataURLFromChallengesUsesBearerChallenge(t *testing.T) { + challenges := []oauthex.Challenge{ + {Scheme: "Basic", Params: map[string]string{"resource_metadata": "https://basic.example/metadata"}}, + {Scheme: "Bearer", Params: map[string]string{"resource_metadata": "https://bearer.example/metadata"}}, + } + if got, want := resourceMetadataURLFromChallenges(challenges), "https://bearer.example/metadata"; got != want { + t.Errorf("resourceMetadataURLFromChallenges() = %q, want %q", got, want) + } +} + +func TestResourceMetadataURLFromChallengesFallsBackToOtherSchemes(t *testing.T) { + challenges := []oauthex.Challenge{ + {Scheme: "Basic", Params: map[string]string{}}, + {Scheme: "Digest", Params: map[string]string{"resource_metadata": "https://digest.example/metadata"}}, + {Scheme: "Basic", Params: map[string]string{"resource_metadata": "https://basic.example/metadata"}}, + } + if got, want := resourceMetadataURLFromChallenges(challenges), "https://digest.example/metadata"; got != want { + t.Errorf("resourceMetadataURLFromChallenges() = %q, want %q", got, want) + } +} diff --git a/auth/extauth/client_credentials.go b/auth/extauth/client_credentials.go index c65d8b066..cdd99b8fb 100644 --- a/auth/extauth/client_credentials.go +++ b/auth/extauth/client_credentials.go @@ -236,14 +236,24 @@ func protectedResourceMetadataURLs(metadataURL, resourceURL string) []prmURL { } // resourceMetadataURLFromChallenges returns a resource metadata URL from -// WWW-Authenticate challenges, or the empty string if there is none. +// WWW-Authenticate challenges, preferring a Bearer challenge and otherwise the +// first other scheme that carries one. It returns the empty string if there is +// none. func resourceMetadataURLFromChallenges(cs []oauthex.Challenge) string { + var fallback string for _, c := range cs { - if u := c.Params["resource_metadata"]; u != "" { + u := c.Params["resource_metadata"] + if u == "" { + continue + } + if strings.EqualFold(c.Scheme, "Bearer") { return u } + if fallback == "" { + fallback = u + } } - return "" + return fallback } // scopesFromChallenges returns scopes from WWW-Authenticate challenges. diff --git a/auth/extauth/client_credentials_test.go b/auth/extauth/client_credentials_test.go index b2973de7c..8c87bb537 100644 --- a/auth/extauth/client_credentials_test.go +++ b/auth/extauth/client_credentials_test.go @@ -418,3 +418,24 @@ func TestSelectTokenAuthMethod(t *testing.T) { }) } } + +func TestResourceMetadataURLFromChallengesUsesBearerChallenge(t *testing.T) { + challenges := []oauthex.Challenge{ + {Scheme: "Basic", Params: map[string]string{"resource_metadata": "https://basic.example/metadata"}}, + {Scheme: "Bearer", Params: map[string]string{"resource_metadata": "https://bearer.example/metadata"}}, + } + if got, want := resourceMetadataURLFromChallenges(challenges), "https://bearer.example/metadata"; got != want { + t.Errorf("resourceMetadataURLFromChallenges() = %q, want %q", got, want) + } +} + +func TestResourceMetadataURLFromChallengesFallsBackToOtherSchemes(t *testing.T) { + challenges := []oauthex.Challenge{ + {Scheme: "Basic", Params: map[string]string{}}, + {Scheme: "Digest", Params: map[string]string{"resource_metadata": "https://digest.example/metadata"}}, + {Scheme: "Basic", Params: map[string]string{"resource_metadata": "https://basic.example/metadata"}}, + } + if got, want := resourceMetadataURLFromChallenges(challenges), "https://digest.example/metadata"; got != want { + t.Errorf("resourceMetadataURLFromChallenges() = %q, want %q", got, want) + } +}