The error occurs when using the client application and the OAuth2 authorization server. I use Spring. I have set up an authorization server, a resource server, and a client. The authorization server and the resource server work correctly when using curl. But when I wrote the settings for the client, including in apllication.yaml, and launched, then got the error Unable to resolve Configuration with the provided Issuer of "http:localhost:9000" and Caused by: org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [java.util.Map<java.lang.String, java.lang.Object>] and content type [text/html;charset=UTF-8]. The authorization server is running at the same time. Authorization Server Settings:

@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        return http
                .formLogin(Customizer.withDefaults())
                .build();
    }

    @Bean
    RegisteredClientRepository registeredClientRepository(PasswordEncoder passwordEncoder) {
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("theatre-admin-client")
                .clientSecret(passwordEncoder.encode("secret"))
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .redirectUri("http:localhost:9090/login/oauth2/code/theatre-admin-client")
                .scope("writeClients")
                .scope("deleteClients")
                .scope("updateClients")
                .scope(OidcScopes.OPENID)
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .build();
        return new InMemoryRegisteredClientRepository(registeredClient);
    }

    @Bean
    JWKSource<SecurityContext> jwkSource() throws NoSuchAlgorithmException {
        RSAKey rsaKey = generateRsa();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return ((jwkSelector, securityContext) -> jwkSelector.select(jwkSet));
    }

    private static RSAKey generateRsa() throws NoSuchAlgorithmException {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        return new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
    }
    
    private static KeyPair generateRsaKey() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(2048);
        return keyPairGenerator.generateKeyPair();
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

}

Security configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        return http.authorizeRequests(authorizeRequests ->
                authorizeRequests.anyRequest().authenticated())
                .formLogin()
                .and().build();
    }

    @Bean
    UserDetailsService userDetailsService(UserRepository userRepository) {
        return username -> userRepository.findByUsername(username);
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

pom.xml:

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-authorization-server</artifactId>
    <version>1.1.2</version>
</dependency>

This is where the client and resource server are configured. Class SecurityConfig:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public UserDetailsService userDetailsService(UserRepository userRepository) {
        return username -> {
            com.example.theatre.entity.User user = userRepository.findByUsername(username);
            if (user != null) return user;
            throw new UsernameNotFoundException("User '" + username + "' not found");
        };
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.authorizeRequests()
                .antMatchers(HttpMethod.POST, "/rest-api-clients").hasAuthority("SCOPE_writeClients")
                .antMatchers(HttpMethod.PUT, "/rest-api-clients").hasAuthority("SCOPE_updateClients")
                .antMatchers(HttpMethod.DELETE, "/rest-api-clients").hasAuthority("SCOPE_deleteClients")
                .antMatchers("/admin").access("hasRole('USER')")
                .antMatchers("/","/**").access("permitAll()")
                .and()
                .oauth2ResourceServer(oauth2 -> oauth2.jwt())
                .formLogin()
                    .loginPage("/login")
                    .defaultSuccessUrl("/")
                .and()
                .logout()
                    .logoutSuccessUrl("/")
                .and()
                .csrf().disable()
                .build();
    }

    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated())
                .oauth2Login(
                        oauth2Login ->
                         oauth2Login.loginPage("/oauth2/authorization/theatre-admin-client")
                )
                .oauth2Client(Customizer.withDefaults());
        return http.build();
    }
}

Service for viewing, and saving clients:

public class RestClientService implements ClientService {

    RestTemplate restTemplate;

    @Autowired
    public RestClientService(String accessToken) {
        this.restTemplate = new RestTemplate();
        if (accessToken != null) {
            this.restTemplate
                    .getInterceptors()
                    .add(getBearerTokenInterceptor(accessToken));

        }

    }

    private ClientHttpRequestInterceptor getBearerTokenInterceptor(String accessToken) {
        ClientHttpRequestInterceptor interceptor = new ClientHttpRequestInterceptor() {
            @Override
            public ClientHttpResponse intercept(HttpRequest request, byte[] body,
                                                ClientHttpRequestExecution execution) throws IOException {
                request.getHeaders().add("Authorization", "Bearer " + accessToken);

                return execution.execute(
                        request,
                        body
                );
            }
        };
        return interceptor;
    }

    @Override
    public Page<Client> findAllClients(Pageable pageable) {
        return (Page<Client>) Arrays.asList(restTemplate.getForObject(
                "http:localhost:9090/rest-api-clients?recent", Client[].class
        ));
    }

    @Override
    public Client save(Client client) {
        return restTemplate.postForObject(
                "http:localhost:9090/rest-api-clients",
                client,
                Client.class);
    }
}

We get a token with each new request for RestClientService:

@Configuration
public class RestClientServiceConfig {

    @Bean
    @RequestScope 
    public ClientService clientService(OAuth2AuthorizedClientService clientService) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        String accessToken = null;

        if (authentication.getClass().isAssignableFrom(OAuth2AuthenticationToken.class)) {
            OAuth2AuthenticationToken oauthToken = (OAuth2AuthenticationToken) authentication;
            String clientRegistrationId = oauthToken.getAuthorizedClientRegistrationId();

            if (clientRegistrationId.equals("theatre-admin-client")) {
                OAuth2AuthorizedClient client = clientService.loadAuthorizedClient(
                        clientRegistrationId, oauthToken.getName()
                );
                accessToken = client.getAccessToken().getTokenValue();
            }
        }
        return new RestClientService(accessToken);
    }
}

application.yaml:

spring:
  profiles:
    active: dev
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: http://localhost:9000/oauth2/jwks
      client:
        registration:
          theatre-admin-client:
            provider: theatre
            client-id: theatre-admin-client
            client-secret: secret
            authorization-grant-type: authorization_code
            redirect-uri: "http://localhost:9090/login/oauth2/code/{registrationId}"
            scope:
              - writeClients
              - deleteClients
              - updateClients
              - openid
        provider:
          theatre:
            issuer-uri: http:localhost:9000

Error log:

Caused by: java.lang.IllegalArgumentException: Unable to resolve Configuration with the provided Issuer of "http:localhost:9000"
    at org.springframework.security.oauth2.client.registration.ClientRegistrations.getBuilder(ClientRegistrations.java:220) ~[spring-security-oauth2-client-5.7.3.jar:5.7.3]
    at org.springframework.security.oauth2.client.registration.ClientRegistrations.fromIssuerLocation(ClientRegistrations.java:144) ~[spring-security-oauth2-client-5.7.3.jar:5.7.3]
    at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesRegistrationAdapter.getBuilderFromIssuerIfPossible(OAuth2ClientPropertiesRegistrationAdapter.java:83) ~[spring-boot-autoconfigure-2.7.4.jar:2.7.4]
    at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesRegistrationAdapter.getClientRegistration(OAuth2ClientPropertiesRegistrationAdapter.java:59) ~[spring-boot-autoconfigure-2.7.4.jar:2.7.4]
    at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesRegistrationAdapter.lambda$getClientRegistrations$0(OAuth2ClientPropertiesRegistrationAdapter.java:53) ~[spring-boot-autoconfigure-2.7.4.jar:2.7.4]
    at java.base/java.util.HashMap.forEach(HashMap.java:1421) ~[na:na]
    at org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientPropertiesRegistrationAdapter.getClientRegistrations(OAuth2ClientPropertiesRegistrationAdapter.java:52) ~[spring-boot-autoconfigure-2.7.4.jar:2.7.4]
    at org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientRegistrationRepositoryConfiguration.clientRegistrationRepository(OAuth2ClientRegistrationRepositoryConfiguration.java:49) ~[spring-boot-autoconfigure-2.7.4.jar:2.7.4]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.23.jar:5.3.23]
    ... 87 common frames omitted
Caused by: org.springframework.web.client.UnknownContentTypeException: Could not extract response: no suitable HttpMessageConverter found for response type [java.util.Map<java.lang.String, java.lang.Object>] and content type [text/html;charset=UTF-8]
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:126) ~[spring-web-5.3.23.jar:5.3.23]
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1037) ~[spring-web-5.3.23.jar:5.3.23]
    at org.springframework.web.client.RestTemplate$ResponseEntityResponseExtractor.extractData(RestTemplate.java:1020) ~[spring-web-5.3.23.jar:5.3.23]
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:778) ~[spring-web-5.3.23.jar:5.3.23]
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:670) ~[spring-web-5.3.23.jar:5.3.23]
    at org.springframework.security.oauth2.client.registration.ClientRegistrations.lambda$oidc$0(ClientRegistrations.java:155) ~[spring-security-oauth2-client-5.7.3.jar:5.7.3]
    at org.springframework.security.oauth2.client.registration.ClientRegistrations.getBuilder(ClientRegistrations.java:208) ~[spring-security-oauth2-client-5.7.3.jar:5.7.3]
    ... 99 common frames omitted

I tried setting up HttpMessageConverter so that the HttpMessageConverter error was fixed, but it didn't help. I tried it like this:


@Configuration
public class HttpMessageConverterConfig {

    @Bean
    public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
        MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        jsonConverter.setObjectMapper(objectMapper);
        return jsonConverter;
    }

}

The output is the same error. I would be grateful if someone would help!

P.S.: I changed the URL because of stackoverflow's spam policy.

1

There are 1 best solutions below

1
On

The OAuth2 client registration for theatre-admin-client is configured with an issuer URI of http:localhost:9000, but the authorization server is actually running at http://localhost:9000.

So put the issuer-uri directly under theatre-admin-client in application.yaml file.

Example

spring:
  security:
    oauth2:
      client:
        registration:
          theatre-admin-client:
            provider: theatre
            client-id: theatre-admin-client
            client-secret: secret
            authorization-grant-type: authorization_code
            redirect-uri: "http://localhost:9090/login/oauth2/code/{registrationId}"
            scope:
              - writeClients
              - deleteClients
              - updateClients
              - openid
            issuer-uri: http://localhost:9000