Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript won't set httpcookie received in XHR response

I have a basic SPA (react) <-> API (net core 2.2) setup, with 2 environments: dev and prod (small project). There is an authentication mechanism on the API side that checks the presence of a httponly cookie in every request containing a JWT.

On the dev environment, it works okey-dokey: allowCredentials() is set in the API and withCredentials = true in the react app as well. Both run on a different port of my localhost.

But in a production environment (separate Heroku dynos), it just WON'T set the httponly cookie: I can login using my credentials, the response-headers contain the cookie with the jwt, but every other request i'll make will NOT contain the cookie header at all in request-headers !

I then get a 401 Unauthorized ... error (which is logical). It drives me nuts as I spent hours trying about everything.

My simple authentication XHR (vanilla) call:

var request = new XMLHttpRequest()
request.open('POST', apiAuthenticateUser, true)
request.setRequestHeader('Content-type', 'application/json')
request.withCredentials = true
request.send(postData)

my Startup.cs config in the .net core api :

public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
    if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
        IdentityModelEventSource.ShowPII = true;
    } else {
        app.UseHsts();
    }
    app.UseHttpsRedirection();

    app.UseCors(
        options => options.WithOrigins(
                "https://localhost:3000",
    "*productionEnvUrl*").AllowAnyMethod().AllowCredentials().AllowAnyHeader()
    );

    app.UseMvc(routes => {
        routes.MapRoute("MainRoute", "api/{controller}/{action}");
    });

    app.UseAuthentication();
}

and thats how i set my httponly cookie containing the jwt in the api controller action response :

Response.Cookies.Append("jwt", jwt, new CookieOptions { HttpOnly = true, Secure = true });

The code is the same on both environments, they just yield different results. In both cases the api sends me the right cookie in authentication response-headers, but in production environment my react app just won't keep it and send it back in other api calls ....

here is the cookie received from the API and that is never sent back from the web app:

Access-Control-Allow-Credentials    :true
Access-Control-Allow-Origin :https://xxxxxxxxxx.com
Connection  :keep-alive
Content-Type    :application/json; charset=utf-8
Date    :Mon, 09 Sep 2019 22:32:54 GMT
Server  :Kestrel
Set-Cookie  :jwt=xxxxxxxx; path=/; secure; samesite=lax; httponly
Transfer-Encoding   :chunked
Vary    :Origin
Via :1.1 vegur

If anyone has any clue i'll be forever grateful.

like image 456
Binarynam Avatar asked Sep 09 '19 22:09

Binarynam


1 Answers

Well turns out i got a lot of things wrong :

  • Everything was fine with my web app code.
  • It worked in development environment because everything was running on localhost (just different ports), which is authorized by the samesite=lax (default value) cookie policy.
  • In order to make it cross-domain you simply have to specify samesite=none, then it works. It creates some potential vulnerabilities since you lose some control over your cookies, but unless you set up some reverse-proxy/api gateway mechanism this is the only way around as far as cookies (httponly included) are concerned.
  • I got over-excited about the rule "jwt should only be stored in httponly cookies" : it has vulnerabilities too, and you still have to make the rest of your application xss (and other techniques) safe. You can safely store a jwt in the local storage if you take all the precautions needed.

Thanks to @Crayon Violent for his time :)

like image 71
Binarynam Avatar answered Oct 26 '22 12:10

Binarynam