I have often had to cope with sessions / access tokens expiring in an iOS app design and have never really quite found a design that I am 100% comfortable with, so I am asking this here to see if anyone can come up with a better design than I currently use.
You have an app that logs in with a username and password. The server returns an access token that should be used for future requests to authenticate that user. At some point in the future (unknown time) the server will expire that token and any request sent with that token will return an authentication failure.
After failure due to the session expiring, the app should re-login using the original credentials and get back a fresh access token. It can then retry the original request.
So imagine you have an API to get a list of news articles that requires authentication. The flow might go like this:
Now imagine that this is done from more than one place in the app.
What I usually do is store the credentials in NSUserDefaults
(if I am not concerned with security of those credentials - obviously better to be using the keychain) and then have a method on a global manager object (singleton) which refreshes the login using these credentials when I notice that the session has expired. This global manager fires off notifications when the login state changes so that other parts of the app can know when they should retry a request after failure due to the session expiring.
Well, I just never have liked the state machine handling of the manager object. Every place that performs a request needs to save some state to know that a login refresh is going on and to retry the request after the login has been refreshed. There's also the problem of what to do if the refresh fails because the password is wrong now (the user changed it maybe) - you probably don't want to log out completely and destroy all user state of the app, because you might just be able to ask for the new password and carry on as before. But the global manager doesn't really relate to UI so it's hard to it to handle the UI of asking for the new login.
I understand that this question is particularly vague and conceptual (I still think it's OK to be on StackOverflow though?) but I'd really just like to know how other people solve this kind of problem. Just an explanation of how you go about handling the session expiration, retrying the failed requests from all over the app and asking the user for new credentials if refreshing didn't work.
I guess the crux of the whole thing is this question:
Where to put the retry logic of requests that failed due to session expiring. I see these places are options:
refreshLogin
is called that is executed once the login has been refreshed).When the session expires, or session timeout occurs, the Session_End event in global. asax is raised (except when session is handled by the DB) and the session collection is finally cleared. If any objects are NOT holding a reference to any of values in the session collection, then GC will collect it.
If your Internet connection is unstable, periodically disconnecting and reconnecting, it can cause a website session to expire. When the Internet connection is lost the website connection can be terminated, resulting in a session expired message if you try to access any page after the Internet reconnects.
Browsers deletes the session cookies when the browser is closed, if you close it normally and not only kills the process, so the session is permanently lost on the client side when the browser is closed.
Answer. Yes the Session cookie expires. In addition to the 30 minute default timeout (if the visitor is idle for 30 minutes) the 'Session ID' cookie will expire at the end of an internet browser session.
Not to raise this question from the dead, but since it hasn't been answered I'll give my input.
What I chose to do for this scenario was to store the creds in the keychain (or wherever, really), and then I subclassed an HTTPClient to check whether to refresh or not before every call. This way, I can identify a refresh is needed, perform it, and retry the call all in one step as well as being able to send an error block up the chain if necessary to handle any cases in which the user could NOT be refreshed accordingly.
This seems to be in line with what you are (or probably were) trying to accomplish. No need for notifications or any of that jazz, and you can write it once and reuse it throughout the entire app by sending your calls through that subclassed HTTPClient.
EDIT: Bear in mind, you should remember to allow any authentication calls to pass!
What you've asked is how to deal with session expiration. The others have answered your question. But I think you're asking the wrong question. Let me explain.
What we want is a design pattern for user sessions on iOS. We need to look at it from the point of view of the iOS device:
Conclusion: Any API designed for iOS shouldn't expire the session token. It's simply kept secret on the device.
So from my experience the answer to your question about a design pattern for session expiration is basically: Do not use session expiration when using an API for iOS.
One of the biggest iOS REST API's out there is doing it this way, and I would have to agree.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With