Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my token being rejected? What is a resource ID? "Invalid token does not contain resource id (oauth2-resource)"

I'm trying to configure OAuth2 for a spring project. I'm using a shared UAA (oauth implementation from cloud foundry) instance my work place provides (so I'm not trying to create an authorization server and the authorization server is separate from the resource server). The frontend is a single-page-application and it gets token directly from the authorization server using the implicit grant. I have the SPA setup where it adds the Authorization: Bearer <TOKEN> header on each web API call to microservices.

My issue is now with the microservices.

I'm trying to use this shared authorization server to authenticate the microservices. I might have a misunderstanding here, buy my current understanding is that these microservices play the role of the resource server because they host the endpoints the SPA uses to get data.

So I tried to configure a microservice like so:

@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
        .authorizeRequests()
        .antMatchers("/api/**").authenticated();
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setVerifierKey("-----BEGIN PUBLIC KEY-----<key omitted>-----END PUBLIC KEY-----");
        return converter;
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        return defaultTokenServices;
    }


    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
         resources.tokenServices(tokenServices());
    }
}

Now whenever I hit a /api/** with the Authorization: Bearer <TOKEN>, I get a 403 with this error:

{
    "error": "access_denied",
    "error_description": "Invalid token does not contain resource id (oauth2-resource)"
}

So here are my questions:

  • How do I configure these microservices to validate the token and insert a Principal in controller methods? I currently have it setup where the SPA has and sends the token and I also have the public key used to verify the signature of the token. I have also used jwt.io to test the token and it says "Signature Verified".
  • What is a resource id? Why do I need it and why does it cause the error above? Is that a Spring only thing??

Thanks!

like image 516
Rico Kahler Avatar asked Dec 27 '17 17:12

Rico Kahler


3 Answers

Spring OAuth expects "aud" claim in JWT token. That claim's value should match to the resourceId value you specify your Spring app (if not specified it defaults to "oauth2-resource").

To fix your issue you need to:

1) Log into your shared UAA and make sure it does include "aud" claim.

2) Change the value of that "aud" claim to be "oauth2-resource" or preferably in your Spring app update resourceId to that claim's value like this:

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
         resources.tokenServices(tokenServices());
         resources.resourceId(value from the aud claim you got from UAA server);
    }
like image 175
tsolakp Avatar answered Oct 10 '22 13:10

tsolakp


I add a similar issue. In my case, I used jdbc authentification and my authorization server and resource server was two separate API.

  • Authentorization server

       @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    oauthServer.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .passwordEncoder(oauthClientPasswordEncoder);
    

    }

    /**
    * Define the client details service. The client may be define either as in memory or in database.
     * Here client with be fetch from the specify database
      */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
       clients.jdbc(dataSource);
    }
    
    /**
    * Define the authorization by providing authentificationManager
    * And the token enhancement
     */
     @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints.tokenStore(tokenStore())
                .tokenEnhancer(getTokenEnhancer())
                .authenticationManager(authenticationManager).userDetailsService(userDetailsService);
     }
    
  • Resource server

    public class OAuth2ResourceServerConfig extends 
        ResourceServerConfigurerAdapter {
    
        private TokenExtractor tokenExtractor = new BearerTokenExtractor();
    
        @Autowired
        private DataSource dataSource;
    
        @Bean
        public TokenStore tokenStore() {
          return new JdbcTokenStore(dataSource);
        }
    
         @Override
         public void configure(HttpSecurity http) throws Exception {
               http.addFilterAfter(new OncePerRequestFilter() {
               @Override
               protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                FilterChain filterChain) throws ServletException, IOException {
            // We don't want to allow access to a resource with no token so clear
            // the security context in case it is actually an OAuth2Authentication
            if (tokenExtractor.extract(request) == null) {
                SecurityContextHolder.clearContext();
            }
            filterChain.doFilter(request, response);
        }
    }, AbstractPreAuthenticatedProcessingFilter.class);
    http.csrf().disable();
    http.authorizeRequests().anyRequest().authenticated();
     }
    
      @Bean
      public AccessTokenConverter accessTokenConverter() {
         return new DefaultAccessTokenConverter();
      }
    
      @Bean
      public RemoteTokenServices remoteTokenServices(final @Value("${auth.server.url}") String checkTokenUrl,
        final @Value("${auth.resource.server.clientId}") String clientId,
        final @Value("${auth.resource.server.clientsecret}") String clientSecret) {
    
           final RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
           remoteTokenServices.setCheckTokenEndpointUrl(checkTokenUrl);
           remoteTokenServices.setClientId(clientId);
           remoteTokenServices.setClientSecret(clientSecret);
          remoteTokenServices.setAccessTokenConverter(accessTokenConverter());
    return remoteTokenServices;
       }
    

With this configuration, I was getting

    {
       "error": "access_denied",
       "error_description": "Invalid token does not contain resource id 
       (xxxxx)"
     }

To solve this, I had to add

    private String resourceIds= "xxxxx". !! maked sure that this resourceids is store in oauth_client_details for the clientid I used to get the token
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
          resources.resourceId(resourceIds).tokenStore(tokenStore());
      }
like image 37
onlyme Avatar answered Oct 10 '22 13:10

onlyme


i faced same issue when i implment oauth2.0 with spring, here are what i found about resourceid.

Spring Security OAuth2 architecture is divided into Authorization Server and Resource Server resource server. We can set a ResourceID for each Resource Server (a microservice instance). When the Authorization Server grants authorization to a client, you can set which Resource Server resource services the client can access.

The purpose of configuring ResourceID for client in authorization server is to restrict the resource services that a client can access.

to set up ResourceID refere below link, https://www.fatalerrors.org/a/resource-of-spring-security-oauth2_-id-configuration-and-verification.html

like image 36
Sachithra Dilshan Avatar answered Oct 10 '22 12:10

Sachithra Dilshan