JwtTokenStore.findTokensByClientId(clientId) always return empty - spring-boot

I am creating a spring-boot-oauth2 project and I'd like to revoke client's access token. Below is my configurations for Oauth2.
#Configuration
#EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private ClientDetailsService clientDetailsService;
#Bean
public JwtTokenStore tokenStore() {
JwtTokenStore store = new JwtTokenStore(jwtAccessTokenConverter());
return store;
}
#Bean
public TokenEnhancerChain tokenEnhancerChain() {
final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(new CustomTokenEnhancer(), jwtAccessTokenConverter()));
return tokenEnhancerChain;
}
#Bean
#Primary
public AuthorizationServerTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setTokenEnhancer(tokenEnhancerChain());
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setSupportRefreshToken(true);
return tokenServices;
}
#Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new CustomTokenEnhancer();
KeyPair keyPair = new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "secret".toCharArray()).getKeyPair("myapp-authkey");
converter.setKeyPair(keyPair);
return converter;
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
// register for backend application
clients.inMemory()
.withClient("myclient-backend")
.secret("secret")
.authorizedGrantTypes(
"password","authorization_code", "refresh_token")
.authorities("ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "update", "delete")
.accessTokenValiditySeconds(1800) //Access token is only valid for 30 mins.
.refreshTokenValiditySeconds(60 * 60 * 1) //Refresh token is only valid for 1 hour.
.autoApprove(true)
;
// #formatter:on
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// #formatter:off
endpoints.tokenServices(tokenServices())
.tokenStore(tokenStore())
.authenticationManager(authenticationManager)
.accessTokenConverter(jwtAccessTokenConverter());
// #formatter:on
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
// #formatter:off
oauthServer.tokenKeyAccess("isAnonymous() || isRememberMe() || hasAuthority('ROLE_TRUSTED_CLIENT')")
.checkTokenAccess("isAuthenticated() and hasAuthority('ROLE_TRUSTED_CLIENT')")
.realm("mysecurityRealm");
// #formatter:on
}
}
When I tried to fetch access tokens from tokenStore with clientId as below codes
#Autowired
private JwtTokenStore tokenStore;
#Autowired
private ConsumerTokenServices consumerTokenServices;
#RequestMapping(value = "/invalidateTokens", method = RequestMethod.POST)
public #ResponseBody Map<String, String> revokeAccessToken(#RequestParam(name = "access_token") String accessToken) {
logger.info("Invalidating access token ==> " + accessToken);
String clientId = "myclient-backend";
List<String> tokenValues = new ArrayList<String>();
Collection<OAuth2AccessToken> tokens = tokenStore.findTokensByClientId(clientId);
logger.debug("Listing all active tokens for clientId '" + clientId + "'" + tokens);
if (tokens != null) {
for (OAuth2AccessToken token : tokens) {
logger.info("==> " + token.getValue());
tokenValues.add(token.getValue());
}
}
consumerTokenServices.revokeToken(accessToken);
OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken);
if (oAuth2AccessToken != null) {
tokenStore.removeAccessToken(oAuth2AccessToken);
}
Map<String, String> ret = new HashMap<>();
ret.put("removed_access_token", accessToken);
return ret;
}
It always output empty arrays as
Listing all active tokens for clientId 'myclient-backend'[]
What am I missing to configure ?

Sorry ... I should configure TokenStore as simple way and it is good enough for in-memory store ..
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}

Related

How to verify signature utilizing accessTokenConverter?

I need to verify signature at resource server. I am signing JWT with private key at auth.server and It is signed OK, but I cannot find a way, how to verify it using accessTokenConverter. In my previous project, I did not use JDBC, so I was using jwtTokenStore and It worked without a problem, but I cannot verify that signature with JDBCTokenStore. How to do that? So code at authorization server works, I need to verify it at resource server... .setVerifiedKey(publicKey) should be working, but I need to configure it with JDBCTokenStore...
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
private TokenStore tokenStore;
// #Autowired
// private JwtAccessTokenConverter accessTokenConverter;
#Autowired
private AuthenticationManager authenticationManager;
#Autowired
private UserDetailsService userCustomService;
#Autowired
private JdbcTemplate jdbcTemplate;
#Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
configurer
.jdbc(jdbcTemplate.getDataSource());
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore)
.reuseRefreshTokens(false)
.accessTokenConverter(accessTokenConverter())
.authenticationManager(authenticationManager)
.userDetailsService(userCustomService);
;
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
final JwtAccessTokenConverter converter = new JwtAccessTokenConverter(){
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
if(authentication.getOAuth2Request().getGrantType().equalsIgnoreCase("password")) {
final Map<String, Object> additionalInfo = new HashMap<String, Object>();
additionalInfo.put("organization", "NEJAKA INFORMACE");
((DefaultOAuth2AccessToken) accessToken)
.setAdditionalInformation(additionalInfo);
}
accessToken = super.enhance(accessToken, authentication);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(new HashMap<>());
return accessToken;
}
};
KeyStoreKeyFactory keyStoreKeyFactory =
new KeyStoreKeyFactory(new ClassPathResource("test.jks"), "password".toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("test"));
return converter;
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("hasAuthority('ROLE_TRUSTED_CLIENT')").checkTokenAccess("hasAuthority('ROLE_TRUSTED_CLIENT')");
}
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Autowired
private ResourceServerTokenServices tokenServices;
#Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.tokenServices(tokenServices); }
#Override
public void configure(HttpSecurity http) throws Exception {
http.
anonymous().disable()
.authorizeRequests()
.antMatchers("/documents/**").authenticated()
.antMatchers("/users/**").authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Bean
public JwtAccessTokenConverter accessTokenConverterr() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
Resource resource = new ClassPathResource("public.txt");
String publicKey = null;
try {
publicKey = IOUtils.toString(resource.getInputStream());
} catch (final IOException e) {
throw new RuntimeException(e);
}
converter.setVerifierKey(publicKey);
return converter;
}
}
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// private String signingKey = "MaYzkSjmkzPC57L";
#Autowired
private UserDetailsService userCustomService;
#Autowired
private JdbcTemplate jdbcTemplate;
private PasswordEncoder encoder;
public SecurityConfig(){
this.encoder = new BCryptPasswordEncoder();
}
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userCustomService).passwordEncoder(encoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.
STATELESS)
.and()
.httpBasic()
.and()
.csrf()
.disable();
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(jdbcTemplate.getDataSource());
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}

spring security Authentication and resource server seprated check_token is not sending authorization header (outh2 jwt spring boot, zuul)

problem
resource server making request for check_token its not passing authorization token which is implemented by spring security. how we can pass authorization token for /check_token endpoint?
I am using zuul as api gateway and all request go through zuul only.
i created auth server which is authentication server (spring cloud project)
and code is given below for authorization and i register authenticationManager with webSecurityConfigurationAdapter
code is given below
AuthorizationServerConfig.java
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Autowired
#Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
#Autowired
private AuthenticationManager authenticationManager;
#Value("${config.oauth2.tokenTimeout}")
private int expiration;
#Value("${config.oauth2.privateKey}")
private String privateKey;
#Value("${config.oauth2.publicKey}")
private String publicKey;
#Autowired
private ClientDetailsService clientDetailsService;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("client")
.secret(passwordEncoder().encode("secret"))
.authorizedGrantTypes("client_credentials", "password", "refresh_token", "authorization_code")
.scopes("read", "write", "trust")
.accessTokenValiditySeconds(expiration)
.refreshTokenValiditySeconds(expiration);
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(privateKey);
return converter;
}
#Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Value("${filters.cors.allowed.origin}")
private String allowedOriginUrlForCordFilter;
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin(allowedOriginUrlForCordFilter);
//config.addAllowedOrigin("http://localhost:8080/");
config.addAllowedHeader("*");
config.addAllowedMethod("GET");
config.addAllowedMethod("PUT");
config.addAllowedMethod("POST");
config.addAllowedMethod("DELETE");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setClientDetailsService(clientDetailsService);
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenEnhancer(accessTokenConverter());
return defaultTokenServices;
}
/**
* Defines the authorization and token endpoints and the token services
* #param endpoints
* #throws Exception
*/
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
.tokenStore(tokenStore())
.tokenServices(tokenServices())
.accessTokenConverter(accessTokenConverter());
}
#Override
public void configure(AuthorizationServerSecurityConfigurer security) {
security.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
}
resource server another spring cloud project which is basically our business services
and for further communication i need authorization token this code is implement by using filter i implemented.
ResourceServerConfiguration.java
#Configuration
#EnableResourceServer
#EnableWebSecurity(debug = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
// private static final Logger LOGGER =
// Logger.getLogger(ResourceServerConfiguration.class);
#Value("${config.oauth2.publicKey}")
private String publicKey;
#Value("${config.oauth2.privateKey}")
private String privateKey;
#Value("${config.oauth2.resource.id}")
private String resourceId;
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().anonymous().disable().authorizeRequests()
// .antMatchers(HttpMethod.OPTIONS).permitAll()
// .antMatchers("/oauth/**").authenticated()
.antMatchers("/register/**").authenticated();
}
// #Override
// public void configure(ResourceServerSecurityConfigurer resources) {
// resources.resourceId(resourceId).tokenServices(tokenServices()).tokenStore(tokenStore());
// }
// #Bean
// #Primary
// public DefaultTokenServices tokenServices() {
// DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
// defaultTokenServices.setTokenStore(tokenStore());
// defaultTokenServices.setSupportRefreshToken(true);
// defaultTokenServices.setTokenEnhancer(accessTokenConverter());
// return defaultTokenServices;
// }
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(privateKey);
return converter;
}
#Bean
public JwtTokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Primary
#Bean
public RemoteTokenServices tokenServices() {
final RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl("http://localhost:8765/auth/oauth/check_token/");
tokenService.setClientId("client");
tokenService.setClientSecret("secret");
//tokenService.setTokenName("");
// tokenService.setTokenStore(tokenStore());
// tokenService.setSupportRefreshToken(true);
tokenService.setAccessTokenConverter(accessTokenConverter());
return tokenService;
}
}
now issue is when resource server making request for check_token there i am anable to pass authorization token.

OAuth2 Authenticate using the social account and storing the jwt token in the database

I’m following the tutorial series: https://www.callicoder.com/spring-boot-security-oauth2-social-login-part-1/ it’s the greatest tutorial but the token doesn't store in the database and token’s format doesn’t fit to me.
I need an access_token and refresh_token, for example:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjdXN0b21pemVkIjoidHJ1ZSIsInVzZXJfbmFtZSI6ImRqb25pa2dhQGdtYWlsLmNvbSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJuYW1lIjoi0JXQstCz0LXQvdC40Lkg0JTQstC-0YDRhtC10LLQvtC5IiwiaWQiOjksImV4cCI6MTU1ODI1NTU1MCwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6Ijg1NjMxZmQ3LTRmNDMtNDIzNC05M2RlLTI5NTUxNDJjZmEzZiIsImNsaWVudF9pZCI6Imp3dENsaWVudElkUGFzc3dvcmQifQ.yP4dWiSdWPh1wsrirXzG6p19gjx5yI9MvsIjyESe1is",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjdXN0b21pemVkIjoidHJ1ZSIsInVzZXJfbmFtZSI6ImRqb25pa2dhQGdtYWlsLmNvbSIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJhdGkiOiI4NTYzMWZkNy00ZjQzLTQyMzQtOTNkZS0yOTU1MTQyY2ZhM2YiLCJuYW1lIjoi0JXQstCz0LXQvdC40Lkg0JTQstC-0YDRhtC10LLQvtC5IiwiaWQiOjksImV4cCI6MTU2MDg0NjY1MCwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6Ijg4Y2FlNzYzLTJmOTItNDQ2Ni1hMmU3LTk0NGZmODQ4NmQ5NCIsImNsaWVudF9pZCI6Imp3dENsaWVudElkUGFzc3dvcmQifQ.0cUifDOtxAryTGD0qn2GHPtiAoNSlDfd3fpamlGGGrE",
"expires_in": 899,
"scope": "read write"
}
After authenticate with social account like the google, facebook I create the token by manual and redirect to get some data with token but the token is not valid.
get - http://localhost:8080/user/me
bearer: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzb3VyY2VJZFRlc3QiXSwiY3VzdG9taXplZCI6InRydWUiLCJ1c2VyX25hbWUiOiJkam9uaWtnYUBnbWFpbC5jb20iLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwibmFtZSI6ItCV0LLQs9C10L3QuNC5INCU0LLQvtGA0YbQtdCy0L7QuSIsImlkIjo5LCJleHAiOjE1NTgyNTM3NTEsImF1dGhvcml0aWVzIjpbIlJPTEVfVVNFUiJdLCJqdGkiOiJhYzY0M2M2Yy0wZDYyLTQ2MDMtOWYxMy0wODE5MmNlYmYzZDAiLCJjbGllbnRfaWQiOiJqd3RDbGllbnRJZFBhc3N3b3JkIn0.d9_1gc_9PbrkG5mwoLRymBiPWpmH0VTcSEdnhz1aaxA
I receive the response
{
"error": "access_denied",
"error_description": "Invalid token does not contain resource id (jwtClientIdPassword)"
}
I have a frontend - React, Authorization server - Spring Security 5.0, postgres.
Also I authenticate with the OAuth 2.0 and password grant flow. The token stores in the database.
My Authorization Adapter
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
#Bean
protected AuthorizationCodeServices authorizationCodeServices() {
return new JdbcAuthorizationCodeServices(dataSource.dataSource());
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter()));
endpoints.authenticationManager(this.authenticationManager)
.tokenEnhancer(tokenEnhancerChain)
.tokenStore(tokenStore());
}
#Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
CustomTokenConverter converter = new CustomTokenConverter();
converter.setSigningKey(signingKey);
return converter;
}
#Bean
#Primary
//Making this primary to avoid any accidental duplication with another token service instance of the same name
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
return defaultTokenServices;
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
My Security Web Adapter
#Bean
public HttpCookieOAuth2AuthorizationRequestRepository cookieAuthorizationRequestRepository() {
return new HttpCookieOAuth2AuthorizationRequestRepository();
}
#Override
public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder
.userDetailsService(customUserDetailsService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return DefaultPasswordEncoderFactories.createDelegatingPasswordEncoder();
}
#Bean(BeanIds.AUTHENTICATION_MANAGER)
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.exceptionHandling()
.authenticationEntryPoint(new RestAuthenticationEntryPoint())
.and()
.authorizeRequests()
.antMatchers("/auth/**", "/oauth2/**").permitAll()
.anyRequest()
.authenticated()
.and()
.oauth2Login()
.authorizationEndpoint().baseUri("/oauth2/authorize")
.authorizationRequestRepository(cookieAuthorizationRequestRepository())
.and()
.redirectionEndpoint().baseUri("/oauth2/callback/*")
.and()
.userInfoEndpoint()
.userService(customOAuth2UserService)
.and()
.successHandler(oAuth2AuthenticationSuccessHandler)
.failureHandler(oAuth2AuthenticationFailureHandler);
http.addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
In the oAuth2AuthenticationSuccessHandler.onAuthenticationSuccess I try to create the token
#Component
public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private TokenProvider tokenProvider;
private AppProperties appProperties;
private HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
=====
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
String targetUrl = determineTargetUrl(request, response, authentication);
if (response.isCommitted()) {
logger.debug("Response has already been committed. Unable to redirect to " + targetUrl);
return;
}
clearAuthenticationAttributes(request, response);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
Optional<String> redirectUri = CookieUtils.getCookie(request, REDIRECT_URI_PARAM_COOKIE_NAME)
.map(Cookie::getValue);
if(redirectUri.isPresent() && !isAuthorizedRedirectUri(redirectUri.get())) {
throw new BadRequestException("Sorry! We've got an Unauthorized Redirect URI and can't proceed with the authentication");
}
String targetUrl = redirectUri.orElse(getDefaultTargetUrl());
OAuth2AccessToken accessToken = tokenProvider.createAccessToken(authentication);
return UriComponentsBuilder.fromUriString(targetUrl)
.queryParam("token", accessToken.getValue())
.build().toUriString();
}
Here I try to create the jwt token
#Service
public class TokenProvider {
private static final Logger logger = LoggerFactory.getLogger(TokenProvider.class);
private AppProperties appProperties;
#Autowired
private TokenStore tokenStore;
#Autowired
private AuthorizationServerEndpointsConfiguration configuration;
public TokenProvider(AppProperties appProperties) {
this.appProperties = appProperties;
}
public OAuth2AccessToken createAccessToken(Authentication authentication) {
UserPrincipal user = (UserPrincipal)authentication.getPrincipal();
List<String> scopes = new ArrayList<>();
scopes.add("read");
scopes.add("write");
Map<String, String> requestParameters = new HashMap<String, String>();
Map<String, Serializable> extensionProperties = new HashMap<String, Serializable>();
requestParameters.put("client_id", "jwtClientIdPassword");
requestParameters.put("grant_type", "password");
requestParameters.put("scope", "read,write");
requestParameters.put("client_secret", "client");
requestParameters.put("username", user.getEmail());
requestParameters.put("password", user.getPassword());
boolean approved = true;
Set<String> responseTypes = new HashSet<String>();
responseTypes.add("code");
OAuth2Request oauth2Request = new OAuth2Request(requestParameters, "jwtClientIdPassword", user.getAuthorities(), approved, new HashSet<String>(scopes), new HashSet<String>(Arrays.asList("resourceIdTest")), null, responseTypes, extensionProperties);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(user, "N/A", user.getAuthorities());
OAuth2Authentication auth = new OAuth2Authentication(oauth2Request, authenticationToken);
AuthorizationServerTokenServices tokenService = configuration.getEndpointsConfigurer().getTokenServices();
OAuth2AccessToken token = tokenService.createAccessToken(auth);
return token;
}
I'm not sure I'm doing it right.
I found out what the problem is. I had the wrong parameter it's
new HashSet<String>(Arrays.asList("resourceIdTest"))
and I changed the code
public OAuth2AccessToken createAccessToken(Authentication authentication) {
UserPrincipal userPrincipal = (UserPrincipal)authentication.getPrincipal();
Set<GrantedAuthority> authorities = new HashSet(userPrincipal.getAuthorities());
Map<String, String> requestParameters = new HashMap<>();
String clientId = "jwtClientIdPassword";
boolean approved = true;
Set<String> resourceIds = new HashSet<>();
Set<String> responseTypes = new HashSet<>();
responseTypes.add("code");
Map<String, Serializable> extensionProperties = new HashMap<>();
List<String> scopes = new ArrayList<>();
scopes.add("read");
scopes.add("write");
OAuth2Request oAuth2Request = new OAuth2Request(requestParameters, clientId,
authorities, approved, new HashSet<>(scopes),
resourceIds, null, responseTypes, extensionProperties);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userPrincipal, null, authorities);
OAuth2Authentication auth = new OAuth2Authentication(oAuth2Request, authenticationToken);
AuthorizationServerTokenServices tokenService = configuration.getEndpointsConfigurer().getTokenServices();
OAuth2AccessToken token = tokenService.createAccessToken(auth);
return token;
}

spirng boot 2 jwt oauth2 + angular 5 can't get the JWT

I'm new working with spring boot and spring security and I'm trying to implement oauth2 to generate a JWT and used this token in an angular5 application, my situation is that after implementation I can get the token if a use postman or curl but when I use my web client in angular I can't get the token.
this is what I did.
My login method is angular
login(username: string, password: string ) {
const params: HttpParams = new HttpParams();
const headers: Headers = new Headers();
params.set('username', 'GDELOSSANTOS');
params.set('password', 'ADMIN');
params.set('client_id', 'ADMIN');
params.set('client_secret', 'ADMIN');
params.set('grant_type', 'password');
params.set('scope', '*');
headers.set('Content-Type', 'application/x-www-form-urlencoded');
return this.http.post(Constante.BACKEND_TOKEN_REQUEST, {headers}, {params} ).subscribe
(res => this.setSession);
}
My authorization server
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
private static final Logger logger = LogManager.getLogger(AuthorizationServerConfig.class);
#Value("${security.oauth2.resource.id}")
private String resourceId;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
logger.traceEntry();
clients
.inMemory()
.withClient(ConstanteUtil.Seguridad.CLIEN_ID)
.secret(Seguridad.CLIENT_SECRET)
.authorizedGrantTypes(Seguridad.GRANT_TYPE_PASSWORD, Seguridad.AUTHORIZATION_CODE, Seguridad.REFRESH_TOKEN, Seguridad.IMPLICIT )
.authorities(UsusarioRoles.ROLE_ADMIN, UsusarioRoles.ROLE_USER)
.resourceIds(resourceId)
.scopes(Seguridad.SCOPE_READ, Seguridad.SCOPE_WRITE, Seguridad.TRUST)
.accessTokenValiditySeconds(Seguridad.ACCESS_TOKEN_VALIDITY_SECONDS).
refreshTokenValiditySeconds(Seguridad.FREFRESH_TOKEN_VALIDITY_SECONDS);
logger.info("Configuracion " + clients);
logger.traceExit();
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
final TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
endpoints.tokenStore(tokenStore())
.tokenEnhancer(tokenEnhancerChain)
.authenticationManager(authenticationManager);
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
}
My Resource Server
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final Logger logger = LogManager.getLogger(AuthorizationServerConfig.class);
#Value("${security.oauth2.resource.id}")
private String resourceId;
#Override
public void configure(final HttpSecurity http) throws Exception {
logger.traceEntry("Entrada configure");
// #formatter:off
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.authorizeRequests().anyRequest().permitAll();
logger.info("Ejecucion de metodo " + http);
// #formatter:on
}
#Override
public void configure(final ResourceServerSecurityConfigurer config) {
config.resourceId(resourceId).stateless(true); }
}
The WebSecurity
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final Logger logger = LogManager.getLogger(WebSecurityConfig.class);
#Autowired
#Resource(name = "UsuarioService")
private UserDetailsService userDetailsService;
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
logger.traceEntry("globalUserDetails", auth);
auth.userDetailsService(userDetailsService)
.passwordEncoder(encoder());
logger.traceExit("globalUserDetails", auth);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
logger.traceEntry();
logger.info("ejecutando configuracion " + http);
http.cors().disable()
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/login", "/logout.do").permitAll()
.antMatchers("/**").authenticated()
.and().formLogin().loginPage("/login").permitAll()
.and().httpBasic();
logger.info("se ejecuto configuracion " + http);
}
#Bean
public BCryptPasswordEncoder encoder(){
return new BCryptPasswordEncoder();
}
#Bean
public FilterRegistrationBean corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/auth/token").allowedOrigins("http://localhost:9000");
}
};
}
}
The implementation of loadUserDetail of UserDetailService
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
logger.traceEntry("Iniciando loadUserByUsername");
/Here we are using dummy data, you need to load user data from
database or other third party application/
try {
Usuario usuario = findAllUsuarioRoleByName(username);
logger.info("Se encontro el usaurio " + usuario);
UserBuilder builder = null;
if (usuario != null) {
List<String> roles = new ArrayList<>();
Collection<UsuarioRole> usuarioRoleByUsuarioName = usuarioRoleRepository.findAllUsuarioRoleByUsuarioName(usuario.getNombreUsuario());
logger.info("Roles encontrados " + usuarioRoleByUsuarioName.size());
for(UsuarioRole usuarioRole : usuarioRoleByUsuarioName) {
roles.add(usuarioRole.getRole().getNombreRole());
}
String[] rolesArray = new String[roles.size()];
rolesArray = roles.toArray(rolesArray);
builder = org.springframework.security.core.userdetails.User.withUsername(username);
builder.password(new BCryptPasswordEncoder().encode(usuario.getClaveUsuario()));
for (String string : rolesArray) {
logger.debug("**** " + string);
}
builder.roles(rolesArray);
} else {
throw new UsernameNotFoundException("User not found.");
}
return builder.build();
}finally {
logger.traceExit("Finalizando loadUserByUsername");
}
}
Make the following adjustments to your angular code.
Pass client_id and client_secret through Authorization header.
Serialize the object before post (you can reference this answer).
login(username: string, password: string ) {
let body = {
username: 'GDELOSSANTOS',
password: 'ADMIN',
grant_type: 'password'
};
// Serialize body object
let bodySerialized = 'grant_type=password&password=ADMIN&username=GDELOSSANTOS';
let headers = new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded')
.set('Authorization', 'Basic ' + btoa("ADMIN:ADMIN"));
return this.http.post(Constante.BACKEND_TOKEN_REQUEST,
bodySerialized,
{
headers: headers
}).subscribe(res => this.setSession);
}

Custom Spring Security OAuth2 with Spring Social integration

Custom Spring security OAuth2 is working fine and now would like to add Spring Social integration(facebook login, google login etc), When the user clicks on Facebook login(user would not provide any username/password), Facebook will return an access_token, but this access_token we can not use to query my application web services, to get my application access_token we need to pass username and password with grant_type as password. Below are my configuration files
AuthorizationServerConfiguration.java
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(
AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.allowFormAuthenticationForClients();
}
#Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.jdbc(dataSource);
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore());
tokenServices.setAccessTokenValiditySeconds(86400000);
tokenServices.setRefreshTokenValiditySeconds(86400000);
return tokenServices;
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints)
throws Exception {
endpoints
.tokenServices(tokenServices())
.authenticationManager(authenticationManager);
}
#Bean
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
}
ResourceServerConfiguration.java
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private String resourceId = "rest_api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
// #formatter:off
resources.resourceId(resourceId);
// #formatter:on
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.antMatchers(HttpMethod.GET, "/**/login").permitAll()
.antMatchers(HttpMethod.GET, "/**/callback").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
and finally WebSecurityConfigurerAdapter.java
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.antMatchers(HttpMethod.GET, "/**/login").permitAll()
.antMatchers(HttpMethod.GET, "/**/callback").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll();
}
}
Have read different posts in SO, but couldn't get any working example, please guide me on this. Thanks in advance.!
String redirectURL = messages.getProperty(Constant.REDIRECT_URI.getValue());
String clientSecret = messages.getProperty(Constant.CLIENT_SECRET.getValue());
HttpHeaders header = new HttpHeaders();
header.setContentType(org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED);
String req = "client_id=myas&" + "client_secret=" + clientSecret + "&grant_type=authorization_code&"
+ "scope=user_profile&" + "code=" + loginReqeust.getCode() + "&redirect_uri="
+ loginReqeust.getRedirectURL();
HttpEntity<String> body = new HttpEntity<String>(req, header);
Map<Object, Object> mapRes = new LinkedHashMap<Object, Object>();
// call to get access token
mapRes = getEndpoint("https://auth.mygov.in/oauth2/token", null, body, null);
String accessToken = mapRes.get("access_token").toString();
// Call for getting User Profile
String userUrl = "https://auth.mygov.in/myasoauth2/user/profile";
HttpHeaders head = new HttpHeaders();
head.add("Authorization", "Bearer " + accessToken);
HttpEntity<String> ent = new HttpEntity<String>(head);
Map<Object, Object> mapResponse = new LinkedHashMap<Object, Object>();
mapResponse.put("userProfile", getEndpoint(userUrl, null, ent, null));
//In my case userKey represents the username basically the email of the user using which he/she logged into facebook/google
String userKey = (String) ((LinkedHashMap<Object, Object>) mapResponse.get("userProfile")).get("mail");
// Store the user profile in your database with basic info like username & an autogenerated password for the time being and other basic fields.
userService.save(userprofileInfo);
mapResponse.put("username", "retrieved from facebook/google user's profile");
mapResponse.put("password", "autogenerated by your application");
//send back this response (mapResponse) to your UI and then from there make a call by passing this username and pwd to retrieve the access_token from your own applicatioon.
I had a similar requirement to get an access token from facebook and generate own JWT token by validating the facebook token on the server side.
I modified the project mentioned here:
https://github.com/svlada/springboot-security-jwt
My customizations are as follows(I am assuming you already have a facebook access token):
LoginRequest.java
public class LoginRequest {
private String token;
#JsonCreator
public LoginRequest(#JsonProperty("token") String token) {
this.token = token;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
AjaxLoginProcessingFilter.java
#Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException, IOException, ServletException {
if (!HttpMethod.POST.name().equals(request.getMethod()) || !WebUtil.isAjax(request)) {
if(logger.isDebugEnabled()) {
logger.debug("Authentication method not supported. Request method: " + request.getMethod());
}
throw new AuthMethodNotSupportedException("Authentication method not supported");
}
LoginRequest loginRequest = objectMapper.readValue(request.getReader(), LoginRequest.class);
if (StringUtils.isBlank(loginRequest.getToken())) {
throw new AuthenticationServiceException("token not provided");
}
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(loginRequest.getToken(), null);
return this.getAuthenticationManager().authenticate(token);
}
AjaxAuthenticationProvider.java
#Component
public class AjaxAuthenticationProvider implements AuthenticationProvider {
#Autowired private BCryptPasswordEncoder encoder;
#Autowired private DatabaseUserService userService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.notNull(authentication, "No authentication data provided");
String username = null;
try {
username = getUsername(authentication.getPrincipal());
} catch (UnsupportedOperationException e) {
} catch (IOException e) {
}
//You can either register this user by fetching additional data from facebook or reject it.
User user = userService.getByUsername(username).orElseThrow(() -> new UsernameNotFoundException("User not found"));
if (user.getRoles() == null) throw new InsufficientAuthenticationException("User has no roles assigned");
List<GrantedAuthority> authorities = user.getRoles().stream()
.map(authority -> new SimpleGrantedAuthority(authority.getRole().authority()))
.collect(Collectors.toList());
UserContext userContext = UserContext.create(user.getUsername(), authorities);
return new UsernamePasswordAuthenticationToken(userContext, null, userContext.getAuthorities());
}
private String getUsername(Object principal) throws UnsupportedOperationException, IOException {
HttpClient client = new DefaultHttpClient();
//I am just accessing the details. You can debug whether this token was granted against your app.
HttpGet get = new HttpGet("https://graph.facebook.com/me?access_token=" + principal.toString());
HttpResponse response = client.execute(get);
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer result = new StringBuffer();
String line = "";
while ((line = rd.readLine()) != null) {
result.append(line);
}
JSONObject o = new JSONObject(result.toString());
//This is just for demo. You should use id or some other unique field.
String username = o.getString("first_name");
return username;
}
#Override
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
}
}
Apart from this, I also had to add custom BeanPostProcessor to override the default behavior of UsernamePasswordAuthenticationFilter to accept only token as a field instead of a username and a password.
UserPassAuthFilterBeanPostProcessor.java
public class UserPassAuthFilterBeanPostProcessor implements BeanPostProcessor {
private String usernameParameter;
private String passwordParameter;
#Override
public final Object postProcessAfterInitialization(final Object bean,
final String beanName) {
return bean;
}
#Override
public final Object postProcessBeforeInitialization(final Object bean,
final String beanName) {
if (bean instanceof UsernamePasswordAuthenticationFilter) {
final UsernamePasswordAuthenticationFilter filter =
(UsernamePasswordAuthenticationFilter) bean;
filter.setUsernameParameter(getUsernameParameter());
filter.setPasswordParameter(getPasswordParameter());
}
return bean;
}
public final void setUsernameParameter(final String usernameParameter) {
this.usernameParameter = usernameParameter;
}
public final String getUsernameParameter() {
return usernameParameter;
}
public final void setPasswordParameter(final String passwordParameter) {
this.passwordParameter = passwordParameter;
}
public final String getPasswordParameter() {
return passwordParameter;
}
Configuration:
#Bean
public UserPassAuthFilterBeanPostProcessor userPassAuthFilterBeanPostProcessor(){
UserPassAuthFilterBeanPostProcessor bean = new UserPassAuthFilterBeanPostProcessor();
bean.setUsernameParameter("token");
bean.setPasswordParameter(null);
return bean;
}

Resources