3

I'm using the MembershipProvider that is part of the MVC2 default project.

I'd like to be able to take a list of user names, and log the users off, and destroy their session if needed. The closest I can seem to come is this:

foreach(string userName in UserNames)
{
    MembershipProvider MembershipProvider = new MembershipProvider();
    MembershipUser membershipUser = MembershipProvider.GetUser(userName, true);
    Session.Abandon();
    FormsAuthentication.SignOut();
}

I think I need to use a session and/or signout method related the user I want to log out, but I am unsure where those would be.

What is the proper way to do this?

Omar
  • 39,496
  • 45
  • 145
  • 213
Sgraffite
  • 1,898
  • 3
  • 22
  • 29

3 Answers3

2

That won't work...

Session.Abandon() will be for the Current HttpContext. Not for each user like you are trying to do. Same for FormsAuthentication.SignOut().

Your best bet is check the current user against that array in the Application_AuthenticateRequest Event and sign them out there:

  protected void Application_AuthenticateRequest(Object sender, EventArgs e)
    {
            if (User.Identity.IsAuthenticated)
            {
                //add your ckeck here

                 if (Usernames.Contains(User.Identity.Name))
                    {
                       Session.Abandon();
                       FormsAuthentication.SignOut();
                    }
            }       
    }
turtlepick
  • 2,694
  • 23
  • 22
  • I was able to get this to log out users when I hard coded their names, however I can't help but think this is an odd place to put this. I would need some way to load in the user names I wish to log out there. Which would probably mean a database call every time that method gets called. – Sgraffite Jan 05 '11 at 06:21
  • I think it feels backwards to me because in that case you are waiting for the user to hit the web site again before force logging them out. There should be a way to invalidate their session server side so their cookie would be invalid regardless. – Sgraffite Jan 05 '11 at 06:52
  • The thing is that every request runs through a HttpContext and one of the modules it processes is the "SessionModule". At that point, you can only read this requester's session. Even doing a "how many open sessions" count you need custom logic and watch the Session Events. – turtlepick Jan 06 '11 at 01:53
  • As for "a call to the DB" everytime you can have your "Usernames" as an Application Variable IList for example and always add / remove accounts from this list instead of going to the DB everytime. – turtlepick Jan 06 '11 at 01:54
1

I Didn't use the Membership provider when i did this but basically i saved the SessionId, Username and lastPage visited in a database when the user logged on. Each page then used the current SessionID to get the username and do related username stuff, like display balance for current user etc. if there isn't a valid user for the session then return to log-on page.

This allowed me the see the users progress through the website and manually disconnect whoever i wanted and gave me Single Signon per user . There was also a bunch of clean-up code in the global.asx page

Gaven
  • 371
  • 1
  • 6
  • I understand how to do this, but I'd really like to know how to do it using the MembershipProvider. Thanks for the suggestion. – Sgraffite Jan 05 '11 at 03:05
0

This turned out to not be related so much to the MembershipProvider, but to the FormsService. My final solution turned out to be a sort of hybrid of the two other answers posted here.

I ended up creating my own FormsAuthenticationTicket in the Account controller in the LogOn action. I generate a unique identifier for the user's authenticated session, and save that identifier to the database. I also added the user's ID to the UserData part of the auth ticket for reliable lookup. By default the auth ticket only contained their user name. The FormsAuthenticationTicket is then stored in a cookie upon successful user log on.

Basically all of that replaced the FormsService.SignIn(model.UserName, model.RememberMe);

I also added public void Application_ReleaseRequestState(object sender, EventArgs args) in the Global.asax. I'm not sure if this is considered expanding a function or what, as it does not appear to be an override. Inside of Application_ReleaseRequestState it gets the user's FormsAuthenticationTicket from a cookie. It then decrypts the auth ticket, and gets the user's UserID from the UserData portion of the ticket. Then it asks the database if the auth ticket is still valid. If the ticket is not valid, it sets the user's cookie to expire.

To force users to log off, I can either change their auth ticket expiration dates in the database, or toggle the disable bit for their auth tickets in the database. Either way when Application_ReleaseRequestState asks the database if their auth tickets are valid, they won't be. Thus their cookies will get set to expire upon the next page the user hits after this check was made.

Sgraffite
  • 1,898
  • 3
  • 22
  • 29