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();
}
@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))
.refreshTokenValiditySeconds(Integer.valueOf(refreshTokenValiditySeconds));
}
@Override
public void configure(final AuthorizationServerEndpointsConfigurer 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<>();
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
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;
@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());
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}