Access objects via signed cookies
Tigris offers compatibility with CloudFront's signed cookies, empowering you to manage access to your content without altering existing URLs. This feature is particularly useful when you need to grant access to multiple restricted files.
At a high level, this is the process of how Signed Cookies operate:
- The user generates an RSA private-public key pair.
- The user associates the public key with the Tigris.
- The user defines access policies using a predefined grammar, specifying what, where, and when access is permitted.
- The user signs the cookie using the private key.
- The user distributes these signed cookies to their web users, granting them access to private resources.
Let's run it by an example.
Create RSA key pair
You can utilize openssl
to create a private and public key pair.
Generate a 2048-bit RSA private key:
openssl genrsa -out private_key.pem 2048
Note: Ensure the security of this private key by keeping it safe and secure.
Generate the public key from this private key:
openssl rsa -pubout -in private_key.pem -out public_key.pem
For example purpose this is the public key we are going to use
% cat public_key.pem
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsX1LSnwzGVZRMhJ1TTNN
TR2NlzGXC/7B780V/f7/G6+T1cyDOU3XqprNq0AyG70+v7F9naUYjlkml9g+EEV+
RHtzKursjNe7QrWw7uLCiOPRN/aH/8W3Ur2v5HnhMV9LN6KNIt0Hs3BDK+2IL6sQ
pe//n614ET/VLOPlFTpIovCLC3HXj3erwSsHncu//DkEsRRozWJLIQ584J0flRhU
RPWZDuVteTPJzYqaOT8+INpPPRg+APJUKkEW6oShWDBiQM+u0NVzAXyiYkPjRgnz
REHldcvu7lx2qpqZ1wclnFoTzpsN56H53aM81nrjGs+tHiVUTb4hsqoNbPIR0TBO
2QIDAQAB
-----END PUBLIC KEY-----
Register public-key with Tigris
Let's proceed with registering the public key on Tigris.
- Create a JSON file with the following content, ensuring to replace the
EncodedKey
field with your own public key:
{
"CallerReference": "Tigris example app public key",
"Name": "Tigris example app key",
"EncodedKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArHJ8Cxp2x18Hcc6ya7Nm\no7bDr0kTDnMjUlhnkQ0D6zB0yhXqbXhVYZmR08wdrWX7q0dNU9mReTr305FMrWLQ\nNSzKVLfEis99YskVnWl9PAq3eHMPRnI1jXtMMmaajndjq+aPxJ5WJuoGNRgeZrSt\nw3ndaCIAgJHFnqvZ24LdrfmpKtzvZQGySjFSyyPOUOQkcmC2jc2HzZJx0jTsuTtv\ndY+kFN2ZSpJofAz+52EOwLM3+MuPCM6KU+3xr1mNJqOfi0GFuFZVK0s1wAI0DgaE\n+jkRm2qNYhE6b4TiXQJpnGlvud5LROl+/h65Ofu2tXfnlCOY/9waiTk8gW6M/uHT\noQIDAQAB\n-----END PUBLIC KEY-----",
"Comment": "This is the tigris example app key"
}
Note: Replace the EncodedKey
field with your own public key.
Using your configured AWS CLI to interact with Tigris, execute the following command to register the public key:
aws cloudfront create-public-key --public-key-config file:///path/to/key.json
Upon execution, you will receive an output similar to this:
{
"PublicKey": {
"Id": "t_pk_id_example",
"CreatedTime": "2024-04-26T19:44:05+00:00",
"PublicKeyConfig": {
"CallerReference": "Tigris example app public key",
"Name": "Tigris example app key",
"EncodedKey": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArHJ8Cxp2x18Hcc6ya7Nm\no7bDr0kTDnMjUlhnkQ0D6zB0yhXqbXhVYZmR08wdrWX7q0dNU9mReTr305FMrWLQ\nNSzKVLfEis99YskVnWl9PAq3eHMPRnI1jXtMMmaajndjq+aPxJ5WJuoGNRgeZrSt\nw3ndaCIAgJHFnqvZ24LdrfmpKtzvZQGySjFSyyPOUOQkcmC2jc2HzZJx0jTsuTtv\ndY+kFN2ZSpJofAz+52EOwLM3+MuPCM6KU+3xr1mNJqOfi0GFuFZVK0s1wAI0DgaE\n+jkRm2qNYhE6b4TiXQJpnGlvud5LROl+/h65Ofu2tXfnlCOY/9waiTk8gW6M/uHT\noQIDAQAB\n-----END PUBLIC KEY-----",
"Comment": "This is the tigris example app key"
}
}
}
Notes:
- The public key ID is generated on the Tigris side and returned for further reference.
- Your access-key has to be admin privileges to make calls to
CreatePublicKey
Create bucket on Tigris
Create bucket named images.example.com
on Tigris.
using Fly
fly storage create
or using AWS CLI
aws s3api create-bucket --bucket=images.example.com
Note: Choose the bucket name to be the custom domain name that you intend to use.
Setup custom domain
Setup custom domain to access this bucket.
flyctl storage update images.example.com --custom-domain images.example.com
See more here
Setup CORS
To enable access to this bucket from the parent domain, let's configure CORS.
First, create a JSON file with the following content:
{
"CORSRules": [
{
"AllowedOrigins": ["https://www.example.com"],
"AllowedHeaders": [],
"AllowedMethods": ["GET"],
"MaxAgeSeconds": 3000
}
]
}
Then, register this CORS configuration with the bucket:
aws s3api put-bucket-cors --bucket images.example.com --cors-configuration file:///path/to/cors.json
Learn more here
Example code to issue signed cookies
For illustrative purposes, below AWS CloudFront SDK for Node.js shows how server issues the CloudFront cookies
import express from "express";
import { getSignedCookies } from "@aws-sdk/cloudfront-signer";
// Function to issue CloudFront cookies
function issueCloudFrontCookies(req, res) {
// Set the expiration time for the cookies (in seconds)
const expires = Math.floor((Date.now() + 3600 * 1000) / 1000); // One hour from now
const cloudfrontDistributionDomain = "https://images.example.com";
const s3ObjectKey = "tiger.png";
const url = `${cloudfrontDistributionDomain}/${s3ObjectKey}`;
const privateKey = `<PRIVATE_KEY_CONTENT>`;
const keyPairId = "t_pk_id_example";
const dateLessThan = "2024-04-30";
const policy = {
Statement: [
{
Resource: url,
Condition: {
DateLessThan: {
"AWS:EpochTime": new Date(dateLessThan).getTime() / 1000, // time in seconds
},
},
},
],
};
// Generate CloudFront cookies
const policyString = JSON.stringify(policy);
const cookies = getSignedCookies({
keyPairId,
privateKey,
policy: policyString,
});
// Set CloudFront cookies in the response headers
Object.keys(cookies).forEach((cookieName) => {
const cookieValue = cookies[cookieName];
res.cookie(cookieName, cookieValue, {
domain: "example.com",
httpOnly: true,
secure: true,
expires: new Date(expires * 1000), // Convert expiration time to milliseconds
});
});
// Send a response
res.send("CloudFront cookies issued successfully!");
}
Note:
- This defines the full grammar of the Policy
- Refer more about Node.js CloudFront SDK here
- Read more about cookie here