From c33fd89c7eb8408f242b3054ca121863ef42bcbd Mon Sep 17 00:00:00 2001 From: shlee1223 Date: Sun, 25 Oct 2026 23:16:20 +0900 Subject: [PATCH] auth/extauth: send the resource parameter in client credentials token requests --- auth/extauth/client_credentials.go | 11 +++++---- auth/extauth/client_credentials_test.go | 31 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/auth/extauth/client_credentials.go b/auth/extauth/client_credentials.go index 29df4426..7476e3c7 100644 --- a/auth/extauth/client_credentials.go +++ b/auth/extauth/client_credentials.go @@ -159,11 +159,12 @@ func (h *ClientCredentialsHandler) Authorize(ctx context.Context, req *http.Requ // Step 3: Exchange client credentials for an access token. cfg := &clientcredentials.Config{ - ClientID: creds.ClientID, - ClientSecret: creds.ClientSecretAuth.ClientSecret, - TokenURL: asm.TokenEndpoint, - Scopes: requestedScopes, - AuthStyle: selectTokenAuthMethod(asm.TokenEndpointAuthMethodsSupported), + ClientID: creds.ClientID, + ClientSecret: creds.ClientSecretAuth.ClientSecret, + TokenURL: asm.TokenEndpoint, + Scopes: requestedScopes, + AuthStyle: selectTokenAuthMethod(asm.TokenEndpointAuthMethodsSupported), + EndpointParams: url.Values{"resource": {prm.Resource}}, } client := util.RestrictRedirects(httpClient) diff --git a/auth/extauth/client_credentials_test.go b/auth/extauth/client_credentials_test.go index a278f219..2140e308 100644 --- a/auth/extauth/client_credentials_test.go +++ b/auth/extauth/client_credentials_test.go @@ -541,6 +541,37 @@ func TestClientCredentialsHandler_TokenRedirectToOtherOrigin(t *testing.T) { } } +// TestClientCredentialsHandler_TokenRequestIncludesResource verifies that the +// token request carries the resource parameter (RFC 8707). +func TestClientCredentialsHandler_TokenRequestIncludesResource(t *testing.T) { + // Without protected resource metadata, the resource server is also its + // own authorization server. + mux := http.NewServeMux() + server := httptest.NewServer(mux) + t.Cleanup(server.Close) + resourceURL := server.URL + "/resource" + mux.HandleFunc("/.well-known/oauth-authorization-server", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + fmt.Fprintf(w, `{"issuer": %q, "token_endpoint": %q, "code_challenge_methods_supported": ["S256"]}`, server.URL, server.URL+"/token") + }) + mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) { + if got := r.PostFormValue("resource"); got != resourceURL { + t.Errorf("token request resource = %q, want %q", got, resourceURL) + } + w.Header().Set("Content-Type", "application/json") + fmt.Fprint(w, `{"access_token": "token", "token_type": "Bearer"}`) + }) + + handler, err := NewClientCredentialsHandler(validClientCredentialsConfig()) + if err != nil { + t.Fatal(err) + } + resp := &http.Response{StatusCode: http.StatusUnauthorized, Header: http.Header{}, Body: http.NoBody} + if err := handler.Authorize(t.Context(), httptest.NewRequest("GET", resourceURL, nil), resp); err != nil { + t.Fatalf("Authorize failed: %v", err) + } +} + func TestSelectTokenAuthMethod(t *testing.T) { tests := []struct { name string