I am using ASP.NET MVC 5 and WebApi 2 and trying to do Ajax login with redirect if successful.
For authentication, I use ASP.NET identity libraries, which by default appear for a new ASP.NET MVC project.
Everything worked for me, and everything was fine with MVC 5. But for life, I cannot get the standard MVC to return just plain JSON. (It wraps my JSON, which I want to return in the parent object) yes, I could fix it on the client side, but this is hacky for me.
Another option that seems better is to use WebApi, which returns objects as I expect them (just my JSON as a body). But the problem I am facing is that ASP.NET Identity SignInManager does not send .ASPNet.Identity cookies unless I return ActionResult.
Below is my WebApi controller, which returns the correct expected minimum JSON, but does not send Set-Cookie commands, so any redirection sees the user as not registered.
[Authorize] public class AccountController : ApiController { public AccountController(IApplicationSignInManager signInManager) { SignInManager = signInManager; } public IApplicationSignInManager SignInManager {....}
which returns the following Json but not cookies:
{"status":"Success"}
If I change this to return an ActionResult rather than an HttpResponseMessage, Set-Cookie commands are sent, but Json is wrapped inside additional properties.
public async Task<ActionResult> Login(LoginViewModel model) {
Which returns cookies, but wrapped by Json:
{"contentEncoding":null,"contentType":null,"data":{"status":"Success"},"jsonRequestBehavior":1,"maxJsonLength":null,"recursionLimit":null}
Now I assume it happens because SignInManager probably assigns cookies to the HttpContext.Current.Response object that was generated earlier. And when I return JsonResult, ASP.NET pulls this result to HttpContext.Current.Response and sends it to the client, so it has cookies.
But when I return the HttpResponseMessage, ASP.NET returns the newly created HttpResponse, which does not have a SignInManager cookie. Am I correct in this?
EDIT 1: As @David Tansey suggested, I tried the following, which still does not set cookies, but returns the 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 the correct json but not cookies:
{"status":"Success"}
FIX: After @David Tansey pointed out using the anonymous type new {}, I decided to try it. And the following two methods work
MVC
had to return an ActionResult / JsonResult for which all fields except Data were empty and had to return the Anonymous type, and not the dynamic ExpandoObject() , since the dynamic object caused the serialization of the returned Json to multiply
[HttpPost] [AllowAnonymous] [NgValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginViewModel model) { var status = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
WebApi 2
It is required to change the type of the returned object to an object that is serialized in Json, and also sets cookies. The return of the HttpResponseMessage causes the cookies that the SignInManager sets to lose, I assume that its beginning uses 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()}; }