Sign requests
Verify a signed request using the HMAC and SHA-256 algorithms or return a 403.
The following Snippet will:
-
For request URLs beginning with
/generate/
, replace/generate/
with/
, sign the resulting path with its timestamp, and return the full, signed URL in the response body. -
For all other request URLs, verify the signed URL and allow the request through.
export default { async fetch(request) { const secretKey = "your_secret_key"; // Replace with your actual secret key const expiration = 60; // Expiration time in seconds (how long an HMAC token should be valid for)
const encoder = new TextEncoder();
// Import the secret key for HMAC-SHA256 signing const key = await crypto.subtle.importKey( "raw", encoder.encode(secretKey), { name: "HMAC", hash: "SHA-256" }, false, ["sign", "verify"], );
const url = new URL(request.url);
// Check if the request URL starts with /generate/ if (url.pathname.startsWith("/generate/")) { // Replace /generate/ with / url.pathname = url.pathname.replace("/generate/", "/");
const currentTimestamp = Math.floor(Date.now() / 1000); // Current timestamp in seconds
// Data to authenticate: combine pathname and timestamp const dataToAuthenticate = `${url.pathname}${currentTimestamp}`;
// Sign the data with HMAC-SHA256 const signature = await crypto.subtle.sign( "HMAC", key, encoder.encode(dataToAuthenticate), );
// Encode the timestamp and HMAC in a secure manner const signatureBase64 = btoa( String.fromCharCode(...new Uint8Array(signature)), ); const signedData = `${currentTimestamp}-${signatureBase64}`; const encodedSignedData = encodeURIComponent(signedData);
// Create the signed URL const signedURL = `${url}?verify=${encodedSignedData}`;
// Return the signed URL in the response body return new Response(signedURL, { status: 200 }); }
// For all other request URLs, verify the signed URL const params = new URLSearchParams(url.search); const verifyParam = params.get("verify");
if (!verifyParam) { return new Response("Verification parameter is missing", { status: 403 }); }
// Decode and split the verify parameter into timestamp and HMAC const decodedVerifyParam = decodeURIComponent(verifyParam); const [timestampStr, receivedMac] = decodedVerifyParam.split("-");
// Parse timestamp and ensure it's a valid number const timestamp = parseInt(timestampStr, 10); if (isNaN(timestamp)) { return new Response("Invalid timestamp", { status: 403 }); }
// Check if the request has expired const currentTimestamp = Math.floor(Date.now() / 1000); if (currentTimestamp > timestamp + expiration) { return new Response("Signed URL has expired", { status: 403 }); }
// Remove the verify parameter to verify the URL params.delete("verify"); url.search = params.toString();
// Construct the data to authenticate for verification const dataToVerify = `${url.pathname}${timestamp}`;
// Verify the signature with HMAC-SHA256 const isValid = await crypto.subtle.verify( "HMAC", key, new Uint8Array([...atob(receivedMac)].map((char) => char.charCodeAt(0))), encoder.encode(dataToVerify), );
if (!isValid) { return new Response("Invalid signature", { status: 403 }); }
// Continue processing the request if the signature is valid return fetch(request); },};