-
-
Notifications
You must be signed in to change notification settings - Fork 159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Help with a session based token REST API #48
Comments
Making this circuit work as you describe, where it retries automatically and you never see the auth failure, isn't really workable with the Siesta API as it stands. You can do something close to what you want, if:
Here’s a sketch: configure {
$0.config.beforeStartingRequest { resource, request in
request.onFailure { error in
if isAuthError(error) {
doAuthorization() {
resource.loadIfNeeded()
}
}
}
}
} That would clearly need cleaning up and building out. Hopefully it helps. |
(And I should clarify: |
It would be nice to be able to do something like this: public static func configureMe() {
service.configure("/me") {
$0.config.beforeStartingRequest { (resource, request) in
guard API.isAuthed else {
switch API.refreshToken {
case let refreshToken?:
request.pause() // Stop the request from going out until we are possibly authed.
API.login(refreshToken: refreshToken).onCompletion { response in
guard case .Success = response else {
request.cancel(API.Error.requiresAuth)
return
}
request.resume() // We are now authed and the request may proceed.
}
case nil:
request.cancel(API.Error.requiresAuth)
}
}
}
}
} What are your thoughts on a system like this? |
Auto-reuath and auto-refresh would certainly have widespread appeal. @vdka, I could imagine something along those lines. Instead of |
I'm doing some initial evaluation of Siesta to handle an API with this exact issue. @vdka, can I ask, what approach did you end up with? |
I haven't tackled the issue just yet @jordanpwood but I will in the next couple days, then report back. |
The following should work, it doesn't do pre-auth for individual requests but it definitely could be made to. You will need some method of finding the expiration time of your tokens for this method (we are using a final class API {
static var service = Service()
// ...
static func login(username username: String, password: String) -> Request {
let request = service.resource(.Auth)
.request(.POST, json: ["grant_type": "password", "username": username, "password": password, "application_id": clientId])
.onSuccess { entity in
let json = entity.json
guard let token = json["token"].string else {
fatalError() // handle this better
}
guard let refreshToken = json["refresh_token"].string else {
fatalError() // handle this better
}
API.token = token
API.refreshToken = refreshToken
}
return request
}
static func login(refreshToken refreshToken: String) -> Request {
let request = service.resource(.Auth)
.request(.POST, json: ["grant_type": "refresh_token", "refresh_token": refreshToken, "application_id": clientId])
.onSuccess { entity in
let json = entity.json
guard let token = json["token"].string else {
fatalError()
}
guard let refreshToken = json["refresh_token"].string else {
fatalError()
}
API.token = token
API.refreshToken = refreshToken
}
return request
}
public static var token: String? {
didSet {
service.invalidateConfiguration()
service.wipeResources()
guard let token = token else { return }
let jwt = try? JWTDecode.decode(token)
tokenExpiry = jwt?.expiresAt
}
}
public private(set) static var tokenExpiry: NSDate? {
didSet {
guard let tokenExpiry = tokenExpiry else { return }
let timeToExpire = abs(tokenExpiry.timeIntervalSinceDate(NSDate()))
// Somewhat before the expiration happens
let timeToRefresh = NSDate(timeInterval: timeToExpire * 0.9, sinceDate: NSDate())
log.info("Token refresh scheduled for \(timeToRefresh.descriptionWithLocale(NSLocale.currentLocale()))")
NSTimer.after(timeToRefresh.timeIntervalSinceNow) {
log.info("attempting auto token refresh")
guard let refreshToken = API.refreshToken else {
log.warning("No refresh token set. Cannot auto-refresh")
return
}
API.login(refreshToken: refreshToken)
.onSuccess { _ in log.info("Token refresh successful!") }
.onFailure { log.error("Token refresh failed with \($0)") }
}
}
}
} |
I think #98 resolved this. If it didn’t, feel free to reopen. |
Hey there,
The API web service that I've got uses a session based token which expires every 24h. This means I have to make a new call to (let's say) the /authenticate_session endpoint and obtain a new session token. Now I have to take the response from this and pass it for every subsequent API call for other endpoints.
That's all nice and simple to implement using your basicAuthToken example.
My problem is, how can I check the error response and retry the call automatically? For example, let's say I make a GET request for the /user endpoint with an expired JSON web token. Here's the error response I get:
What I'd like to do is whenever I get an error with the above code (E1100), I'd like it to call the /authenticate_session endpoint, use the new token that gets returned and automatically make another GET /user request. I don't want the initial GET /user request to fail, instead I want it to try and handle/recover from it. If it then fails, I want to update the UI etc.
Is this possible with Siesta right now or do I need to manually handle this (manually as in inside my resourceChanged delegate function, I manually check what the error is and handle it there for every endpoint the API supports)? Can I instead somehow handle this using the responseTransformer for the Service object?
Any help in the right direction would be appreciated.
Thanks
The text was updated successfully, but these errors were encountered: