spring-bootoauth-2.0google-oauthspring-security-oauth2spring-social

Attach OAuth2 login to existing users in a Spring Boot application


I have a Spring Boot application that is "invitation only". Ie. users are sent a signup link and there is no "Sign up" functionality. This works fine and users log on with their username and password.

I would like to allow logon with FaceBook and Google using OAuth2 as a supplementary logon method. This would involve mapping the existing users to their social account in some way. The users and their passwords are stored in a MySQL database. I have found a number of articles on OAuth2 and Spring Boot, but none that address this exact use-case.

I can create the Google OAuth2 token/client secret etc, but how do I design the flow to allow only the existing users to logon with their social accounts?

The usernames have been chosen by the users themselves, and are therefore not necessarily the same as their email.

Do I need to create a custom authentication flow in this case? And do I need to change the authentication mechanism from cookies to JWT tokens?


Solution

  • I found myself in a similar situation, where I needed to know if the OAuth request that I'm receiving is coming from an already authenticated user or not.

    Although in my case I needed to know that because I want users to be able to "link" their existing account to social ones.

    What I ended up doing was implementing an OAuth2UserService which would have a single method:

    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException{
    
        // this will be null if the OAuth2UserRequest is not from an authenticated user 
        // otherwise, it would contain the current user's principle which you can use to check if the OAuth request should be handled or not
        Authentication currentAuth =  SecurityContextHolder.getContext().getAuthentication();
    
        // for example: 
        // if(currentAuth == null)
        //     throw new OAuth2AuthenticationException(OAuth2ErrorCodes.ACCESS_DENIED);
    
        // Use the default service to load the user
        DefaultOAuth2UserService defaultService = new DefaultOAuth2UserService();
        OAuth2User defaultOAuthUser = defaultService.loadUser(userRequest);
    
        // here you might have extra logic to map the defaultOAuthUser's info to the existing user
        // and if you're implementing a custom OAuth2User you should also connect them here and return the custom OAuth2User
    
        return defaultOAuthUser;
    }
    

    then just register the custom OAuth2UserService in your security configuration:

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                // your existing config
                .oauth2Login()
                .userInfoEndpoint()
                .userService(oauthUserService())
            ;
        }
    
        @Bean
        public OAuth2UserService oauthUserService(){
            return new MyCustomOAuth2UserService();
        }