Skip to content
Advertisement

Why this API is forbidden for an user having a JWT token containing the “correct” authority?

I am not so into Spring Security and JWT token and I have the following doubt on a project which I am working on.

Basically I have this SecurityConfiguration class containing my Spring Security configuration, as you can see it is intended to handle JWT token:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    @Autowired
    @Qualifier("customUserDetailsService")
    private UserDetailsService userDetailsService;
    
    @Autowired
    private JwtConfig jwtConfig;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    
    private static final String[] USER_MATCHER = { "/api/user/email/**"};
    private static final String[] ADMIN_MATCHER = { "/api/users/email/**"};
    
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception 
    {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception 
    {
        return super.authenticationManagerBean();
    }   
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        /*
         * NOTE:
         * Using hasRole expects the authority names to start with 'ROLE_' prefix
         * Instead, we use hasAuthority as we can use the names as it is
         */
        http.csrf().disable()
                   .authorizeRequests()
                   //.antMatchers(USER_MATCHER).hasAnyAuthority("USER")
                   .antMatchers(USER_MATCHER).hasAnyAuthority("CLIENT")
                   .antMatchers(ADMIN_MATCHER).hasAnyAuthority("ADMIN")
                   .antMatchers("/api/users/test").authenticated()
                   .antMatchers(HttpMethod.POST, jwtConfig.getUri()).permitAll()
                   .anyRequest().denyAll()
                   .and()
                   .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.addFilterBefore(
            new TokenVerificationFilter(authenticationManager(), jwtConfig, jwtTokenUtil),UsernamePasswordAuthenticationFilter.class);
    }
    
    /* To allow Pre-flight [OPTIONS] request from browser */
    @Override
    public void configure(WebSecurity web) 
    {
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder()
    {
        return new BCryptPasswordEncoder();
    };

    
}

As you can see in the previous code I have the following two matcher lists:

private static final String[] USER_MATCHER = { "/api/user/email/**"};
private static final String[] ADMIN_MATCHER = { "/api/users/email/**"};

At the moment the both contains the same API path: /api/users/email/**. This because my original idea was that this API should be avaiable for simple users and admin users.

Then in the code you can find the following matcher definition based on the authorities contained into the generated token:

.antMatchers(USER_MATCHER).hasAnyAuthority("CLIENT")
.antMatchers(ADMIN_MATCHER).hasAnyAuthority("ADMIN")

(the USER_MATCHER is related to the CLIENT authority that, at the moment, is the simplest type of operation that can be performed…please don’t pay too much attention to the authority’s name…these are mainly some example then I will better define my authority list).

So doing in this way I expected that this /api/users/email/ API must be enabled both for an user having the ADMIN authority but also for an user having the CLIENT authority.

But it seems not to be true, doing an example. I generate a token for an user having the ADMIN authority, something like this:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ4eHhAZ21haWwuY29tIiwibmFtZSI6IlJlbmF0byBudWxsIEdpYWxsaSIsInVzZXJfcHJvZmlsZXMiOlsiQURNSU4iXSwiZXhwIjoxNjQwMjg2NTY5LCJpYXQiOjE2NDAyMDAxNjksImF1dGhvcml0aWVzIjpbIkFETUlOIl19.WQGYKbo_ihrV2Nu1RlxrCweBpgU-Y-dNh9L6R9vrj3vhTyvbPlsyzWNPe8ljtP6WZ8_Vvv8FUDJIa6y5BLS1SA

using https://jwt.io/ website you can see that this token have the ADMIN authority:

{
  "sub": "xxx@gmail.com",
  "name": "Renato null Gialli",
  "user_profiles": [
    "ADMIN"
  ],
  "exp": 1640286569,
  "iat": 1640200169,
  "authorities": [
    "ADMIN"
  ]
}

So I use this token to call my target API (/api/users/email/) and I am obtaining what I expect:

enter image description here

Ok, now I generate a brand new JWT token for another user of my system having only the CLIENT authority. It generate something like this:

eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ5eXlAZ21haWwuY29tIiwibmFtZSI6Ik1hcmlvIG51bGwgUm9zc2kiLCJ1c2VyX3Byb2ZpbGVzIjpbIkNMSUVOVCJdLCJleHAiOjE2NDAyODY5MzEsImlhdCI6MTY0MDIwMDUzMSwiYXV0aG9yaXRpZXMiOlsiQ0xJRU5UIl19.MYM6J3bGSK2PJvxPpi01BHjbcyOiONegYlbZ--lfEtg3p6Hw91acyKYC7KADC2KcJgcXnICJGmLTkPcrVIpfEw

As usual, using https://jwt.io/, I can check that it contains this authority and infact here it is:

{
  "sub": "yyy@gmail.com",
  "name": "Mario null Rossi",
  "user_profiles": [
    "CLIENT"
  ],
  "exp": 1640286931,
  "iat": 1640200531,
  "authorities": [
    "CLIENT"
  ]
}

So now I use this newtoken to call my target API (/api/users/email/) but the API is not accessible by this user:

enter image description here

As you can see using this token the API access seems to be forbidden.

Why if in my configuration I specified that the API defined into the USER_MATCHER list (so the previous target API) should be accessible also by the user having a token containing the CLIENT authority?

What is wrong? Or what am I missing in the authority definition logic?

Advertisement

Answer

it sounds like you would like the /api/users/email/ endpoint to be accessible by both CLIENT & ADMIN

instead of .antMatchers(ADMIN_MATCHER).hasAnyAuthority("ADMIN")

try .antMatchers(ADMIN_MATCHER).hasAnyAuthority("ADMIN", "CLIENT)

5 People found this is helpful
Advertisement