Search code examples
.netasp.net-mvcforms-authenticationhttpcontext

.net Forms Authentication - manually setting HttpContext.Current.User not working in custom AuthorizeAttribute


I've been hitting this one for hours and I'm stumped. I'm making an ajax post request to an MVC 5 controller in an attempt to auto-login a specific pre-defined "super" user. In the controller method, I'm trying to programmatically set the HttpContext.Current.User and authenticate, so the super user can skip the process of manually logging in. The consensus on this seems to be here, which I implemented:

setting HttpContext.Current.User

This seems to work until I try to view any other controller methods with a custom AuthorizeAttribute.

Controller method:

[HttpPost]
[AllowAnonymous]
public ActionResult Login(string username)
{
    string password = ConfigurationManager.AppSettings["Pass"];

    User user = service.Login(username, password);

    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(',');
            System.Web.HttpContext.Current.User = new GenericPrincipal(new FormsIdentity(ticket), roles);
        }
    }

    //...processing result

    return Json(result);
}

The service.Login method above creates the cookie:

FormsAuthentication.SetAuthCookie(cookieValue, false);

Though I'm setting the User, which has an Identity and IsAuthenticated is true, the filterContext.HttpContext.User below is not the same user. It's essentially empty as if it was never assigned, and is not authenticated.

public override void OnAuthorization(AuthorizationContext filterContext) 
{
    string[] userDetails = filterContext.HttpContext.User.Identity.Name.Split(char.Parse("|"));
}

The closest post I could find is here: IsAuthenticated works on browser - but not with Air client!

However, the fix for that was already in place for me:

<authentication mode="Forms">
  <forms cookieless="UseCookies" timeout="60" loginUrl="~/Account/Login" />
</authentication>

What am I missing to make the AuthorizationContext.User match the HttpContext.Current.User that I'm authenticating in the controller?

UPDATE:

I realize I need a redirect to set the cookie properly, I'm just not able to make that work remotely, via an ajax call. Here's what the script in site A looks like, when executing the controller method on site B. This redirect doesn't set the session. It still isn't present when authenticating the user on the next controller method. It just redirects me back to the login view.

function remoteLogin(id) {
    $.ajax({
        url: "/MyController/RemoteLogin",
        type: "POST",
        dataType: "json",
        data: { "id": id }
    }).done(function (data) {
        if (data) {
            if (data.user) {
                var user = data.user;
                $.ajax({
                    url: "http://siteB.xyz/Account/Login",
                    type: "POST",
                    dataType: "json",
                    data: { "username": user.username, "password": user.password }
                }).done(function (data) {
                    if (data) {
                        window.location.href = "http://siteB.xyz/Next"
                    } else {
                        alert("Fail.");
                    }
                }).fail(function (data) {
                    alert("Fail.");
                });
            } else {
                alert("Fail.");
            }
        }
    }).fail(function (data) {
        alert("Fail.");
    });
}

Solution

  • The problem you have is at this point you're only setting the authentication cookie, the IPrincipal that gets created inside the forms authentication module will not happen until there is a new request - so at that point the HttpContext.User is in a weird state. Once the redirect happens then, because it's a new request from the browser the cookie will get read before your page is reached and the correct user object created.

    Cookies are only set on the browser after a request is completed.