8

I'm trying to to access a RestAPI-Endpoint with the help of Spring's RestTemplate

public List<Transaction> getTransactions() {
    // only a 24h token for the sandbox, so not security critical
    // still I replaced the last 10 digits here with 'x' but not in my original code
    String authToken = "tylhtvGM6Duy8q0ZBbGaTg2FZefLfyeEeMZvCXlU2bEiinnZcLSACTxxxxxxxxxx";
    String encodedAuthToken = Base64.getEncoder().encodeToString(authToken.getBytes());

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    headers.add("Authorization", "Bearer "+encodedAuthToken );

    ResponseEntity<TransactionsResponse> response = restTemplate.exchange(
            "https://api-sandbox.starlingbank.com/api/v1/transactions",
            HttpMethod.GET,
            new HttpEntity<>("parameters", headers),
            TransactionsResponse.class
    );

    return response.getBody().getEmbedded().getTransactions();
}

but I get a HttpClientErrorException saying "403 Forbidden". Long version

Caused by: org.springframework.web.client.HttpClientErrorException: 403 Forbidden
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:94) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:79) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:766) ~[spring-web-5.0.8.RELEASE.jar:5.0.8.RELEASE]

My code is based on a former stackoverflow thread and a call with the same parameters via Postman is successful: enter image description here

So what is the problem?

Update

not encoding the authToken makes no difference

headers.add("Authorization", "Bearer tylhtvGM6Duy8q0ZBbGaTg2FZefLfyeEeMZvCXlU2bEiinnZcLSACTxxxxxxxxxx");

still leads to the same HttpClientErrorException: 403 Forbidden

Update2

I answered my question! (short version: UserAgent required. final code in anwser)

StephanS
  • 843
  • 1
  • 8
  • 20
  • in the doc [link](https://jsapi.apiary.io/apis/starlingbankapi/reference/0/contact-api/get-contact-accounts.html) say this Http status code 403 = ForbiddenYour authentication failed, usually due to the access token being expired or an attempt to access a resource beyond the scope of the token. Do you have a token with permissions for this scope? or are you using the same token in both cases? – Fernix Oct 12 '18 at 20:16
  • as you can see in the Postman screenshot i use the same authToken. And I still succeed with Postman now so the token is still valid for a few hours (and afterwards I can simply renew it). – StephanS Oct 13 '18 at 12:57

4 Answers4

17

this particular server requires an UserAgent! The value can be whatever, but it has to be present!

So the final version is:

public List<Transaction> getTransactions() {
    // only a 24h token for the sandbox, so not security critical
    // still I replaced the last 10 digits here with 'x' but not in my original code
    String authToken = "tylhtvGM6Duy8q0ZBbGaTg2FZefLfyeEeMZvCXlU2bEiinnZcLSACTxxxxxxxxxx";

    HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));        
    headers.add("User-Agent", "Spring's RestTemplate" );  // value can be whatever
    headers.add("Authorization", "Bearer "+authToken );

    ResponseEntity<TransactionsResponse> response = restTemplate.exchange(
            "https://api-sandbox.starlingbank.com/api/v1/transactions",
            HttpMethod.GET,
            new HttpEntity<>("parameters", headers),
            TransactionsResponse.class
    );

    return response.getBody().getEmbedded().getTransactions();
}
StephanS
  • 843
  • 1
  • 8
  • 20
2

If you are using OAuth Bearer tokens for authentication you don't need to encode them prior to making the request. The following line should be sufficient:

headers.add("Authorization", "Bearer " + authToken);

The code you mentioned has Http Basic authentication which consists of sending the credentials as username:password pair encoded with Base64.

senjin.hajrulahovic
  • 2,961
  • 2
  • 17
  • 32
  • I updated the main text to say that this doesn't make a difference. So I went back to the version you mentioned - without the extra encoding step - but it's the same result. – StephanS Oct 13 '18 at 13:04
  • Are you sure that the token is still valid, that it didn't expire? – senjin.hajrulahovic Oct 13 '18 at 14:42
  • 100% (each token is only valid for 24h, so I just renewed it for the third time, copied it over to my code, run mvn clean and then executed the code, still leads to _403 Forbidden_ ) – StephanS Oct 14 '18 at 10:43
2

For Bearer authentication Spring has a convenience method setBearerAuth(String token), since version 5.1:

// anonymised: replaced the last 10 digits here with 'x'
String authToken = "tylhtvGM6Duy8q0ZBbGaTg2FZefLfyeEeMZvCXlU2bEiinnZcLSACTxxxxxxxxxx";
HttpHeaders headers = new HttpHeaders();

headers.setBearerAuth(authToken);
hc_dev
  • 8,389
  • 1
  • 26
  • 38
1

You are encoding your token in Base64 and its already encoded, your code is based in this but in this example they are encoding a Basic Authorization header with (user:password) encoded

probe this

 HttpHeaders headers = new HttpHeaders();
    headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
    headers.add("Authorization", "Bearer "+ authToken );
Fernix
  • 361
  • 3
  • 10
  • makes no difference :( (that's what I started up with - and now went back to - and I still leads to 403 – StephanS Oct 13 '18 at 13:02