Protecting HLS video with CloudFront signed cookies
Why signed cookies fit HLS better than signed URLs, and how StreamVault issues them after a Cognito PKCE sign-in.
, Cloud, AWS, Security
An HLS player doesn’t request one video file. It requests a master playlist, then a media playlist, then a segment every few seconds. Signing each URL means rewriting playlists on the fly. A signed cookie scoped to the distribution path covers every request the player makes, and CloudFront validates it at the edge before anything reaches S3.
The sign-in flow
- /auth/loginPKCE verifier + HMAC-signed state
- Cognito Hosted UIauthorization code
- /auth/callbackverify state, redeem code
- Verify ID tokenaws-jwt-verify
- Set cookiesCloudFront-Policy, -Signature, -Key-Pair-Id
The Lambda keeps the PKCE verifier in a short-lived HttpOnly cookie scoped to /auth and signs it with a secret from Secrets Manager, so a tampered or replayed state is rejected before the code is redeemed. After the ID token verifies, it writes a custom policy that allows the viewer domain for four hours:
const policy = JSON.stringify({
Statement: [{
Resource: `https://${config.viewerDomain}/*`,
Condition: { DateLessThan: { "AWS:EpochTime": Math.floor(expiresAt.getTime() / 1000) } },
}],
});Details that matter
- Every cookie is HttpOnly and Secure, so page scripts can’t read the signature.
- The auth-state cookie uses SameSite=Lax so it survives the redirect back from Cognito.
- Sign-out clears all three CloudFront cookies and the state cookie explicitly.
- The public key, key group, user pool, secrets and API are declared in one CDK stack, so the setup is reproducible.
Takeaway: for segmented media, the unit of access control is a path prefix, not a file.