Preventing Duplicate Logins in ASP.NET

ASP.NET provides a convenient and highly extensible authentication and authorization system that is shared by both ASP.NET MVC and ASP.NET Web Forms. I have cheerfully used it for years, without ever worrying about the fact that there is nothing in the system to prevent the same user from logging in multiple times. The other day, though, I received an email from a former student who was concerned about just that. Their business rules had changed, and now they needed the following behavior: if a user logs on twice with the same identity, the first user should be automatically logged out.

It was not something I had ever thought about before, and I became intrigued. I could imagine preventing the second user from logging in – but how to allow the second login to go through, and bounce the first user from the site? The idea intrigued me so much, I decided to put together a little test site.

I created a standard ASP.NET MVC3 Web application.

I then added code to the LogOn action method:

(The reason for the Session (“fixer”) is that if nothing is in the session, the ID can change between requests – and we’re relying on it staying the same).

Now that we have saved the sessionID of the current user (and overwritten any existing value), we can test for it on every request – and bounce out any user who doesn’t have the current one. In ASP.NET MVC, this is best done using an action filter. Here is the code for the LogUserOut action filter:

Now all we need to do is create an action method and View for Bounce, and assign the LogUserOut attribute to the appropriate action methods – in this case, Index and About:

Then I tested it. I logged on in Internet Explorer, created a new user and had no problem clicking Home and About.

Then I opened Firefox and logged in as the same user. Again, this was no problem and everything worked as it should.

Then I went back and clicked on a link in IE – and was bounced out: exactly what was supposed to happen.

So I logged in again in using IE.. and then went back to Firefox, where the current user was once again bounced out.

So the principle works. I wasn’t that happy with the solution, though. The use of the UserName is less than optimum – what if two users had the same name? So I reworked it to use ProviderUserKey as the cache key. That added a little complexity, as the MembershipUser is not available inside the LogOn action. To get around the problem, I redirected to a new Welcome action after a successful login, and used that to add the user to the cache. I had to do a bit of work to ensure the returnUrl was still honored and that unauthenticated users didn’t hit the Welcome method and cause issues, but it was a better solution as a result. (In Web Forms, I would have used the LoggedIn event to access the MembershipUser. I would also have inherited from a base page to provide the bounce logic, rather than using an action filter.) Another possible change would be to use a database instead of the cache, which would be slower but more reliable in a Web farm.

If you’re interested in the code for the complete solution, then you can download it from here. If you want to find out more about ASP.NET MVC, you might want to check out Building Web Applications with ASP.NET MVC. If you’re interested I learning more about security and state management in Web Forms, then I’d recommend Building Web Applications with ASP.NET and Ajax (of which I’m the author).

Kevin Rattan

4 Responses to “Preventing Duplicate Logins in ASP.NET”


  1. 1 Stuart July 12, 2011 at 5:00 pm

    Hi,

    Great solution for the scenario. You could register the action filter as a global filter and then totally forget about needing to use it for each action.

    Stuart

    • 2 Kevin Rattan, Learning Tree July 12, 2011 at 5:17 pm

      Thanks, Stuart.

      My concern with applying the filter globally was that it might cause problems when applied to the LogOn method itself. On reflection, though, I think you’re right: since the first thing the filter checks is whether the user is authenticated, there shouldn’t be any problem. A nice refinement.

      Cheers,

      Kevin

  2. 3 Wade August 15, 2012 at 8:20 am

    Hi Kevin,

    Very nice post.
    I try to convert this in to C#, but
    HttpContext.Current.Cache[memberShipUser.ProviderUserKey.ToString()]; always null.

    VB version works perfectly.

    Am I miss something?

    Wade

    • 4 Kevin Rattan, Learning Tree August 20, 2012 at 1:16 pm

      Hi Wade,

      Which is always null? The ProviderUserKey? Or the cached item? If the former – the user has to be logged in before you get the ProviderUserKey. If the latter, make sure you are adding the cache item when the user logs in (in my sample, look at the code in the Welcome function in the AccountController).

      Cheers,

      Kevin


Comments are currently closed.



Learning Tree International

.NET & Visual Studio Courses

Learning Tree offers over 210 IT training and Management courses, including a full curriculum of .NET training courses.

Free White Papers

Questions on current IT or Management topics? Access our Complete Online Resource Library of over 65 White Papers, Articles and Podcasts

Enter your email address to subscribe to this blog and receive notifications of new posts by e-mail.

Join 29 other followers

Follow Learning Tree on Twitter

Archives

Do you need a customized .NET training solution delivered at your facility?

Last year Learning Tree held nearly 2,500 on-site training events worldwide. To find out more about hosting one at your location, click here for a free consultation.
Live, online training

%d bloggers like this: