Skip to content

Kvs updates #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions kvs-conditional-read/ABUriMappingFunction.js
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -77,4 +77,4 @@ function log(message) {
if (loggingEnabled) {
console.log(message);
}
}
}
5 changes: 5 additions & 0 deletions kvs-conditional-read/README.md
Original file line number Diff line number Diff line change
@@ -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. 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).
60 changes: 60 additions & 0 deletions kvs-jwt-verify/README.md
Original file line number Diff line number Diff line change
@@ -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 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.

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=<generated JWT>` (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\":{}}}"
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
"accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
"accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
"accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
"accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@
"accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] }
}
}
}
}
15 changes: 6 additions & 9 deletions kvs-jwt-verify/verify-jwt.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
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 = {
statusCode: 401,
statusDescription: 'Unauthorized'
};

// Replace the KVS_ID with your KVS ID
const kvsId = "KVS_ID";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You remove the KVS_ID from the code, but its still referenced in live 131. You will also need to remove it from line 131.

// 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;
Expand Down Expand Up @@ -67,12 +64,12 @@ 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;
}

Expand Down Expand Up @@ -130,7 +127,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}`);
Expand All @@ -143,4 +140,4 @@ function log(message) {
if (loggingEnabled) {
console.log(message);
}
}
}
9 changes: 9 additions & 0 deletions kvs-key-value-pairs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## Use key-value pairs

**CloudFront Functions event type: viewer request**

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, 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](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html).
26 changes: 26 additions & 0 deletions kvs-key-value-pairs/kvs-key-value-pairs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import cf from 'cloudfront';

// 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
// For example http(s)://domain/<key>/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/<value>/something/else
pathSegments[1] = await kvsHandle.get(key);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're not defining kvsHandle anywhere so this function would get an error. You need something like this:
const kvsHandle = cf.kvs(); outside the function handler.

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;
}
7 changes: 7 additions & 0 deletions normalize-query-string-parameters/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +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.
15 changes: 15 additions & 0 deletions normalize-query-string-parameters/normalize-query-string.js
Original file line number Diff line number Diff line change
@@ -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;
}
51 changes: 0 additions & 51 deletions verify-jwt/README.md

This file was deleted.

47 changes: 0 additions & 47 deletions verify-jwt/generate-jwt.sh

This file was deleted.

Loading