I'm trying to use Siesta swift package with my API on the server. We've setup a JWT authentication with Access and Refresh tokens. We can successfully authenticate and get new access tokens with refresh procedure. But the solution we've made looks a bit hacky me.
We are using request decorators like this
func init() {
service.configure("**") {
if let session = self.appSession {
$0.headers["Authorization"] = "Bearer \(session.tokens.access)"
}
$0.decorateRequests {
self.globalApiFailHandler(request: $1)
}
}
service.configure(authRefreshResource) {
if let session = self.appSession {
$0.headers["Authorization-Refresh"] = "Bearer \(session.tokens.refresh)"
}
$0.decorateRequests {
self.refreshTokenFailure(request: $1)
}
}
}
private func globalApiFailHandler(request: Siesta.Request) -> Request {
return request.chained { //special case to Refresh Token On Auth Failure
if case
.failure(let error) = $0.response, // Did request fail…
error.httpStatusCode == 401, // …because of expired token?
self.appSession != nil { // we have refreshToken
log.warning("Seems like Access Token is expired, Trying to refresh it!")
return .passTo(
self.refreshAuth().chained { // 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.
}
}
)
}
if case
.failure(let error) = $0.response,
error.httpStatusCode != 409 {
log.warning("Something went wrong during request: \(error)")
self.retryLaterEvent() // TODO: Really need this here?
}
return .useThisResponse // If not, use the response we got.
}
}
private func refreshTokenFailure(request: Siesta.Request) -> Request {
return request.chained {
if case
.failure(let error) = $0.response { // Did request fail…
log.error("Refresh token procedure failed with \(error).")
if error.httpStatusCode == 409 {
log.warning("409, Resetting app session storage! Relogin or app recreation is needed!")
self.relogin = true //Reset saved sessions to create new app
self.reloginEvent()
} else {
log.warning("Something went wrong during refresh token procedure. Please retry later!")
self.retryLaterEvent()
}
///let requestError = RequestError(userMessage: "Unable to refresh access token", cause: "")
let response = Response.failure(error) //(requestError)
let responseInfo = ResponseInfo(response: response)
return .useResponse(responseInfo) // If not, use the response we got.
}
return .useThisResponse // We have new token!
}
}
please note 409
return code check in globalApiFailHandler
. It is there because global decorator is always called for authRefreshResource
. If we omit that check, an API will stuck in an infinite loop refreshing token under some server errors.
The question is how to disable global decorator for particular resource we want? Having this will elegantly resolve our problem.