Skip to main content

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: