Spring boot security x-auth-token not found in header - spring

I have a spring boot application having REST services secured with spring security. Redis is used for storing sessions. I have deployed the application in Glassfish 4.1.2. When trying to login using basic auth, x-auth-token is not returned in response header. What could be the issue ?
Below are my configuration classes:
ApplicationSecurityConfig
#Configuration
#EnableWebSecurity
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
private CustomAuthenticationDetailsSource source;
#Autowired
private HttpLogoutSuccessHandler logoutSuccessHandler;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Bean
public HttpSessionStrategy httpSessionStrategy() {
return new HeaderHttpSessionStrategy();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/crr/**").access("hasRole('CRR')")
.anyRequest().authenticated()
.and()
.requestCache()
.requestCache(new NullRequestCache())
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessHandler(logoutSuccessHandler)
.and()
.httpBasic().authenticationDetailsSource(source).authenticationEntryPoint(authenticationEntryPoint);
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.csrf().disable();
}
}
CORSCustomFilter
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSCustomFilter implements Filter {
public void doFilter(ServletRequest servletRequest,
ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "POST, GET");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers",
"X-Requested-With,content-type, Authorization");
chain.doFilter(servletRequest, servletResponse);
}
public void init(FilterConfig filterConfig) {
}
public void destroy() {
}
}
Note: When I deploy the application in Tomcat,x-auth-token is successfully generated in response header.

To retrieve it from response headers, Add x-auth-token to Access-Control-Allow-Credentials and Access-Control-Expose-Headers
response.setHeader("Access-Control-Expose-Headers", "x-auth-token");
response.setHeader("Access-Control-Allow-Credentials", "x-auth-token");
This worked for me.

Related

Spring security bypass if has different Authorization header

I have implemented spring security with jwt and is working fine.
#Configuration
#Component
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomJWTProvider jwtTokenProvider;
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/health").permitAll()
.anyRequest().authenticated()
.and()
.apply(new CustomJwtConfigurer(jwtTokenProvider));
}
#Override
public void configure(WebSecurity web) {
web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
}
}
But i have a typical use case where i get jwt token or 64bit fixed length token in Authorization header.
If it is JWT token, spring security should work as is and if it is not, the control should be passed to some method (where i have my logic to validate that token)
My filter looks like
#Component
public class CustomFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterchain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String authToken = httpRequest.getHeader("Authorization");
if(myCustomValidator(authToken)){
//if this is true it should skip spring security jwt token verification
//which is in configure phase
}else{
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.sendError(403, "Access denied");
}
System.out.println("authToken="+authToken);
filterchain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {}
#Override
public void destroy() {}
}
How/Where can i add this filter to bypass spring security, or any other mechanism to skip spring security based on authorization header?
You can use OncePerRequestFilter. You need to create a class and extend this class where you override its method(doFilterInternal) and put your logic there.
You can configure that filter like below.
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class YourClass extends WebSecurityConfigurerAdapter {
#Autowired
private YourFilter authFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAt(authFilter, UsernamePasswordAuthenticationFilter.class);
}
}

How to enable CORS in spring boot with spring security

I have implemented WebMvcConfigurerAdapter as well as added a CorsFilter and configured headers.
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings( CorsRegistry registry )
{
registry.addMapping("/**").allowedOrigins("http://localhost:3000").allowCredentials(false);
}
}
#Slf4j
public class CustomCorsFilter implements javax.servlet.Filter {
#Override
public void init( FilterConfig filterConfig ) throws ServletException
{
}
#Override
public void doFilter( ServletRequest req, ServletResponse res, FilterChain chain )
throws IOException, ServletException
{
if( req instanceof HttpServletRequest && res instanceof HttpServletResponse )
{
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
// Access-Control-Allow-Origin
String origin = request.getHeader("Origin");
response.setHeader("Access-Control-Allow-Origin", "http://localhost:3000");
response.setHeader("Vary", "Origin");
// Access-Control-Max-Age
response.setHeader("Access-Control-Max-Age", "3600");
// Access-Control-Allow-Credentials
response.setHeader("Access-Control-Allow-Credentials", "false");
// Access-Control-Allow-Methods
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
// Access-Control-Allow-Headers
response.setHeader("Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, " + "X-CSRF-TOKEN");
log.info("********************** Configured ****************************************");
}
chain.doFilter(req, res);
}
#Override
public void destroy()
{
}
}
I have two other filters which does Authentication and Authorisation . But when a frontend app in a local system tries to hit the API, I am getting the following error,
Access to XMLHttpRequest at 'http://3.12.228.75:8090/rest/noauth/otp/sandesha#test.com' from origin 'http://0.0.0.0:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: 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'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
How to resolve this? I am using spring-boot 1.5.10
and my WebSecurityConfig class is,
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private CustomLogoutHandler logoutHandler;
#Autowired
private HttpLogoutSuccessHandler logoutSuccessHandler;
#Autowired
private UserModelRepository userModelRepository;
#Autowired
private RefreshTokenService refreshTokenService;
#Autowired
private AuthTokenModelRepository authTokenModelRepository;
#Autowired
private UserActivitiesRepository userActivitiesRepository;
#Autowired
private UserSubscriptionRepository userSubscriptionRepository;
#Autowired
private HandlerExceptionResolver handlerExceptionResolver;
#Autowired
private StringRedisTemplate redisTemplate;
#Autowired
private UserService userService;
#Override
protected void configure( HttpSecurity http ) throws Exception
{
http.csrf().disable()
.authorizeRequests()
.antMatchers("/rest/noauth/**").permitAll()
.antMatchers("/rest/login").permitAll()
.antMatchers("/rest/logout").permitAll()
.antMatchers("/static/**").permitAll()
.antMatchers("/ws/**").permitAll()
.antMatchers("/rest/razorpay/hook").permitAll()
.antMatchers("/rest/user/cc").permitAll()
.antMatchers("/v2/api-docs/**", "/configuration/ui/**", "/swagger-resources/**",
"/configuration/security/**", "/swagger-ui.html/**", "/webjars/**")
.permitAll()
.antMatchers("/rest/file/invoiceFileDownload", "/rest/file/fileDownload", "/rest/file/fileDownload/**")
.permitAll()
.anyRequest().authenticated()
.and()
.logout().addLogoutHandler(logoutHandler).logoutSuccessHandler(logoutSuccessHandler)
.logoutUrl("/rest/logout")
.and()
.addFilterBefore(
new JWTAuthenticationFilter("/rest/login", tokenService(), refreshTokenService,
authTokenModelRepository, userService, userActivitiesRepository,
handlerExceptionResolver, bCryptPasswordEncoder, redisTemplate),
UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(new JWTAuthorizationFilter(authenticationManager(), authTokenModelRepository,
userSubscriptionRepository, handlerExceptionResolver, redisTemplate),
UsernamePasswordAuthenticationFilter.class);
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
#Override
public void configure( AuthenticationManagerBuilder auth ) throws Exception
{
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
#Bean
public TokenService tokenService()
{
return new TokenService(userModelRepository);
}
}
You must keep the configured value same as what you are actually requesting from.
Here you request from 0.0.0.0:3000 and set header as localhost:3000. There is string comparison that happens in org.springframework.web.cors.CorsConfiguration#checkOrigin which will fail in your case.

Spring security AuthenticationServiceException converted to InsufficientAuthenticationException

I've a problem with exception handling during authentication using Spring Security.
This is my AuthenticationProvider where on every exception an AuthenticationServiceException is thrown.
#Component
public class MyAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
try {
// ...
} catch (Exception e) {
throw new AuthenticationServiceException(e.getMessage(), e);
}
}
}
Below my custom AuthenticationProvider.
#Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
log.debug(e.toString());
}
}
This is the security config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationProvider authenticationProvider;
#Autowired
private AuthenticationEntryPoint authenticationEntryPoint;
#Autowired
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint);
}
}
Everything is triggered as expected. The problem is that in the AuthenticationProvider the AuthenticationException is an instance of InsufficientAuthenticationException and not AuthenticationServiceException as thrown by the AuthenticationProvider.
What I want instead in MyAuthenticationEntryPoint is the exception thrown with the cause set, which is a custom exception.
How can I solve this?
Why Spring replace an AuthenticationServiceException with an InsufficientAuthenticationException?
Thanks in advance.
SOLUTION
I found the solution! The problem is in SecurityConfig class. .authenticationEntryPoint(authenticationEntryPoint) must be under .httpBasic() and not set globally.
The right configuration is the following:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.csrf().disable();
}

Spring using MockMvc Test with CORS filter

I have am trying to run a basic MVC test
#Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World")));
}
However, this will always result in java.lang.IllegalArgumentException: Header value must not be null
I found out that if I deactivate the CORS filter the test will work without errors.
My SimpleCORSFilter
#Component
public class SimpleCORSFilter implements Filter {
private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class);
public SimpleCORSFilter() {
log.info("SimpleCORSFilter init");
}
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Credentials", "true");
//...
chain.doFilter(req, res);
}
}
Part of my Security Config
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsServiceImp userDetailsService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new SimpleCORSFilter(),UsernamePasswordAuthenticationFilter.class);
}
}
Only if I remove the #Component in the SimpleCORSFilter and remove the line .addFilterBefore(new SimpleCORS...) in SecurityConfig the test works.
How can I use mockMVC in my test? Either how do I disable the CORSFilter for the test or how do I make the request in mockMvc correctly so it doesn't throw an error about "header value must not be null".
I have tried setting a random header value in the mockMvc but that didn't change the error.
java.lang.IllegalArgumentException: Header value must not be null.so pass the header value using .header(key,value) like below:
#Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/").header("Origin","*")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World")));
}

spring security oauth2 manipulate request url before redirect

I have a Vaadin application that is secured using spring security OAuth2. This works fine except for the occasional PUSH or HEARTBEAT endpoint being used to request first and thus triggering the auth process and the user ends up on the wrong page (These endpoints should not be visited directly by the user).
A simple but unsecure fix is to permitAll() on these endpoints. However as this poses a threat I need to close this hole up.
To do this I would like to parse and potentially edit the request url before redirecting to it at successfull auth. How would I go about doing this?
I would guess I need to add a filter somewhere in the chain to intercept the request and edit it. But I'm not sure where.
Here is my client:
#Configuration
#EnableOAuth2Sso
public class OAuthConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.csrf().disable()
.authorizeRequests()
.antMatchers("/login**").permitAll()
.antMatchers("/vaadinServlet/PUSH/**").permitAll() //todo fix this hole
.antMatchers("/vaadinServlet/HEARTBEAT/**").permitAll() //todo fix this hole
.anyRequest().authenticated()
.and()
.logout()
.logoutSuccessUrl("/")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
}
#Override
public void configure(WebSecurity web) throws Exception
{
web.ignoring().antMatchers("/css/*").antMatchers("/VAADIN/**"); // Static resources are ignored
}
}
And the server:
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter
{
//jwt token stuff & my own client/auth providers. Should not be important.
...
}
server login form:
#Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
#Autowired
private RestAuthenticationProvider authenticationProvider;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.authenticationProvider(authenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception
{
http
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/forgetPassword*").permitAll()
.antMatchers(HttpMethod.POST,"/user/resetPassword*").permitAll()
.antMatchers(HttpMethod.GET,"/user/changePassword*").permitAll()
.antMatchers("/user/updatePassword*", "/user/savePassword*", "/updatePassword*")
.hasAnyAuthority("CHANGE_PASSWORD_PRIVILEGE","ROLE_USER")
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.csrf().csrfTokenRepository(csrfTokenRepository());
}
private CsrfTokenRepository csrfTokenRepository()
{
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
Just add some implementation with your project
1: create Authentication Failure handler
#Component
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
System.out.print("here failure");
String s=request.getParameter("username");
setDefaultFailureUrl("/login?error&username="+s);
super.onAuthenticationFailure(request,response,exception);
}
}
2: Authentication Success Handler
#Component
public class CustomAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request , HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
/* custom Block
Do any thing here
*/
setDefaultTargetUrl("/home/");
super.onAuthenticationSuccess(request,response,authentication);
}
}
3: access request entry point
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
System.out.print("Unauthorized Access");
httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
}
Implement the components as per your requirement.

Resources