Search code examples
spring-bootsalesforcespring-cloudspring-security-oauth2spring-oauth2

Refresh OAuth Token in Spring boot OAuth2RestOperations


I am using Spring Boot Cloud OAuth client to connect with Salesforce restapi. However I am getting Session expired or invalid error. Is there anyway to refresh token I was under the assumption that Spring Boot handles this automatically under the hood but seems like that is not the case. Here is the relevant code.

@Configuration
public class SalesforceConfiguration {

    @Value("${salesforce.tokenUrl}")
    private String tokenUrl;

    @Value("${salesforce.clientId}")
    private String clientId;

    @Value("${salesforce.clientSecret}")
    private String clientSecret;

    @Value("${salesforce.username}")
    private String username;

    @Value("${salesforce.password}")
    private String password;

    @Bean
    protected OAuth2ProtectedResourceDetails resource() {

        ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();

        resource.setAccessTokenUri(tokenUrl);
        resource.setClientId(clientId);
        resource.setClientSecret(clientSecret);
        resource.setClientAuthenticationScheme(AuthenticationScheme.form);
        resource.setUsername(username);
        resource.setPassword(password);

        return resource;
    }

    @Bean
    public OAuth2RestOperations restTemplate() {

        OAuth2RestTemplate operations = new OAuth2RestTemplate(resource(), new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()));
        operations.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        operations.getMessageConverters().add(new StringHttpMessageConverter());

        return operations;
    }
}

and this is how I am using it in service.

@Component
public class QueryExecutor extends AbstractExecutor implements SalesforceExecutor {

    private OAuth2RestOperations restOperations;

    public QueryExecutor(OAuth2RestOperations restOperations) {

        this.restOperations = restOperations;
    }

    @Override
    public Response process(Request request) throws Exception {

        JsonNode jsonNode = restOperations.getForObject(buildUrl(request), JsonNode.class);

        return new Response<>(ResponseCode.SUCCESS_GET.getCode(), jsonNode, request.getResponseHandler());
    }

    private String buildUrl(Request request) {

        return new StringBuilder().append(getServiceUrl(restOperations))
                                  .append("/services/data/v41.0/query/?q=")
                                  .append(request.getPayload().get("query"))
                                  .toString();
    }
}

Is there anyway to refresh token seamlessly using this approach if I get session expired error?


Solution

  • Alright finally after days of debugging I think I found the solution of this issue. This is how I was able to resolve it. I am putting the solution here so that others can benefit from it.

    @Component
    public class QueryExecutor implements SalesforceExecutor {
    
        private OAuth2RestOperations restOperations;
    
        public QueryExecutor(OAuth2RestOperations restOperations) {
    
            this.restOperations = restOperations;
        }
    
        @Override
        public Response process(Request request) throws Exception {
    
            try {
    
                JsonNode jsonNode = restOperations.getForObject(buildUrl(request), JsonNode.class);
    
                return new Response<>(ResponseCode.SUCCESS_GET.getCode(), jsonNode, request.getResponseHandler());
    
            } catch (HttpClientErrorException | HttpServerErrorException e) {
    
                if (HttpStatus.UNAUTHORIZED.value() == e.getStatusCode().value()) {
    
                    restOperations.getOAuth2ClientContext().setAccessToken(this.getRenewedToken(restOperations.getResource()));
                }
    
                // retry again with renewed token
            }
        }
    
        private String buildUrl(Request request) {
    
            return "someurl";
        }
    
        private OAuth2AccessToken getRenewedToken(OAuth2ProtectedResourceDetails resource) {
    
            OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resource, new DefaultOAuth2ClientContext(new DefaultAccessTokenRequest()));
    
            return oAuth2RestTemplate.getOAuth2ClientContext().getAccessToken();
        }
    }
    
    

    Thats it.