# OpenID Connect (OIDC) and OAuth2 dynamic client registration

| This technology is considered experimental. In experimental mode, early feedback is requested to mature the idea. There is no guarantee of stability nor long term presence in the platform until the solution matures. Feedback is welcome on our mailing list or as issues in our GitHub issue tracker. For a full list of possible statuses, check our FAQ entry.

Typically, you have to register an OIDC client (application) manually in your OIDC provider’s dashboard. During this process, you specify the human readable application name, allowed redirect and post logout URLs, and other properties. After the registration has been completed, you copy the generated client id and secret to your Quarkus OIDC application properties.

OpenID Connect and OAuth2 dynamic client registration allows you to register OIDC clients dynamically, and manage individual client registrations. You can read more about it in the OIDC client registration and OAuth2 Dynamic Client Registration Management Protocol specification documents.

You can use Quarkus `quarkus-oidc-client-registration` extension to register one or more clients using OIDC client registration configurations and read, update and delete metadata of the registered clients.

OIDC TenantConfigResolver can be used to create OIDC tenant configurations using the metadata of the registered clients.

| Currently, Quarkus `quarkus-oidc-client-registration` extension has an `experimental` status. Dynamic client registration API provided by this extension may change while this extension has an experiemental status.

## OIDC Client Registration

Add the following dependency:

    <dependency>
        <groupId>io.quarkus</groupId>
        <artifactId>quarkus-oidc-client-registration</artifactId>
    </dependency>

The `quarkus-oidc-client-registration` extension allows register one or more clients using OIDC client registration configurations, either on start-up or on demand, and read, update and delete metadata of the registered clients.

You can register and manage client registrations from the custom OIDC TenantConfigResolver.

Alternatively, you can register clients without even using OIDC. For example, it can be a command line tool which registers clients and passes metadata of the registered clients to Quarkus services which require them.

Each OIDC client registration configuration represents an OIDC client registration endpoint which can accept many individual client registrations.

### Register clients on start-up

You start with declaring one or more OIDC client registration configurations, for example:

    # Default OIDC client registration which auto-discovers a standard client registration endpoint.
    # It does not require an initial registration token.

    quarkus.oidc-client-registration.auth-server-url=${quarkus.oidc.auth-server-url}
    quarkus.oidc-client-registration.metadata.client-name=Default Client
    quarkus.oidc-client-registration.metadata.redirect-uri=http://localhost:8081/protected

    # Named OIDC client registration which configures a registration endpoint path:
    # It require an initial registration token for a client registration to succeed.

    quarkus.oidc-client-registration.tenant-client.registration-path=${quarkus.oidc.auth-server-url}/clients-registrations/openid-connect
    quarkus.oidc-client-registration.tenant-client.metadata.client-name=Tenant Client
    quarkus.oidc-client-registration.tenant-client.metadata.redirect-uri=http://localhost:8081/protected/tenant
    quarkus.oidc-client-registration.initial-token=${initial-registration-token}

The above configuration will lead to two new client registrations created in your OIDC provider.

You or may not need to acquire an initial registration access token. If you don’t, then you will have to enable one or more client registration policies in your OIDC provider’s dashboard. For example, see Keycloak client registration policies.

The next step is to inject either `quarkus.oidc.client.registration.OidcClientRegistration` if only a single default client registration is done, or `quarkus.oidc.client.registration.OidcClientRegistrations` if more than one registration is configured, and use metadata of the clients registered with these configurations.

For example:

    package io.quarkus.it.keycloak;

    import java.net.URI;
    import java.util.List;
    import java.util.Optional;

    import jakarta.inject.Inject;
    import jakarta.inject.Singleton;

    import org.eclipse.microprofile.config.inject.ConfigProperty;

    import io.quarkus.oidc.OidcRequestContext;
    import io.quarkus.oidc.OidcTenantConfig;
    import io.quarkus.oidc.OidcTenantConfig.ApplicationType;
    import io.quarkus.oidc.TenantConfigResolver;
    import io.quarkus.oidc.client.registration.ClientMetadata;
    import io.quarkus.oidc.client.registration.OidcClientRegistration;
    import io.quarkus.oidc.client.registration.OidcClientRegistrations;
    import io.quarkus.oidc.client.registration.RegisteredClient;
    import io.smallrye.mutiny.Uni;
    import io.vertx.ext.web.RoutingContext;

    @Singleton
    public class CustomTenantConfigResolver implements TenantConfigResolver {

        @Inject
        OidcClientRegistration clientReg;

        @Inject
        OidcClientRegistrations clientRegs;

        @Override
        public Uni<OidcTenantConfig> resolve(RoutingContext routingContext,
                OidcRequestContext<OidcTenantConfig> requestContext) {

            if (routingContext.request().path().endsWith("/protected")) {
                // Use the registered client created from the default OIDC client registration
                return clientReg.registeredClient().onItem().transform(client -> createTenantConfig("registered-client", client));
            } else if (routingContext.request().path().endsWith("/protected/tenant")) {
                // Use the registered client created from the named 'tenant-client' OIDC client registration
                OidcClientRegistration tenantClientReg = clientRegs.getClientRegistration("tenant-client");
                return tenantClientReg.registeredClient().onItem().transform(client -> createTenantConfig("registered-client-tenant", client));
            }
            return null;
        }

        // Convert metadata of registered clients to OidcTenantConfig
        private OidcTenantConfig createTenantConfig(String tenantId, RegisteredClient client) {
            ClientMetadata metadata = client.getMetadata();

            String redirectPath = URI.create(metadata.getRedirectUris().get(0)).getPath();
            OidcTenantConfig oidcConfig = OidcTenantConfig
                    .authServerUrl(authServerUrl)
                    .tenantId(tenantId)
                    .applicationType(ApplicationType.WEB_APP)
                    .clientName(metadata.getClientName())
                    .clientId(metadata.getClientId())
                    .credentials(metadata.getClientSecret())
                    .authentication().redirectPath(redirectPath).end()
                    .build();
            return oidcConfig;
        }
    }

### Register clients on demand

You can register new clients on demand. You can add new clients to the existing, already configured `OidcClientConfiguration` or to a newly created `OidcClientConfiguration`.

Start from configuring one or more OIDC client registrations:

    quarkus.oidc-client-registration.auth-server-url=${quarkus.oidc.auth-server-url}

The above configuration is sufficient for registering new clients using this configuration. For example:

    package io.quarkus.it.keycloak;

    import java.net.URI;
    import java.util.List;
    import java.util.Optional;

    import jakarta.inject.Inject;
    import jakarta.inject.Singleton;

    import org.eclipse.microprofile.config.inject.ConfigProperty;

    import io.quarkus.oidc.OidcRequestContext;
    import io.quarkus.oidc.OidcTenantConfig;
    import io.quarkus.oidc.OidcTenantConfig.ApplicationType;
    import io.quarkus.oidc.TenantConfigResolver;
    import io.quarkus.oidc.client.registration.ClientMetadata;
    import io.quarkus.oidc.client.registration.OidcClientRegistration;
    import io.smallrye.mutiny.Uni;
    import io.vertx.ext.web.RoutingContext;

    @Singleton
    public class CustomTenantConfigResolver implements TenantConfigResolver {

        @Inject
        OidcClientRegistration clientReg;

        @Inject
        @ConfigProperty(name = "quarkus.oidc.auth-server-url")
        String authServerUrl;

        @Override
        public Uni<OidcTenantConfig> resolve(RoutingContext routingContext,
                OidcRequestContext<OidcTenantConfig> requestContext) {
            if (routingContext.request().path().endsWith("/protected/oidc-client-reg-existing-config")) {
                // New client registration done dynamically at the request time using the configured client registration
                ClientMetadata metadata = createMetadata("http://localhost:8081/protected/dynamic-tenant",
                        "Dynamic Tenant Client");

                return clientReg.registerClient(metadata).onItem().transform(r ->
                      createTenantConfig("registered-client-dynamically", r));
            }
            return null;
        }

        // Create metadata of registered clients to OidcTenantConfig
        private OidcTenantConfig createTenantConfig(String tenantId, ClientMetadata metadata) {
            String redirectPath = URI.create(metadata.getRedirectUris().get(0)).getPath();
            OidcTenantConfig oidcConfig = OidcTenantConfig
                    .authServerUrl(authServerUrl)
                    .tenantId(tenantId)
                    .applicationType(ApplicationType.WEB_APP)
                    .clientName(metadata.getClientName())
                    .clientId(metadata.getClientId())
                    .credentials(metadata.getClientSecret())
                    .authentication().redirectPath(redirectPath).end()
                    .build();
            return oidcConfig;
        }

        protected static ClientMetadata createMetadata(String redirectUri, String clientName) {
            return ClientMetadata.builder()
                    .setRedirectUri(redirectUri)
                    .setClientName(clientName)
                    .build();
        }
    }

Alternatively, you can use `OidcClientRegistrations` to prepare a new `OidcClientRegistration` and use `OidcClientRegistration` to register a client. For example:

    package io.quarkus.it.keycloak;

    import java.net.URI;
    import java.util.List;
    import java.util.Map;
    import java.util.Optional;

    import jakarta.inject.Inject;
    import jakarta.inject.Singleton;

    import org.eclipse.microprofile.config.inject.ConfigProperty;

    import io.quarkus.oidc.OidcRequestContext;
    import io.quarkus.oidc.OidcTenantConfig;
    import io.quarkus.oidc.OidcTenantConfig.ApplicationType;
    import io.quarkus.oidc.TenantConfigResolver;
    import io.quarkus.oidc.client.registration.ClientMetadata;
    import io.quarkus.oidc.client.registration.OidcClientRegistration;
    import io.quarkus.oidc.client.registration.OidcClientRegistrations;
    import io.quarkus.oidc.client.registration.OidcClientRegistrationConfig;
    import io.smallrye.mutiny.Uni;
    import io.vertx.ext.web.RoutingContext;

    @Singleton
    public class CustomTenantConfigResolver implements TenantConfigResolver {

        @Inject
        OidcClientRegistrations clientRegs;

        @Inject
        @ConfigProperty(name = "quarkus.oidc.auth-server-url")
        String authServerUrl;

        @Override
        public Uni<OidcTenantConfig> resolve(RoutingContext routingContext,
                OidcRequestContext<OidcTenantConfig> requestContext) {
            if (routingContext.request().path().endsWith("/protected/new-oidc-client-reg")) {
                // New client registration done dynamically at the request time

                OidcClientRegistrationConfig clientRegConfig = OidcClientRegistrationConfig
                        .authServerUrl(authServerUrl)
                        .metadata("Dynamic Client", "http://localhost:8081/protected/new-oidc-client-reg")
                        .build();

                return clientRegs.newClientRegistration(clientRegConfig)
                        .onItem().transform(reg ->
                        createTenantConfig("registered-client-dynamically", reg.registeredClient());
            }

            return null;
        }

        // Create metadata of registered clients to OidcTenantConfig
        private OidcTenantConfig createTenantConfig(String tenantId, ClientMetadata metadata) {
            String redirectPath = URI.create(metadata.getRedirectUris().get(0)).getPath();
            OidcTenantConfig oidcConfig = OidcTenantConfig
                    .authServerUrl(authServerUrl)
                    .tenantId(tenantId)
                    .applicationType(ApplicationType.WEB_APP)
                    .clientName(metadata.getClientName())
                    .clientId(metadata.getClientId())
                    .credentials(metadata.getClientSecret())
                    .authentication().redirectPath(redirectPath).end()
                    .build();
            return oidcConfig;
        }

        protected static ClientMetadata createMetadata(String redirectUri, String clientName) {
            return ClientMetadata.builder()
                    .setRedirectUri(redirectUri)
                    .setClientName(clientName)
                    .build();
        }
    }

### Managing registered clients

`io.quarkus.oidc.client.registration.RegisteredClient` represents a registered client and can be used to read and update its metadata. It can also be used to delete this client.

For example:

    package io.quarkus.it.keycloak;

    import jakarta.enterprise.event.Observes;
    import jakarta.inject.Inject;
    import jakarta.inject.Singleton;

    import io.quarkus.oidc.OidcRequestContext;
    import io.quarkus.oidc.OidcTenantConfig;
    import io.quarkus.oidc.TenantConfigResolver;
    import io.quarkus.oidc.client.registration.OidcClientRegistration;
    import io.quarkus.oidc.client.registration.RegisteredClient;
    import io.quarkus.runtime.StartupEvent;
    import io.smallrye.mutiny.Uni;
    import io.vertx.ext.web.RoutingContext;

    @Singleton
    public class CustomTenantConfigResolver implements TenantConfigResolver {

        @Inject
        OidcClientRegistration clientReg;

        RegisteredClient registeredClient;

        void onStartup(@Observes StartupEvent event) {

            // Default OIDC client registration, client has already been registered at startup, `await()` will return immediately.
            registeredClient = clientReg.registeredClient().await().indefinitely();

            // Read the latest client metadata
            registeredClient = registeredClient.read().await().indefinitely();
        }

        @Override
        public Uni<OidcTenantConfig> resolve(RoutingContext routingContext,
                OidcRequestContext<OidcTenantConfig> requestContext) {

            if (routingContext.request().path().endsWith("/protected")) {
                // Use the registered client created from the default OIDC client registration

                return createTenantConfig("registered-client", registeredClient));
            }
            return null;
        }

        // Convert metadata of registered clients to OidcTenantConfig
        private OidcTenantConfig createTenantConfig(String tenantId, RegisteredClient client) {
            ClientMetadata metadata = client.getMetadata();

            String redirectPath = URI.create(metadata.getRedirectUris().get(0)).getPath();
            OidcTenantConfig oidcConfig = OidcTenantConfig
                    .authServerUrl(authServerUrl)
                    .tenantId(tenantId)
                    .applicationType(ApplicationType.WEB_APP)
                    .clientName(metadata.getClientName())
                    .clientId(metadata.getClientId())
                    .credentials(metadata.getClientSecret())
                    .authentication().redirectPath(redirectPath).end()
                    .build();
            return oidcConfig;
        }
    }

### Avoiding duplicate registrations

When you register clients in startup, as described in the Register clients on start-up section, you will most likely want to avoid creating duplicate registrations after a restart.

In this case, you should configure OIDC client registration to perform the registration at the request time, as opposed to at the startup time:

    quarkus.oidc-client-registration.register-early=false

The next thing you should do is to persist the already registered client’s registration URI and registration token at the shutdown time, you can get them from the `io.quarkus.oidc.client.registration.RegisteredClient` instance.

Finally, at the startup time, you should restore the already registered client instead of registering it again.

For example:

    package io.quarkus.it.keycloak;

    import jakarta.enterprise.event.Observes;
    import jakarta.inject.Inject;
    import jakarta.inject.Singleton;

    import io.quarkus.oidc.OidcRequestContext;
    import io.quarkus.oidc.OidcTenantConfig;
    import io.quarkus.oidc.TenantConfigResolver;
    import io.quarkus.oidc.client.registration.OidcClientRegistration;
    import io.quarkus.oidc.client.registration.RegisteredClient;
    import io.quarkus.runtime.ShutdownEvent;
    import io.quarkus.runtime.StartupEvent;
    import io.smallrye.mutiny.Uni;
    import io.vertx.ext.web.RoutingContext;

    @Singleton
    public class CustomTenantConfigResolver implements TenantConfigResolver {

        @Inject
        OidcClientRegistration clientReg;

        RegisteredClient registeredClient;

        void onStartup(@Observes StartupEvent event) {

            String registrationUri = readRegistrationUriFromDatabase("Registered Client");
            String registrationToken = readRegistrationTokenFromDatabase("Registered Client");

            if (registrationUri != null && registrationToken != null) {
                // Read an already registered client
                registeredClient = clientReg.readClient(registrationUri, registrationToken).await().indefinitely();
            } else {
                // Register a new client
                registeredClient = clientReg.registeredClient().await().indefinitely();
            }

        }

        void onShutdown(@Observes ShutdownEvent event) {

            saveRegistrationUriToDatabase("Registered Client", registeredClient.registrationUri());
            saveRegistrationTokenToDatabase("Registered Client", registeredClient.registrationToken());

        }

        String readRegistrationUriFromDatabase(String clientName) {
            // implementation is not shown for brevity
        }
        String readRegistrationTokenFromDatabase(String clientName) {
            // implementation is not shown for brevity
        }
        void saveRegistrationUriToDatabase(String clientName, String registrationUri) {
            // implementation is not shown for brevity
        }
        void saveRegistrationTokenToDatabase(String clientName, String registrationToken) {
            // implementation is not shown for brevity
        }

        @Override
        public Uni<OidcTenantConfig> resolve(RoutingContext routingContext,
                OidcRequestContext<OidcTenantConfig> requestContext) {

            if (routingContext.request().path().endsWith("/protected")) {
                // Use the registered client created from the default OIDC client registration

                return createTenantConfig("registered-client", registeredClient));
            }
            return null;
        }

        // Convert metadata of registered clients to OidcTenantConfig
        private OidcTenantConfig createTenantConfig(String tenantId, RegisteredClient client) {
            ClientMetadata metadata = client.getMetadata();

            String redirectPath = URI.create(metadata.getRedirectUris().get(0)).getPath();
            OidcTenantConfig oidcConfig = OidcTenantConfig
                    .authServerUrl(authServerUrl)
                    .tenantId(tenantId)
                    .applicationType(ApplicationType.WEB_APP)
                    .clientName(metadata.getClientName())
                    .clientId(metadata.getClientId())
                    .credentials(metadata.getClientSecret())
                    .authentication().redirectPath(redirectPath).end()
                    .build();
            return oidcConfig;
        }
    }

If you register clients dynamically, on demand, as described in the Register clients on demand section, the problem of the duplicate client registration should not arise. You can persist the already registered client’s registration URI and registration token if necessary though and check them too to avoid any duplicate reservation risk.

## OIDC request filters

You can filter OIDC client registration and registered client requests registering one or more `OidcRequestFilter` implementations, which can update or add new request headers, as well as customize a request body.

For example, a filter can analyze the request body and add its digest as a new header value:

You can have a single filter intercepting all the OIDC registration and registered client requests, or use an `@OidcEndpoint` annotation to apply this filter to either OIDC registration or registered client endpoint responses only. For example:

    package io.quarkus.it.keycloak;

    import org.jboss.logging.Logger;

    import io.quarkus.arc.Unremovable;
    import io.quarkus.oidc.common.OidcEndpoint;
    import io.quarkus.oidc.common.OidcEndpoint.Type;
    import io.quarkus.oidc.common.OidcRequestFilter;
    import io.vertx.core.json.JsonObject;
    import jakarta.enterprise.context.ApplicationScoped;

    @ApplicationScoped
    @Unremovable
    @OidcEndpoint(value = Type.CLIENT_REGISTRATION) (1)
    public class ClientRegistrationRequestFilter implements OidcRequestFilter {
        private static final Logger LOG = Logger.getLogger(ClientRegistrationRequestFilter.class);

        @Override
        public void filter(OidcRequestContext rc) {
            JsonObject body = rc.requestBody().toJsonObject();
            if ("Default Client".equals(body.getString("client_name"))) { (2)
                LOG.debug("'Default Client' registration request");
            }
        }

    }

1 | Restrict this filter to requests targeting the OIDC client registration endpoint only.
2 | Check the 'client_name' property in the request metadata JSON.

`OidcRequestContextProperties` can be used to access request properties. Currently, you can use a `client_id` key to access the client tenant id and a `grant_type` key to access the grant type which the OIDC client uses to acquire tokens.

`OidcRequestFilter` can customize a request body by preparing an instance of `io.vertx.mutiny.core.buffer.Buffer` and setting it on a request context, for example:

    package io.quarkus.it.keycloak;

    import jakarta.enterprise.context.ApplicationScoped;

    import io.quarkus.arc.Unremovable;
    import io.quarkus.oidc.common.OidcEndpoint;
    import io.quarkus.oidc.common.OidcEndpoint.Type;
    import io.quarkus.oidc.common.OidcRequestContextProperties;
    import io.quarkus.oidc.common.OidcRequestFilter;
    import io.vertx.core.json.JsonObject;
    import io.vertx.mutiny.core.buffer.Buffer;

    @ApplicationScoped
    @Unremovable
    @OidcEndpoint(value = Type.CLIENT_REGISTRATION)
    public class ClientRegistrationReRequestFilter implements OidcRequestFilter {

        @Override
        public void filter(OidcRequestContext rc) {
            // Update the client name
            JsonObject body = rc.requestBody().toJsonObject();
            if ("Dynamic Tenant Client".equals(body.getString("client_name"))) {
                body.put("client_name", "Registered Dynamic Tenant Client");
                rc.requestBody(Buffer.buffer(body.toString()));
            }
        }
    }

## OIDC response filters

You can filter responses to OIDC client registration and registered client requests by registering one or more `OidcResponseFilter` implementations, which can check the response status, headers and body in order to log them or perform other actions.

You can have a single filter intercepting responses to all the OIDC registration and registered client requests, or use an `@OidcEndpoint` annotation to apply this filter to responses from either OIDC registration or registered client endpoint only. For example:

    package io.quarkus.it.keycloak;

    import jakarta.enterprise.context.ApplicationScoped;

    import org.jboss.logging.Logger;

    import io.quarkus.arc.Unremovable;
    import io.quarkus.oidc.common.OidcEndpoint;
    import io.quarkus.oidc.common.OidcEndpoint.Type;
    import io.quarkus.oidc.common.OidcResponseFilter;
    import io.vertx.core.json.JsonObject;

    @ApplicationScoped
    @Unremovable
    @OidcEndpoint(value = Type.CLIENT_REGISTRATION) (1)
    public class ClientRegistrationResponseFilter implements OidcResponseFilter {
        private static final Logger LOG = Logger.getLogger(ClientRegistrationResponseFilter.class);

        @Override
        public void filter(OidcResponseContext rc) {
            String contentType = rc.responseHeaders().get("Content-Type"); (2)
            JsonObject body = rc.responseBody().toJsonObject();
            if (contentType.startsWith("application/json")
                    && "Default Client".equals(body.getString("client_name"))) { (3)
                LOG.debug("'Default Client' has been registered");
            }
        }

    }

1 | Restrict this filter to requests targeting the OIDC client registration endpoint only.
2 | Check the response `Content-Type` header.
3 | Check the 'client_name' property in the response metadata JSON.

or

    package io.quarkus.it.keycloak;

    import jakarta.enterprise.context.ApplicationScoped;

    import org.jboss.logging.Logger;

    import io.quarkus.arc.Unremovable;
    import io.quarkus.oidc.common.OidcEndpoint;
    import io.quarkus.oidc.common.OidcEndpoint.Type;
    import io.quarkus.oidc.common.OidcResponseFilter;

    @ApplicationScoped
    @Unremovable
    @OidcEndpoint(value = Type.REGISTERED_CLIENT) (1)
    public class RegisteredClientResponseFilter implements OidcResponseFilter {
        private static final Logger LOG = Logger.getLogger(RegisteredClientResponseFilter.class);

        @Override
        public void filter(OidcResponseContext rc) {
            String contentType = rc.responseHeaders().get("Content-Type"); (2)
            if (contentType.startsWith("application/json")
                    && "Default Client Updated".equals(rc.responseBody().toJsonObject().getString("client_name"))) { (3)
                LOG.debug("Registered 'Default Client' has had its name updated to 'Default Client Updated'");
            }
        }

    }

1 | Restrict this filter to requests targeting the registered OIDC client endpoint only.
2 | Check the response `Content-Type` header.
3 | Confirm the client name property was updated.

`OidcResponseFilter` can customize a response body by preparing an instance of `io.vertx.mutiny.core.buffer.Buffer` and setting it as a property on a response context, for example:

    package io.quarkus.it.keycloak;

    import jakarta.enterprise.context.ApplicationScoped;

    import org.jboss.logging.Logger;

    import io.quarkus.arc.Unremovable;
    import io.quarkus.oidc.common.OidcEndpoint;
    import io.quarkus.oidc.common.OidcEndpoint.Type;
    import io.quarkus.oidc.common.OidcRequestContextProperties;
    import io.quarkus.oidc.common.OidcResponseFilter;
    import io.vertx.core.json.JsonObject;
    import io.vertx.mutiny.core.buffer.Buffer;

    @ApplicationScoped
    @Unremovable
    @OidcEndpoint(value = Type.CLIENT_REGISTRATION)
    public class ClientRegistrationResponseFilter implements OidcResponseFilter {

        @Override
        public void filter(OidcResponseContext rc) {
            // Update the client name
            JsonObject body = rc.responseBody().toJsonObject();
            if ("Registered Dynamic Tenant Client".equals(body.getString("client_name"))) {
                body.put("client_name", "Registered Dynamically Tenant Client");
                rc.responseContext(Buffer.buffer(body.toString()));
            }
        }
    }

## Configuration reference

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property | Type | Default
--- | --- | ---
`quarkus.oidc-client-registration.enabled` If the OIDC client registration extension is enabled. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_ENABLED` | boolean | `true`
`quarkus.oidc-client-registration.auth-server-url` `quarkus.oidc-client-registration."id".auth-server-url` The base URL of the OpenID Connect (OIDC) server, for example, `https://host:port/auth`. Do not set this property if you use 'quarkus-oidc' and the public key verification (`public-key`) or certificate chain verification only (`certificate-chain`) is required. The OIDC discovery endpoint is called by default by appending a `.well-known/openid-configuration` path to this URL. For Keycloak, use `https://host:port/realms/{realm}`, replacing `{realm}` with the Keycloak realm name. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_AUTH_SERVER_URL` | string |
`quarkus.oidc-client-registration.discovery-enabled` `quarkus.oidc-client-registration."id".discovery-enabled` Discovery of the OIDC endpoints. If not enabled, you must configure the OIDC endpoint URLs individually. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_DISCOVERY_ENABLED` | boolean | `true`
`quarkus.oidc-client-registration.registration-path` `quarkus.oidc-client-registration."id".registration-path` The relative path or absolute URL of the OIDC dynamic client registration endpoint. Set if `discovery-enabled` is `false` or a discovered token endpoint path must be customized. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_REGISTRATION_PATH` | string |
`quarkus.oidc-client-registration.connection-delay` `quarkus.oidc-client-registration."id".connection-delay` The duration to attempt the initial connection to an OIDC server. For example, setting the duration to `20S` allows 10 retries, each 2 seconds apart. This property is only effective when the initial OIDC connection is created. For dropped connections, use the `connection-retry-count` property instead. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_CONNECTION_DELAY` | Duration |
`quarkus.oidc-client-registration.connection-retry-count` `quarkus.oidc-client-registration."id".connection-retry-count` The number of times to retry re-establishing an existing OIDC connection if it is temporarily lost. Different from `connection-delay`, which applies only to initial connection attempts. For instance, if a request to the OIDC token endpoint fails due to a connection issue, it will be retried as per this setting. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_CONNECTION_RETRY_COUNT` | int | `3`
`quarkus.oidc-client-registration.connection-timeout` `quarkus.oidc-client-registration."id".connection-timeout` The number of seconds after which the current OIDC connection request times out. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_CONNECTION_TIMEOUT` | Duration | `10S`
`quarkus.oidc-client-registration.use-blocking-dns-lookup` `quarkus.oidc-client-registration."id".use-blocking-dns-lookup` Whether DNS lookup should be performed on the worker thread. Use this option when you can see logged warnings about blocked Vert.x event loop by HTTP requests to OIDC server. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_USE_BLOCKING_DNS_LOOKUP` | boolean | `false`
`quarkus.oidc-client-registration.max-pool-size` `quarkus.oidc-client-registration."id".max-pool-size` The maximum size of the connection pool used by the WebClient. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_MAX_POOL_SIZE` | int |
`quarkus.oidc-client-registration.follow-redirects` `quarkus.oidc-client-registration."id".follow-redirects` Follow redirects automatically when WebClient gets HTTP 302. When this property is disabled only a single redirect to exactly the same original URI is allowed but only if one or more cookies were set during the redirect request. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_FOLLOW_REDIRECTS` | boolean | `true`
`quarkus.oidc-client-registration.id` `quarkus.oidc-client-registration."id".id` OIDC Client Registration id Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_ID` | string |
`quarkus.oidc-client-registration.registration-enabled` `quarkus.oidc-client-registration."id".registration-enabled` If this client registration configuration is enabled. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_REGISTRATION_ENABLED` | boolean | `true`
`quarkus.oidc-client-registration.register-early` `quarkus.oidc-client-registration."id".register-early` If the client configured with `metadata` must be registered at startup. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_REGISTER_EARLY` | boolean | `true`
`quarkus.oidc-client-registration.initial-token` `quarkus.oidc-client-registration."id".initial-token` Initial access token Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_INITIAL_TOKEN` | string |
`quarkus.oidc-client-registration.metadata.client-name` `quarkus.oidc-client-registration."id".metadata.client-name` Client name Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_METADATA_CLIENT_NAME` | string |
`quarkus.oidc-client-registration.metadata.redirect-uri` `quarkus.oidc-client-registration."id".metadata.redirect-uri` Redirect URI Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_METADATA_REDIRECT_URI` | string |
`quarkus.oidc-client-registration.metadata.post-logout-uri` `quarkus.oidc-client-registration."id".metadata.post-logout-uri` Post Logout URI Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_METADATA_POST_LOGOUT_URI` | string |
`quarkus.oidc-client-registration.metadata.extra-props."extra-props"` `quarkus.oidc-client-registration."id".metadata.extra-props."extra-props"` Additional metadata properties Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_METADATA_EXTRA_PROPS__EXTRA_PROPS_` | Map<String,String> |
HTTP proxy configuration | Type | Default
`quarkus.oidc-client-registration.proxy.host` `quarkus.oidc-client-registration."id".proxy.host` The host name or IP address of the Proxy. Note: If the OIDC adapter requires a Proxy to talk with the OIDC server (Provider), set this value to enable the usage of a Proxy. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_PROXY_HOST` | string |
`quarkus.oidc-client-registration.proxy.port` `quarkus.oidc-client-registration."id".proxy.port` The port number of the Proxy. The default value is `80`. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_PROXY_PORT` | int | `80`
`quarkus.oidc-client-registration.proxy.username` `quarkus.oidc-client-registration."id".proxy.username` The username, if the Proxy needs authentication. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_PROXY_USERNAME` | string |
`quarkus.oidc-client-registration.proxy.password` `quarkus.oidc-client-registration."id".proxy.password` The password, if the Proxy needs authentication. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_PROXY_PASSWORD` | string |
TLS configuration | Type | Default
`quarkus.oidc-client-registration.tls.tls-configuration-name` `quarkus.oidc-client-registration."id".tls.tls-configuration-name` The name of the TLS configuration to use. If a name is configured, it uses the configuration from `quarkus.tls.<name>.*` If a name is configured, but no TLS configuration is found with that name then an error will be thrown. The default TLS configuration is not used by default. Environment variable: `QUARKUS_OIDC_CLIENT_REGISTRATION_TLS_TLS_CONFIGURATION_NAME` | string |

| About the Duration format To write duration values, use the standard `java.time.Duration` format. See the Duration#parse() Java API documentation for more information. You can also use a simplified format, starting with a number: If the value is only a number, it represents time in seconds. If the value is a number followed by `ms`, it represents time in milliseconds. In other cases, the simplified format is translated to the `java.time.Duration` format for parsing: If the value is a number followed by `h`, `m`, or `s`, it is prefixed with `PT`. If the value is a number followed by `d`, it is prefixed with `P`.

## References

- OIDC client registration

- OAuth2 Dynamic Client Registration Management Protocol

- Keycloak Dynamic Client Registration Service

- OIDC Bearer token authentication

- OIDC code flow mechanism for protecting web applications

- Quarkus Security overview