Spring Boot 2.0.3 Oauth2 Security: Getting 401 error even when using access token in header

11,295

Try to added

@Order(SecurityProperties.BASIC_AUTH_ORDER)

for the securityConfig? so the chain will check your Resource server's config first.

And not sure if that your type error, remove the abstract from the resource server.

Share:
11,295
Aditya Kumar
Author by

Aditya Kumar

Updated on June 13, 2022

Comments

  • Aditya Kumar
    Aditya Kumar almost 2 years

    I'm creating a spring boot 2.0 application and trying to enable oauth2 security. I have Auth server and Resource server in the same application as of now. My client and user details as well as token generated are persisted in databases (mysql) and database schema is the same as provided by spring documentation. When I hit the '/oauth/token/' endpoint providing clientId and clientSecret in header and user's credentials in body using Postman, I'm getting access token successfully.

    {
    "access_token": "bef2d974-2e7d-4bf0-849d-d80e8021dc50",
    "token_type": "bearer",
    "refresh_token": "32ed6252-e7ee-442c-b6f9-d83b0511fcff",
    "expires_in": 6345,
    "scope": "read write trust"
    }
    

    But when I try to hit my rest api using this access token, I'm getting 401 Unauthorized error:

    {
    "timestamp": "2018-08-13T11:17:19.813+0000",
    "status": 401,
    "error": "Unauthorized",
    "message": "Unauthorized",
    "path": "/myapp/api/unsecure"
    }
    

    The rest APIs I'm hitting are as follows:

    http://localhost:8080/myapp/api/unsecure

    http://localhost:8080/myapp/api/secure

    myapp is the context path of my application.

    For 'secure' api, I have provided access token in request header as described in Spring documentation:

    Authorization: Bearer bef2d974-2e7d-4bf0-849d-d80e8021dc50
    

    Whereas for unsecure api, I have tried with and without Authentication header. In all cases I'm getting same error for both apis.

    Also when I try to print currently authenticated user, its getting printed as anonymousUser.

    What I want are as follows:

    1) I want my secure api to be accessible only when access token is provided in request header.

    2) I want my unsecure api to be accessible by unauthorised user.

    3) I should get currently authenticated user using SecurityContextHolder when accessing secure url.

    My WebSecurityConfigurerAdapter is as follows:

    @Configuration
    @EnableWebSecurity(debug=true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private DataSource dataSource;
    
    @Autowired
    UserDetailsService userDetailsService;
    
    @Autowired
    private ClientDetailsService clientDetailsService;
    
    @Bean
    public PasswordEncoder userPasswordEncoder() {
        return new BCryptPasswordEncoder(8);
    }
    
    @Bean
    public TokenStore tokenStore() {
        return new JdbcTokenStore(dataSource);
    }
    
    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws 
    Exception {
        auth
        .userDetailsService(userDetailsService)
        .passwordEncoder(userPasswordEncoder());
    }
    
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws 
    Exception {
        return super.authenticationManagerBean();
    }
    
    @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;
    }
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .csrf().disable()
        .cors().disable()
        .anonymous().disable()
        .authorizeRequests()
        .antMatchers("/index.html", "/**.js", "/**.css", "/").permitAll()
        .anyRequest().authenticated()
        .and()
        .httpBasic();
    }
    

    Here using antMatchers I have permitted static pages of Angular 6 application, as I'm planning to use those in my real app. And no, the following line does not work to allow static pages of angular application:

    .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
    

    My AuthorizationServerConfigurerAdapter is as follows:

    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfig extends 
    AuthorizationServerConfigurerAdapter {
    
    @Autowired
    private DataSource dataSource;
    
    @Autowired
    UserDetailsService userDetailsService;
    
    @Autowired
    PasswordEncoder passwordEncoder;
    
    @Autowired
    TokenStore tokenStore;
    
    @Autowired
    private UserApprovalHandler userApprovalHandler;
    
    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;
    
    
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer
        .tokenKeyAccess("permitAll()")
        .checkTokenAccess("isAuthenticated()")
        .passwordEncoder(passwordEncoder);
    }
    
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.jdbc(dataSource);
    }
    
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
        .tokenStore(tokenStore)
        .userApprovalHandler(userApprovalHandler)
        .authenticationManager(authenticationManager)
        .userDetailsService(userDetailsService);
    }
    }
    

    My ResourceServerConfigurerAdapter is as follows:

    @Configuration
    @EnableResourceServer
    public abstract class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    
    private static final String RESOURCE_ID = "resource-server-rest-api";
    
    @Autowired
    TokenStore tokenStore;
    
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources
        .resourceId(RESOURCE_ID)
        .tokenStore(tokenStore);
    }
    
    @Override
    public void configure(HttpSecurity http) throws Exception {
    
        http
        .csrf().disable()
        .cors().disable()
        .anonymous().disable()
        .requestMatchers()
        .antMatchers("/api/**").and()
        .authorizeRequests()
        .antMatchers("/api/secure").authenticated()
        .antMatchers("/api/unsecure").permitAll();
    }
    }
    

    But when I enable anonymous access in SecurityConfig and declare my unsecure url as permitAll, then I'm able to access that url.

    .antMatchers("/api/unsecure", "/index.html", "/**.js", "/**.css", "/").permitAll()
    

    My Controller class is as follows:

    @RestController
    @RequestMapping("/api")
    public class DemoController {
    
    @GetMapping("/secure")
    public void sayHelloFriend() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        System.out.println("Current User: "+authentication.getName());
        System.out.println("Hello Friend");
    }
    
    @GetMapping("/unsecure")
    public void sayHelloStranger() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        System.out.println("Current User: "+authentication.getName());
        System.out.println("Hello Stranger");
    }
    }
    

    Let me know if any more information is needed. Any help will be appreciated. But please keep in mind that its Spring Boot 2.0 not 1.5 as both have some critical differences as per my findings.