Skip to content

Commit b7511cd

Browse files
authored
Merge pull request #45 from aws-samples/KVS-updates
Kvs updates
2 parents c253cc3 + c12a682 commit b7511cd

File tree

16 files changed

+135
-227
lines changed

16 files changed

+135
-227
lines changed

kvs-conditional-read/ABUriMappingFunction.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import cf from 'cloudfront';
22

3-
// Replace KVS_ID with actual KVS ID
3+
// (Optional) Replace KVS_ID with actual KVS ID
44
const kvsId = "KVS_ID";
55
// enable stickiness by setting a cookie from origin or using another edge function
66
const stickinessCookieName = "appversion";
@@ -77,4 +77,4 @@ function log(message) {
7777
if (loggingEnabled) {
7878
console.log(message);
7979
}
80-
}
80+
}

kvs-conditional-read/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
## Rewrite request URI
2+
3+
**CloudFront Functions event type: viewer request**
4+
5+
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).

kvs-jwt-verify/README.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
## Verify a JSON Web Token (JWT) using SHA256 HMAC signature
2+
3+
**CloudFront Functions event type: viewer request**
4+
5+
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.
6+
7+
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.
8+
9+
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.
10+
11+
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/).
12+
13+
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`).
14+
15+
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.
16+
17+
**Testing the function**
18+
19+
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:
20+
21+
```
22+
$ aws cloudfront test-function --if-match EXXXXXXXXXXXX --name kvs-jwt-verify --event-object fileb://kvs-jwt-verify/test-objects/valid-jwt.json
23+
```
24+
25+
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:
26+
```
27+
{
28+
"TestResult": {
29+
"FunctionSummary": {
30+
"Name": "kvs-jwt-verify",
31+
"Status": "UNASSOCIATED",
32+
"FunctionConfig": {
33+
"Comment": "",
34+
"Runtime": "cloudfront-js-2.0",
35+
"KeyValueStoreAssociations": {
36+
"Quantity": 1,
37+
"Quantity": 1,
38+
"Items": [
39+
{
40+
"KeyValueStoreARN": "arn:aws:cloudfront::123456789012:key-value-store/6ed3b692-38e9-4952-b89b-bea9cexample"
41+
}
42+
]
43+
}
44+
},
45+
"FunctionMetadata": {
46+
"FunctionARN": "arn:aws:cloudfront::123456789012:function/kvs-jwt-verify",
47+
"Stage": "DEVELOPMENT",
48+
"CreatedTime": "2021-04-09T22:02:12.937000+00:00",
49+
"LastModifiedTime": "2021-04-09T22:09:19.277000+00:00"
50+
}
51+
},
52+
"ComputeUtilization": "19",
53+
"FunctionExecutionLogs": [
54+
"Valid JWT token"
55+
],
56+
"FunctionErrorMessage": "",
57+
"FunctionOutput": "{\"request\":{\"headers\":{\"host\":{\"value\":\"www.example.com\"},\"accept\":{\"value\":\"text/html\"}},\"method\":\"GET\",\"querystring\":{\"test\":{\"value\":\"anotherQueryString\"}},\"uri\":\"/index.html\",\"cookies\":{}}}"
58+
}
59+
}
60+
```

verify-jwt/test-objects/expired-jwt.json renamed to kvs-jwt-verify/test-objects/expired-jwt.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
"accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] }
1919
}
2020
}
21-
}
21+
}

verify-jwt/test-objects/invalid-jwt.json renamed to kvs-jwt-verify/test-objects/invalid-jwt.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
"accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] }
1919
}
2020
}
21-
}
21+
}

verify-jwt/test-objects/invalid-nbe-jwt.json renamed to kvs-jwt-verify/test-objects/invalid-nbe-jwt.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
"accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] }
1919
}
2020
}
21-
}
21+
}

verify-jwt/test-objects/missing-jwt.json renamed to kvs-jwt-verify/test-objects/missing-jwt.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@
1717
"accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] }
1818
}
1919
}
20-
}
20+
}

verify-jwt/test-objects/valid-jwt.json renamed to kvs-jwt-verify/test-objects/valid-jwt.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
"accept": { "value": "text/html", "multivalue": [ { "value": "text/html" }, { "value": "application/xhtml+xml" } ] }
1919
}
2020
}
21-
}
21+
}

kvs-jwt-verify/verify-jwt.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
import crypto from 'crypto';
22
import cf from 'cloudfront';
33

4-
// updated the original example from below URL to use KVS
5-
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example-function-validate-token.html
6-
74
//Response when JWT is not valid.
85
const response401 = {
96
statusCode: 401,
107
statusDescription: 'Unauthorized'
118
};
129

13-
// Replace the KVS_ID with your KVS ID
14-
const kvsId = "KVS_ID";
10+
// Remember to associate the KVS with your function before calling the const kvsKey = 'jwt.secret'.
11+
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-associate.html
1512
const kvsKey = 'jwt.secret';
1613
// set to true to enable console logging
1714
const loggingEnabled = false;
@@ -67,12 +64,12 @@ function _constantTimeEquals(a, b) {
6764
if (a.length != b.length) {
6865
return false;
6966
}
70-
67+
7168
let xor = 0;
7269
for (let i = 0; i < a.length; i++) {
7370
xor |= (a.charCodeAt(i) ^ b.charCodeAt(i));
7471
}
75-
72+
7673
return 0 === xor;
7774
}
7875

@@ -130,7 +127,7 @@ async function handler(event) {
130127
async function getSecret() {
131128
// initialize cloudfront kv store and get the key value
132129
try {
133-
const kvsHandle = cf.kvs(kvsId);
130+
const kvsHandle = cf.kvs();
134131
return await kvsHandle.get(kvsKey);
135132
} catch (err) {
136133
log(`Error reading value for key: ${kvsKey}, error: ${err}`);
@@ -143,4 +140,4 @@ function log(message) {
143140
if (loggingEnabled) {
144141
console.log(message);
145142
}
146-
}
143+
}

kvs-key-value-pairs/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
## Use key-value pairs
2+
3+
**CloudFront Functions event type: viewer request**
4+
5+
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.
6+
7+
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)```.
8+
9+
The example works with [JavaScript runtime 2.0](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html).
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import cf from 'cloudfront';
2+
3+
// This fails if there is no key value store associated with the function
4+
const kvsHandle = cf.kvs();
5+
6+
// Remember to associate the KVS with your function before referencing KVS in your code.
7+
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/kvs-with-functions-associate.html
8+
async function handler(event) {
9+
const request = event.request;
10+
// Use the first segment of the pathname as key
11+
// For example http(s)://domain/<key>/something/else
12+
const pathSegments = request.uri.split('/')
13+
const key = pathSegments[1]
14+
try {
15+
// Replace the first path of the pathname with the value of the key
16+
// For example http(s)://domain/<value>/something/else
17+
pathSegments[1] = await kvsHandle.get(key);
18+
const newUri = pathSegments.join('/');
19+
console.log(`${request.uri} -> ${newUri}`)
20+
request.uri = newUri;
21+
} catch (err) {
22+
// No change to the pathname if the key is not found
23+
console.log(`${request.uri} | ${err}`);
24+
}
25+
return request;
26+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
## Normalize query string parameters
2+
3+
**CloudFront functions event type: viewer request**
4+
5+
You can normalize query string parameters to improve the cache hit ratio.
6+
7+
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.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
function handler(event) {
2+
var qs=[];
3+
for (var key in event.request.querystring) {
4+
if (event.request.querystring[key].multiValue) {
5+
event.request.querystring[key].multiValue.forEach((mv) => {qs.push(key + "=" + mv.value)});
6+
} else {
7+
qs.push(key + "=" + event.request.querystring[key].value);
8+
}
9+
};
10+
11+
event.request.querystring = qs.sort().join('&');
12+
13+
14+
return event.request;
15+
}

verify-jwt/README.md

Lines changed: 0 additions & 51 deletions
This file was deleted.

verify-jwt/generate-jwt.sh

Lines changed: 0 additions & 47 deletions
This file was deleted.

0 commit comments

Comments
 (0)