5

I am implementing a client that authenticates with OAuth2 against WSO2 and I have serious trouble refreshing the access token, getting 401 UNAUTHORIZED. While I have already found out, what the Spring OAuth2 code does, I do not know why its behavior was changed in 2.2.1.RELEASE and to me it seems plain wrong. Actually using 2.0.14.RELEASE works.

Before I am going to show to you, what I have done and what I have already found out, let me formulate my question:

How am I supposed to realize an OAuth2 client with automatic token refresh with client credentials instead of user credentials?

So here is, what I have implemented so far. The client configures an OAuth2RestTemplate with ResourceOwnerPasswordResourceDetails with isClientOnly flag true, as there are no user sessions. The client session can successfully be established and an access token and a refresh token are set.

@Bean
protected OAuth2ProtectedResourceDetails resource() {
    ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails() {
        @Override
        public boolean isClientOnly() {
            return true;
        }               
    };
    List<String> scopes = new ArrayList<>(2);
    scopes.add("write");
    scopes.add("read");
    resource.setScope(scopes);
    resource.setGrantType("password");
    resource.setAccessTokenUri(TOKEN_URL);
    resource.setClientId(MY_CLIENT_ID);
    resource.setClientSecret(MY_CLIENT_SECRET);
    resource.setUsername(MY_SERVICE_USER);
    resource.setPassword(MY_SERVICE_USER_PW);
    return resource;
}


@Bean
public OAuth2RestOperations restTemplate() {
    AccessTokenRequest atr = new DefaultAccessTokenRequest();
    OAuth2RestTemplate template = new OAuth2RestTemplateWithBasicAuth(resource(), new DefaultOAuth2ClientContext(atr));
    List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
    interceptors.add(new LoggingRequestInterceptor());      
    template.setInterceptors(interceptors);
    template.setRetryBadAccessTokens(true);
    return template;
}

So far so good. I have verified that this basically works.

But as soon as the access token expires I frequently run into 401 errors, because the token refresh is not executed. Instead, an ordinary authentication request is carried out, but using the client key and secret instead of user/password. To cut a long story short, I have debugged my way through spring-security-oauth2 into AccessTokenProviderChain#obtainAccessToken and found out, that whether a token refresh request is executed is decided upon in the following bit of code. See on Github

    if (resource.isClientOnly() || (auth != null && auth.isAuthenticated())) {   // P1
        existingToken = request.getExistingToken();
        if (existingToken == null && clientTokenServices != null) {
            existingToken = clientTokenServices.getAccessToken(resource, auth);
        }

        if (existingToken != null) {
            if (existingToken.isExpired()) {
                if (clientTokenServices != null) {
                    clientTokenServices.removeAccessToken(resource, auth);
                }
                OAuth2RefreshToken refreshToken = existingToken.getRefreshToken();
                if (refreshToken != null && !resource.isClientOnly()) {  // P2
                    accessToken = refreshAccessToken(resource, refreshToken, request);
                }
            }
            else {
                accessToken = existingToken;
            }
        }
    }

As you can see at P1, the block is entered if either an authorized user session exists (auth) or the resource is configured as clientOnly. As I do not have users but I am in a linked service scenario, I have isClientOnly() == true && auth == null. But at P2 the final decision upon actually doing the refresh is contraticted by requiring !isClientOnly(). So this effectively bans refresh requests in client only scenarios.

This was the way to go in versions before 2.2.1 and I have found out, that this seems to be a fix to the following Issue. To me this seems plain wrong.

Furthermore, to me the patch appears to break client functionality to fix an actual server misbehavior. As you can see in the issue discussion, I have already commented the there. But as that issue is closed and the spring-security-oauth2 forum states that discussions should be held here on StackOverflow, I am asking for help here.

Again the question: How should a client application be configured to consume OAuth2 secured services via OAuth2RestTemplate and an access token runtime of an hour and refresh token runtime of lets say two hours.

hbergmey
  • 51
  • 1
  • 4
  • I changed the title into a question to conform with the StackOverflow format. Sorry for not getting that right, immediately. – hbergmey Feb 21 '18 at 13:46
  • First responses on other channels indicate, that the problem is using the user session dedicated grant type "password" instead of the grant type "client_credentials". The latter should be used for service-to-service-authentication and would not use token refresh at all, but would always re-login. This would explain the change in V2.2.1.RELEASE. But in my scenario, the authentication server is not configured for providing access via client_credentials and for now I cannot change that. So unfortunately I still have to setup the connection using the "password" grant. – hbergmey Feb 28 '18 at 14:53
  • did you manage to resolve this issue? – Minisha Feb 11 '20 at 23:23
  • @Minisha: I explicitly had to stick to the old spring-security-oauth2:2.0.14.RELEASE in that project. In new projects since then I've gotten rid of Spring alltogether and base on Scala with akka-http or dispatch-http. The solutions are much more flexible and transparent (and faster by the way). – hbergmey Feb 17 '20 at 17:25

0 Answers0