diff --git a/spring-social/src/main/java/com/example/springsocial/config/SecurityConfig.java b/spring-social/src/main/java/com/example/springsocial/config/SecurityConfig.java index 41f5b4c..e40749f 100644 --- a/spring-social/src/main/java/com/example/springsocial/config/SecurityConfig.java +++ b/spring-social/src/main/java/com/example/springsocial/config/SecurityConfig.java @@ -1,13 +1,23 @@ package com.example.springsocial.config; -import com.example.springsocial.security.*; +import com.example.springsocial.security.CustomUserDetailsService; +import com.example.springsocial.security.RestAuthenticationEntryPoint; +import com.example.springsocial.security.TokenAuthenticationFilter; import com.example.springsocial.security.oauth2.CustomOAuth2UserService; import com.example.springsocial.security.oauth2.HttpCookieOAuth2AuthorizationRequestRepository; import com.example.springsocial.security.oauth2.OAuth2AuthenticationFailureHandler; import com.example.springsocial.security.oauth2.OAuth2AuthenticationSuccessHandler; + +import java.util.ArrayList; +import java.util.List; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.MediaType; +import org.springframework.http.converter.AbstractHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; @@ -18,9 +28,12 @@ import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.oauth2.client.web.AuthorizationRequestRepository; -import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; +import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest; +import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; +import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.client.RestTemplate; @Configuration @EnableWebSecurity @@ -123,7 +136,7 @@ protected void configure(HttpSecurity http) throws Exception { .baseUri("/oauth2/callback/*") .and() .userInfoEndpoint() - .userService(customOAuth2UserService) + .userService(oauth2UserService()) .and() .successHandler(oAuth2AuthenticationSuccessHandler) .failureHandler(oAuth2AuthenticationFailureHandler); @@ -131,4 +144,36 @@ protected void configure(HttpSecurity http) throws Exception { // Add our custom Token based authentication filter http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); } + + private OAuth2UserService oauth2UserService() { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler()); + enhanceJsonMessageConverter(restTemplate); + customOAuth2UserService.setRestOperations(restTemplate); + return customOAuth2UserService; + } + + private void enhanceJsonMessageConverter(RestTemplate restTemplate) { + // NOTE: + // Facebook's UserInfo API -> https://graph.facebook.com/me + // returns "text/javascript; charset=UTF-8" for the "content-type" response header + // even though the content is JSON. This is not correct and should be reported to Facebook to fix. + // + // This is a temporary workaround that adds "text/javascript; charset=UTF-8" + // as a supported MediaType in MappingJackson2HttpMessageConverter, + // which is used to convert the UserInfo response to a Map. + + HttpMessageConverter jsonMessageConverter = restTemplate.getMessageConverters().stream() + .filter(c -> c instanceof MappingJackson2HttpMessageConverter) + .findFirst() + .orElse(null); + + if (jsonMessageConverter == null) { + return; + } + + List supportedMediaTypes = new ArrayList<>(jsonMessageConverter.getSupportedMediaTypes()); + supportedMediaTypes.add(MediaType.valueOf("text/javascript;charset=UTF-8")); + ((AbstractHttpMessageConverter) jsonMessageConverter).setSupportedMediaTypes(supportedMediaTypes); + } } diff --git a/spring-social/src/main/resources/application.yml b/spring-social/src/main/resources/application.yml index 2adc89a..0bbda73 100644 --- a/spring-social/src/main/resources/application.yml +++ b/spring-social/src/main/resources/application.yml @@ -2,7 +2,7 @@ spring: datasource: url: jdbc:mysql://localhost:3306/spring_social?useSSL=false username: root - password: callicoder + password: root jpa: show-sql: true @@ -17,31 +17,31 @@ spring: client: registration: google: - clientId: 5014057553-8gm9um6vnli3cle5rgigcdjpdrid14m9.apps.googleusercontent.com - clientSecret: tWZKVLxaD_ARWsriiiUFYoIk - redirectUriTemplate: "{baseUrl}/oauth2/callback/{registrationId}" + client-id: 5014057553-8gm9um6vnli3cle5rgigcdjpdrid14m9.apps.googleusercontent.com + client-secret: tWZKVLxaD_ARWsriiiUFYoIk + redirect-uri-template: "{baseUrl}/oauth2/callback/{registrationId}" scope: - email - profile facebook: - clientId: 121189305185277 - clientSecret: 42ffe5aa7379e8326387e0fe16f34132 - redirectUriTemplate: "{baseUrl}/oauth2/callback/{registrationId}" + client-id: 610771539429908 + client-secret: 7991e62f49649df0e15d8b5405ebb162 + redirect-uri-template: "{baseUrl}/oauth2/callback/{registrationId}" scope: - email - public_profile github: - clientId: d3e47fc2ddd966fa4352 - clientSecret: 3bc0f6b8332f93076354c2a5bada2f5a05aea60d - redirectUriTemplate: "{baseUrl}/oauth2/callback/{registrationId}" + client-id: d3e47fc2ddd966fa4352 + client-secret: 3bc0f6b8332f93076354c2a5bada2f5a05aea60d + redirect-uri-template: "{baseUrl}/oauth2/callback/{registrationId}" scope: - user:email - read:user provider: facebook: - authorizationUri: https://www.facebook.com/v3.0/dialog/oauth - tokenUri: https://graph.facebook.com/v3.0/oauth/access_token - userInfoUri: https://graph.facebook.com/v3.0/me?fields=id,first_name,middle_name,last_name,name,email,verified,is_verified,picture.width(250).height(250) + authorization-uri: https://www.facebook.com/v3.2/dialog/oauth + token-uri: https://graph.facebook.com/v3.2/oauth/access_token + user-info-uri: https://graph.facebook.com/v3.2/me?fields=id,first_name,last_name,name,email,picture.width(250).height(250) app: auth: tokenSecret: 926D96C90030DD58429D2751AC1BDBBC