Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

hls.js CORS using AWS Cloudfront issues with Cookies

I'm trying to set up a video streaming using Cloudfront HLS capabilities but I'm having trouble getting Hls.js to send my credential cookies in the request.

I already have Cloudfront configured to forward cookies and to forward Access-control headers. I also have set my S3 CORS policies to include GET, HEAD.

The problem I'm having is that even though I'm setting the xhr.withCredentials=true and the cookies are defined in the session, when I look at the request using chrome console, I can see that the HLS request has no cookies. As a result I get an error response from cloudfront saying I need to include the credential cookies.

Code: First I do an ajax request to my server to generate the cookies. The server returns three Set-Cookies headers stored as session cookies in the browser:

$.ajax(
{
type: 'GET',
url: 'http://subdomain.mydomain.com:8080/service-
webapp/rest/resourceurl/cookies/98400738-a415-4e32-898c-9592d48d1ad7',
success: function (data) {
        playMyVideo();
},
headers: { "Authorization": 'Bearer XXXXXX' }
});

Once the cookies are stored the test function is called to play my video using HLS.js:

function test(){
  if (Hls.isSupported()) {
  var video = document.getElementById('video');
  var config = {
    debug: true,
    xhrSetup: function (xhr,url) {
        xhr.withCredentials = true; // do send cookie
    xhr.setRequestHeader("Access-Control-Allow-Headers","Content-Type, Accept, X-Requested-With");
        xhr.setRequestHeader("Access-Control-Allow-Origin","http://sybdomain.domain.com:8080");
    xhr.setRequestHeader("Access-Control-Allow-Credentials","true");
    }
  };
  var hls = new Hls(config);
  // bind them together
  hls.attachMedia(video);
  hls.on(Hls.Events.MEDIA_ATTACHED, function () {
    console.log("video and hls.js are now bound together !");
    hls.loadSource("http://cloudfrontDomain.net/small.m3u8");
    hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
      console.log("manifest loaded, found " + data.levels.length + " quality level");
    });
  });
}
video.play();
}

As you can see below HLS OPTIONS and GET request do not set the session cookies:

HLS OPTIONS request:

OPTIONS /hls/98400738-a415-4e32-898c-9592d48d1ad7/small.m3u8 HTTP/1.1
Host: cloudfrontDomain.net
Connection: keep-alive
Access-Control-Request-Method: GET
Origin: subdomain.mydomain.com:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36
Access-Control-Request-Headers: access-control-allow-credentials,access-control-allow-headers,access-control-allow-origin
Accept: */*
Referer: http://subdomain.mydomain.com:8080/play.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,es;q=0.6

CloudFront response:

HTTP/1.1 200 OK
Content-Length: 0
Connection: keep-alive
Date: Fri, 07 Jul 2017 00:16:31 GMT
Access-Control-Allow-Origin: http://subdomain.mydomain.com:8080
Access-Control-Allow-Methods: GET, HEAD
Access-Control-Allow-Headers: access-control-allow-credentials, access-control-allow-headers, access-control-allow-origin
Access-Control-Max-Age: 3000
Access-Control-Allow-Credentials: true
Server: AmazonS3
Vary: Origin,Access-Control-Request-Headers,Access-Control-Request-Method
Age: 845
X-Cache: Hit from cloudfront
Via: 1.1 cloudfrontDomain.net (CloudFront)
X-Amz-Cf-Id: XXXXXX

HLS subsequent GET request missing the cookies:

GET /hls/98400738-a415-4e32-898c-9592d48d1ad7/small.m3u8 HTTP/1.1
Host: cloudfrontDomain.net
Connection: keep-alive
Origin: http://subdomain.mydomain.com:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36
Access-Control-Allow-Origin: http://subdomain.mydomain.com:8080
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Content-Type, Accept, X-Requested-With
Accept: */*
Referer: http://subdomain.mydomain.com:8080/play.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8,es;q=0.6

I've spent 4 days trying to figure this out. I've done plenty of research but I just can't figure out the solution. I'm new to CORS so maybe I'm not understanding some principle. I thought if the cookies are stored in the session they would get set if you enabled xhr with credentials but it doesn't seem to be the case.

Another thing I noted is that the GET request generated by HLS.js is not setting any xmlhttprequest header.

Thanks for your help :)

like image 778
Ismakun Avatar asked Jul 07 '17 00:07

Ismakun


People also ask

How do I enable CORS in CloudFront?

For enabling CORS we need to configure Cloudfront to allow forwarding of required headers. We can configure the behavior of Cloudfront by clicking on Cloudfront Distribution's "Distribution Settings". Then from the "Behaviour" tab click on "Edit". Here we need to whitelist the headers that need to be forwarded.

How do I fix CORS policy no access-control-allow-origin?

< access-control-allow-origin: * You can solve this temporarily by using the Firefox add-on, CORS Everywhere. Just open Firefox, press Ctrl+Shift+A , search the add-on and add it! Thanks this helps to avoid all the hassle and test the code from localhost.

What is CloudFront signed cookies?

CloudFront signed cookies allow you to control who can access your content when you don't want to change your current URLs or when you want to provide access to multiple restricted files, for example, all of the files in the subscribers' area of a website.


2 Answers

I was finally able to make it work. Thanks Michael for helping! Turns out it was a mix of not understanding how CORS principles work and properly configuring aws services. The main issue is to avoid cross domain requests by using cloudfront to serve both your webservice and s3 bucket. One important note I want to add is that any change you make in aws you have to wait for it to propagate. As a new aws dev I didn't know that and got very frustrated making changes that had no effect. Here is the solution:

1) Create your S3 bucket.

2) Create a Cloudfront distribution.

3) In the distribution set as the default origin your web-service domain.

4) Add a second origin and add a behavior in the distribution to forward all .m3u8 and .ts files to your S3 bucket.

5) When you add your bucket origin make sure you mark restrict access and also update bucket policy checkboxes.

6) In your bucket distribution behavior make sure you forward all white list headers and cookies. This can all be set in aws console.

7) If you are using different ports in your service make sure you set those too in the distribution.

8) Go to your S3 bucket settings and update the CORS config to the following:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration> 

It is important that if your are using HLS.js to set the following config:

var config = {
debug: true,
xhrSetup: function (xhr,url) {
xhr.withCredentials = true; // do send cookie
xhr.setRequestHeader("Access-Control-Allow-Headers","Content-Type, Accept, X-Requested-With");
    xhr.setRequestHeader("Access-Control-Allow-Origin","http://sybdomain.domain.com:8080");
xhr.setRequestHeader("Access-Control-Allow-Credentials","true");
}
};
var hls = new Hls(config);

Other important notes:

When you serve a cookie with your web service you can set the Path to be "/" and it will apply to all request in your domain.

like image 99
Ismakun Avatar answered Dec 04 '22 02:12

Ismakun


For anyone that might be having this issue only on Chrome for Android, our problem was that the browser was caching the m3u8 files and giving the same CORS error. The solution was to append a timestamp parameter to the querystring of the file url:

var config = {
  xhrSetup: function (xhr, url) {
    xhr.withCredentials = true; // do send cookies
    url = url + '?t=' + new Date().getTime();
    xhr.open('GET', url, true);
  }
};
var hls = new Hls(config);
like image 33
Ale Felix Avatar answered Dec 04 '22 03:12

Ale Felix