I'm trying to implement the signup phase with Facebook of my Spring webapp using Spring OAuth2. I'm following this guide https://www.callicoder.com/spring-boot-security-oauth2-social-login-part-2/ but something in my code does not work as expected. The object of type OAuth2User has just the id and the name of the facebook account inside the attributes.
In particular inside the @Service class CustomOAuth2UserService inside the method processOAuth2User
private OAuth2User processOAuth2User(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) {
OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(oAuth2UserRequest.getClientRegistration().getRegistrationId(), oAuth2User.getAttributes());
if(StringUtils.isEmpty(oAuth2UserInfo.getEmail())) {
throw new OAuth2AuthenticationProcessingException("Email not found from OAuth2 provider");
}
...
}
oAuth2User.getAttributes(), as I said, has only two attributes: id and name but not the email.
My CustomOAuth2UserService class is this:
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
@Autowired
private UserService userService;
@Override
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(oAuth2UserRequest);
try {
return processOAuth2User(oAuth2UserRequest, oAuth2User);
} catch (AuthenticationException ex) {
throw ex;
} catch (Exception ex) {
// Throwing an instance of AuthenticationException will trigger the OAuth2AuthenticationFailureHandler
throw new InternalAuthenticationServiceException(ex.getMessage(), ex.getCause());
}
}
private OAuth2User processOAuth2User(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) {
OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(oAuth2UserRequest.getClientRegistration().getRegistrationId(), oAuth2User.getAttributes());
if(StringUtils.isEmpty(oAuth2UserInfo.getId())) {
throw new RuntimeException("Id not found from OAuth2 provider");
}
FacebookUser user = null;
try {
user = (FacebookUser) userService.getByFacebookId(oAuth2UserInfo.getId());
} catch (UserNotFoundException e) {
user = registerNewUser(oAuth2UserRequest, oAuth2UserInfo);
}
return new CustomUserDetails(user);
}
private FacebookUser registerNewUser(OAuth2UserRequest oAuth2UserRequest, OAuth2UserInfo oAuth2UserInfo) {
FacebookUser user = new FacebookUser(oAuth2UserInfo.getId());
user.setProvider(AuthProvider.valueOf(oAuth2UserRequest.getClientRegistration().getRegistrationId()));
user.setIdentity(new Identity(oAuth2UserInfo.getName()));
user.setEmail(oAuth2UserInfo.getEmail());
return (FacebookUser) userService.addFacebookUser(user);
}
}
which is inserted in the SecurityConfiguration class:
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@EnableJpaRepositories(basePackageClasses = UserRepository.class)
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private CustomOAuth2UserService customOAuth2UserService;
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
auth
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.formLogin()
.loginPage("/login")
.failureUrl("/login?error=true")
.and()
.oauth2Login()
.loginPage("/login")
.userInfoEndpoint()
.userService(customOAuth2UserService);
}
}
I found the solution of this problem. I needed to specify the fields I was interested inside the properties file. So I needed to add inside the application.properties file the following properties:
spring.security.oauth2.client.registration.facebook.scope=email,public_profile
spring.security.oauth2.client.provider.facebook.authorizationUri = https://www.facebook.com/v3.0/dialog/oauth
spring.security.oauth2.client.provider.facebook.tokenUri = https://graph.facebook.com/v3.0/oauth/access_token
spring.security.oauth2.client.provider.facebook.userInfoUri = https://graph.facebook.com/v3.0/me?fields=id,first_name,middle_name,last_name,name,email,verified,is_verified,picture
So my application.properties file, for what concerns the OAuth2 module is this:
spring.security.oauth2.client.registration.facebook.client-id=****
spring.security.oauth2.client.registration.facebook.client-secret=****
spring.security.oauth2.client.registration.facebook.scope=email,public_profile
spring.security.oauth2.client.provider.facebook.authorizationUri = https://www.facebook.com/v3.0/dialog/oauth
spring.security.oauth2.client.provider.facebook.tokenUri = https://graph.facebook.com/v3.0/oauth/access_token
spring.security.oauth2.client.provider.facebook.userInfoUri = https://graph.facebook.com/v3.0/me?fields=id,first_name,middle_name,last_name,name,email,verified,is_verified,picture
With this online tool https://developers.facebook.com/tools/explorer/ you can automatically generate the url with all the field you need. Once you have it, you just have to paste it next to this property spring.security.oauth2.client.provider.facebook.userInfoUri and that's it.