It would be nice to have a means to add entries or modify entries in the ClientRegistration.ProviderDetails.configurationMetadata

The method ClientRegistrations.fromOidcIssuerLocation returns a ClientRegistration.Builder which can be used to further modify the ClientRegistration. The providerConfigurationMetadata can also be overridden, but since it contains a Map, those values returned by the call to the ".well-known" endpoint will be lost.

        .providerConfigurationMetadata( 
                Collections.singletonMap("end_session_endpoint", "https://myprovider/oidc/logout")
        )

Since build() copies the values into an `unmodifiableMap', the Map also cannot be updated afterwards.

        clientRegistration.getProviderDetails().getConfigurationMetadata()
                    .put("end_session_endpoint", "https://myprovider/oidc/logout");
....
  java.lang.UnsupportedOperationException
        at java.util.Collections$UnmodifiableMap.put(Unknown Source)

My particular use case is to be able to customize the value of the "end_session_endpoint" which is stored in the providerConfigurationMetadata, but this might also be a handy place to store custom values which otherwise would require extending ClientRegistration.

Comment From: jgrandja

@pthorson You can customize the value of the end_session_endpoint as follows:

ClientRegistration registration = ClientRegistrations.fromOidcIssuerLocation("issuer-uri").build();

Map<String, Object> configurationMetadata = registration.getProviderDetails().getConfigurationMetadata();
// TODO update value of end_session_endpoint

ClientRegistration updatedRegistration = ClientRegistration.withClientRegistration(registration)
        .providerConfigurationMetadata(configurationMetadata)
        .build();

NOTE: ClientRegistration.withClientRegistration(registration) is available in 5.2.0.

I'm going to close this issue as I feel I provided you a solution. We can always reopen if need be.

Comment From: pthorson

OK I was still on 5.2.0 M2. I see the new method now that I have upgraded 5.2.1.

The ClientRegistration Build still sets the Map unmodifiable in its call to create:

    providerDetails.configurationMetadata = Collections.unmodifiableMap(this.configurationMetadata);

So I just have to copy it after grabbing it and then it can be modified and built again:

    Map<String, Object> unmofifiableConfigurationMetadata = clientRegistration.getProviderDetails().getConfigurationMetadata();
    Map<String, Object> configurationMetadata = new HashMap<>(unmofifiableConfigurationMetadata);
    configurationMetadata.put("end_session_endpoint", "my_uri");
    ClientRegistration updatedRegistration = ClientRegistration.withClientRegistration(clientRegistration)
            .providerConfigurationMetadata(configurationMetadata)
            .build();
    return updatedRegistration;

Thank you.

Comment From: downvoteit

Here is a full solution based on Keycloak OpenID for the wanderers.

package com.tb.ws.cscommon.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesRegistrationAdapter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Configuration
public class ClientRegistrationConfig {

  @Bean
  @ConditionalOnMissingBean({ClientRegistrationRepository.class})
  InMemoryClientRegistrationRepository clientRegistrationRepository(
      OAuth2ClientProperties properties) {
    List<ClientRegistration> registrations =
        OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(properties)
            .values()
            .stream()
            .map(
                o ->
                    ClientRegistration.withClientRegistration(o)
                        .providerConfigurationMetadata(
                            Map.of(
                                "end_session_endpoint",
                                "http://127.0.0.1:8080/auth/realms/OAuth2/protocol/openid-connect/logout"))
                        .build())
            .collect(Collectors.toList());

    return new InMemoryClientRegistrationRepository(registrations);
  }
}

We alter properties loading bean required for immutable InMemoryClientRegistrationRepository initialization.

Comment From: uavst

@pthorson could you please provide the complete class for this customisation?

OK I was still on 5.2.0 M2. I see the new method now that I have upgraded 5.2.1.

The ClientRegistration Build still sets the Map unmodifiable in its call to create:

providerDetails.configurationMetadata = Collections.unmodifiableMap(this.configurationMetadata);

So I just have to copy it after grabbing it and then it can be modified and built again:

Map<String, Object> unmofifiableConfigurationMetadata = clientRegistration.getProviderDetails().getConfigurationMetadata(); Map<String, Object> configurationMetadata = new HashMap<>(unmofifiableConfigurationMetadata); configurationMetadata.put("end_session_endpoint", "my_uri"); ClientRegistration updatedRegistration = ClientRegistration.withClientRegistration(clientRegistration) .providerConfigurationMetadata(configurationMetadata) .build(); return updatedRegistration;

Thank you.