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()};
}
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.
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).
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).
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.
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/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With