I know this topic has been asked and answered multiple times all over the internet but unfortunately I'm still having issues although trying basically every single related post out there!
I have an S3 bucket with video files for a frontend streaming service. The files get served via a CloudFront Distribution and in order to secure the file access, I have set up an API Gateway which redirect requests to a Custom Lambda to get my Signed Cookies for CloudFront.
However, tests for the individual services were all successful:
*.m3u8
video manifest directly from the S3 works as expected (stream works like a charm).Set-Cookie
headers for the CloudFront domain.I want to emphasise, that S3, CloudFront, Lambda and the API Gateway are working individually but only as long as I don't attach the cookies to the request!
So my guess is that something is wrong with the signing of the cookies.
Since the lambda is the source of truth for cookie signing, I will show a minimal example of what I'm doing here. My lambda is written in Node JS
and I'm using "@aws-sdk/cloudfront-signer": "^3.229.0",
.
const url = `https://my-cloudfront.domian.com/path-to-src/*`;
const expTime = new Date(Date.now() + 5 * (60 * 60 * 1000)).toISOString();
const signedCookie = getSignedCookies({
keyPairId: awsCloudfrontKeyPairId, // from CloudFront setup
privateKey: awsCloudfrontPrivateKey, // from CloudFront setup
url: url,
dateLessThan: getExpTime,
});
const response = {
statusCode: 200,
isBase64Encoded: false,
body: JSON.stringify({ url: url, bucket: bucket, key: key }),
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true,
"Access-Control-Allow-Methods": "OPTIONS,POST,GET",
},
multiValueHeaders: {
"Set-Cookie": [
`CloudFront-Expires=${signedCookie["CloudFront-Expires"]}; Domain=domain.com; Path=/*`,
`CloudFront-Signature=${signedCookie["CloudFront-Signature"]}; Domain=domain.com; Path=/*`,
`CloudFront-Key-Pair-Id=${signedCookie["CloudFront-Key-Pair-Id"]}; Domain=domain.com; Path=/*`,
],
},
};
callback(null, response);
This works, I get the 3 Set-Cookie
headers which also get submitted when requesting the file. However, the result is always the same:
ACCESS DENIED
To test the individual services I did the following:
viewer restriction
--> stream worksviewer restriction
but no cookies --> Missing Key
errorviewer restriction
and cookies --> Access Denied
errorThere obviously is a lot more important setup steps (e.g. bucket policies, custom domains, etc), which I did not mention here but can be posted on request if necessary.
This topic keeps me busy since a month now! Any help appreciated! 🙌
I found the solution and it's pretty embarrassing...
My above example signs cookies via a Custom Policy but uses the Set-Cookie
header keys from a Canned Policy. The difference in the docs is not really obvious but its clearly there...
However, instead of
Set-Cookie: CloudFront-Expires= ...
you use
Set-Cookie: CloudFront-Policy=<your policy string>
The keys
Set-Cookie: CloudFront-Expires= ...
Set-Cookie: CloudFront-Signature= ...
are identical.
Hope this helps!