Skip to content

Instantly share code, notes, and snippets.

@ershad
Created September 9, 2018 16:41
Show Gist options
  • Save ershad/30e33ccd3f56c413e9db452c769b5844 to your computer and use it in GitHub Desktop.
Save ershad/30e33ccd3f56c413e9db452c769b5844 to your computer and use it in GitHub Desktop.
addEventListener("fetch", event => {
event.respondWith(verifyAndCache(event.request));
});
async function verifyAndCache(request) {
/**
source:
https://jameshfisher.com/2017/10/31/web-cryptography-api-hmac.html
https://github.com/diafygi/webcrypto-amples#hmac-verify
https://stackoverflow.com/questions/17191945/conversion-between-utf-8-arraybuffer-and-string
**/
// Convert the string to array of its ASCII values
function str2ab(str) {
var uintArray = new Uint8Array(
str.split("").map(function(char) {
return char.charCodeAt(0);
})
);
return uintArray;
}
// Retrieve to token from query string which is in the format "<time>-<auth_code>"
function getFullToken(url, query_string_key) {
var full_token = url.split(query_string_key)[1];
return full_token
}
// Fetch the authentication code from token
function getAuthCode(full_token) {
var token = full_token.split("-");
return token[1].split("/")[0];
}
// Fetch timestamp from token
function getExpiryTimestamp(full_token) {
var timestamp = full_token.split("-");
return timestamp[0];
}
// Fetch file path from URL
function getFilePath(url) {
let urlx = new URL(url);
return decodeURI(urlx.pathname)
}
const full_token = getFullToken(request.url, "&verify=")
const token = getAuthCode(full_token);
const str = getFilePath(encodeURI(request.url)) + "/" + getExpiryTimestamp(full_token);
const secret = "< HMAC KEY >";
// Generate the SHA-256 hash from the secret string
let key = await crypto.subtle.importKey(
"raw",
str2ab(secret),
{ name: "HMAC", hash: { name: "SHA-256" } },
false,
["sign", "verify"]
);
// Sign the "str" with the key generated previously
let sig = await crypto.subtle.sign({ name: "HMAC" }, key, str2ab(str));
// convert the Arraybuffer "sig" in string and then, in Base64 digest, and then URLencode it
var verif = encodeURIComponent(
btoa(String.fromCharCode.apply(null, new Uint8Array(sig)))
);
// Get time in Epoch
var time = Math.floor(Date.now() / 1000);
if (time > getExpiryTimestamp(full_token) || verif != token) {
// Render error response
const init = {
status: 403
};
const modifiedResponse = new Response(
`Invalid token`,
init
);
return modifiedResponse;
} else {
let url = new URL(request.url);
// Generate a cache key from URL excluding the unique query string
let cache_key = url.host + url.pathname;
let headers = new Headers(request.headers)
/**
Set an optional header/auth token for additional security in origin.
For example, using AWS Web Application Firewall (WAF), it is possible to create a filter
that allows requests only with a custom header to pass through CloudFront distribution.
**/
headers.set("X-Auth-token", "< Optional Auth Token >")
/**
Fetch the file using cache_key. File will be served from cache if it's already there,
or it will send the request to origin.
**/
const response = await fetch(request, { cf: { cacheKey: cache_key }, headers: headers })
return response;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment