4
votes

I'm attempting to use signed cookies to access private content on AWS Cloudfront.

  • Rails 4.2.1 app running in development on Puma server - http://localhost:3000
  • Using latest Chrome (and can reproduce same in Firefox and Safari)

My app successfully sets the correct cookies required by cloudfront, and each cookie has the correct value. For reference, the three cookies that need to be set are:

  • CloudFront-Policy
  • CloudFront-Signature
  • CloudFront-Key-Pair-Id

I know that the values are correct as if I grab those cookies and perform a curl request using them, it is successful (values replaced with ... for the sake of brevity):

curl -v -b "CloudFront-Signature=...; CloudFront-Policy==...; CloudFront-Key-Pair-Id==...;" http://mydistribution.cloudfront.net/myfile.jpg

The fault

When I use the browser, whilst I can see the cookies have been set in my original request to the server http://localhost:3000/, my requests to Cloudfront urls (for example, in my image tags) do not pass on any of these cookies. This results in

<Error><Code>MissingKey</Code><Message>Missing Key-Pair-Id query parameter or cookie value</Message></Error>

Any ideas why my cookies are not being sent to cloudfront? Advice and support appreciated :)

4

4 Answers

5
votes

This is normal: each cookie has an associated domain and for each request the browser will only send cookies that match the URL the request is being made for.

Furthermore you cannot set cookies for arbitrary domains - if your application was on example.com then it can set cookies for example.com and any subdomains, but not for other domains (eg other-domain.com)

Therefore if your app is being accessed by the browser as localhost then it can't set cookies sent to foo.cloudfront.net. The signed cookies function in cloudfront is pretty new so I'm not sure what the recommended approach is here, however you can configure cloudfront to pass certain paths through to your app (check the cloudfront documentation on behaviour and origins) at this point the browser thinks it is talking to cloudfront so it will let you set cookies that will be sent in subsequent requests to your cloudfront distribution.

Your app does need to be reachable from cloudfront for this to work though, so I don't think this will work in development.

Alternatively use a CNAME so that your cloudfront distribution and your app are served from the same domain. This will however mean that you can't use the default cloudfront ssl certificate, and unless SNI is acceptable, you'll need to pay extra to use your own certificate.

1
votes

Solution to this was to set up a CNAME alias on Cloudfront to content.mydomain.com and then set up my development environment to be on mydomain.com . When I move to production in the future, this will need to be set up as well.

Now that they both share the same TLD, the cookie is free to pass to cloudfront and it works as expected.

Thanks to Frederick Cheung for pointing me in the right direction.

1
votes

Yes, this is the way we are setting up, using CNAME alias and set the cookies on the base domain, then you will be able to pass your cookie.

Let's put more detail to it in case people want to know what would be the next step is, let's use the above example :-

  1. You are developing on my.fancy.site.mydomain.com
  2. Your Cloudfront CNAME alias is content.mydomain.com
  3. Make sure you set your cloudfront signed cookies to .mydomain.com from your fancy app
  4. From this point on, you are able to pass the cookie for the CF.
  5. One quick way to test if your cookie is set appropriately, try to get your assets URL, and put the url in the browser directly. If the cookie set correctly, you will be able to access the file directly.

If you are using javascript to get the cdn assets, make sure in your JS code, you need to pass withCredentials option, or it won't work. For example, if you are using jQuery, you will need something like the following :-

$.ajax({
   url: a_cross_domain_url,
   xhrFields: {
       withCredentials: true
   }
});

And if the request is successful, you should get a response header from CloudFront with "Access-Control-blah-blah".

Hope it helps people if they search this answer.

0
votes

The aws document is quite confusing and bad. As it doesn't say you will need to pass the CloundFront-Policy over if you want the customized policy.

First, you will need to get the CNAME working.

Second, just set the value through cookie under following, it would work. You don't need to specify the final url in Path, just put '/'

    $.cookie('CloudFront-Signature', '', {
                    path: '/', domain: '.mydomain.com'
});

    $.cookie('CloudFront-Key-Pair-Id', '', {
                    path: '/', domain: '.mydomain.com'
});

    $.cookie('CloudFront-Policy', '', {
                    path: '/', domain: '.mydomain.com'
});

It works for me.