diff --git a/client/handler.go b/client/handler.go index 79e55beb15..515bd58934 100644 --- a/client/handler.go +++ b/client/handler.go @@ -448,6 +448,10 @@ func (h *Handler) patchOAuth2Client(w http.ResponseWriter, r *http.Request) { oldSecret := client.Secret + // RegistrationAccessTokenSignature is lost when the client is marshalled to JSON + // Store it prior to the patch and re-add it later + oldRegistrationAccessTokenSig := client.RegistrationAccessTokenSignature + client, err = jsonx.ApplyJSONPatch(patchJSON, client, "/id") if err != nil { h.r.Writer().WriteError(w, r, err) @@ -462,6 +466,9 @@ func (h *Handler) patchOAuth2Client(w http.ResponseWriter, r *http.Request) { client.Secret = "" } + // Re-add the registration access token signature before updating the client in DB + client.RegistrationAccessTokenSignature = oldRegistrationAccessTokenSig + if err := h.updateClient(r.Context(), client, h.r.ClientValidator().Validate); err != nil { h.r.Writer().WriteError(w, r, err) return diff --git a/client/sdk_test.go b/client/sdk_test.go index 536b551533..05be9743ae 100644 --- a/client/sdk_test.go +++ b/client/sdk_test.go @@ -208,6 +208,27 @@ func TestClientSDK(t *testing.T) { assertx.EqualAsJSONExcept(t, expected, result, nil) }) + t.Run("case=patch preserves registration access token", func(t *testing.T) { + created, _, err := c.OAuth2API.CreateOAuth2Client(context.Background()).OAuth2Client(createTestClient("")).Execute() + require.NoError(t, err) + require.NotNil(t, created.RegistrationAccessToken) + originalRAT := *created.RegistrationAccessToken + + _, _, err = c.OAuth2API.PatchOAuth2Client(context.Background(), *created.ClientId). + JsonPatch([]hydra.JsonPatch{{Op: "replace", Path: "/client_uri", Value: "http://foo.bar"}}).Execute() + require.NoError(t, err) + + dynURL := publicServer.URL + client.DynClientsHandlerPath + "/" + *created.ClientId + + req, err := http.NewRequest(http.MethodGet, dynURL, nil) + require.NoError(t, err) + req.Header.Set("Authorization", "Bearer "+originalRAT) + res, err := http.DefaultClient.Do(req) + require.NoError(t, err) + _ = res.Body.Close() + assert.Equal(t, http.StatusOK, res.StatusCode, "registration access token must still be valid after admin PATCH") + }) + t.Run("case=patch client illegally", func(t *testing.T) { op := "replace" path := "/id"