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

  1. Set the client secret to null in the metadata passed to update() so the server keeps the existing secret
  2. Only modify fields you intend to change; build the new metadata from the registered metadata without touching clientId/clientSecret
  3. 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

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


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/c03c29f560c5161a. Report an issue: GitHub.