Fastboot and cookies


#1

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!


#2

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


#3

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.


#4

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)


#5

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!


#6

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.


#7

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


#8

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


#9

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