Fastboot and cookies

I’m prototyping an e-commerce setup with ember, and using cookies to maintain some state with the back-end. This works fine with ember in the browser, but not fastboot. When I print out the fsatboot request headers with this:

console.log("fastboot request", this.get('fastboot.request.headers'));

I see the cookies in there:

cookie: [ 'esaSession=%7B%22authenticated%22%3A%7B%7D%7D; cartId=b49395c6-bdcd-11e7-b990-1d6293bbf186; _tct=84030ea6ebbc8b27e4835646fdd5df0b8552225a11d780049947b5bf95037dd0' ],

But when I view the request from the back-end, no cookie in the request:

EnvironHeaders([('X-Forwarded-Server', 'my.webtool.com'), ('Connection', 'Keep-Alive'), ('Host', 'my.webtool.com'), ('X-Forwarded-For', '127.0.0.1'), ('X-Forwarded-Host', 'my.webtool.com')])

But I can see the cookie in the request from my browser:

EnvironHeaders(... ('Cookie', 'cartId=b49395c6-bdcd-11e7-b990-1d6293bbf186; _tct=84030ea6ebbc8b27e4835646fdd5df0b8552225a11d780049947b5bf95037dd0; esaSession=%7B%22authenticated%22%3A%7B%7D%7D'), ...)

I’m thinking that possibly credentials:true needs to be set somewhere, maybe? But I’d have no idea where.

Any help appreciated!

FastBoot doesn’t automatically share values from the incoming request to an outgoing request, they are quite separate. We ended up manually copying the headers we need:

const HeadersToCopy = ['Host', 'Cookie', 'User-Agent'];

// method on API service, called by public methods before calling `fetch`
_buildHeaders(headers = {}) {
  let isFastBoot = get(this, 'fastboot.isFastBoot');

  if(!isFastBoot) {
    return headers;
  }

  let requestHeaders = this.get('fastboot.request.headers');
  HeadersToCopy.forEach((n) => headers[n] = requestHeaders.get(n));
  headers['X-forwarded-by'] = 'FastBoot';

  return headers;
}

Note that requestHeaders isn’t an Ember.Object, so get(requestHeader, n) won’t work

Thanks for that. So are you using ember-data or doing everything with fetch?

I’m wondering if there’s a way to add a function like this in an adapter.

I’m not using Ember Data but it does provide a way to do this via customising the adapter (see the second code example where headers is a function)

Ah - thanks for pointing that out. I was able to get this to work in my application.js DS.RESTAdapter:

headers: Ember.computed(function() {
	if (this.get('fastboot.isFastBoot')) {
		return {
	      'Cookie': `cartId=${this.get('cookies').read('cartId')}; _tct=${this.get('cookies').read('_tct')};`
	  	}
	}
}),

and I see the cookie now on the incoming request (from fastboot) to the backend:

EnvironHeaders([('X-Forwarded-Server', 'my.webtool.com'), ('Connection', 'Keep-Alive'), ('Host', 'my.webtool.com'), ('Cookie', 'cartId=ef581c1c-bddd-11e7-b990-1d6293bbf186; _tct=087a0e8f98ef47c530991b3b4a9a80a10c4770e8892d5e7f714093f895f37122;'), ('X-Forwarded-For', '127.0.0.1'), ('X-Forwarded-Host', 'my.webtool.com')])

and the back-end is recognizing the session and sending headers back with the cookies in them:

response Content-Type: application/json
Set-Cookie: cartId=ef581c1c-bddd-11e7-b990-1d6293bbf186; Expires=Tue, 30-Oct-2018 21:26:25 GMT; Secure; Path=/
Set-Cookie: _tct=087a0e8f98ef47c530991b3b4a9a80a10c4770e8892d5e7f714093f895f37122; Expires=Tue, 30-Oct-2018 21:26:25 GMT; Secure; HttpOnly; Path=/

Which is all great…but what I don’t understand is why the cookies sent back from the webserver don’t appear in the fastboot response headers:

fastboot response headers:
FastBootHeaders {
  headers:
   { 'x-powered-by': [ 'Express' ],
	 'cache-control': [ 'private, max-age=0, must-revalidate' ],
	 'set-cookie':
	  [ 'esaSession=%7B%22authenticated%22%3A%7B%7D%7D; path=/',
		'esaSession=%7B%22authenticated%22%3A%7B%7D%7D; path=/' ] } }

Which I am logging out in the terminal (in my application route) with:

console.log(this.get('fastboot.response.headers'));

Am I supposed to be seeing them there (i.e. am I still missing a setting somewhere)? Or am I just misunderstanding what fastboot is doing here? When I disable JS in my browser to view just the fastboot render, the cookie (state) related data isn’t there.

Thanks for the help!

UPDATE: I found the reason I wasn’t getting the correct state in my fastboot-only render…the cart instance-initializer was only invoked for non-fastboot. Working now.

For completeness, if you want to set response headers then you access them in a similar fashion to the request (see docs)

Hmmm…I’m into completeness for sure. But what would be the reason for setting the cookie in the fastboot response headers?

If there are headers or cookies on an API response that need to be present in the client (I’ve needed to set a cookie to tell the load balancer which leg the user session was on). It’s not a common need, just thought I’d mention it