Authentication Token

709 Views Asked by At

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
      }
    }
  }
}
1

There are 1 best solutions below

0
On

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
    }
  }
}