Created
September 9, 2018 16:41
-
-
Save ershad/30e33ccd3f56c413e9db452c769b5844 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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