{"record":{"id":"c03c29f560c5161a","repo":"quarkusio/quarkus","slug":"client-secret-can-not-be-modified","errorCode":null,"errorMessage":"Client secret can not be modified","messagePattern":"Client secret can not be modified","errorType":"exception","errorClass":"OidcClientRegistrationException","httpStatus":null,"severity":"error","filePath":"extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/runtime/RegisteredClientImpl.java","lineNumber":94,"sourceCode":"        checkClientRequestUri();\n        HttpRequest<Buffer> request = client.getAbs(registrationClientUri);\n        request.putHeader(HttpHeaders.ACCEPT.toString(), APPLICATION_JSON);\n        OidcRequestContextProperties requestProps = getRequestProps();\n        return makeRequest(requestProps, request, Buffer.buffer())\n                .transformToUni(resp -> newRegisteredClient(resp, requestProps));\n    }\n\n    @Override\n    public Uni<RegisteredClient> update(ClientMetadata newMetadata) {\n\n        checkClosed();\n        checkClientRequestUri();\n        if (newMetadata.getClientId() != null && !registeredMetadata.getClientId().equals(newMetadata.getClientId())) {\n            throw new OidcClientRegistrationException(\"Client id can not be modified\");\n        }\n        if (newMetadata.getClientSecret() != null\n                && !registeredMetadata.getClientSecret().equals(newMetadata.getClientSecret())) {\n            throw new OidcClientRegistrationException(\"Client secret can not be modified\");\n        }\n\n        JsonObjectBuilder builder = jsonProvider().createObjectBuilder();\n\n        JsonObject newJsonObject = newMetadata.getJsonObject();\n        JsonObject currentJsonObject = registeredMetadata.getJsonObject();\n\n        LOG.debugf(\"Current client metadata: %s\", currentJsonObject.toString());\n\n        // Try to ensure the same order of properties as in the original metadata\n        for (Map.Entry<String, JsonValue> entry : currentJsonObject.entrySet()) {\n            if (PRIVATE_PROPERTIES.contains(entry.getKey())) {\n                continue;\n            }\n            boolean newPropValue = newJsonObject.containsKey(entry.getKey());\n            builder.add(entry.getKey(), newPropValue ? newJsonObject.get(entry.getKey()) : entry.getValue());\n        }\n        for (Map.Entry<String, JsonValue> entry : newJsonObject.entrySet()) {","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/runtime/RegisteredClientImpl.java#L76-L112","documentation":"RegisteredClientImpl.update() rejects registration-update requests that attempt to change the client_id or client_secret of an already-registered OIDC client. The OAuth2 dynamic client registration spec does not allow the secret to be modified via a registration update; changing it would invalidate the credentials the client itself uses to authenticate to the registration endpoint. So any update whose new metadata carries a client secret different from the currently registered one is rejected with OidcClientRegistrationException.","triggerScenarios":"Calling OidcClientRegistration.update(newMetadata) (or a mutate/patch flow built on it) where OidcClientMetadata.getClientSecret() is non-null and differs from the secret stored in registeredMetadata.","commonSituations":"Applications that read the registration response, tweak fields (e.g. redirect URIs, token endpoint auth), and accidentally carry over a stale or newly-generated client secret; code that regenerates the secret locally and tries to push it; copying metadata from a different client.","solutions":["Set the client secret to null in the metadata passed to update() so the server keeps the existing secret","Only modify fields you intend to change; build the new metadata from the registered metadata without touching clientId/clientSecret","If a new secret is genuinely required, re-register the client or use the vendor-specific rotation endpoint instead of a registration update"],"exampleFix":"// before\nmetadata.setRedirectUri(newUri);\nmetadata.setClientSecret(generatedSecret);\nregisteredClient.update(metadata);\n// after\nmetadata.setRedirectUri(newUri);\nmetadata.setClientSecret(null); // secret cannot be modified via update\nregisteredClient.update(metadata);","handlingStrategy":"validation","validationCode":"if (newMetadata.getClientSecret() != null\n        && !registeredMetadata.getClientSecret().equals(newMetadata.getClientSecret())) {\n    throw new IllegalArgumentException(\"client secret cannot be modified; set it to null\");\n}\nregisteredClient.update(newMetadata);","typeGuard":"boolean isSecretUnchanged(OidcClientMetadata old, OidcClientMetadata next) {\n    return next.getClientSecret() == null\n            || next.getClientSecret().equals(old.getClientSecret());\n}","tryCatchPattern":"try {\n    registeredClient.update(newMetadata);\n} catch (OidcClientRegistrationException e) {\n    LOG.warn(\"Registration update rejected: \" + e.getMessage());\n}","preventionTips":["Always null out clientId/clientSecret in metadata used for updates","Build update metadata from the registered metadata and change only intended fields","Never push locally regenerated secrets via registration update"],"tags":["oidc","client-registration","immutable-field"],"backgroundTag":"oidc-client-registration-update-rejected","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}