Spring #CookieValue is null while sending request from POSTMAN - spring

From chrome browser When i send request to endpoint /slogin, cookie value is getting set and also i am able to retrieve the same value in endpoint rlogin but when i am sending it from POSTMAN i am not able to get refreshToken cookie value in endpoint rlogin , how can i make it working?
Setting cookie like this
#PostMapping(path = "/slogin", consumes = MediaType.APPLICATION_JSON_VALUE)
private ResponseEntity<String> loginUser(#Valid #RequestBody User user)
.....
.....
ResponseCookie springCookie = ResponseCookie.from("refresh-tokens", refreshToken)
.httpOnly(true)
.secure(true)
.path("/auth/rlogin")
.maxAge(60)//30days=2592000
.sameSite("None")
.domain("localhost")
.build();
This is refreshToken coming as null
#PostMapping(path = "/rlogin")
private ResponseEntity<String> refreshLoginUser(#CookieValue(name = "refresh-tokens", defaultValue = "null") String refreshToken)
throws ClientProtocolException, IOException {
try {
HttpClient httpclient = HttpClientBuilder.create().build();
springsecurity
package com.webservice.ansoftsys.configs;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Value("${security.signing-key}")
private String signingKey;
#Value("${security.encoding-strength}")
private Integer encodingStrength;
#Value("${security.security-realm}")
private String securityRealm;
#Bean
#Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
#Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic()
.realmName(securityRealm)
.and()
.csrf()
.disable();
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey(signingKey);
return converter;
}
#Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
#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);
return defaultTokenServices;
}
#Bean
public FilterRegistrationBean<CorsFilter> corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.setAllowCredentials(true);
source.registerCorsConfiguration("/**", config);
FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}
enter image description here
enter image description here
I have tried the above code, i am expecting the cookie value to be available in the endpoint rlogin

Related

POST operation on /logout and getting No mapping for GET /login

I'm trying to perform logout so I send POST on /logout but what I get is:
{
"timestamp": 1590427554418,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/login"
}
I'm using POSTMAN. Despite having error logout perform correclly, because I cannot make any actions on other endpoints when I'm not logged.
The point is when code is uploaded on the server I got timeout:
I thought it might be somehow related.
WebSecurityConfiguration
package odd.jobs.configuration;
import odd.jobs.configuration.authentication.JsonObjectAuthenticationFilter;
import odd.jobs.configuration.authentication.RestAuthenticationFailureHandler;
import odd.jobs.configuration.authentication.RestAuthenticationSuccessHandler;
import odd.jobs.services.user.UserCrudService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
import java.util.Collections;
#Configuration
#EnableWebSecurity()
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final UserCrudService userService;
private final RestAuthenticationSuccessHandler authenticationSuccessHandler;
private final RestAuthenticationFailureHandler authenticationFailureHandler;
#Autowired
public WebSecurityConfiguration(UserCrudService userService, RestAuthenticationSuccessHandler authenticationSuccessHandler, RestAuthenticationFailureHandler authenticationFailureHandler) {
this.userService = userService;
this.authenticationSuccessHandler = authenticationSuccessHandler;
this.authenticationFailureHandler = authenticationFailureHandler;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.exceptionHandling()
.authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.permitAll();
http.cors();
}
#Bean
public JsonObjectAuthenticationFilter authenticationFilter() throws Exception {
JsonObjectAuthenticationFilter filter = new JsonObjectAuthenticationFilter();
filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
filter.setAuthenticationFailureHandler(authenticationFailureHandler);
filter.setAuthenticationManager(super.authenticationManagerBean());
return filter;
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Collections.unmodifiableList(Arrays.asList("*")));
configuration.setAllowedMethods(Collections.unmodifiableList(Arrays.asList("HEAD",
"GET", "POST", "PUT", "DELETE", "PATCH")));
// setAllowCredentials(true) is important, otherwise:
// The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
configuration.setAllowCredentials(true);
// setAllowedHeaders is important! Without it, OPTIONS preflight request
// will fail with 403 Invalid CORS request
configuration.setAllowedHeaders(Collections.unmodifiableList(Arrays.asList("Authorization", "Cache-Control", "Content-Type")));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
It seems you used postman but with wrong method type as postman exception implies
Please change postman HTTP method from GET to POST from here :
Hope it should work!
If you want to use POST /logout, configure Spring Security with new AntPathRequestMatcher("/logout", "POST") instead of new AntPathRequestMatcher("/logout") (GET is used by default)

Spring-boot 2.0 session issue when request is coming from different host

So I have updated my application from spring-boot 1.5 to spring-boot 2.0. I was able to login properly with spring-boot 1.5 version when the request was coming from any host, but now the problem with Spring boot 2.0 is only requests from same host is working, but request coming from different host their session is getting changed. Below is the spring security config I have in spring-boot 2.0 whic is causing the issue.
package com.s4m.digiid.config;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import com.s4m.digiid.handler.CustomAccessDeniedHandler;
import com.s4m.digiid.handler.CustomAuthFailureHandler;
import com.s4m.digiid.handler.LoginHandler;
import com.s4m.digiid.handler.LogoutHandler;
import com.s4m.digiid.service.impl.AuthenticationService;
import com.s4m.digiid.util.ApiConstants;
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#EnableJpaRepositories(basePackages = "com.s4m.digiid.repository")
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
#Autowired
private LogoutHandler logoutHandler;
#Autowired
private LoginHandler loginHandler;
#Autowired
private CustomAuthFailureHandler customAuthFailure;
#Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
#Autowired
private AuthenticationService authService;
#Value("${maximum.sessions}")
private Integer maxSessions;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(authService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Autowired
private FindByIndexNameSessionRepository<? extends Session> sessionRepository;
#Bean
public SpringSessionBackedSessionRegistry sessionRegistry() {
return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);
}
#Bean
public SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(ApiConstants.SWAGGER_DOC_API, ApiConstants.SWAGGER_CONFIG_UI, ApiConstants.SWAGGER_RESOURCES,
ApiConstants.SWAGGER_CONFIG, ApiConstants.SWAGGER_HTML_UI, ApiConstants.WEB_JARS, ApiConstants.FAVICON).permitAll()
.and().formLogin().successHandler(loginHandler).failureHandler(customAuthFailure)
.and().logout()
.logoutUrl("/appLogout")
.logoutSuccessHandler(logoutHandler)
.and().exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.and().csrf().disable()
.cors().and()
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.sessionManagement().maximumSessions(maxSessions).sessionRegistry(sessionRegistry()).maxSessionsPreventsLogin(false);
}
#Bean
public AuthenticationFilter authenticationFilter() throws Exception {
AuthenticationFilter filter = new AuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationFailureHandler(customAuthFailure);
filter.setAuthenticationSuccessHandler(loginHandler);
return filter;
}
#Bean
public CorsConfigurationSource corsConfigurationSource()
{
List<String> allowedClients = Arrays.asList(env.getProperty("digiid.allowed.clients").split(","));
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(allowedClients);
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS"));
configuration.setMaxAge(Long.parseLong("3600"));
configuration.setAllowedHeaders(Arrays.asList("X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization,X-Auth-Token"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
Below is the code in spring-boot 1.5 which is working fine.
package com.s4m.digiid.config;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import com.s4m.digiid.handler.CustomAccessDeniedHandler;
import com.s4m.digiid.handler.CustomAuthFailureHandler;
import com.s4m.digiid.handler.LoginHandler;
import com.s4m.digiid.handler.LogoutHandler;
import com.s4m.digiid.service.impl.AuthenticationService;
import com.s4m.digiid.util.ApiConstants;
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#EnableRedisHttpSession
#EnableJpaRepositories(basePackages = "com.s4m.digiid.repository")
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private Environment env;
#Autowired
private LogoutHandler logoutHandler;
#Autowired
private LoginHandler loginHandler;
#Autowired
private CustomAuthFailureHandler customAuthFailure;
#Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
#Autowired
private AuthenticationService authService;
#Autowired
private SpringSessionBackedSessionRegistry sessionRegistry;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(authService)
.passwordEncoder(passwordEncoder());
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers(ApiConstants.SWAGGER_DOC_API, ApiConstants.SWAGGER_CONFIG_UI, ApiConstants.SWAGGER_RESOURCES,
ApiConstants.SWAGGER_CONFIG, ApiConstants.SWAGGER_HTML_UI, ApiConstants.WEB_JARS, ApiConstants.FAVICON).permitAll()
.and().formLogin().successHandler(loginHandler).failureHandler(customAuthFailure)
.and().logout()
.logoutUrl("/appLogout")
.logoutSuccessHandler(logoutHandler)
.and().exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.and().csrf().disable()
.cors().and()
.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class)
.sessionManagement().maximumSessions(1).sessionRegistry(sessionRegistry).maxSessionsPreventsLogin(false);
}
#Bean
public AuthenticationFilter authenticationFilter() throws Exception {
AuthenticationFilter filter = new AuthenticationFilter();
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationFailureHandler(customAuthFailure);
filter.setAuthenticationSuccessHandler(loginHandler);
return filter;
}
#Bean
public CorsConfigurationSource corsConfigurationSource()
{
List<String> allowedClients = Arrays.asList(env.getProperty("digiid.allowed.clients").split(","));
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(allowedClients);
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS"));
configuration.setMaxAge(Long.parseLong("3600"));
configuration.setAllowedHeaders(Arrays.asList("X-Requested-With", "Origin", "Content-Type", "Accept", "Authorization,X-Auth-Token"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
With the spring-boot upgrade to 2.x, "SameSite" attribute of Cookie handled in DefaultCookieSerializer.java class is set to "Lax" by default. 
 
  
Try setting  "SameSite" attribute's value to "None".
For more details, https://datatracker.ietf.org/doc/html/draft-west-first-party-cookies-07
  
  
  

How to get refresh token with grant type client_credentials

I am using spring-boot 2.5.0 for a REST API and implemented OAuth using following classes. I am aware that in grant type 'client_credentials' refresh token is not returned. However, the android team I am working with is adamant about having refresh token in grant type 'client_credentials' .
So I am seeking a way to get refresh tokens in client_credential
Currently , as per the default, I am only getting refresh_token in grant_type password.
The classes I am using :
1) AuthorizationServerConfiguration
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static String REALM="MY_OAUTH_REALM";
#Autowired
private TokenStore tokenStore;
#Autowired
private UserApprovalHandler userApprovalHandler;
#Autowired
#Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
int accessTokenMinutesValidity = 60;
int refreshTokenMinutesValidity = 24 * 60;
clients.inMemory()
.withClient("my-trusted-client")
.authorizedGrantTypes("client_credentials", "refresh_token")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.secret("{noop}secret")
.accessTokenValiditySeconds(60 * accessTokenMinutesValidity ).//Access token is only valid for 60 minutes.
refreshTokenValiditySeconds(60 * refreshTokenMinutesValidity);//Refresh token is only valid for 24 hours
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)
.authenticationManager(authenticationManager);
}
#Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
.allowFormAuthenticationForClients()
.realm(REALM+"/client");
}
}
2) MethodSecurityConfig
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true, proxyTargetClass = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
#Autowired
private OAuth2SecurityConfiguration securityConfig;
#Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
return new OAuth2MethodSecurityExpressionHandler();
}
}
3) OAuth2SecurityConfiguration
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.OAuth2RestTemplate;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
#SuppressWarnings("deprecation")
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
private DataSource dataSource;
#Primary
#Bean
public DataSource customDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUrl("<connection string>");
dataSource.setUsername("<username>");
dataSource.setPassword("<password>");
return dataSource;
}
#Bean
#ConfigurationProperties("spring.datasource")
public DataSource ds() {
//return DataSourceBuilder.create().build();
return customDataSource();
}
//USERS FROM DATABASE
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception
{
PasswordEncoder encoder = NoOpPasswordEncoder.getInstance();
BCryptPasswordEncoder enc;
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select USERNAME, ENC_PASSWD as PASSWORD, IS_ACTIVE AS ENABLED FROM USER_MSTR WHERE USERNAME=?")
.authoritiesByUsernameQuery("select USERNAME, 'ROLE_CLIENT' as ROLE from USER_MSTR where USERNAME=?")
.passwordEncoder(NoOpPasswordEncoder.getInstance())
;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.anonymous().disable()
.authorizeRequests()
.antMatchers("/oauth/token").permitAll();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
4) ResourceServerConfiguration
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
#Configuration
#EnableResourceServer
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
private static final String RESOURCE_ID = "my_rest_api";
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID).stateless(false);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.
anonymous().disable()
.requestMatchers()
.antMatchers("/category_mstr/**", "/equipment/**", "/param_mstr/**", "/chklist_txn/**", "/settings/**", "/user/**")
.and().authorizeRequests()
.antMatchers("/category_mstr/**", "/equipment/**", "/param_mstr/**", "/chklist_txn/**", "/settings/**", "/user/**")
.access("hasRole('ROLE_CLIENT')")
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
}
currently with grant_type = password, I am getting response as
{
"access_token": "25ca1254-4460-44bd-94c3-367f4b21e3b0",
"token_type": "bearer",
"refresh_token": "ef64adad-3772-4e0c-ae30-50358cdfffe1",
"expires_in": 3599,
"scope": "read trust write"
}
and with grant_type = client_credentials, I am getting response as
{
"access_token": "8c2bcdac-0a6b-4262-8664-75f1e0dc8351",
"token_type": "bearer",
"expires_in": 3599,
"scope": "read trust write"
}
How do I get response with refresh_token in above ??
I have tried extending ClientCredentialsAccessTokenProvider as follows
import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsAccessTokenProvider;
public class CCAccessTokenProvider extends ClientCredentialsAccessTokenProvider {
#Override
public boolean supportsRefresh(OAuth2ProtectedResourceDetails resource) {
return true;
}
}
but I don't know where to fit it in my current architecture
--EDIT
I am seeking a solution to this critical issue and the other thread doesn't address it .

How to secure Rest API using Spring Boot OAuth2

I want to create a sample Spring Boot application with OAuth2 integration which is having a CustomTokenEnhancer and it should expose /oauth/token URL to client without access token but all the other URL with can be queried only if they have a valid access token.
I am able to setup the CustomTokenEnhancer by which I am able to send the additional stuff when request comes for /oauth/token.
Project Structure
Application.java - Spring Boot Application class
AuthorizationServerConfiguration.java - Authorization Server
ResourceServer.java - Resource Server
OAuth2SecurityConfiguration.java - extends WebSecurityConfigurerAdapter and defines in memory user
CustomTokenEnhancer.java - Send additional stuff with access token like cookies
In OAuth2SecurityConfiguration.java , I am configuring the 3 URLs , /oauth/token can be queried by anyone which have the clientId and secret and once you have the access token. Client should be able to query the all the other URLs in my case /test and /inventory/sku/{skuID}
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/oauth/token").permitAll().antMatchers("/inventory/**","/test").hasAnyRole("USER","ADMIN");
}
But I am getting forbidden 401 when I am querying /test or /inventory/sku/1100.
I need help in making two URL /test and /inventory/sku/1100 available only with access token and /oauth/token without access token.
OAuth2SecurityConfiguration.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
#Configuration
#EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
public OAuth2SecurityConfiguration() {
System.out.println("OAuth2SecurityConfiguration()");
}
#Autowired
private ClientDetailsService clientDetailsService;
#Autowired
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("OAuth2SecurityConfiguration globalUserDetails() ,invoke BJSCustomAuthentication");
auth.inMemoryAuthentication() // authenticationProvider(new
// BJsCustomAuthentication());
.withUser("bill").password("abc123").roles("ADMIN").and().withUser("bob").password("abc123")
.roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/oauth/token").permitAll().antMatchers("/inventory/**","/test").hasAnyRole("USER","ADMIN");
//http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll().anyRequest().authenticated().and()
// .httpBasic().and().csrf().disable();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
#Bean
#Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore) {
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
#Bean
#Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
}
AuthorizationServer.java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
public AuthorizationServerConfiguration() {
System.out.println("AuthorizationServerConfiguration()");
}
#Autowired
AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
System.out.println("AuthorizationServerConfiguration configure()");
//endpoints.authenticationManager(authenticationManager).tokenEnhancer(tokenEnhancer()).tokenStore(tokenStore());
endpoints.authenticationManager(authenticationManager);
}
/*#Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
#Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}*/
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
System.out.println("OAUTH CLIENT CONFIGURED in AuthorizationServerConfiguration !!!");
clients.inMemory().withClient("my-trusted-client")
.authorizedGrantTypes("password", "authorization_code",
"refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust").resourceIds("sparklr")
.accessTokenValiditySeconds(60).
and()
.withClient("my-client-with-registered-redirect")
.authorizedGrantTypes("authorization_code").authorities("ROLE_CLIENT")
.scopes("read", "trust").resourceIds("sparklr")
.redirectUris("http://anywhere?key=value").
and()
.withClient("angular") //my-client-with-secret
.authorizedGrantTypes("password","refresh")
.authorities("ROLE_CLIENT").scopes("read","write").resourceIds("sparklr").accessTokenValiditySeconds(100)
.secret("secret");
}
}
CustomTokenEnhancer.java
import java.util.HashMap;
import java.util.Map;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
public class CustomTokenEnhancer implements TokenEnhancer {
public CustomTokenEnhancer() {
System.out.println("CustomTokenEnhancer()");
}
#Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
System.out.println("Custom Token Enhancer Initialized");
WCSResponse wcsResponse = (WCSResponse) authentication.getPrincipal();
authentication.getOAuth2Request().getRequestParameters();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("wcsResponse", wcsResponse);
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}

Upgrading to spring-boot 1.4.1 - OAuth2 Security integration tests pass but client apps cannot log in

I have just upgraded an app from spring-boot 1.3.5 to spring-boot 1.4.1
For some reason, all my integration tests pass using oauth2. The test client can register users and perform operations with them during the maven integration test phase.
However when I start up my API server (which also contains the resource and authorization servers currently) clients can no longer get a bearer token.
My ClientDetailsService is never called on client login - but is registered with
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
on application startup.
Any ideas anyone?
Here is the relevant code:
Authorization Server Config:
import javax.inject.Inject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import blablabla.api.security.ClientDetailsServiceImpl;
#Configuration
#EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Inject
private AuthenticationManager authenticationManager;
#Inject
private ClientDetailsServiceImpl clientDetailsService;
#Inject
private UserDetailsService userDetailsService;
public AuthorizationServerConfiguration() {
System.out.println("loaded");
}
// beans
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
final JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
// FIXME: Include a more secure key in the java token store
accessTokenConverter.setSigningKey("3434343434");
return accessTokenConverter;
}
#Bean
public TokenStore tokenStore() {
//return new InMemoryTokenStore();
return new JwtTokenStore(accessTokenConverter());
}
// config
#Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager).accessTokenConverter(accessTokenConverter());
endpoints.userDetailsService(userDetailsService);
}
#Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
}
Resource Server Config:
import javax.inject.Inject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import blablabla.security_lib.config.UserDetailsServiceImpl;
#Configuration
#EnableResourceServer
#EnableWebSecurity
#ComponentScan("blablabla.api.security")
#EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Inject
private UserDetailsServiceImpl userDetailsService;
#Inject
private TokenStore tokenStore;
// Beans
#Bean
public TokenStore tokenStore() {
//return new InMemoryTokenStore();
return new JwtTokenStore(accessTokenConverter());
}
#Bean
public JwtAccessTokenConverter accessTokenConverter() {
final JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
accessTokenConverter.setSigningKey("3434343434");
return accessTokenConverter;
}
// global security concerns
#Bean
public AuthenticationProvider authProvider() {
final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
return authProvider;
}
#Autowired
public void configureGlobal(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authProvider());
}
#Primary
#Bean
public ResourceServerTokenServices getTokenServices () {
DefaultTokenServices services = new DefaultTokenServices();
services.setSupportRefreshToken(true);
services.setTokenStore(tokenStore);
return services;
}
// http security concerns
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll().and().csrf().disable();
/*http
.authorizeRequests().regexMatchers("^/members").anonymous()
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.csrf()
.disable();*/
}
}
A few things I've also noticed - this all might mean something to somebody.
When login does not work (starting up the api and using a client)
The org.springframework.security.web.access.expression.WebExpressionVoter is passed a Collection containing one attribute of type org.springframework.security.web.access.expression.WebExpressionConfigAttribute with the value of
"#oauth2.throwOnError(authenticated)"
When login does work (running an acceptance test)
The org.springframework.security.web.access.expression.WebExpressionVoter is passed a Collection containing one attribute of type org.springframework.security.web.access.expression.WebExpressionConfigAttribute with the value of
"[fullyAuthenticated]"
So this turned out to not be a spring-boot issue at all, but instead some kind of issue that was caused in our gateway server when upgrading from spring-cloud Brixton.M2 release train to Brixton.RELEASE. This is sort of an unhelpful answer to an unhelpful question, so - happy to see this removed from SO.

Resources