Pergunta

Is there a way to be notified when a user becomes logged in with an ASP.net website?

Note: A user can become logged in without visiting a "login page". If the "remember me" cookie exists, they can hit an arbitrary page and be logged in.

When a user is logged in i want to fetch some session related information.

Note: There is the Login.LoggedIn event. Problem is that that control doesn't exist on every page; and the one page it is present on (Login.aspx) doesn't call OnLoggedIn event.

In the same way that Global.asax has a global On Session Start notification:

void Session_Start(object sender, EventArgs e) 
{
}

i assume somewhere there's a On User Logged In notifiation:

void LoggedIn(object sender, EventArgs e)
{
}

Bonus Reading

Foi útil?

Solução

I think you don't have a unique place to do that. In my case (MVC + log4net) I use this:

  • In Global.asax I check for authenticated users with pre-existing cookie.

    protected void Session_Start()
    {
        string ip = HttpContext.Current.Request.UserHostAddress;
    
        log.InfoFormat("Starting session: {0} from {1}.",Session.SessionID, ip);
    
        if ((HttpContext.Current != null) &&
            (HttpContext.Current.User != null) &&
            (HttpContext.Current.User.Identity.IsAuthenticated) )
        {
            string user = HttpContext.Current.User.Identity.Name;
            string type = "Cookie";
    
            log.InfoFormat("User {0} logged in with {1}.", user, type);
        }
    
    }
    
  • In my Account controller I check for local logins (I'm using Internet Application Template from MVC4, but you can do this in your Login.OnLoggedIn if you're using Web forms)

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Login(LoginModel model, string returnUrl)
    {
        if (ModelState.IsValid && WebSecurity.Login(model.EMail, model.Password, persistCookie: model.RememberMe))
        {
            string user = model.EMail;
            string type = "Forms";
    
            log.InfoFormat("User {0} logged in with {1}.", user, type);
    
            return RedirectToLocal(returnUrl);
        }
    
        // If we got this far, something failed, redisplay form
        ModelState.AddModelError("", "The user name or password provided is incorrect.");
        log.ErrorFormat("Bad password or user name. User={0}", model.EMail, model.Password);
        return View(model);
    }
    
  • But I need to check for OAuth logins too, like this:

    [AllowAnonymous]
    public ActionResult ExternalLoginCallback(string returnUrl)
    {
        AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
        if (!result.IsSuccessful)
        {
            log.Debug("External login failure.");
    
            return RedirectToAction("ExternalLoginFailure");
        }
    
        if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
        {
            log.InfoFormat("User {0} logged in with External {1} login. External UserID = {2}",
                Membership.GetUser(OAuthWebSecurity.GetUserName(result.Provider, result.ProviderUserId)).UserName,
                result.Provider,
                result.ProviderUserId);
    
            return RedirectToLocal(returnUrl);
        }
    
        ...
    }
    

Outras dicas

You can make the check on Application_AuthenticateRequest on global.asax, is the place that you can check if the request is logged in or not together with your session data, and decide if session data needs initialization as you say.

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    string cookieName = FormsAuthentication.FormsCookieName;
    HttpCookie authCookie = Context.Request.Cookies[cookieName];

    //  check for logged in or not
    if (null != authCookie)
    {
        // is logged in... check if the session needs init

    }   
}

Or the same results with

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    //  check for logged in or not
    if(HttpContext.Current.User != null && 
        HttpContext.Current.User.Identity != null 
            && HttpContext.Current.User.Identity.IsAuthenticated)
    {
        // is logged in... check if the session needs init

    }   
}    

You can call your code in those two places: OnLoggedIn event from the Login control and also when the session starts (using the Session_Start event in your Global.asax), as this will be the first request with the user data. There you can check if the user is logged and if so, do what you need.

Although technically being logged in is the same as being authenticated, I have a different mental model of this.

In my mind the three following things are separate issues:

  • User has/gets a session
  • User is authenticated
  • User is logged in

For me the last one of these means: "A session has been created for the user, the user is authenticated and the session has been initialized for the authenticated user".

Using this model, a user can become logged in when:

  • The user logs in on the login page and the pre-existing session is initialized with necessary user data
  • A pre-authenticated user comes to the site and a new session is created and initialized for him/her

Similarly, the user becomes logged out when his/her initialized session is destroyed.

Using this model will mean:

  • You can identify when a user "logs in" either in the Login.OnLoggedIn event the or the Session_Start event in Global.asax. Of course, the session start event fires also for unauthenticated users, so you need to verify that the user is authenticated when the event fires.
  • You can somewhat reliably tell when a user "logs out", either by explicitly loggin out or when a properly initialized session is destoyed in the Session_End event in Global.asax. I say somewhat reliably, because I think the Session_End event(s) will not necessarily be fired when the application pool recycles or dies in a crash. Although I haven't tested this so I might be wrong.
  • A user can be simultaneously "logged in" multiple times. At least in IE you can start a "New session" from the File menu. This starts a new IE which is not sharing the session cookies with any preexisting IE windows. This means a new session will be created by the server when the user comes to the site, and depending on the autentication mechanism used it might mean he/she will also have to authenticate again.

It will not let you "list all currently logged in users" out of the box. You will need to create som way of keeping track of that yourself I think. This can me more or less difficult to do. Especially in the case when your application is running in some sort of load balanced environment, getting a list of all current user can be tricky.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top