2

I'm trying to setup a simple iOS example to better understand Siesta. My REST api requires an access token to accompany each request. So (1) at the start of the app and (2) anytime I retrieve a HTTP 401 I need to request an access token and then put that in all future Authorization headers.

Working off this example from the documentation, I assume the line containing showLoginScreen is where I need to make a call to my authenticationResource to retrieve the token BUT how do I make the failed call immediately after (and not infinite loop of course)? Thank you.

let authURL = authenticationResource.url

configure({ url in url != authURL }, description: "catch auth failures") {
  $0.config.beforeStartingRequest { _, req in  // For all resources except auth:
    req.onFailure { error in                     // If a request fails...
      if error.httpStatusCode == 401 {         // ...with a 401...
        showLoginScreen()                      // ...then prompt the user to log in
      }
    }
  }
}
Paul Cantrell
  • 9,175
  • 2
  • 40
  • 48
user2199730
  • 151
  • 1
  • 5
  • Can you clarify what you mean by “how do I make the failed call immediately after?” Are you asking about how to call authenticationResource, or about how to reattempt the call that produced the 401 if authentication succeeds? – Paul Cantrell Jan 23 '16 at 17:37
  • Yes, how do I reattempt the call. Right now I do an call at the beginning of the view controller to get the token but I know that's wrong because I made need to get a token again (timeout?) while in the same view. So the logic would be to always check if I have a token before a rest call, get the token and reattempt. Thank you. – user2199730 Jan 24 '16 at 20:07

1 Answers1

2

Since you asked the question, the docs have been updated with an example that answers it.

The crux of it is to use decorateRequests(…) and Request.chained(…) to wrap all your service’s requests so that they automatically attempt to refresh the token before returning a response.

Here is the code from that example:

authToken: String??

init() {
  ...
  configure("**", description: "auth token") {
    if let authToken = self.authToken {
      $0.headers["X-Auth-Token"] = authToken         // Set the token header from a var that we can update
    }
    $0.decorateRequests {
      self.refreshTokenOnAuthFailure(request: $1)
    }
  }
}

// Refactor away this pyramid of doom however you see fit
func refreshTokenOnAuthFailure(request: Request) -> Request {
  return request.chained {
    guard case .failure(let error) = $0.response,  // Did request fail…
      error.httpStatusCode == 401 else {           // …because of expired token?
        return .useThisResponse                    // If not, use the response we got.
    }

    return .passTo(
      self.createAuthToken().chained {             // If so, first request a new token, then:
        if case .failure = $0.response {           // If token request failed…
          return .useThisResponse                  // …report that error.
        } else {
          return .passTo(request.repeated())       // We have a new token! Repeat the original request.
        }
      }
    )
  }
}

func createAuthToken() -> Request {
  return tokenCreationResource
    .request(.post, json: userAuthData())
    .onSuccess {
      self.authToken = $0.jsonDict["token"] as? String  // Store the new token, then…
      self.invalidateConfiguration()                    // …make future requests use it
    }
  }
}
Paul Cantrell
  • 9,175
  • 2
  • 40
  • 48