From 332876c7844e3d9f9969529936c696230dee05ec Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:06:18 -0700 Subject: [PATCH 01/27] Create README.md --- kvs-jwt-verify/README.md | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 kvs-jwt-verify/README.md diff --git a/kvs-jwt-verify/README.md b/kvs-jwt-verify/README.md new file mode 100644 index 0000000..0e45bdb --- /dev/null +++ b/kvs-jwt-verify/README.md @@ -0,0 +1,60 @@ +## Verify a JSON Web Token (JWT) using SHA256 HMAC signature + +**CloudFront Functions event type: viewer request** + +This function validates a JSON Web Token (JWT) in the query string of the incoming request. It is compatible with JavaScript 2.0 runtime. Using your CloudFront KeyValueStore ID is optional. + +JWT is an open, industry standard [RFC 7519](https://tools.ietf.org/html/rfc7519) method for representing claims securely between two parties. You can use JWTs to validate that a viewer has the right access to view the content being requested. You can use this type of tokenization to give a user of your site a URL that is timebound. After the predetermined expiry time is reached, the user no longer has access to the content on that URL. + +This function has two components. First, your origin or application must be able to generate a JWT and append that token as a query string to the URL. Second, you must use this sample function (or some variation of this function) on a viewer request event type to validate the JWT in the query string, ensuring that the URL hasn't been changed or tampered with and the expiry time hasn't passed. If the token is valid and the expiry time hasn't passed, the request passes through to CloudFront and the request is served. If the token is invalid or the expiry time has passed, the function generates and serves a 401 Unauthorized response to the viewer. + +In this example, your origin or application establishes a JWT. We have provided a simple bash script for building a JWT called `generate-jwt.sh`. There are many libraries across multiple different languages for signing and verifying JWTs available on [jwt.io](https://jwt.io/). + +The output of `generate-jwt.sh` is the JWT that the function will validate. Append the output to the URL as a query string in the following format `token=` (for example, `token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJuYmYiOjE1MTYyMzkwMjIsImV4cCI6MTcxNjIzOTAyMn0.jyu6HjS95wU8iSofQ8nBlmPjFYODxn4PQAdFM-Cv8JY`). + +CloudFront already provides a [signed URLs](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-urls.html) feature that you can use instead of this function. A signed URL can include additional information, such as an expiration date and time, start date and time, and client IP address. This gives you more control over access to your content. However, creating a signed URL creates long and complex URLs and is more computationally costly to produce. If you need a simple and lightweight way to validate timebound URLs, this function can be easier than using CloudFront signed URLs. + +**Testing the function** + +To validate that the function is working as expected, you can use the JSON test objects in the `test-objects` directory. To test, you can use the `test-function` CLI command as shown in the following example: + +``` +$ aws cloudfront test-function --if-match EXXXXXXXXXXXX --name kvs-jwt-verify --event-object fileb://kvs-jwt-verify/test-objects/valid-jwt.json +``` + +If the function has been set up correctly, you should see a log entry saying the token is valid in the `FunctionExecutionLogs` and JWT token removed in the `FunctionOutput` JSON object: +``` +{ + "TestResult": { + "FunctionSummary": { + "Name": "kvs-jwt-verify", + "Status": "UNASSOCIATED", + "FunctionConfig": { + "Comment": "", + "Runtime": "cloudfront-js-2.0", + "KeyValueStoreAssociations": { + "Quantity": 1, + "Quantity": 1, + "Items": [ + { + "KeyValueStoreARN": "arn:aws:cloudfront::123456789012:key-value-store/6ed3b692-38e9-4952-b89b-bea9cexample" + } + ] + } + }, + "FunctionMetadata": { + "FunctionARN": "arn:aws:cloudfront::123456789012:function/kvs-jwt-verify", + "Stage": "DEVELOPMENT", + "CreatedTime": "2021-04-09T22:02:12.937000+00:00", + "LastModifiedTime": "2021-04-09T22:09:19.277000+00:00" + } + }, + "ComputeUtilization": "19", + "FunctionExecutionLogs": [ + "Valid JWT token" + ], + "FunctionErrorMessage": "", + "FunctionOutput": "{\"request\":{\"headers\":{\"host\":{\"value\":\"www.example.com\"},\"accept\":{\"value\":\"text/html\"}},\"method\":\"GET\",\"querystring\":{\"test\":{\"value\":\"anotherQueryString\"}},\"uri\":\"/index.html\",\"cookies\":{}}}" + } +} +``` From 04d3d50b21e2e1684f728634c217f89826d8366c Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:06:46 -0700 Subject: [PATCH 02/27] Update verify-jwt.js --- kvs-jwt-verify/verify-jwt.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kvs-jwt-verify/verify-jwt.js b/kvs-jwt-verify/verify-jwt.js index 7b8bbde..42a7d93 100644 --- a/kvs-jwt-verify/verify-jwt.js +++ b/kvs-jwt-verify/verify-jwt.js @@ -1,8 +1,6 @@ import crypto from 'crypto'; import cf from 'cloudfront'; -// updated the original example from below URL to use KVS -// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example-function-validate-token.html //Response when JWT is not valid. const response401 = { @@ -10,7 +8,7 @@ const response401 = { statusDescription: 'Unauthorized' }; -// Replace the KVS_ID with your KVS ID +// (Optional) Replace the KVS_ID with your KVS ID const kvsId = "KVS_ID"; const kvsKey = 'jwt.secret'; // set to true to enable console logging @@ -143,4 +141,4 @@ function log(message) { if (loggingEnabled) { console.log(message); } -} \ No newline at end of file +} From b137bd6423f54223624e641b6646d78332c80715 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:08:28 -0700 Subject: [PATCH 03/27] Update ABUriMappingFunction.js --- kvs-conditional-read/ABUriMappingFunction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kvs-conditional-read/ABUriMappingFunction.js b/kvs-conditional-read/ABUriMappingFunction.js index c2ad3bf..8dd348c 100644 --- a/kvs-conditional-read/ABUriMappingFunction.js +++ b/kvs-conditional-read/ABUriMappingFunction.js @@ -1,6 +1,6 @@ import cf from 'cloudfront'; -// Replace KVS_ID with actual KVS ID +// (Optional) Replace KVS_ID with actual KVS ID const kvsId = "KVS_ID"; // enable stickiness by setting a cookie from origin or using another edge function const stickinessCookieName = "appversion"; @@ -77,4 +77,4 @@ function log(message) { if (loggingEnabled) { console.log(message); } -} \ No newline at end of file +} From 6577620e0420bdecd887c3617a161c686afc0c52 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:12:55 -0700 Subject: [PATCH 04/27] Create expired-jwt.json --- kvs-jwt-verify/test-objects/expired-jwt.json | 21 ++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 kvs-jwt-verify/test-objects/expired-jwt.json diff --git a/kvs-jwt-verify/test-objects/expired-jwt.json b/kvs-jwt-verify/test-objects/expired-jwt.json new file mode 100644 index 0000000..fa98773 --- /dev/null +++ b/kvs-jwt-verify/test-objects/expired-jwt.json @@ -0,0 +1,21 @@ +{ + "version": "1.0", + "context": { + "eventType": "viewer-request" + }, + "viewer": { + "ip": "0.0.0.0" + }, + "request": { + "method": "GET", + "uri": "/index.html", + "querystring": { + "jwt": { "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJuYmYiOjE1MTYyMzkwMjIsImV4cCI6MTUxNjIzOTAyMn0.4lhOcnLYkO-eq5M1_TU1ZB9RUL_Y195MGH2eZ6Fr_BI"}, + "test": {"value": "anotherQueryString"} + }, + "headers": { + "host": { "value": "www.example.com" }, + "accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] } + } + } +} From cb68a2187e02fb289b54c85683da556bb8026ba9 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:14:52 -0700 Subject: [PATCH 05/27] Rename invalid-jwt.json to invalid-jwt.json --- {verify-jwt => kvs-jwt-verify}/test-objects/invalid-jwt.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {verify-jwt => kvs-jwt-verify}/test-objects/invalid-jwt.json (99%) diff --git a/verify-jwt/test-objects/invalid-jwt.json b/kvs-jwt-verify/test-objects/invalid-jwt.json similarity index 99% rename from verify-jwt/test-objects/invalid-jwt.json rename to kvs-jwt-verify/test-objects/invalid-jwt.json index d116c87..a3e73d6 100644 --- a/verify-jwt/test-objects/invalid-jwt.json +++ b/kvs-jwt-verify/test-objects/invalid-jwt.json @@ -18,4 +18,4 @@ "accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] } } } -} \ No newline at end of file +} From 5fecae33dd2078f10a86d2ac1dcbc4fd054aa83b Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:15:38 -0700 Subject: [PATCH 06/27] Rename invalid-nbe-jwt.json to invalid-nbe-jwt.json --- .../test-objects/invalid-nbe-jwt.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {verify-jwt => kvs-jwt-verify}/test-objects/invalid-nbe-jwt.json (99%) diff --git a/verify-jwt/test-objects/invalid-nbe-jwt.json b/kvs-jwt-verify/test-objects/invalid-nbe-jwt.json similarity index 99% rename from verify-jwt/test-objects/invalid-nbe-jwt.json rename to kvs-jwt-verify/test-objects/invalid-nbe-jwt.json index 8832ca8..b758257 100644 --- a/verify-jwt/test-objects/invalid-nbe-jwt.json +++ b/kvs-jwt-verify/test-objects/invalid-nbe-jwt.json @@ -18,4 +18,4 @@ "accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] } } } -} \ No newline at end of file +} From ba0fc7c97a92f650420c75a5a0d8ef5264e9adf1 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:16:14 -0700 Subject: [PATCH 07/27] Rename missing-jwt.json to missing-jwt.json --- {verify-jwt => kvs-jwt-verify}/test-objects/missing-jwt.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {verify-jwt => kvs-jwt-verify}/test-objects/missing-jwt.json (99%) diff --git a/verify-jwt/test-objects/missing-jwt.json b/kvs-jwt-verify/test-objects/missing-jwt.json similarity index 99% rename from verify-jwt/test-objects/missing-jwt.json rename to kvs-jwt-verify/test-objects/missing-jwt.json index bfed220..939c679 100644 --- a/verify-jwt/test-objects/missing-jwt.json +++ b/kvs-jwt-verify/test-objects/missing-jwt.json @@ -17,4 +17,4 @@ "accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] } } } -} \ No newline at end of file +} From ec26de20151a331bf133eb65b1853aaa26daf8df Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:16:46 -0700 Subject: [PATCH 08/27] Rename valid-jwt.json to valid-jwt.json --- {verify-jwt => kvs-jwt-verify}/test-objects/valid-jwt.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {verify-jwt => kvs-jwt-verify}/test-objects/valid-jwt.json (99%) diff --git a/verify-jwt/test-objects/valid-jwt.json b/kvs-jwt-verify/test-objects/valid-jwt.json similarity index 99% rename from verify-jwt/test-objects/valid-jwt.json rename to kvs-jwt-verify/test-objects/valid-jwt.json index 022cc34..f5f7cd4 100644 --- a/verify-jwt/test-objects/valid-jwt.json +++ b/kvs-jwt-verify/test-objects/valid-jwt.json @@ -18,4 +18,4 @@ "accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] } } } -} \ No newline at end of file +} From aebcaa88d8c43cdb95032b40959fc111d31dda4e Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:17:26 -0700 Subject: [PATCH 09/27] Delete verify-jwt directory --- verify-jwt/README.md | 51 ---------- verify-jwt/generate-jwt.sh | 47 ---------- verify-jwt/index.js | 113 ----------------------- verify-jwt/test-objects/expired-jwt.json | 21 ----- 4 files changed, 232 deletions(-) delete mode 100644 verify-jwt/README.md delete mode 100644 verify-jwt/generate-jwt.sh delete mode 100644 verify-jwt/index.js delete mode 100644 verify-jwt/test-objects/expired-jwt.json diff --git a/verify-jwt/README.md b/verify-jwt/README.md deleted file mode 100644 index 0afe066..0000000 --- a/verify-jwt/README.md +++ /dev/null @@ -1,51 +0,0 @@ -## Verify a JSON Web Token (JWT) using SHA256 HMAC signature - -**CloudFront Functions event type: viewer request** - -**Note:** We recommend using the [kvs-jwt-verify example](https://github.com/aws-samples/amazon-cloudfront-functions/tree/main/kvs-jwt-verify) instead. It uses Amazon CloudFront KeyValueStore to store the secret key, and is compatible with JavaScript 2.0 runtime. - -This function validates a JSON Web Token (JWT) in the query string of the incoming request. JWT is an open, industry standard [RFC 7519](https://tools.ietf.org/html/rfc7519) method for representing claims securely between two parties. You can use JWTs to validate that a viewer has the right access to view the content being requested. You can use this type of tokenization to give a user of your site a URL that is time bound. Once the predetermined expiry time is reached, the user no longer has access to the content on that URL. - -This function has two components. First, your origin or application must be able to generate a JWT and append that token as a query string to the URL. Second, you must use this sample function (or some variation of this function) on a viewer request event type to validate the JWT in the query string, ensuring that the URL hasn't been changed or tampered with and the expiry time hasn't passed. If the token is valid and the expiry time hasn't passed, the request passes through to CloudFront and the request is served. If the token is invalid or the expiry time has passed, the function generates and serves a 401 Unauthorized response to the viewer. - -In this example, your origin or application establish a JWT. We have provided a simple bash script for building a JWT called `generate-jwt.sh`. There are many libraries across multiple different languages for signing and verifying JWTs available on [jwt.io](https://jwt.io/). - -The output of `generate-jwt.sh` is the JWT that the function will validate. Append the output to the URL as a query string in the following format `token=` (e.g. `token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJuYmYiOjE1MTYyMzkwMjIsImV4cCI6MTcxNjIzOTAyMn0.jyu6HjS95wU8iSofQ8nBlmPjFYODxn4PQAdFM-Cv8JY`). - -CloudFront already provides a [signed URLs](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-urls.html) feature that you can use instead of this function. A signed URL can include additional information, such as an expiration date and time, start date and time, and client IP address, which gives you more control over access to your content. However, creating a signed URL creates long and complex URLs and is more computationally costly to produce. If you need a simple and lightweight way to validate time bound URLs, this function can be easier than using CloudFront signed URLs. - -**Testing the function** - -To validate that the function is working as expected, you can use the JSON test objects in the `test-objects` directory. To test, you can use the `test-function` CLI command as shows in the following example: - -``` -$ aws cloudfront test-function --if-match EXXXXXXXXXXXX --name verify-jwt --event-object fileb://verify-jwt/test-objects/valid-jwt.json -``` - -If the function has been set up correctly, you should see a log entry saying the token is valid in the `FunctionExecutionLogs` and JWT token removed in the `FunctionOutput` JSON object: -``` -{ - "TestResult": { - "FunctionSummary": { - "Name": "verify-jwt", - "Status": "UNPUBLISHED", - "FunctionConfig": { - "Comment": "", - "Runtime": "cloudfront-js-1.0" - }, - "FunctionMetadata": { - "FunctionARN": "arn:aws:cloudfront::1234567890:function/verify-jwt", - "Stage": "DEVELOPMENT", - "CreatedTime": "2021-04-09T22:02:12.937000+00:00", - "LastModifiedTime": "2021-04-09T22:09:19.277000+00:00" - } - }, - "ComputeUtilization": "19", - "FunctionExecutionLogs": [ - "Valid JWT token" - ], - "FunctionErrorMessage": "", - "FunctionOutput": "{\"request\":{\"headers\":{\"host\":{\"value\":\"www.example.com\"},\"accept\":{\"value\":\"text/html\"}},\"method\":\"GET\",\"querystring\":{\"test\":{\"value\":\"anotherQueryString\"}},\"uri\":\"/index.html\",\"cookies\":{}}}" - } -} -``` diff --git a/verify-jwt/generate-jwt.sh b/verify-jwt/generate-jwt.sh deleted file mode 100644 index 14500a7..0000000 --- a/verify-jwt/generate-jwt.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash - -# -# JWT Encoder Bash Script -# - -secret='LzdWGpAToQ1DqYuzHxE6YOqi7G3X2yvNBot9mCXfx5k' - -# Static header fields. -header='{ - "alg": "HS256", - "typ": "JWT" -}' - -payload='{ - "sub": "1234567890", - "name": "John Doe", - "iat": 1516239022, - "nbf" : 161623932, - "exp" : 1616239042 -}' - -base64_encode() -{ - declare input=${1:-$( payload.exp*1000) { - throw new Error('Token expired'); - } - } - - return payload; -} - -//Function to ensure a constant time comparison to prevent -//timing side channels. -function _constantTimeEquals(a, b) { - if (a.length != b.length) { - return false; - } - - var xor = 0; - for (var i = 0; i < a.length; i++) { - xor |= (a.charCodeAt(i) ^ b.charCodeAt(i)); - } - - return 0 === xor; -} - -function _verify(input, key, method, type, signature) { - if(type === "hmac") { - return _constantTimeEquals(signature, _sign(input, key, method)); - } - else { - throw new Error('Algorithm type not recognized'); - } -} - -function _sign(input, key, method) { - return crypto.createHmac(method, key).update(input).digest('base64url'); -} - -function _base64urlDecode(str) { - return String.bytesFrom(str, 'base64url') -} - -function handler(event) { - var request = event.request; - - //Secret key used to verify JWT token. - //Update with your own key. - var key = "LzdWGpAToQ1DqYuzHxE6YOqi7G3X2yvNBot9mCXfx5k"; - - // If no JWT token, then generate HTTP redirect 401 response. - if(!request.querystring.jwt) { - console.log("Error: No JWT in the querystring"); - return response401; - } - - var jwtToken = request.querystring.jwt.value; - - try{ - jwt_decode(jwtToken, key); - } - catch(e) { - console.log(e); - return response401; - } - - //Remove the JWT from the query string if valid and return. - delete request.querystring.jwt; - console.log("Valid JWT token"); - return request; -} diff --git a/verify-jwt/test-objects/expired-jwt.json b/verify-jwt/test-objects/expired-jwt.json deleted file mode 100644 index 9210544..0000000 --- a/verify-jwt/test-objects/expired-jwt.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "version": "1.0", - "context": { - "eventType": "viewer-request" - }, - "viewer": { - "ip": "0.0.0.0" - }, - "request": { - "method": "GET", - "uri": "/index.html", - "querystring": { - "jwt": { "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJuYmYiOjE1MTYyMzkwMjIsImV4cCI6MTUxNjIzOTAyMn0.4lhOcnLYkO-eq5M1_TU1ZB9RUL_Y195MGH2eZ6Fr_BI"}, - "test": {"value": "anotherQueryString"} - }, - "headers": { - "host": { "value": "www.example.com" }, - "accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] } - } - } -} \ No newline at end of file From c9f864f40c8eb9aa3111dc8d66964bb47c503d0b Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:24:10 -0700 Subject: [PATCH 10/27] Create normalize-query-string.js --- .../normalize-query-string.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 normalize-query-string-parameters/normalize-query-string.js diff --git a/normalize-query-string-parameters/normalize-query-string.js b/normalize-query-string-parameters/normalize-query-string.js new file mode 100644 index 0000000..f5fb365 --- /dev/null +++ b/normalize-query-string-parameters/normalize-query-string.js @@ -0,0 +1,15 @@ +function handler(event) { + var qs=[]; + for (var key in event.request.querystring) { + if (event.request.querystring[key].multiValue) { + event.request.querystring[key].multiValue.forEach((mv) => {qs.push(key + "=" + mv.value)}); + } else { + qs.push(key + "=" + event.request.querystring[key].value); + } + }; + + event.request.querystring = qs.sort().join('&'); + + + return event.request; +} From 9e85f34cd67e3425c31d3338c381b1fce0ce6330 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:24:33 -0700 Subject: [PATCH 11/27] Create README.md --- normalize-query-string-parameters/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 normalize-query-string-parameters/README.md diff --git a/normalize-query-string-parameters/README.md b/normalize-query-string-parameters/README.md new file mode 100644 index 0000000..711b9ea --- /dev/null +++ b/normalize-query-string-parameters/README.md @@ -0,0 +1,3 @@ +You can normalize query string parameters to improve the cache hit ratio. + +The following example works with JavaScript runtime 1.0 and 2.0. The example shows how to improve your cache hit ratio by putting the query strings in alphabetical order before CloudFront forwards requests to your origin. From ce8899c9bb44f616df11480a8a1d683a6de24f70 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:25:51 -0700 Subject: [PATCH 12/27] Create kvs-key-value-pairs.js --- kvs-key-value-pairs/kvs-key-value-pairs.js | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 kvs-key-value-pairs/kvs-key-value-pairs.js diff --git a/kvs-key-value-pairs/kvs-key-value-pairs.js b/kvs-key-value-pairs/kvs-key-value-pairs.js new file mode 100644 index 0000000..71326bc --- /dev/null +++ b/kvs-key-value-pairs/kvs-key-value-pairs.js @@ -0,0 +1,27 @@ +import cf from 'cloudfront'; +​ +// (Optional) Declare the ID of the key value store that you have associated with this function + +const kvsId = "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"; +​ +const kvsHandle = cf.kvs(kvsId); +​ +async function handler(event) { + const request = event.request; + // Use the first segment of the pathname as key + // For example http(s)://domain//something/else + const pathSegments = request.uri.split('/') + const key = pathSegments[1] + try { + // Replace the first path of the pathname with the value of the key + // For example http(s)://domain//something/else + pathSegments[1] = await kvsHandle.get(key); + const newUri = pathSegments.join('/'); + console.log(`${request.uri} -> ${newUri}`) + request.uri = newUri; + } catch (err) { + // No change to the pathname if the key is not found + console.log(`${request.uri} | ${err}`); + } + return request; +} From 920ecfb4bbaad98579ff5e27f28d89f806ad9375 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:27:37 -0700 Subject: [PATCH 13/27] Create README.md --- kvs-key-value-pairs/README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 kvs-key-value-pairs/README.md diff --git a/kvs-key-value-pairs/README.md b/kvs-key-value-pairs/README.md new file mode 100644 index 0000000..c79f625 --- /dev/null +++ b/kvs-key-value-pairs/README.md @@ -0,0 +1,5 @@ +You can use key-value pairs from an Amazon CloudFront [key value store](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions.html) in a CloudFront function. + +The example shows a function that uses the content of the URL in the HTTP request to look up a custom path in the key value store. CloudFront then uses that custom path to make the request. This function helps manage the multiple paths that are part of a website. + +The example works with JavaScript runtime 2.0. The CloudFront KeyValueStore ID is optional. From ce1542f67c87dca2cd8614ab89288a17666d20df Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:35:47 -0700 Subject: [PATCH 14/27] Create README.md --- kvs-conditional-read/README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 kvs-conditional-read/README.md diff --git a/kvs-conditional-read/README.md b/kvs-conditional-read/README.md new file mode 100644 index 0000000..5d37b13 --- /dev/null +++ b/kvs-conditional-read/README.md @@ -0,0 +1,5 @@ +## Rewrite request URI + +**CloudFront Functions event type: viewer request** + +This example provides a dynamic request URI rewriting mechanism, allowing for A/B testing or gradual rollout of application versions, while also maintaining user stickiness to ensure a consistent experience. It rewrites the request URI based on a configuration stored in CloudFront KeyValueStore. Using your KeyValueStore ID in the code is optional. From 46c76b437de269f5592b3334a45be8dbe692e12c Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:38:30 -0700 Subject: [PATCH 15/27] Update README.md --- kvs-key-value-pairs/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/kvs-key-value-pairs/README.md b/kvs-key-value-pairs/README.md index c79f625..4976f8d 100644 --- a/kvs-key-value-pairs/README.md +++ b/kvs-key-value-pairs/README.md @@ -1,4 +1,8 @@ -You can use key-value pairs from an Amazon CloudFront [key value store](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions.html) in a CloudFront function. +## Use key-value pairs + +**CloudFront Functions event type: viewer request** + +The example uses key-value pairs from an Amazon CloudFront [key value store](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions.html) in a CloudFront function. The example shows a function that uses the content of the URL in the HTTP request to look up a custom path in the key value store. CloudFront then uses that custom path to make the request. This function helps manage the multiple paths that are part of a website. From b89dd05b495116992d143a011bc5cd6bb786bd09 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Fri, 9 Aug 2024 13:39:57 -0700 Subject: [PATCH 16/27] Update README.md --- normalize-query-string-parameters/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/normalize-query-string-parameters/README.md b/normalize-query-string-parameters/README.md index 711b9ea..c1270f1 100644 --- a/normalize-query-string-parameters/README.md +++ b/normalize-query-string-parameters/README.md @@ -1,3 +1,7 @@ +## Normalize query string parameters + +**CloudFront functions event type: viewer request** + You can normalize query string parameters to improve the cache hit ratio. The following example works with JavaScript runtime 1.0 and 2.0. The example shows how to improve your cache hit ratio by putting the query strings in alphabetical order before CloudFront forwards requests to your origin. From 23125bbcb61a7c28dcca535db30d9b1e55212ff1 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:12:23 -0700 Subject: [PATCH 17/27] Update README.md --- kvs-jwt-verify/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvs-jwt-verify/README.md b/kvs-jwt-verify/README.md index 0e45bdb..58f7d84 100644 --- a/kvs-jwt-verify/README.md +++ b/kvs-jwt-verify/README.md @@ -2,7 +2,7 @@ **CloudFront Functions event type: viewer request** -This function validates a JSON Web Token (JWT) in the query string of the incoming request. It is compatible with JavaScript 2.0 runtime. Using your CloudFront KeyValueStore ID is optional. +This function validates a JSON Web Token (JWT) in the query string of the incoming request. It is compatible with the CloudFront Functions [JavaScript 2.0 runtime](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html) and uses [CloudFront KeyValueStore](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions.html) to store the secret. Using your CloudFront KeyValueStore ID is optional. JWT is an open, industry standard [RFC 7519](https://tools.ietf.org/html/rfc7519) method for representing claims securely between two parties. You can use JWTs to validate that a viewer has the right access to view the content being requested. You can use this type of tokenization to give a user of your site a URL that is timebound. After the predetermined expiry time is reached, the user no longer has access to the content on that URL. From 4a3c686df9a8a6f4dcb768635363d772cf814a50 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:15:48 -0700 Subject: [PATCH 18/27] Update README.md --- kvs-conditional-read/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvs-conditional-read/README.md b/kvs-conditional-read/README.md index 5d37b13..823459c 100644 --- a/kvs-conditional-read/README.md +++ b/kvs-conditional-read/README.md @@ -2,4 +2,4 @@ **CloudFront Functions event type: viewer request** -This example provides a dynamic request URI rewriting mechanism, allowing for A/B testing or gradual rollout of application versions, while also maintaining user stickiness to ensure a consistent experience. It rewrites the request URI based on a configuration stored in CloudFront KeyValueStore. Using your KeyValueStore ID in the code is optional. +This example provides a dynamic request URI rewriting mechanism, allowing for A/B testing or gradual rollout of application versions, while also maintaining user stickiness to ensure a consistent experience. It rewrites the request URI based on a configuration stored in CloudFront KeyValueStore. To use this example, you [create a KeyValueStore](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-create.html) for your secret and [associate the KeyValueStore with the CloudFront function](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-associate.html). From 85732aaaeb54b124b55c858146d5db3dc58e62f6 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:37:24 -0700 Subject: [PATCH 19/27] Update verify-jwt.js --- kvs-jwt-verify/verify-jwt.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kvs-jwt-verify/verify-jwt.js b/kvs-jwt-verify/verify-jwt.js index 42a7d93..521fda1 100644 --- a/kvs-jwt-verify/verify-jwt.js +++ b/kvs-jwt-verify/verify-jwt.js @@ -8,8 +8,8 @@ const response401 = { statusDescription: 'Unauthorized' }; -// (Optional) Replace the KVS_ID with your KVS ID -const kvsId = "KVS_ID"; +// Remember to associate the KVS to your function before calling the const kvsKey = 'jwt.secret'. +// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-associate.html const kvsKey = 'jwt.secret'; // set to true to enable console logging const loggingEnabled = false; From d1561f698395be01ddd99c445201c46a66ad9b2a Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:44:02 -0700 Subject: [PATCH 20/27] Update README.md --- kvs-key-value-pairs/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kvs-key-value-pairs/README.md b/kvs-key-value-pairs/README.md index 4976f8d..39688fd 100644 --- a/kvs-key-value-pairs/README.md +++ b/kvs-key-value-pairs/README.md @@ -2,8 +2,8 @@ **CloudFront Functions event type: viewer request** -The example uses key-value pairs from an Amazon CloudFront [key value store](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions.html) in a CloudFront function. +The example uses key-value pairs from an Amazon CloudFront [KeyValueStore](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions.html) in a CloudFront function. -The example shows a function that uses the content of the URL in the HTTP request to look up a custom path in the key value store. CloudFront then uses that custom path to make the request. This function helps manage the multiple paths that are part of a website. +The example shows a function that uses the content of the URL in the HTTP request to look up a custom path in the key value store. CloudFront then uses that custom path to make the request. This function helps manage the multiple paths that are part of a website, such as updating the version of a blog platform on a website. For example, if the earlier blog has origin path ```/blog-v1``` and the new blog has origin path ```/blog-v2```, this function can look up the URL path of the incoming request and rewrite the URL path ```(/blog-v1)``` to the new version of the blog ```(/blog-v2)```. -The example works with JavaScript runtime 2.0. The CloudFront KeyValueStore ID is optional. +The example works with [JavaScript runtime 2.0](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html). From 0289868ed37583c546e34096d9823678b098a332 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:46:51 -0700 Subject: [PATCH 21/27] Update kvs-key-value-pairs.js --- kvs-key-value-pairs/kvs-key-value-pairs.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kvs-key-value-pairs/kvs-key-value-pairs.js b/kvs-key-value-pairs/kvs-key-value-pairs.js index 71326bc..3d08e90 100644 --- a/kvs-key-value-pairs/kvs-key-value-pairs.js +++ b/kvs-key-value-pairs/kvs-key-value-pairs.js @@ -1,6 +1,7 @@ import cf from 'cloudfront'; ​ -// (Optional) Declare the ID of the key value store that you have associated with this function +// Remember to associate the KVS with your function before referencing KVS in your code. +// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-associate.html const kvsId = "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"; ​ From faa87f928dbff73b2786ad63d49333db5ab159a1 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:48:14 -0700 Subject: [PATCH 22/27] Update kvs-key-value-pairs.js --- kvs-key-value-pairs/kvs-key-value-pairs.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/kvs-key-value-pairs/kvs-key-value-pairs.js b/kvs-key-value-pairs/kvs-key-value-pairs.js index 3d08e90..6dabf93 100644 --- a/kvs-key-value-pairs/kvs-key-value-pairs.js +++ b/kvs-key-value-pairs/kvs-key-value-pairs.js @@ -2,10 +2,6 @@ import cf from 'cloudfront'; ​ // Remember to associate the KVS with your function before referencing KVS in your code. // https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-associate.html - -const kvsId = "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"; -​ -const kvsHandle = cf.kvs(kvsId); ​ async function handler(event) { const request = event.request; From bfb6668642c425d91829610d2c34de258dca7419 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:08:30 -0700 Subject: [PATCH 23/27] Update kvs-key-value-pairs.js --- kvs-key-value-pairs/kvs-key-value-pairs.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kvs-key-value-pairs/kvs-key-value-pairs.js b/kvs-key-value-pairs/kvs-key-value-pairs.js index 6dabf93..cf4a8fb 100644 --- a/kvs-key-value-pairs/kvs-key-value-pairs.js +++ b/kvs-key-value-pairs/kvs-key-value-pairs.js @@ -3,6 +3,8 @@ import cf from 'cloudfront'; // Remember to associate the KVS with your function before referencing KVS in your code. // https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-associate.html ​ +const kvsHandle = cf.kvs(); + async function handler(event) { const request = event.request; // Use the first segment of the pathname as key From 4adcdc3781e6140a29ef06f8325ee366fcfc73a1 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Mon, 19 Aug 2024 15:18:16 -0700 Subject: [PATCH 24/27] Update verify-jwt.js --- kvs-jwt-verify/verify-jwt.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kvs-jwt-verify/verify-jwt.js b/kvs-jwt-verify/verify-jwt.js index 521fda1..d7a8cd4 100644 --- a/kvs-jwt-verify/verify-jwt.js +++ b/kvs-jwt-verify/verify-jwt.js @@ -128,7 +128,7 @@ async function handler(event) { async function getSecret() { // initialize cloudfront kv store and get the key value try { - const kvsHandle = cf.kvs(kvsId); + const kvsHandle = cf.kvs(); return await kvsHandle.get(kvsKey); } catch (err) { log(`Error reading value for key: ${kvsKey}, error: ${err}`); From 257053b2bed9c1d29752fb6c929814e357b9b3d2 Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Tue, 20 Aug 2024 09:42:41 -0700 Subject: [PATCH 25/27] Update verify-jwt.js --- kvs-jwt-verify/verify-jwt.js | 152 ++++------------------------------- 1 file changed, 17 insertions(+), 135 deletions(-) diff --git a/kvs-jwt-verify/verify-jwt.js b/kvs-jwt-verify/verify-jwt.js index d7a8cd4..07c1978 100644 --- a/kvs-jwt-verify/verify-jwt.js +++ b/kvs-jwt-verify/verify-jwt.js @@ -1,144 +1,26 @@ -import crypto from 'crypto'; import cf from 'cloudfront'; +// This fails if there is no key value store associated with the function +const kvsHandle = cf.kvs(); -//Response when JWT is not valid. -const response401 = { - statusCode: 401, - statusDescription: 'Unauthorized' -}; - -// Remember to associate the KVS to your function before calling the const kvsKey = 'jwt.secret'. +// Remember to associate the KVS with your function before referencing KVS in your code. // https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-associate.html -const kvsKey = 'jwt.secret'; -// set to true to enable console logging -const loggingEnabled = false; - - -function jwt_decode(token, key, noVerify, algorithm) { - // check token - if (!token) { - throw new Error('No token supplied'); - } - // check segments - const segments = token.split('.'); - if (segments.length !== 3) { - throw new Error('Not enough or too many segments'); - } - - // All segment should be base64 - const headerSeg = segments[0]; - const payloadSeg = segments[1]; - const signatureSeg = segments[2]; - - // base64 decode and parse JSON - const payload = JSON.parse(_base64urlDecode(payloadSeg)); - - if (!noVerify) { - const signingMethod = 'sha256'; - const signingType = 'hmac'; - - // Verify signature. `sign` will return base64 string. - const signingInput = [headerSeg, payloadSeg].join('.'); - - if (!_verify(signingInput, key, signingMethod, signingType, signatureSeg)) { - throw new Error('Signature verification failed'); - } - - // Support for nbf and exp claims. - // According to the RFC, they should be in seconds. - if (payload.nbf && Date.now() < payload.nbf*1000) { - throw new Error('Token not yet active'); - } - - if (payload.exp && Date.now() > payload.exp*1000) { - throw new Error('Token expired'); - } - } - - return payload; -} - -//Function to ensure a constant time comparison to prevent -//timing side channels. -function _constantTimeEquals(a, b) { - if (a.length != b.length) { - return false; - } - - let xor = 0; - for (let i = 0; i < a.length; i++) { - xor |= (a.charCodeAt(i) ^ b.charCodeAt(i)); - } - - return 0 === xor; -} - -function _verify(input, key, method, type, signature) { - if(type === "hmac") { - return _constantTimeEquals(signature, _sign(input, key, method)); - } - else { - throw new Error('Algorithm type not recognized'); - } -} - -function _sign(input, key, method) { - return crypto.createHmac(method, key).update(input).digest('base64url'); -} - -function _base64urlDecode(str) { - return Buffer.from(str, 'base64url') -} - async function handler(event) { - let request = event.request; - - //Secret key used to verify JWT token. - //Update with your own key. - const secret_key = await getSecret() - - if(!secret_key) { - return response401; - } - - // If no JWT token, then generate HTTP redirect 401 response. - if(!request.querystring.jwt) { - log("Error: No JWT in the querystring"); - return response401; - } - - const jwtToken = request.querystring.jwt.value; - - try{ - jwt_decode(jwtToken, secret_key); - } - catch(e) { - log(e); - return response401; - } - - //Remove the JWT from the query string if valid and return. - delete request.querystring.jwt; - log("Valid JWT token"); - return request; -} - -// get secret from key value store -async function getSecret() { - // initialize cloudfront kv store and get the key value + const request = event.request; + // Use the first segment of the pathname as key + // For example http(s)://domain//something/else + const pathSegments = request.uri.split('/') + const key = pathSegments[1] try { - const kvsHandle = cf.kvs(); - return await kvsHandle.get(kvsKey); + // Replace the first path of the pathname with the value of the key + // For example http(s)://domain//something/else + pathSegments[1] = await kvsHandle.get(key); + const newUri = pathSegments.join('/'); + console.log(`${request.uri} -> ${newUri}`) + request.uri = newUri; } catch (err) { - log(`Error reading value for key: ${kvsKey}, error: ${err}`); - return null; - } - -} - -function log(message) { - if (loggingEnabled) { - console.log(message); + // No change to the pathname if the key is not found + console.log(`${request.uri} | ${err}`); } + return request; } From b71db4433561e03c3c5963da14ebc7e0ca29c89d Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Tue, 20 Aug 2024 10:08:41 -0700 Subject: [PATCH 26/27] Update verify-jwt.js --- kvs-jwt-verify/verify-jwt.js | 151 +++++++++++++++++++++++++++++++---- 1 file changed, 134 insertions(+), 17 deletions(-) diff --git a/kvs-jwt-verify/verify-jwt.js b/kvs-jwt-verify/verify-jwt.js index 07c1978..5775c0b 100644 --- a/kvs-jwt-verify/verify-jwt.js +++ b/kvs-jwt-verify/verify-jwt.js @@ -1,26 +1,143 @@ +import crypto from 'crypto'; import cf from 'cloudfront'; -// This fails if there is no key value store associated with the function -const kvsHandle = cf.kvs(); +//Response when JWT is not valid. +const response401 = { + statusCode: 401, + statusDescription: 'Unauthorized' +}; -// Remember to associate the KVS with your function before referencing KVS in your code. +// Remember to associate the KVS with your function before calling the const kvsKey = 'jwt.secret'. // https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-associate.html +const kvsKey = 'jwt.secret'; +// set to true to enable console logging +const loggingEnabled = false; + + +function jwt_decode(token, key, noVerify, algorithm) { + // check token + if (!token) { + throw new Error('No token supplied'); + } + // check segments + const segments = token.split('.'); + if (segments.length !== 3) { + throw new Error('Not enough or too many segments'); + } + + // All segment should be base64 + const headerSeg = segments[0]; + const payloadSeg = segments[1]; + const signatureSeg = segments[2]; + + // base64 decode and parse JSON + const payload = JSON.parse(_base64urlDecode(payloadSeg)); + + if (!noVerify) { + const signingMethod = 'sha256'; + const signingType = 'hmac'; + + // Verify signature. `sign` will return base64 string. + const signingInput = [headerSeg, payloadSeg].join('.'); + + if (!_verify(signingInput, key, signingMethod, signingType, signatureSeg)) { + throw new Error('Signature verification failed'); + } + + // Support for nbf and exp claims. + // According to the RFC, they should be in seconds. + if (payload.nbf && Date.now() < payload.nbf*1000) { + throw new Error('Token not yet active'); + } + + if (payload.exp && Date.now() > payload.exp*1000) { + throw new Error('Token expired'); + } + } + + return payload; +} + +//Function to ensure a constant time comparison to prevent +//timing side channels. +function _constantTimeEquals(a, b) { + if (a.length != b.length) { + return false; + } + + let xor = 0; + for (let i = 0; i < a.length; i++) { + xor |= (a.charCodeAt(i) ^ b.charCodeAt(i)); + } + + return 0 === xor; +} + +function _verify(input, key, method, type, signature) { + if(type === "hmac") { + return _constantTimeEquals(signature, _sign(input, key, method)); + } + else { + throw new Error('Algorithm type not recognized'); + } +} + +function _sign(input, key, method) { + return crypto.createHmac(method, key).update(input).digest('base64url'); +} + +function _base64urlDecode(str) { + return Buffer.from(str, 'base64url') +} + async function handler(event) { - const request = event.request; - // Use the first segment of the pathname as key - // For example http(s)://domain//something/else - const pathSegments = request.uri.split('/') - const key = pathSegments[1] + let request = event.request; + + //Secret key used to verify JWT token. + //Update with your own key. + const secret_key = await getSecret() + + if(!secret_key) { + return response401; + } + + // If no JWT token, then generate HTTP redirect 401 response. + if(!request.querystring.jwt) { + log("Error: No JWT in the querystring"); + return response401; + } + + const jwtToken = request.querystring.jwt.value; + + try{ + jwt_decode(jwtToken, secret_key); + } + catch(e) { + log(e); + return response401; + } + + //Remove the JWT from the query string if valid and return. + delete request.querystring.jwt; + log("Valid JWT token"); + return request; +} + +// get secret from key value store +async function getSecret() { + // initialize cloudfront kv store and get the key value try { - // Replace the first path of the pathname with the value of the key - // For example http(s)://domain//something/else - pathSegments[1] = await kvsHandle.get(key); - const newUri = pathSegments.join('/'); - console.log(`${request.uri} -> ${newUri}`) - request.uri = newUri; + const kvsHandle = cf.kvs(); + return await kvsHandle.get(kvsKey); } catch (err) { - // No change to the pathname if the key is not found - console.log(`${request.uri} | ${err}`); + log(`Error reading value for key: ${kvsKey}, error: ${err}`); + return null; + } + +} + +function log(message) { + if (loggingEnabled) { + console.log(message); } - return request; } From c12a6822c7a7c0984d8b12077e121c1d4cde68bd Mon Sep 17 00:00:00 2001 From: Liz Kellebrew-Davies <35241259+lkdavies@users.noreply.github.com> Date: Tue, 20 Aug 2024 10:11:09 -0700 Subject: [PATCH 27/27] Update kvs-key-value-pairs.js --- kvs-key-value-pairs/kvs-key-value-pairs.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kvs-key-value-pairs/kvs-key-value-pairs.js b/kvs-key-value-pairs/kvs-key-value-pairs.js index cf4a8fb..07c1978 100644 --- a/kvs-key-value-pairs/kvs-key-value-pairs.js +++ b/kvs-key-value-pairs/kvs-key-value-pairs.js @@ -1,10 +1,10 @@ import cf from 'cloudfront'; -​ -// Remember to associate the KVS with your function before referencing KVS in your code. -// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-associate.html -​ + +// This fails if there is no key value store associated with the function const kvsHandle = cf.kvs(); +// Remember to associate the KVS with your function before referencing KVS in your code. +// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-associate.html async function handler(event) { const request = event.request; // Use the first segment of the pathname as key