Mobile Native Social Sign Up Using Spring OAuth 2.0 Security and JWT

I implemented a password for my authentication server using Spring Security OAuth 2.0 and JWT. I create a server by extending LicenseAccessizationServerConfigurerAdapter. I can give the server a username / password and return a JWT token. I am using ResourceConfiguration, which extends the ResourceServerConfigurerAdapter class in other services, to check the JWT anytime a web service call is made. Nested below is my code.

I would like to give my Native Mobile app the ability to log in using Facebook, Gmail, Linked, etc. I would like to follow the steps in the attached article:

https://ole.michelsen.dk/blog/social-signin-spa-jwt-server.html

  • A user does an OAuth dance on the mobile side and sends me an access token for the social service they use.
  • I get an access token and use it to call the appropriate social service.
  • If the token is valid, the user cannot log in, an error is issued.
  • If the token is valid, I get the user data from the social service and use it to create a “social user” in my data warehouse, which will be linked to an existing or new system user.
  • After a system user is created using a social user or a social user is tied to an existing system user, the JWT token will be sent back to the mobile application.
  • JWT JWT, Spring Security OAuth 2.0 Password Grant Flow, ResourceServerConfiguration .

, , . ? , / , JWT. , , OAuth2ClientAuthenticationProcessingFilter , , , OAuth2ClientAuthenticationProcessingFilter . - , , , , .

:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;

    @Value("${clientId}")
    private String clientId;

    @Value("${clientSecret}")
    private String clientSecret;

    @Value("${jwtSigningKey}")
    private String jwtSigningKey;

    @Value("${accessTokenValiditySeconds}")
    private String accessTokenValiditySeconds;

    @Value("${refreshTokenValiditySeconds}")
    private String refreshTokenValiditySeconds;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter(){
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        accessTokenConverter.setSigningKey(jwtSigningKey);
        return accessTokenConverter;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new JWTTokenEnhancer();
    }

    // Added for refresh token capability
    @Bean
    @Primary
    public DefaultTokenServices tokenServices(){
        final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }

    @Override
    public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()
                    .withClient(clientId)
                    .secret(clientSecret)
                    .authorizedGrantTypes("password", "refresh_token")
                    .scopes("read","write")
                    .accessTokenValiditySeconds(Integer.valueOf(accessTokenValiditySeconds)) // 1 hour
                    .refreshTokenValiditySeconds(Integer.valueOf(refreshTokenValiditySeconds));// 30 days
    }

    @Override
    public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {

        // Add the JWT token enhancer to the token enhancer chain then add to endpoints
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));

        endpoints.tokenStore(tokenStore())
                .tokenEnhancer(tokenEnhancerChain)
                .authenticationManager(authenticationManager)
                .userDetailsService(userDetailsService)
                .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST)
                .accessTokenConverter(accessTokenConverter());
    }

    @Override
    public void configure(final AuthorizationServerSecurityConfigurer securityConfigurer) throws Exception {
        securityConfigurer.checkTokenAccess("permitAll()");
        super.configure(securityConfigurer);
    }
}

public class JWTTokenEnhancer implements TokenEnhancer {

    @Override
    public OAuth2AccessToken enhance(final OAuth2AccessToken accessToken,
                                     final OAuth2Authentication authentication) {

        Map<String, Object> additionalInfo = new HashMap<>();

        // Get the user detail implementation
        UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();

        // add userId and roles to the JWT token
        additionalInfo.put("user_id", userDetails.getUserId());
        additionalInfo.put("email", userDetails.getEmail());
        additionalInfo.put("user_name", userDetails.getUsername());
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);

        return accessToken;
    }
}

:

@Configuration
@EnableResourceServer
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
@ComponentScan("com.test.security")
@Profile({"prod", "qa", "dev"})
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

  @Value("${jwtSigningKey}")
  private String jwtSigningKey;

  // http security concerns
  @Override
  public void configure(final HttpSecurity http) throws Exception {

    http.authorizeRequests()
        .antMatchers("/swagger-ui.html").permitAll()
        .antMatchers("/hystrix/**").permitAll()
        .antMatchers("/admin/hystrix.stream/**").permitAll()
        .antMatchers("/admin/health/**").permitAll()
        .antMatchers("/admin/info/**").permitAll()
        .antMatchers("/admin/**").authenticated()
        .antMatchers("/greetings/**").authenticated()
        .and()
        .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and().csrf().disable();
  }

  @Override
  public void configure(final ResourceServerSecurityConfigurer config) {
    config.tokenServices(tokenServices());
  }

  @Bean
  public JwtAccessTokenConverter accessTokenConverter() {
    JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
    accessTokenConverter.setSigningKey(jwtSigningKey);
    return accessTokenConverter;
  }

  @Bean
  public TokenStore tokenStore() {
    return new JwtTokenStore(accessTokenConverter());
  }

  // Added for refresh token capability
  @Bean
  @Primary
  public DefaultTokenServices tokenServices() {
    final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
    defaultTokenServices.setTokenStore(tokenStore());
    defaultTokenServices.setSupportRefreshToken(true);
    return defaultTokenServices;
  }
}
+6

All Articles