Set-Cookie in Response Header not being created in browser - Using http POST
I wasted a full day on the same problem. What I've found is that these are the important bits:
1. You need to add the following headers on the server (replace with your client host address). These are needed for CORS, which you need because your two sites are using different port numbers.
Access-Control-Allow-Origin: http://localhost:5555
Access-Control-Allow-Credentials: true
For WebAPI, this means placing the following right at the top of WebApiConfig.Register
:
var cors = new EnableCorsAttribute("http://localhost:5555", "*", "GET,POST");
cors.SupportsCredentials = true;
config.EnableCors(cors);
2. Be precise about how you set your cookies
Look at these:
response.Headers.AddCookies(new CookieHeaderValue[]
{
new CookieHeaderValue("WorkingCookie", cookieValue) { Path="/" },
new CookieHeaderValue("NotWorkingCookie", cookieValue)
});
I couldn't get a CORS cookie to work without specifying Path. Also, this SO answer claims that in production environments, you'll need to specify Domain as well. I'm not there yet, so I'm just noting it for now.
3. Be aware of back-end limitations
Not really an issue with Web API that I know of, but for PHP multiple Set-Cookie
headers don't work well. I could only get the last one listed to be persisted on the client.
4. Use withCredentials on your HTTP request*
This is my take on your call listed earlier:
return this._http.post(this._globalVariables.BACKEND_API_HANDLE_LOGIN,
loginModel, {headers: headers, withCredentials: true})
.map((response : Response) => response);
Note withCredentials
being passed through as part of the options.
This was the missing part for you and is absolutely key. The other steps are there to ensure the cookies get sent through correctly by the server and are persisted by the browser, but withCredentials
governs whether the browser will include its cookies in the request header.
You'll want to ensure you're using a newer version of Angular though; even some of the RC versions just flat-out ignored the withCredentials
option and could therefore not be used for CORS calls like these at all.
Final note If you are creating a CORS authentication system with cookies, there are two additional things to take care of:
- Mark your session cookie
HttpOnly
in Web API, so it may not be tampered with by injected JavaScript. - Generate and store server-side a key that you return in a (non-
HttpOnly
) cookie namedXSRF-TOKEN
. On each API call, compare this stored value with the value of theX-XSRF-TOKEN
header that Angular will return. This is needed for protection against CSRF attacks.
Simon Azzopardi
Updated on June 04, 2022Comments
-
Simon Azzopardi almost 2 years
I have been stuck on such issue related to cookies.
My scenario is this:
- I have my backend on server (localhst:12456)
- I have my Angular 2 app running on another server (localhost:5555)
My backend is just an ASP.NET application and I am trying to authenticate user from an apiController. the api Controller is like this:
[System.Web.Mvc.HttpPost] public HttpResponseMessage HandleLogin([FromBody] LoginModel loginModel) { if (!String.IsNullOrEmpty(loginModel.Username) && !String.IsNullOrEmpty(loginModel.Password)) { if (Members.Login(loginModel.Username, loginModel.Password)) { var resp = Request.CreateResponse<UserAuthenticationModel>( HttpStatusCode.OK, new UserAuthenticationModel() { IsAuthenticated = true} ); //create and set cookie in response var cookie = new CookieHeaderValue("customCookie", "cookieVal"); cookie.Expires = DateTimeOffset.Now.AddDays(1); cookie.Domain = Request.RequestUri.Host; cookie.Path = "/"; resp.Headers.AddCookies(new CookieHeaderValue[] { cookie }); return resp; } } return Request.CreateResponse<UserAuthenticationModel>( new UserAuthenticationModel() { IsAuthenticated = false } ); }
Now, from my angular app, I am calling an http post:
headers.append('Access-Control-Allow-Credentials', true);
return this._http.post(this._globalVariables.BACKEND_API_HANDLE_LOGIN, loginModel, {headers: headers}) .map((response : Response) => response);
this._loginService.getLoginModel() .subscribe( loginModel => this._signInPageModel = loginModel, error => console.log(error) ); Now, in my api controller method (HandleLogin) I have a test cookie and another one for FormsAuthentication and when user logged in successfully, and return back to my angular app, no cookies are created.
Now, the cookies can be seen as well as post of my response headers:
I'm quite confusing about it and appreciate any help in order to get the login process successfully.
Thank you in advance.