I'm having trouble using SpringSecurity with a non-compliant OAuth provider (Clerk.com). When I use Spring Security with a default configuration, it complains that it's "Missing JSON object member with key subject_types_supported". When I check the returned JSON, I see that it is in fact missing the field. Given that, I'm trying to manually override the response object and add the field with a static value using the below bean. But when I try to do that, creating the bean itself throws the missing field exception. Seemingly, it fails before even reaching my custom adapter. I can't figure why it's doing that.
What's the correct way to manually add the field?
//Code adapted from https://github.com/spring-projects/spring-security/issues/5983#issuecomment-1369926906
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.core.endpoint.DefaultMapOAuth2AccessTokenResponseConverter;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.client.RestTemplate;
@Configuration
public class SecurityConfig {
@Primary
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
System.out.println("Loading custom security filter chain"); // Log to check if bean is loaded
return http.authorizeHttpRequests(httpReq -> httpReq.anyRequest().authenticated()).oauth2Login(
oauth2 -> oauth2.tokenEndpoint(token -> token.accessTokenResponseClient(linkedinTokenResponseClient())))
.build();
}
private static DefaultAuthorizationCodeTokenResponseClient linkedinTokenResponseClient() {
var defaultMapConverter = new DefaultMapOAuth2AccessTokenResponseConverter();
Converter<Map<String, Object>, OAuth2AccessTokenResponse> linkedinMapConverter = tokenResponse -> {
var withTokenType = new HashMap<>(tokenResponse);
withTokenType.put("subject_types_supported", List.of("public"));
return defaultMapConverter.convert(withTokenType);
};
var httpConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
httpConverter.setAccessTokenResponseConverter(linkedinMapConverter);
var restOperations = new RestTemplate(List.of(new FormHttpMessageConverter(), httpConverter));
restOperations.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
var client = new DefaultAuthorizationCodeTokenResponseClient();
client.setRestOperations(restOperations);
return client;
}
}