Welcome to the 3rd part of the Spring Boot User Registration and OAuth2 social login tutorial series. In this article, we’ll learn how to perform social login using Spring Security.
Creating custom classes for OAuth2 Authentication
CustomOAuth2UserService.java
The CustomOAuth2UserService
extends Spring Security’s DefaultOAuth2UserService
and implements its loadUser()
method. This method is called after an access token is obtained from the OAuth2 provider.
In this method, we first fetch the user’s details from the OAuth2 provider. If a user with the same email already exists in our database then we update his details, otherwise, we register a new user.
If the OAuth2 provider is LinkedIn, then we need to invoke the email address endpoint with the access token to get the user email address since it will not be returned in the response of user-info endpoint.
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
@Autowired
private UserService userService;
@Autowired
private Environment env;
@Override
public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(oAuth2UserRequest);
try {
Map<String, Object> attributes = new HashMap<>(oAuth2User.getAttributes());
String provider = oAuth2UserRequest.getClientRegistration().getRegistrationId();
if (provider.equals(SocialProvider.LINKEDIN.getProviderType())) {
populateEmailAddressFromLinkedIn(oAuth2UserRequest, attributes);
}
return userService.processUserRegistration(provider, attributes, null, null);
} catch (AuthenticationException ex) {
throw ex;
} catch (Exception ex) {
ex.printStackTrace();
// Throwing an instance of AuthenticationException will trigger the
// OAuth2AuthenticationFailureHandler
throw new OAuth2AuthenticationProcessingException(ex.getMessage(), ex.getCause());
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public void populateEmailAddressFromLinkedIn(OAuth2UserRequest oAuth2UserRequest, Map<String, Object> attributes) throws OAuth2AuthenticationException {
String emailEndpointUri = env.getProperty("linkedin.email-address-uri");
Assert.notNull(emailEndpointUri, "LinkedIn email address end point required");
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION, "Bearer " + oAuth2UserRequest.getAccessToken().getTokenValue());
HttpEntity<?> entity = new HttpEntity<>("", headers);
ResponseEntity<Map> response = restTemplate.exchange(emailEndpointUri, HttpMethod.GET, entity, Map.class);
List<?> list = (List<?>) response.getBody().get("elements");
Map map = (Map<?, ?>) ((Map<?, ?>) list.get(0)).get("handle~");
attributes.putAll(map);
}
}
CustomOidcUserService.java
OidcUserService
is an implementation of an OAuth2UserService that supports OpenID Connect 1.0 Providers. Google is an OpenID Connect provider. Hence, we are creating this service to load the user with Google’s OAuth 2.0 APIs.
The CustomOidcUserService
extends Spring Security’s OidcUserService
and implements its loadUser()
method. This method is called after an access token is obtained from the OAuth2 provider.
@Service
public class CustomOidcUserService extends OidcUserService {
@Autowired
private UserService userService;
@Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser oidcUser = super.loadUser(userRequest);
try {
return userService.processUserRegistration(userRequest.getClientRegistration().getRegistrationId(), oidcUser.getAttributes(), oidcUser.getIdToken(),
oidcUser.getUserInfo());
} catch (AuthenticationException ex) {
throw ex;
} catch (Exception ex) {
ex.printStackTrace();
// Throwing an instance of AuthenticationException will trigger the
// OAuth2AuthenticationFailureHandler
throw new OAuth2AuthenticationProcessingException(ex.getMessage(), ex.getCause());
}
}
}
OAuth2AccessTokenResponseConverterWithDefaults.java
LinkedIn OAuth2 access token API is returning only the access_token
and expires_in
values but not the token_type
in the response. This results in the following error.
org.springframework.http.converter.HttpMessageNotReadableException: An error occurred reading the OAuth 2.0 Access Token Response: tokenType cannot be null; nested exception is java.lang.IllegalArgumentException: tokenType cannot be null
The following code snippet throws that error
public OAuth2AccessToken(TokenType tokenType, String tokenValue, Instant issuedAt, Instant expiresAt, Set<String> scopes) {
super(tokenValue, issuedAt, expiresAt);
Assert.notNull(tokenType, "tokenType cannot be null");
this.tokenType = tokenType;
this.scopes = Collections.unmodifiableSet(
scopes != null ? scopes : Collections.emptySet());
}
Hence the default OAuth2AccessTokenResponseConverter
class has been copied as OAuth2AccessTokenResponseConverterWithDefaults
and modified to set a default token type to resolve this issue
/**
* @author Joe Grandja
*/
public class OAuth2AccessTokenResponseConverterWithDefaults implements Converter<Map<String, String>, OAuth2AccessTokenResponse> {
private static final Set<String> TOKEN_RESPONSE_PARAMETER_NAMES = Stream
.of(OAuth2ParameterNames.ACCESS_TOKEN, OAuth2ParameterNames.TOKEN_TYPE, OAuth2ParameterNames.EXPIRES_IN, OAuth2ParameterNames.REFRESH_TOKEN, OAuth2ParameterNames.SCOPE)
.collect(Collectors.toSet());
private OAuth2AccessToken.TokenType defaultAccessTokenType = OAuth2AccessToken.TokenType.BEARER;
@Override
public OAuth2AccessTokenResponse convert(Map<String, String> tokenResponseParameters) {
String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);
OAuth2AccessToken.TokenType accessTokenType = this.defaultAccessTokenType;
if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(tokenResponseParameters.get(OAuth2ParameterNames.TOKEN_TYPE))) {
accessTokenType = OAuth2AccessToken.TokenType.BEARER;
}
long expiresIn = 0;
if (tokenResponseParameters.containsKey(OAuth2ParameterNames.EXPIRES_IN)) {
try {
expiresIn = Long.valueOf(tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES_IN));
} catch (NumberFormatException ex) {
}
}
Set<String> scopes = Collections.emptySet();
if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {
String scope = tokenResponseParameters.get(OAuth2ParameterNames.SCOPE);
scopes = Arrays.stream(StringUtils.delimitedListToStringArray(scope, " ")).collect(Collectors.toSet());
}
Map<String, Object> additionalParameters = new LinkedHashMap<>();
tokenResponseParameters.entrySet().stream().filter(e -> !TOKEN_RESPONSE_PARAMETER_NAMES.contains(e.getKey()))
.forEach(e -> additionalParameters.put(e.getKey(), e.getValue()));
return OAuth2AccessTokenResponse.withToken(accessToken).tokenType(accessTokenType).expiresIn(expiresIn).scopes(scopes).additionalParameters(additionalParameters).build();
}
public final void setDefaultAccessTokenType(OAuth2AccessToken.TokenType defaultAccessTokenType) {
Assert.notNull(defaultAccessTokenType, "defaultAccessTokenType cannot be null");
this.defaultAccessTokenType = defaultAccessTokenType;
}
}
OAuth2UserInfo mapping
Every OAuth2 provider returns a different JSON response when we fetch the authenticated user’s details. Spring security parses the response in the form of a generic map
of key-value pairs.
The following classes are used to get the required details of the user from the generic map
of key-value pairs
OAuth2UserInfo.java
public abstract class OAuth2UserInfo {
protected Map<String, Object> attributes;
public OAuth2UserInfo(Map<String, Object> attributes) {
this.attributes = attributes;
}
public Map<String, Object> getAttributes() {
return attributes;
}
public abstract String getId();
public abstract String getName();
public abstract String getEmail();
public abstract String getImageUrl();
}
FacebookOAuth2UserInfo.java
public class FacebookOAuth2UserInfo extends OAuth2UserInfo {
public FacebookOAuth2UserInfo(Map<String, Object> attributes) {
super(attributes);
}
@Override
public String getId() {
return (String) attributes.get("id");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
@SuppressWarnings("unchecked")
public String getImageUrl() {
if (attributes.containsKey("picture")) {
Map<String, Object> pictureObj = (Map<String, Object>) attributes.get("picture");
if (pictureObj.containsKey("data")) {
Map<String, Object> dataObj = (Map<String, Object>) pictureObj.get("data");
if (dataObj.containsKey("url")) {
return (String) dataObj.get("url");
}
}
}
return null;
}
}
GoogleOAuth2UserInfo.java
public class GoogleOAuth2UserInfo extends OAuth2UserInfo {
public GoogleOAuth2UserInfo(Map<String, Object> attributes) {
super(attributes);
}
@Override
public String getId() {
return (String) attributes.get("sub");
}
@Override
public String getName() {
return (String) attributes.get("name");
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getImageUrl() {
return (String) attributes.get("picture");
}
}
GithubOAuth2UserInfo.java
public class GithubOAuth2UserInfo extends OAuth2UserInfo {
public GithubOAuth2UserInfo(Map<String, Object> attributes) {
super(attributes);
}
@Override
public String getId() {
return ((Integer) attributes.get("id")).toString();
}
@Override
public String getName() {
return (String) attributes.get("name");
}
@Override
public String getEmail() {
return (String) attributes.get("email");
}
@Override
public String getImageUrl() {
return (String) attributes.get("avatar_url");
}
}
LinkedinOAuth2UserInfo.java
public class LinkedinOAuth2UserInfo extends OAuth2UserInfo {
public LinkedinOAuth2UserInfo(Map<String, Object> attributes) {
super(attributes);
}
@Override
public String getId() {
return (String) attributes.get("id");
}
@Override
public String getName() {
return ((String) attributes.get("localizedFirstName")).concat(" ").concat((String) attributes.get("localizedLastName"));
}
@Override
public String getEmail() {
return (String) attributes.get("emailAddress");
}
@Override
public String getImageUrl() {
return (String) attributes.get("pictureUrl");
}
}
OAuth2UserInfoFactory.java
public class OAuth2UserInfoFactory {
public static OAuth2UserInfo getOAuth2UserInfo(String registrationId, Map<String, Object> attributes) {
if (registrationId.equalsIgnoreCase(SocialProvider.GOOGLE.getProviderType())) {
return new GoogleOAuth2UserInfo(attributes);
} else if (registrationId.equalsIgnoreCase(SocialProvider.FACEBOOK.getProviderType())) {
return new FacebookOAuth2UserInfo(attributes);
} else if (registrationId.equalsIgnoreCase(SocialProvider.GITHUB.getProviderType())) {
return new GithubOAuth2UserInfo(attributes);
} else if (registrationId.equalsIgnoreCase(SocialProvider.LINKEDIN.getProviderType())) {
return new LinkedinOAuth2UserInfo(attributes);
} else if (registrationId.equalsIgnoreCase(SocialProvider.TWITTER.getProviderType())) {
return new GithubOAuth2UserInfo(attributes);
} else {
throw new OAuth2AuthenticationProcessingException("Sorry! Login with " + registrationId + " is not supported yet.");
}
}
}
PagesController.java
This is our main controller which handles all the incoming requests
@RestController
public class PagesController {
private final Logger logger = LogManager.getLogger(getClass());
@Resource
private MessageService messageService;
@Autowired
private UserService userService;
@GetMapping("/login")
public ModelAndView login(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "error", required = false) String error,
@RequestParam(value = "logout", required = false) String logout, @RequestParam(value = "errorCode", required = false) String errorCode)
throws ServletException, IOException {
ModelAndView model = new ModelAndView();
if (error != null) {
model.addObject("css", "danger");
model.addObject("msg", error);
} else if (logout != null) {
model.addObject("css", "success");
model.addObject("msg", messageService.getMessage("message.logout." + logout));
}
model.addObject("title", "Login Page");
model.setViewName("login");
return model;
}
@GetMapping("/register")
public ModelAndView register(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
return new ModelAndView("register", "userRegistrationForm", new UserRegistrationForm());
}
@PostMapping("/register")
public ModelAndView registerUser(@Valid UserRegistrationForm userRegistrationForm, BindingResult result, final HttpServletRequest request, RedirectAttributes attributes) {
ModelAndView model = new ModelAndView("register");
if (!result.hasErrors()) {
try {
userService.registerNewUser(userRegistrationForm);
attributes.addFlashAttribute("css", "success");
attributes.addFlashAttribute("msg", messageService.getMessage("message.regSucc"));
model = new ModelAndView("redirect:/login");
} catch (UserAlreadyExistAuthenticationException e) {
logger.error(e);
result.rejectValue("email", "message.regError");
}
}
return model;
}
@GetMapping({ "/", "/home" })
public ModelAndView home(@RequestParam(value = "view", required = false) String view) {
logger.info("Entering home page");
ModelAndView model = new ModelAndView("home");
model.addObject("title", "Home");
model.addObject("view", view);
return model;
}
}
AppConfig.java
@Configuration
annotation indicates that a class declares one or more @Bean methods and may be processed by the Spring container to generate bean definitions and service requests for those beans at runtime.
@Bean
annotation is applied on a method to specify that it returns a bean to be managed by Spring context
@Configuration
public class AppConfig implements WebMvcConfigurer {
// beans
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasename("classpath:messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
@Bean
public LocaleResolver localeResolver() {
final CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver();
cookieLocaleResolver.setDefaultLocale(Locale.ENGLISH);
return cookieLocaleResolver;
}
@Override
public Validator getValidator() {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setValidationMessageSource(messageSource());
return validator;
}
}
WebSecurityConfig.java
Add the annotation @EnableWebSecurity
to the class to tell spring that this class is spring security configuration.
In this configuration class, we have also configured the OAuth2AccessTokenResponseClient
with our custom OAuth2AccessTokenResponseConverterWithDefaults
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String[] IGNORED_RESOURCE_LIST = new String[] { "/fonts/**", "/webjars/**", "/files/**", "/static/**", "/robots.txt" };
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private LogoutSuccessHandler logoutSuccessHandler;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private CustomOAuth2UserService customOAuth2UserService;
@Autowired
CustomOidcUserService customOidcUserService;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.authorizeRequests().antMatchers("/", "/home").hasRole(Role.USER);
// Pages do not require login
http.authorizeRequests().antMatchers("/**").permitAll();
// Form Login config
http.authorizeRequests().and().formLogin()
.loginPage("/login")//
.defaultSuccessUrl("/home")//
.failureUrl("/login?error=true").failureHandler(authenticationFailureHandler)//
.usernameParameter("j_username")//
.passwordParameter("j_password").and().oauth2Login().loginPage("/login").failureHandler(authenticationFailureHandler).defaultSuccessUrl("/home").userInfoEndpoint()
.oidcUserService(customOidcUserService).userService(customOAuth2UserService).and().tokenEndpoint()
.accessTokenResponseClient(authorizationCodeTokenResponseClient());
// Logout Config
http.authorizeRequests().and().logout().deleteCookies("JSESSIONID").logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
//
http.sessionManagement().invalidSessionUrl("/login").maximumSessions(1).expiredUrl("/login?session=expired");
}
// This bean is used to load the user specific data when form login is used.
@Override
public UserDetailsService userDetailsService() {
return userDetailsService;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder(10);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(IGNORED_RESOURCE_LIST);
}
private OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeTokenResponseClient() {
OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
tokenResponseHttpMessageConverter.setTokenResponseConverter(new OAuth2AccessTokenResponseConverterWithDefaults());
RestTemplate restTemplate = new RestTemplate(Arrays.asList(new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
DefaultAuthorizationCodeTokenResponseClient tokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
tokenResponseClient.setRestOperations(restTemplate);
return tokenResponseClient;
}
}
SetupDataLoader.java
This class is responsible for inserting the user role into the DB if it doesn’t exist on application startup
@Component
is the most generic Spring annotation. A Java class decorated with this annotation is found during classpath scanning and registered in the context as a Spring bean
@Component
public class SetupDataLoader implements ApplicationListener<ContextRefreshedEvent> {
private boolean alreadySetup = false;
@Autowired
private RoleRepository roleRepository;
@Override
@Transactional
public void onApplicationEvent(final ContextRefreshedEvent event) {
if (alreadySetup) {
return;
}
// Create initial roles
createRoleIfNotFound(Role.ROLE_USER);
alreadySetup = true;
}
@Transactional
private final Role createRoleIfNotFound(final String name) {
Role role = roleRepository.findByName(name);
if (role == null) {
role = new Role(name);
}
role = roleRepository.save(role);
return role;
}
}
DemoApplication.java
@SpringBootApplication
Indicates a configuration
class that declares one or more @Bean
methods and also triggers auto-configuration
and component scanning
. This is a convenience annotation that is equivalent to declaring @Configuration
, @EnableAutoConfiguration
and @ComponentScan
.
@EnableJpaRepositories
annotation is used to enable JPA repositories. It will scan the package of the annotated configuration class for Spring Data repositories by default.
@EnableTransactionManagement
enables Spring’s annotation-driven transaction management capability, similar to the support found in Spring’s <tx:*>
XML namespace.
@SpringBootApplication(scanBasePackages = "com.javachinna")
@EnableJpaRepositories
@EnableTransactionManagement
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplicationBuilder app = new SpringApplicationBuilder(DemoApplication.class);
app.run();
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(DemoApplication.class);
}
}
application.properties
Replace the <your-client-id>
and <your-client-secret>
with your app credentials from the respective social login provider
# Database configuration props
spring.datasource.url=jdbc:mysql://localhost:3306/demo?createDatabaseIfNotExist=true
spring.datasource.username=root
spring.datasource.password=chinna44
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Hibernate props
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=create
#spring.jpa.hibernate.ddl-auto=validate
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.mvc.view.prefix=/WEB-INF/views/pages/
spring.mvc.view.suffix=.jsp
# Social login provider props
spring.security.oauth2.client.registration.google.clientId=<your-client-id>
spring.security.oauth2.client.registration.google.clientSecret=<your-client-secret>
spring.security.oauth2.client.registration.facebook.clientId=<your-client-id>
spring.security.oauth2.client.registration.facebook.clientSecret=<your-client-secret>
spring.security.oauth2.client.provider.facebook.user-info-uri=https://graph.facebook.com/me?fields=id,name,email,picture
spring.security.oauth2.client.registration.github.clientId=<your-client-id>
spring.security.oauth2.client.registration.github.clientSecret=<your-client-secret>
spring.security.oauth2.client.registration.linkedin.clientId=<your-client-id>
spring.security.oauth2.client.registration.linkedin.clientSecret=<your-client-secret>
spring.security.oauth2.client.registration.linkedin.client-authentication-method=post
spring.security.oauth2.client.registration.linkedin.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.linkedin.scope=r_liteprofile, r_emailaddress
spring.security.oauth2.client.registration.linkedin.redirect-uri-template={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.linkedin.client-name=Linkedin
spring.security.oauth2.client.registration.linkedin.provider=linkedin
spring.security.oauth2.client.provider.linkedin.authorization-uri=https://www.linkedin.com/oauth/v2/authorization
spring.security.oauth2.client.provider.linkedin.token-uri=https://www.linkedin.com/oauth/v2/accessToken
spring.security.oauth2.client.provider.linkedin.user-info-uri=https://api.linkedin.com/v2/me
spring.security.oauth2.client.provider.linkedin.user-name-attribute=id
linkedin.email-address-uri=https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))
# For detailed logging during development
logging.level.com=TRACE
logging.level.org.springframework=TRACE
logging.level.org.hibernate.SQL=TRACE
logging.level.org.hibernate.type=TRACE
Run with Maven
You can run the application using mvn clean spring-boot:run
and visit http://localhost:8080
Source code
https://github.com/JavaChinna/spring-boot-oauth2
References
https://www.callicoder.com/spring-boot-security-oauth2-social-login-part-2/
Conclusion
That’s all folks! In this tutorial series, you’ve learned how to add social as well as email and password based login to your spring boot application.
I hope you enjoyed this series. Please share it with your friends if you like this article. Thank you for reading.
in application.properties folder (spring.security.oauth2.client.registration.linkedin.redirect-uri-template={baseUrl}/login/oauth2/code/{registrationId} ) what is registration id how we get the registration id
Hi Pankaj,
Property key contains the registrationId linkedin which I’ve highlighted below and we don’t need to get it.
spring.security.oauth2.client.registration.linkedin.clientId=
This redirect URI is a template and Spring Security will replace {baseUrl} and {registrationId} with application base url and linkedin respectively. For example, the final URL will be like the one below
https://www.some-domain-name.com/login/oauth2/code/linkedin
Please let me know if you need more clarifications.
I am getting error ‘token type cannot be null’. What should I do? And Can you please share processUserRegisteration method.
Refer here to fix this issue. For processUserRegisteration method, refer here.
Hello, I’m a newbie. I have a question I want to ask you in case of login with LinkedIn, the handling after the callback (get Code -> get Token -> get User info-> map JSON User info to the object object named OAuth2UserRequest of the loadUser method) is what we do or is Spring Framework going to do it?
For LinkedIn, we are mapping the user attributes returned by the user-info endpoint to LinkedinOAuth2UserInfo object. In addition to that, we are calling the email-address-uri endpoint to fetch the email address of the user. Rest all will be done by the Spring framework. In simple terms, whatever the logic is there in the CustomOAuth2UserService is what we do.
Field roleRepository in com.javachinna.config.SetupDataLoader required a bean named ‘entityManagerFactory’ that could not be found.
The injection point has the following annotations:
– @org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean named ‘entityManagerFactory’ in your configuration.
got these error
Did you configure the data source in the application.properties?
Hi Chinna,
in application.properties file
app.oauth2.authorizedRedirectUris=myandroidapp://oauth2/redirect,myiosapp://oauth2/redirect
I’m using IONIC framework to build Android and iOS apps, I use @codetrix-studio/capacitor-google-auth library to sign in with Google, after logging in I have information like email, name , id, token…, but how does [BACKEND SpringBoot] get this data and save to database
Hi Huy Nguyen, You can have a user registration rest api and call this with the info you have received from the social login to create a new user in your database.
Hello, I have oauth apps configure for both GIthub and Google. Google works fine, Github’s not. Do I need anything extra to pull name and email for Github login? Getting “Name not found from OAuth2 provider” error. both are coming as null. Github’s auth callback url: http://localhost:8080/login/oauth2/code/github.
Hello, I have oauth apps configure for both GIthub and Google. Google works fine, Github’s not. Do I need anything extra to pull name and email for Github login? Getting “Name not found from OAuth2 provider” error. both are coming as null. Github’s auth callback url: http://localhost:8080/login/oauth2/code/github.
Hello, did you check the response of the Github user-info api? do you get any user information at all? it will return a map of user attributes. based on the attribute name only we will extract the user information from the response. maybe they have changed the attribute name? Its been a while since I have tested this. The social providers will be keep on updating the API’s. Let me know if further info is required.
When running the application its giving following error on UI – http://localhost:8080/login
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed Jul 20 15:55:22 IST 2022
There was an unexpected error (type=Not Found, status=404).
No message available
This can happen when there is an exception occurring during the application startup. When any exception occurs, Spring will redirect to the /error page. Since there is no explicit mapping for the /error page in this application, it shows the White label error page. So check your console or log file for errors. Probably due to incorrect database configuration in the application.properties?
hihi Chinna ,why we don’t need to set up the <a href="” jsp page link etc.. in the oauth2Login() ?.Does the spring security know the url to trig the oauth2 login for any social provider like facebook ,google etc. ??
Hi, Yes based on the URLs (/oauth2/authorization/google, /oauth2/authorization/facebook, etc), spring security performs the OAuth2 Login.