14

I am developing an asp.net mvc 3.0 application which has a simple authentication process. User fills a form which is sent to server by ajax call and gets response, but the problem here is that using the following method :

FormsAuthentication.SetAuthCookie(person.LoginName,false);

is not enough to fill 'HttpContext.Current.User' and it needs the below method to be run :

FormsAuthentication.RedirectFromLoginPage("...");

Problem here is that as i mentioned, the loggin form uses an ajax form, and get responses with json, so redirecting is not possible.

How could I fill 'HttpContext.Current.User' ?

Thanks.

Update :

Here is register method :

 [HttpPost]
        public ActionResult Register(Person person)
        {
            var q = da.Persons.Where(x => x.LoginName == person.LoginName.ToLower()).FirstOrDefault();

            if (q != null)
            {
                ModelState.AddModelError("", "Username is repettive, try other one");

                return Json(new object[] { false, this.RenderPartialViewToString("RegisterControl", person) });
            }
            else
            {
                if (person.LoginName.ToLower() == "admin")
                {
                    person.IsAdmin = true;
                    person.IsActive = true;
                }

                da.Persons.Add(person);

                da.SaveChanges();

                FormsAuthentication.SetAuthCookie(person.LoginName,false);
return Json(new object[] { true, "You have registered successfully!" });

}

}
Babak Fakhriloo
  • 2,076
  • 4
  • 44
  • 80

3 Answers3

22

FormsAuthentication doesn't support immediate setting of user's identity, but you should be able to fake it by something like this:

HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(
            new System.Security.Principal.GenericIdentity(person.LoginName), 
            new string[] { /* fill roles if any */ } );
Serkan Arslan
  • 13,158
  • 4
  • 29
  • 44
Lukáš Novotný
  • 9,012
  • 3
  • 38
  • 46
15

Here is the version I ended up using, which is based on the answer by @AdamTuliper-MSFT. It is only meant to be used right after logging in, but before redirect, to allow other code to access HttpContext.User.

  • Don't do anything if already authenticated
  • Doesn't modify the cookie, since this should only be used for the lifetime of this request
  • Shorten some things, and a little safer with userdata (should never be null, but...)

Call this after you call SetAuthCookie(), like below:

// in login function
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
AuthenticateThisRequest();

private void AuthenticateThisRequest()
{
    //NOTE:  if the user is already logged in (e.g. under a different user account)
    //       then this will NOT reset the identity information.  Be aware of this if
    //       you allow already-logged in users to "re-login" as different accounts 
    //       without first logging out.
    if (HttpContext.User.Identity.IsAuthenticated) return;

    var name = FormsAuthentication.FormsCookieName;
    var cookie = Response.Cookies[name]; 
    if (cookie != null)
    {   
        var ticket = FormsAuthentication.Decrypt(cookie.Value);
        if (ticket != null && !ticket.Expired)
        {
            string[] roles = (ticket.UserData as string ?? "").Split(',');
            HttpContext.User = new GenericPrincipal(new FormsIdentity(ticket), roles);
        }
    }
}

Edit: Remove call to Request.Cookies, as @AdamTuplier-MSFT mentioned.

Andrew
  • 8,322
  • 2
  • 47
  • 70
  • The code below was also used for renewing an expired (but valid) token. The code in this case that matters is simply setting HttpContext.Current.User (or HttpContext.User) to a generic principal. Since this is only meant for authentication and not renewing, then you can completely ignore Request, as you wouldn't ever be checking it. Upon a login it would be null (or expired) and thus you don't really care. This can actually be simplified even further as this overlaps into 'renewal' code, I'll update my post below. – Adam Tuliper Jul 15 '13 at 16:46
  • Agree.. I had one odd case where it seemed to ignore request, but maybe that was another mistake. In my actual code I just check response... And ironically, I ended up removing it and moving the code into a 'first-request' type of action-filter anyway, since the login process has changed. – Andrew Jul 17 '13 at 12:24
  • 1
    if the user is authenticated and register/login with another user this will return the old user even the cookie is generated for the new one. Should remove first line or do some checks. – Bart Calixto Jan 25 '14 at 02:01
  • You are correct. I will update the code to show that. The first line in the function is meant as a guard clause, to not do that work if the user is already authenticated. This is mean't to be used in an emergency, where you KNOW the user isn't authenticated but you need to set that information so an API call or other thing works. – Andrew Jan 26 '14 at 03:29
8

You need to manually set it. Rather than reinventing the wheel, note the section here on updating the current principal for the request - thats your option here.

How to set Request.IsAuthenticated to true when not using FormsAuthentication.RedirectFromLoginPage?

public void RenewCurrentUser()
{
    System.Web.HttpCookie authCookie =
        System.Web.HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
    if (authCookie != null)
    {
        FormsAuthenticationTicket authTicket = null;
        authTicket = FormsAuthentication.Decrypt(authCookie.Value);

    if (authTicket != null && !authTicket.Expired)
    {
        FormsAuthenticationTicket newAuthTicket = authTicket;

        if (FormsAuthentication.SlidingExpiration)
        {
            newAuthTicket = FormsAuthentication.RenewTicketIfOld(authTicket);
        }
        string userData = newAuthTicket.UserData;
        string[] roles = userData.Split(',');

        System.Web.HttpContext.Current.User =
            new System.Security.Principal.GenericPrincipal(new FormsIdentity(newAuthTicket), roles);
    }
}

}

Community
  • 1
  • 1
Adam Tuliper
  • 29,982
  • 4
  • 53
  • 71
  • Thanks. Your solution is more sophisticated and I am so grateful for that, but for simplicity I chose Lukáš Novotný answer. Regards – Babak Fakhriloo Jan 02 '12 at 20:18
  • this solution has the exact line you need. I simply provided the code as a reference to the other article. Its just: System.Web.HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(new FormsIdentity(newAuthTicket), roles); – Adam Tuliper Jan 02 '12 at 20:49
  • A lot of this can be simplfied / removed. Also, remember to change `Request` to `Response` if needed. – Andrew Jul 12 '13 at 08:14
  • @AndrewBacker, quite possibly - and it potentially changes in the new identity system anyways, however if you have a better way please post it so others can make use. The trick is sinply to set HttpContext.Current.User, the meat of it is what you want to set it with. – Adam Tuliper Jul 14 '13 at 05:22