svlada / springboot-security-jwt

Token-based authentication using Spring Boot and JWT.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Refresh token problem

goelprateek opened this issue · comments

Hi Vladimir,

Your article with code is dam good, I have just implemented it in one of my projects, but instead of access token if I make a request with refresh token it is able to authenticate the request. Is this a desirable functionality?

Is refresh token just for getting access token or they can validate your request too?

I think that should not be done and another thing I need your help is in implementing BloomFilterTokenVerifier for token verification.

Hi @goelprateek,

Thanks for your kind works.

I've tried to reproduce your issue but without success.

Refresh Token contains only one privilege added under the scopes claim: "ROLE_REFRESH_TOKEN". So basically this token can only be used to obtain new Access Token.

{
  "sub": "svlada@gmail.com",
  "scopes": [
    "ROLE_REFRESH_TOKEN"
  ],
  "iss": "http://svlada.com",
  "jti": "18d79a65-3168-4f29-a448-b870f0bcb90c",
  "iat": 1492447282,
  "exp": 1492450882
}

Following is the HTTP response I'm getting when I include refresh token instead of access token when accessing protected resource (e.g. /api/me)

{
  "status": 401,
  "message": "Authentication failed",
  "errorCode": 10,
  "timestamp": 1492447334678
}

Could you please check your protected resource configuration in the WebSecurityConfig class?

Regarding, Bloom Filter, I can help but you might want to use Redis / Memcached for production.

Hi Vladimir,

Thanks for your reply, I need to talk you on this matter just let me know when u r available. I have few doub't , as i am new to spring security peice.

Sure I am ready to pick up any API for token verification.

I checked the code and I am getting the same problem with refresh token, my refresh token generated is

{ "token" : "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJnb25vZ28td2ViLWNsaWVudCIsInNjb3BlcyI6WyJnbmctY29yZSJdLCJpc3MiOiJzb2Z0Y2VsbC5pbiIsImlhdCI6MTQ5MjUwMzgwMywiZXhwIjoxNDkyNTA0NzAzfQ.cSh7EieWqVVFvnIsoT64dFaSW-m6qYbN7lA7McA4feBxJFgOWdzC4i7QOn5tdZ8zmQJTmaerl1HOiToDpBHpUA", "refreshToken" : "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJnb25vZ28td2ViLWNsaWVudCIsInNjb3BlcyI6WyJST0xFX1JFRlJFU0hfVE9LRU4iXSwiaXNzIjoic29mdGNlbGwuaW4iLCJqdGkiOiJjZTk1M2M5Ny1kNDYzLTRmZTEtYmRlZi0xYTYxY2U2N2U1YTYiLCJpYXQiOjE0OTI1MDM4MDMsImV4cCI6MTQ5MjUwNzQwM30.vzkKEe_TkXtyIcWiTVe3OL2BntaFMwdEO48XcLR1MQiasPYeRqEWb-MEw3RSSkjf_aUU2KCPqkPMqqPcHoov3Q" }

referesh-token
{
"sub": "XXX-XXX",
"scopes": [
"ROLE_REFRESH_TOKEN"
],
"iss": "XXX",
"jti": "ddc17efd-e07c-40cc-aebd-0ac34b929f07",
"iat": 1492501610,
"exp": 1492505210
}

Request with a refresh token:

curl -v -X POST -H "X-Requested-With: XMLHttpRequest" -H "Content-Type: application/json" -H "X-Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJnb25vZ28td2ViLWNsaWVudCIsInNjb3BlcyI6WyJnbmctY29yZSJdLCJpc3MiOiJzb2Z0Y2VsbC5pbiIsImlhdCI6MTQ5MjUwMzgwMywiZXhwIjoxNDkyNTA0NzAzfQ.cSh7EieWqVVFvnIsoT64dFaSW-m6qYbN7lA7McA4feBxJFgOWdzC4i7QOn5tdZ8zmQJTmaerl1HOiToDpBHpUA" localhost:9090/uam/api/me

Response with a refresh token:

{
"username" : "XXX-XXX",
"authorities" : [ {
"authority" : "ROLE_REFRESH_TOKEN"
} ]

As far as my WebSecurityConfiguration I have following code in it

`package com.softcell.gonogo.config.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.softcell.gonogo.security.RestAuthenticationEntryPoint;
import com.softcell.gonogo.security.auth.ajax.AjaxAuthenticationProvider;
import com.softcell.gonogo.security.auth.ajax.AjaxLoginProcessingFilter;
import com.softcell.gonogo.security.auth.jwt.JwtAuthenticationProvider;
import com.softcell.gonogo.security.auth.jwt.JwtTokenAuthenticationProcessingFilter;
import com.softcell.gonogo.security.auth.jwt.SkipPathRequestMatcher;
import com.softcell.gonogo.security.auth.jwt.extractor.TokenExtractor;
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.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import java.util.Arrays;
import java.util.List;

/**

  • Created by prateek on 16/4/17.
    */
    @configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    public static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";
    public static final String FORM_BASED_LOGIN_ENTRY_POINT = "/api/auth/login";
    public static final String TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**";
    public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token";

    @Autowired private RestAuthenticationEntryPoint authenticationEntryPoint;
    @Autowired private AuthenticationSuccessHandler successHandler;
    @Autowired private AuthenticationFailureHandler failureHandler;
    @Autowired private AjaxAuthenticationProvider ajaxAuthenticationProvider;
    @Autowired private JwtAuthenticationProvider jwtAuthenticationProvider;

    @Autowired private TokenExtractor tokenExtractor;

    @Autowired private AuthenticationManager authenticationManager;

    @Autowired private ObjectMapper objectMapper;

    protected AjaxLoginProcessingFilter buildAjaxLoginProcessingFilter() throws Exception {
    AjaxLoginProcessingFilter filter = new AjaxLoginProcessingFilter(FORM_BASED_LOGIN_ENTRY_POINT, successHandler, failureHandler, objectMapper);
    filter.setAuthenticationManager(this.authenticationManager);
    return filter;
    }

    protected JwtTokenAuthenticationProcessingFilter buildJwtTokenAuthenticationProcessingFilter() throws Exception {
    List pathsToSkip = Arrays.asList(TOKEN_REFRESH_ENTRY_POINT, FORM_BASED_LOGIN_ENTRY_POINT);
    SkipPathRequestMatcher matcher = new SkipPathRequestMatcher(pathsToSkip, TOKEN_BASED_AUTH_ENTRY_POINT);
    JwtTokenAuthenticationProcessingFilter filter
    = new JwtTokenAuthenticationProcessingFilter(failureHandler, tokenExtractor, matcher);
    filter.setAuthenticationManager(this.authenticationManager);
    return filter;
    }

    @bean
    @OverRide
    public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
    }

    @OverRide
    protected void configure(AuthenticationManagerBuilder auth) {
    auth.authenticationProvider(ajaxAuthenticationProvider);
    auth.authenticationProvider(jwtAuthenticationProvider);
    }

    @OverRide
    protected void configure(HttpSecurity http) throws Exception {
    http
    .csrf().disable() // We don't need CSRF for JWT based authentication
    .exceptionHandling()
    .authenticationEntryPoint(this.authenticationEntryPoint)
    .and()
    .sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    .and()
    .authorizeRequests()
    .antMatchers(FORM_BASED_LOGIN_ENTRY_POINT).permitAll() // Login end-point
    .antMatchers(TOKEN_REFRESH_ENTRY_POINT).permitAll() // Token refresh end-point
    .antMatchers("/console").permitAll() // H2 Console Dash-board - only for testing
    .and()
    .authorizeRequests()
    .antMatchers(TOKEN_BASED_AUTH_ENTRY_POINT).authenticated() // Protected API End-points
    .and()
    .addFilterBefore(buildAjaxLoginProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
    .addFilterBefore(buildJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
    }

}
`

Hi @goelprateek,

You are sending request to the following endpoint http://localhost:9090/uam/api/me which is not protected.

Please check the WebSecurityConfig class and TOKEN_BASED_AUTH_ENTRY_POINT = "/api/**" variable.

Hi Vladimir,

Under this url "http://localhost:9090/uam/api/me" uam is application context and I am hitting /api/me which is under path regex "/api/**" .

So is this not protected url ?

Even I tried with "http://localhost:9090/api/me" it is having the same problem

Hi @svlada

I have cloned the latest version of this repo and tried authenticating a request to /api/me using the refresh token and it passed through the validation. So I am seconding @goelprateek in that the current implementation is broken.

By looking at the code I don't see how missing some scope values could prevent the request from being authenticated. Once JwtAuthenticationProvider returns an instance of Authentication, the request is considered authenticated by Spring for all purposes. Or does the ROLE_REFRESH_TOKEN have some special meaning in Spring?

I just don't see how is this supposed to work. Could you verify it and fix it if possible? Thanks

Hi @vojtapol,

I think that you are mixing Authentication with Authorization.

You should create your custom roles and use com.svlada.security.config.WebSecurityConfig or annotations to prevent access to resources based on their ROLE.

Hope this answers your question.

OK, I thought that might be the case. It just confused me that the example did not show that. Thanks.