quarkusio/quarkus · error · OidcClientRegistrationException
Client secret can not be modified
Error message
Client secret can not be modified
What it means
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.
Source
Thrown at extensions/oidc-client-registration/runtime/src/main/java/io/quarkus/oidc/client/registration/runtime/RegisteredClientImpl.java:94
checkClientRequestUri();
HttpRequest<Buffer> request = client.getAbs(registrationClientUri);
request.putHeader(HttpHeaders.ACCEPT.toString(), APPLICATION_JSON);
OidcRequestContextProperties requestProps = getRequestProps();
return makeRequest(requestProps, request, Buffer.buffer())
.transformToUni(resp -> newRegisteredClient(resp, requestProps));
}
@Override
public Uni<RegisteredClient> update(ClientMetadata newMetadata) {
checkClosed();
checkClientRequestUri();
if (newMetadata.getClientId() != null && !registeredMetadata.getClientId().equals(newMetadata.getClientId())) {
throw new OidcClientRegistrationException("Client id can not be modified");
}
if (newMetadata.getClientSecret() != null
&& !registeredMetadata.getClientSecret().equals(newMetadata.getClientSecret())) {
throw new OidcClientRegistrationException("Client secret can not be modified");
}
JsonObjectBuilder builder = jsonProvider().createObjectBuilder();
JsonObject newJsonObject = newMetadata.getJsonObject();
JsonObject currentJsonObject = registeredMetadata.getJsonObject();
LOG.debugf("Current client metadata: %s", currentJsonObject.toString());
// Try to ensure the same order of properties as in the original metadata
for (Map.Entry<String, JsonValue> entry : currentJsonObject.entrySet()) {
if (PRIVATE_PROPERTIES.contains(entry.getKey())) {
continue;
}
boolean newPropValue = newJsonObject.containsKey(entry.getKey());
builder.add(entry.getKey(), newPropValue ? newJsonObject.get(entry.getKey()) : entry.getValue());
}
for (Map.Entry<String, JsonValue> entry : newJsonObject.entrySet()) {View on GitHub (pinned to e1c734241f)
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
Example fix
// before metadata.setRedirectUri(newUri); metadata.setClientSecret(generatedSecret); registeredClient.update(metadata); // after metadata.setRedirectUri(newUri); metadata.setClientSecret(null); // secret cannot be modified via update registeredClient.update(metadata);
Defensive patterns
Strategy: validation
Validate before calling
if (newMetadata.getClientSecret() != null
&& !registeredMetadata.getClientSecret().equals(newMetadata.getClientSecret())) {
throw new IllegalArgumentException("client secret cannot be modified; set it to null");
}
registeredClient.update(newMetadata); Type guard
boolean isSecretUnchanged(OidcClientMetadata old, OidcClientMetadata next) {
return next.getClientSecret() == null
|| next.getClientSecret().equals(old.getClientSecret());
} Try / catch
try {
registeredClient.update(newMetadata);
} catch (OidcClientRegistrationException e) {
LOG.warn("Registration update rejected: " + e.getMessage());
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Client id can not be modified
- Registered OIDC Client can not make requests to the client c
- Failed to generate key id
- Application 'web-app' type is only supported if access token
- Failed to parse the realm name.
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/c03c29f560c5161a.
Report an issue: GitHub.