Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Identity WebApi 2 vs MVC 5 Sign In Cookies

I am using ASP.NET MVC 5 and WebApi 2 and trying to do an Ajax login with redirect if successfull.

For authentication I am using the ASP.NET Identity libraries that come by default for a new ASP.NET MVC project.

I got everything working and all was fine using MVC 5. But for the life of me, I cannot get the standard MVC controller to return just simple JSON. (It wraps my JSON I want to return in a parent object) yes i could fix this on the client side, but that to me is hacky.

My other option which seems better, is to use WebApi which returns objects as I expect them (just my JSON as the body). But the problem I am having is that ASP.NET Identity SignInManager is not sending the .ASPNet.Identity cookies, unless I return an ActionResult.

The below is my WebApi controller, which is returning correct expected minimal JSON but is not sending Set-Cookie commands, therefore any redirect sees the user as not logged in.

[Authorize]
public class AccountController : ApiController
{
    public AccountController(IApplicationSignInManager signInManager)
    {
        SignInManager = signInManager;
    }

    public IApplicationSignInManager SignInManager {....}

    //
    // POST: /Account/Login
    [HttpPost]
    [AllowAnonymous]
    [NgValidateAntiForgeryToken]
    public async Task<HttpResponseMessage> Login(LoginViewModel model)
    {
        // Temporarily using Dynamic 
        dynamic res = new ExpandoObject();

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, change to shouldLockout: true
        var status = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);

        res.status = status.ToString();

        return new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(res))
        };
    }
}

which returns the following Json but no cookies:

{"status":"Success"}

If i change this to return an ActionResult rather than HttpResponseMessage, the Set-Cookie commands are being sent, but the Json is wrapped inside extra properties.

    public async Task<ActionResult> Login(LoginViewModel model)
    {
        // Temporarily using Dynamic 
        dynamic res = new ExpandoObject();

        // This doesn't count login failures towards account lockout
        // To enable password failures to trigger account lockout, change to shouldLockout: true
        var status = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);

        res.status = status.ToString();

        return new JsonResult{Data = res};
    }

Which returns cookies but wrapped Json:

 {"contentEncoding":null,"contentType":null,"data":{"status":"Success"},"jsonRequestBehavior":1,"maxJsonLength":null,"recursionLimit":null}

Now I am guessing that his is happening because the SignInManager is probably assigning the cookies to HttpContext.Current.Response object, which was generated earlier. And when I return a JsonResult, ASP.NET bolts this result on to the HttpContext.Current.Response and sends to client, therefore having the cookies.

But when I return a HttpResponseMessage, ASP.NET returns the newly created HttpResponse, which does not have the SignInManager cookies. Am I right to think that?

EDIT 1: As Suggested by @David Tansey I tried the following, which still does not set cookies but returns correct Json

    public async Task<IHttpActionResult> Login(LoginViewModel model)
    {            
        var status = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);

        return Json(new {status = status.ToString()});
    }

Returns correct json, but no cookies:

{"status":"Success"}

FIX: After @David Tansey pointed out using an anonymous type new { }, i decided to try it out. And the following two methods work

MVC

had to return a ActionResult/JsonResult, for which all fields apart from Data were null, and had to return an Anonymous type rather than a dynamic ExpandoObject() as the dynamic object was causing the serializer to bloat the returned Json

    [HttpPost]
    [AllowAnonymous]
    [NgValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model)
    {
        var status = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);

        //return Json(new {status = status.ToString()});
        // OR
        return new JsonResult { Data = new { status = status.ToString() } };
    }

WebApi 2

Had to change the return type to object, which in gets serialized to Json, and also sets cookies. Returning a HttpResponseMessage causes the cookies that SignInManager set to get lost I guess as its start using the newly returned response object.

    [HttpPost]
    [AllowAnonymous]
    [NgValidateAntiForgeryToken]
    public async Task<object> Login(LoginViewModel model)
    {
        var status = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);

        return new {status = status.ToString()};
    }
like image 874
Michal Ciechan Avatar asked Jan 24 '15 22:01

Michal Ciechan


People also ask

Is Web API better than MVC?

Web API can return data as JSON, XML, and other formats, but MVC only returns data as JSON using JSONResult. MVC does not support content negotiation or self-hosting, while Web API does. Even though Web API supports features of MVC, like routing and model binding, they are different, coming from System.

How does MVC get cookies from client?

In ASP.Net MVC application, a Cookie is created by sending the Cookie to Browser through Response collection (Response. Cookies) while the Cookie is accessed (read) from the Browser using the Request collection (Request. Cookies).

Why we use cookies in ASP NET MVC?

Cookies are one of the State Management techniques, so that we can store information for later use. Cookies are small files that are created in the web browser's memory (if they're temporary) or on the client's hard drive (if they're permanent).

What is the difference between Web API and MVC routing?

If you are familiar with ASP.NET MVC, Web API routing is very similar to MVC routing. The main difference is that Web API uses the HTTP verb, not the URI path, to select the action. You can also use MVC-style routing in Web API.


1 Answers

Perhaps you have set AuthenticationMode as Passive. Try to change this to Active.

Source http://brockallen.com/2013/10/27/host-authentication-and-web-api-with-owin-and-active-vs-passive-authentication-middleware/

like image 78
Kautsky Lozano Avatar answered Oct 03 '22 06:10

Kautsky Lozano