I am currently building a rails app, using the devise gem for authentication.
Currently in the app there is only client-side timeout functionality implemented, which I don't feel is a good final solution. Because it doesn't cover the case when a user puts their computer to sleep just for 1 example.
So I wanted to implement the timeout module in devise, however there are several issues I am facing. The issues are because with server-side timeout the user needs to navigate to a different page before they are redirected to the sign in page. And there are a lot of interactions in my app when a user will open a modal in the UI, which will trigger an AJAX call (which will fail if they have been timed out on the server-side).
Here are 2 approaches I have thought of, but I don't see them as good solutions so maybe someone can build on one of these approaches or help point me in a different direction:
1.) In my AJAX requests, add a handler inside the 'error' callback that will tell the user to refresh the page or go to the login page if the error callback returns a 401 Unauthorized response.
Cons: There are a lot of these ajax requests in the app, so there would be a lot of repetitive code and I see this as being difficult to maintain.
2.) Add a click handler to the body and every time it is triggered, send a request to the backend to validate if the user is still logged in. If they aren't redirect the user to the login page.
Cons: Performance issues
Any advice would be appreciated. Thanks.
You can add this from server site.
:expire_after: 120.minute
in your initializers/session_store.rb, Below example.
Tastebook::Application.config.session_store :cookie_store,
key: '_tastebook_app_session',
expire_after: 120.minute
I spoke with another developer who helped me come up with a solution to this issue.
With the client-side timeout, I had a countdown timer starting at 15 minutes, which obviously only works if the user's computer is not asleep.
So instead of a countdown timer, I create a new date object, setting the time to 15 minutes in the future (UTC). And then I set an interval which compares the current datetime (UTC) with the future date object and if it has been reach or surpassed it renders a message with a link to the sign in page. When this message is rendered is also makes a call to the backend to kill the user session to cover the case where the user tries to refresh their browser, which would then result in them being sent back to the login page.
Related
My question is about the MVC Antiforgery system (described here).
Consider a simple app which posts todos to /Todo/Create. The corresponding action method has the ValidateAntiForgeryToken attribute. Consider the following client workflow:
User A logs on and goes to the page to create a todo, but doesn't do it yet.
User B (physically on the same computer) opens a new tab in the same browser, logs out of User A's account, logs in as User B. The browser then gets User B's validation cookie.
Some time later, User A switches back to their original tab and hits 'create' on the todo they were making.
In this scenario, the Antiforgery verification will not pass because the form token was meant for User A, while the validation cookie is for User B.
I'm sure there are valid security reasons for this behavior (e.g. a script on another site that manages to login as malicious user so that the 'todo' data is posted to their account instead), but it doesn't stop the above scenario happening for my legitimate users sometimes.
My questions are:
Is there a 'best practices' way to handle this scenario? Is it usually just a case of showing a custom error, telling them to reload the page and/or login again etc?
Is there any way to know when the out-of-the-box MVC Antiforgery system runs into this error? It seems to only ever throw the same kind of Exception (HttpAntiForgeryException). Would I need to revert to using/modifying their source?
I see two ways of handling it:
Use Javascript callback to the server before hitting a button to detect if the user is still logged in. If not - display him a message. It should be relatively easy to do this. But it requires one additional call, and little bit more time to execute your request.
One solution to avoid callbacks could be using html 5 localStorage (and you can support that on other browsers using modernizr, for example). It is shared between tabs. But I'm not sure if this approach is good. Additional research required.
Catch HttpAntiForgeryException on the server, check if the user is logged in. If the user is not logged in, display him a message.
Usually approach (1) is used. On banking websites they detect with JavaScript when you logged out in other browser tab.
In our Rails application, it is common for users to keep multiple browser tabs open for hours or days at a time. The problem occurs when in one of these tabs, the user logs out then logs back in (or the session expires and a new session is created).
This causes the CSRF authenticity tokens on all the other tabs to become invalid. If they try to submit any form or make any ajax request on those tabs without refreshing, they will get an error (and in fact get logged out because that is the default Rails behavior when a bad authenticity token is passed).
This behavior is clearly undesirable. I was wondering what people do to gracefully handle situations where a user has a window open to your site but the authenticity token is out of date.
What I don't want to do is just redirect them to the login page, because then they might lose their work, if for example they have been writng a long blog post or something.
The solution that comes to mind is to have some javascript that either polls the server to check whether the authenticity token has changed, or polls the user's cookies to check whether the session has changed. I have never heard of anyone doing either of these, so I wanted to see what the community thought.
First of: logging in/out/in won't lead to appearing a new csrf-token. It still will be saved in the user's cookie. Next time it logs in via the same browser it'll get the same token.
In latest versions of Rails no errors will be thrown in the case of incorrect token: all the Rails does -- just resets the session before passing it to a controller.
So, update your Rails and you'll get one pain less.
Are you sure you are talking about CSRF token and not session token? It does not make any sense at all to redirect to login on a CSRF token mismatch. You just tell the user to repeat whatever he tried to do. (In a traditional web application this typically comes up when a form is submitted; you can treat the CSRF mismatch as a validation error, and show the form again, keeping all the field values, and ask the user to resubmit. In a more AJAX-heavy application you can use some sort of generic CSRF flag in the response, and if it is set, ask the user to do whatever he did (press the button etc) once more, or even automate the whole thing without bothering the user.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 4 years ago.
Improve this question
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.
The problem
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.
Example
So imagine you have an API to get a list of news articles that requires authentication. The flow might go like this:
User logs in and app receives token.
View controller refreshes list of news articles.
API request is made with token attached.
API request successful and view is updated with new articles.
App is closed and some time passes.
App is opened at which point the view controller wants to refresh the list of news articles.
API request is made with token attached.
API request is unsuccessful because token has expired.
View controller requests a refresh of the token and waits patiently.
Once token has been refreshed, API request is retried.
Now imagine that this is done from more than one place in the app.
How I currently solve it
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.
What I don't feel is right
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.
What I want to know in answers
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:
At the view controller level (i.e. keep a flag to say we need to retry the last request once login refresh has finished).
At the API request level (i.e. encapsulate the API request in an object that knows to retry after the login has been refreshed).
At the global login manager level (i.e. perhaps take a block in when refreshLogin is called that is executed once the login has been refreshed).
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:
User installs the app
User authenticates with details and a session token is returned
The app then stores the (secret) session token on the device
Any future requests include the session token
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.
You are mentioning two things in your question which seem key to answering it:
A) the app is in a state with data that could be lost if the user is not logged in.
B) the app needs to log back in to save the data.
If these are the constraints you should work around them accordingly:
Devise a scheme in which you can save changes locally. Keep track of the syncing state with the server.
If login status changes and something unexpected happens, unobtrusively alert the user and give her the opportunity to fix it.
In my point of view, the correct place to put you logic is in the View Controller level.
If I correctly understood you question, you have a Network API that handles the server calls and return the result (possibly from a JSON) to the view controller.
The best approach should be to create a Login View Controller, with the username/email and password fields, separate from the rest of the application logic. After receiving the OK from the server, this view controller can be dismissed and the application will flow as intended.
If your token was invalidated, the server should return an Error 401 to your Network API.
You can simply encapsulate that error into an NSError object, and pass to the View Controller to handle.
Anywhere in the app, a server call can return Error 401, and as a result you apologize to your user and pull back the Login View Controller, to force a new token to be created.
I hope I helped.
My approach is similar to your '(2) At the API request level' - except rather than encapsualting the request in an object, encapsulate the whole concept of the API server.
Therefor I usually end up wrapping each type of request with an async method signature like this:
(Sorry, its C#, not obj-c (I use Xamarin.iOS) but the concept is the same - Action<T> is equivalent to obj-c Blocks)
void GetLatestNews(GetLatestRequest request, Action<NewsListingResponse> success, Action<Exception> error)
void Search(SearchRequest request, Action<SearchResponse> success, Action<Exception> error)
I usually just make these static methods on a static class (very rarely will there be more than 1 server of the same type so a single static class will work fine) but I do sometimes put them within a singleton instance class so that I can pass in a mocked instance for unit tests.
Anyway, now your VC client code just consumes the api as needed:
override void ViewDidAppear(bool animated)
{
SomeNewsSiteApi.GetLatestNews( new GetLatestRequest{Count=20},
response =>
{
// Update table view here
},
error =>
{
// Show some error alert
});
}
The beauty of this is that the implementation of those methods handle sending the request with the current token, and if it fails, obtains a new token, and then resends the same request with the new token, only then finally calling back into the Action<T> success callback. The client VC code has no idea about any re-obtaining of tokens, all it knows and cares about is that it is making a request for some data, and waiting for either a response or an error.
Facing the same issue from couple of months. My approach '(2) At the API request level'
Please let me know if you got the best solution to resolve this.
That will really help many people who are facing these issues.
refresh the auth token when session expire is very big solution.
I have an ASP.NET MVC 3 application. The site involves people writing lengthy responses using a textarea in a web form. Occasionally, users are complaining that they are getting redirected to the log in form after they post their data. I am not sure exactly why they are getting logged out because the users do not typically provide enough information on their errors. I believe it is due either to a session time out or the application has been restarted for some reason. This is on a shared web hosting site and it does not have its own app pool.
In any case, regardless of the reason, I would like to capture that post data and save it to a db or text file. How can I get the post data and save it while the controller redirects the user to the login screen.
I know the long term plan would be to identify why the timeout is occurring. But for now I want to be able to grab the post data and recover it at a later time.
First, in order to avoid timeouts, I would recommend using client-side heartbeat solution (like http://plugins.jquery.com/project/Heartbeat)
Second, assuming that you are using forms authentication, in order to save posted data, when Forms Authorization Module is redirecting your users, you will need to intercept redirects in EndRequest HttpApplication event handler in Global.asax or your own module.
The way to intercept those requests is not that straightforward, since on "EndRequest" pipeline step you will see 302 HTTP status code (redirect instruction), not 401 (Unauthorized error). So you may check if request is not authenticated (HttpContext.User.Identity.IsAuthenticated) and request is redirected - in this case you may save what you see in the request.
Otherwise you would need to disable forms authentication and use some solution, which is closer to ASP.NET MVC.
one solution can be to put a javasscript timer which keeps on hitting the server after specified interval to keep session alive until u figure out the cause of session time out (only i its the session timeout problem)
If you want to stop the session from timing out, you can add a hidden iframe on the page. For example, create a new page called KeepSessionAlive and do this:
<meta http-equiv="refresh" content="600">
where content = seconds.
I don't know about MVC 3, but the way you can get and store the post values is to catch them before redirecting the user to the Login page.
If I have a site that uses cookies for authorisation, so when the user returns they don't have to login again.
If for some reason the site admin cancel this users account what is the best way to check for this. I don't want to have to hit the database every time the user visits a page to make sure their account is still live.
So how should I handle this situation?
Several options. Set a shorter expiration on the cookie so they'd have to get authenticated afresh sooner.
Another alternative is to have any important action require that they be authenticated against the database. Thus you would only be hitting the database for more privileged actions that would more likely overlap with the high priority things you'd want a cancelled user be unable to do.