@infoxchange/make-it-so 2.10.0-internal-testing-vdt-199-add-oidc-auth.5 → 2.10.0-internal-testing-vdt-199-add-oidc-auth.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdk-constructs/CloudWatchOidcAuth/auth-check.d.ts +1 -9
- package/dist/cdk-constructs/CloudWatchOidcAuth/auth-check.d.ts.map +1 -1
- package/dist/cdk-constructs/CloudWatchOidcAuth/auth-check.js +28 -27
- package/dist/cdk-constructs/CloudWatchOidcAuth/auth-route.d.ts.map +1 -1
- package/dist/cdk-constructs/CloudWatchOidcAuth/auth-route.js +9 -11
- package/dist/cdk-constructs/CloudWatchOidcAuth/index.js +1 -1
- package/package.json +1 -1
- package/src/cdk-constructs/CloudWatchOidcAuth/auth-check.ts +28 -26
- package/src/cdk-constructs/CloudWatchOidcAuth/auth-route.ts +12 -15
- package/src/cdk-constructs/CloudWatchOidcAuth/index.ts +1 -1
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const handler: (event: AWSCloudFrontFunction.Event, context: AWSCloudFrontFunction.Context) => Promise<{
|
|
3
|
-
statusCode: number;
|
|
4
|
-
headers: {
|
|
5
|
-
location: {
|
|
6
|
-
value: string;
|
|
7
|
-
};
|
|
8
|
-
};
|
|
9
|
-
} | AWSCloudFrontFunction.Request>;
|
|
1
|
+
export {};
|
|
10
2
|
//# sourceMappingURL=auth-check.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-check.d.ts","sourceRoot":"","sources":["../../../src/cdk-constructs/CloudWatchOidcAuth/auth-check.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth-check.d.ts","sourceRoot":"","sources":["../../../src/cdk-constructs/CloudWatchOidcAuth/auth-check.ts"],"names":[],"mappings":""}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// Based off: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example_cloudfront_functions_kvs_jwt_verify_section.html
|
|
2
|
+
// Note that as a CloudFront Function, this code has limitations compared to a Lambda@Edge function.
|
|
3
|
+
// For example, no external libraries can be used, and the runtime is more limited.
|
|
2
4
|
import crypto from "crypto";
|
|
3
5
|
import cf from "cloudfront";
|
|
4
6
|
//Response when JWT is not valid.
|
|
@@ -9,13 +11,9 @@ const redirectResponse = {
|
|
|
9
11
|
},
|
|
10
12
|
};
|
|
11
13
|
const kvsKey = "__placeholder-for-jwt-secret-key__";
|
|
12
|
-
//
|
|
13
|
-
const loggingEnabled =
|
|
14
|
+
// Set to true to enable console logging
|
|
15
|
+
const loggingEnabled = false;
|
|
14
16
|
function jwtDecode(token, key, noVerify) {
|
|
15
|
-
// check token
|
|
16
|
-
if (!token) {
|
|
17
|
-
throw new Error("No token supplied");
|
|
18
|
-
}
|
|
19
17
|
// check segments
|
|
20
18
|
const segments = token.split(".");
|
|
21
19
|
if (segments.length !== 3) {
|
|
@@ -47,8 +45,7 @@ function jwtDecode(token, key, noVerify) {
|
|
|
47
45
|
}
|
|
48
46
|
return payload;
|
|
49
47
|
}
|
|
50
|
-
//Function to ensure a constant time comparison to prevent
|
|
51
|
-
//timing side channels.
|
|
48
|
+
// Function to ensure a constant time comparison to prevent timing side channels.
|
|
52
49
|
function _constantTimeEquals(a, b) {
|
|
53
50
|
if (a.length != b.length) {
|
|
54
51
|
return false;
|
|
@@ -73,22 +70,14 @@ function _sign(input, key, method) {
|
|
|
73
70
|
function _base64urlDecode(str) {
|
|
74
71
|
return Buffer.from(str, "base64url").toString();
|
|
75
72
|
}
|
|
76
|
-
|
|
77
|
-
console.log("🟢 Auth check event:", event);
|
|
78
|
-
console.log("🔵 Auth check context:", context);
|
|
73
|
+
async function handler(event) {
|
|
79
74
|
const request = event.request;
|
|
80
75
|
const secret_key = await getSecret();
|
|
81
76
|
if (!secret_key) {
|
|
82
|
-
|
|
77
|
+
// It's not possible for us to validate requests without the secret key so we have no choice but to block all requests.
|
|
78
|
+
throw new Error("Error retrieving JWT secret key");
|
|
83
79
|
}
|
|
84
|
-
|
|
85
|
-
// console.log(request.cookies);
|
|
86
|
-
// console.log(request.cookies["auth-token"]);
|
|
87
|
-
// console.log(Object.keys(request.cookies));
|
|
88
|
-
const jwtToken = request.cookies["auth-token"]?.value;
|
|
89
|
-
console.log("jwtToken:", jwtToken);
|
|
90
|
-
// console.log(Object.keys(request.cookies));
|
|
91
|
-
// If no JWT token, then generate HTTP redirect 401 response.
|
|
80
|
+
const jwtToken = request.cookies["auth-token"] && request.cookies["auth-token"].value;
|
|
92
81
|
if (!jwtToken) {
|
|
93
82
|
log("Error: No JWT in the cookies");
|
|
94
83
|
return redirectResponse;
|
|
@@ -100,11 +89,9 @@ export const handler = async (event, context) => {
|
|
|
100
89
|
log(e);
|
|
101
90
|
return redirectResponse;
|
|
102
91
|
}
|
|
103
|
-
// //Remove the JWT from the query string if valid and return.
|
|
104
|
-
// delete request.querystring.jwt;
|
|
105
92
|
log("Valid JWT token");
|
|
106
93
|
return request;
|
|
107
|
-
}
|
|
94
|
+
}
|
|
108
95
|
// Get secret from key value store
|
|
109
96
|
async function getSecret() {
|
|
110
97
|
try {
|
|
@@ -116,8 +103,22 @@ async function getSecret() {
|
|
|
116
103
|
return null;
|
|
117
104
|
}
|
|
118
105
|
}
|
|
119
|
-
const log = (
|
|
120
|
-
if (loggingEnabled)
|
|
121
|
-
|
|
122
|
-
|
|
106
|
+
const log = function () {
|
|
107
|
+
if (!loggingEnabled)
|
|
108
|
+
return;
|
|
109
|
+
// CloudFront Function runtime only prints first argument passed to console.log so add other args to the first one if given.
|
|
110
|
+
// eslint-disable-next-line prefer-rest-params -- We can't use spread or rest parameters in CloudFront Functions
|
|
111
|
+
let message = arguments[0];
|
|
112
|
+
if (arguments.length > 1) {
|
|
113
|
+
const otherArgs = [];
|
|
114
|
+
for (let i = 1; i < arguments.length; i++) {
|
|
115
|
+
// eslint-disable-next-line prefer-rest-params
|
|
116
|
+
otherArgs[i - 1] = arguments[i];
|
|
117
|
+
}
|
|
118
|
+
message += " - additional args: " + JSON.stringify(otherArgs);
|
|
119
|
+
}
|
|
120
|
+
console.log(message);
|
|
123
121
|
};
|
|
122
|
+
// This serves no purpose other than to make TypeScript and eslint happy by showing that that handler is used. We can't
|
|
123
|
+
// export handler as an alterative because CloudFront Functions don't support exports.
|
|
124
|
+
handler;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-route.d.ts","sourceRoot":"","sources":["../../../src/cdk-constructs/CloudWatchOidcAuth/auth-route.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,OAAO,
|
|
1
|
+
{"version":3,"file":"auth-route.d.ts","sourceRoot":"","sources":["../../../src/cdk-constructs/CloudWatchOidcAuth/auth-route.ts"],"names":[],"mappings":"AAyBA,eAAO,MAAM,OAAO,4CAsCnB,CAAC"}
|
|
@@ -24,28 +24,26 @@ export const handler = addRequiredContext(AuthHandler({
|
|
|
24
24
|
issuer: await Issuer.discover(oidcIssuerConfigUrl.href),
|
|
25
25
|
clientID: oidcClientId,
|
|
26
26
|
scope: oidcScope,
|
|
27
|
-
onSuccess: async (tokenset) => {
|
|
28
|
-
console.log("
|
|
29
|
-
// console.log("Config.jwtSecret:", jwtSecret);
|
|
27
|
+
onSuccess: async (tokenset, client) => {
|
|
28
|
+
console.log("🟢", client);
|
|
30
29
|
// Payload to include in the token
|
|
31
30
|
const payload = {
|
|
32
31
|
userID: tokenset.claims().sub,
|
|
33
32
|
};
|
|
34
|
-
|
|
35
|
-
const options = {
|
|
36
|
-
algorithm: "HS256",
|
|
37
|
-
expiresIn: "1h",
|
|
38
|
-
};
|
|
33
|
+
const expiresInMs = 1000 * 60 * 60;
|
|
39
34
|
// Create the token
|
|
40
|
-
const token = jwt.sign(payload, jwtSecret,
|
|
41
|
-
|
|
35
|
+
const token = jwt.sign(payload, jwtSecret, {
|
|
36
|
+
algorithm: "HS256",
|
|
37
|
+
expiresIn: expiresInMs / 1000,
|
|
38
|
+
});
|
|
39
|
+
const expiryDate = new Date(Date.now() + expiresInMs);
|
|
42
40
|
return {
|
|
43
41
|
statusCode: 302,
|
|
44
42
|
headers: {
|
|
45
43
|
location: "/",
|
|
46
44
|
},
|
|
47
45
|
cookies: [
|
|
48
|
-
`auth-token=${token}; HttpOnly; SameSite=None; Secure; Path=/; Expires=${
|
|
46
|
+
`auth-token=${token}; HttpOnly; SameSite=None; Secure; Path=/; Expires=${expiryDate}`,
|
|
49
47
|
],
|
|
50
48
|
};
|
|
51
49
|
},
|
|
@@ -126,7 +126,7 @@ export class CloudWatchOidcAuth extends Construct {
|
|
|
126
126
|
throw new Error("Could not find the underlying Lambda function of the AwsCustomResource");
|
|
127
127
|
}
|
|
128
128
|
fn.addEnvironment("NODE_OPTIONS", "--require=@aws-sdk/signature-v4-crt");
|
|
129
|
-
const edgeFuncAuthCheck = new CloudFront.Function(scope, `${this.id}
|
|
129
|
+
const edgeFuncAuthCheck = new CloudFront.Function(scope, `${this.id}AuthCheckFunction`, {
|
|
130
130
|
code: CloudFront.FunctionCode.fromInline(fs.readFileSync(path.join(import.meta.dirname, "auth-check.js"), "utf8").replace("__placeholder-for-jwt-secret-key__", key)),
|
|
131
131
|
runtime: CloudFront.FunctionRuntime.JS_2_0,
|
|
132
132
|
keyValueStore: cfKeyValueStore,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@infoxchange/make-it-so",
|
|
3
|
-
"version": "2.10.0-internal-testing-vdt-199-add-oidc-auth.
|
|
3
|
+
"version": "2.10.0-internal-testing-vdt-199-add-oidc-auth.7",
|
|
4
4
|
"description": "Makes deploying services to IX infra easy",
|
|
5
5
|
"repository": "github:infoxchange/make-it-so",
|
|
6
6
|
"type": "module",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Based off: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example_cloudfront_functions_kvs_jwt_verify_section.html
|
|
2
|
-
|
|
2
|
+
// Note that as a CloudFront Function, this code has limitations compared to a Lambda@Edge function.
|
|
3
|
+
// For example, no external libraries can be used, and the runtime is more limited.
|
|
3
4
|
import crypto from "crypto";
|
|
4
5
|
import cf from "cloudfront";
|
|
5
6
|
|
|
@@ -12,14 +13,10 @@ const redirectResponse = {
|
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
const kvsKey = "__placeholder-for-jwt-secret-key__";
|
|
15
|
-
//
|
|
16
|
-
const loggingEnabled =
|
|
16
|
+
// Set to true to enable console logging
|
|
17
|
+
const loggingEnabled = false;
|
|
17
18
|
|
|
18
19
|
function jwtDecode(token: string, key: string, noVerify?: boolean) {
|
|
19
|
-
// check token
|
|
20
|
-
if (!token) {
|
|
21
|
-
throw new Error("No token supplied");
|
|
22
|
-
}
|
|
23
20
|
// check segments
|
|
24
21
|
const segments = token.split(".");
|
|
25
22
|
if (segments.length !== 3) {
|
|
@@ -61,8 +58,7 @@ function jwtDecode(token: string, key: string, noVerify?: boolean) {
|
|
|
61
58
|
return payload;
|
|
62
59
|
}
|
|
63
60
|
|
|
64
|
-
//Function to ensure a constant time comparison to prevent
|
|
65
|
-
//timing side channels.
|
|
61
|
+
// Function to ensure a constant time comparison to prevent timing side channels.
|
|
66
62
|
function _constantTimeEquals(a: string, b: string) {
|
|
67
63
|
if (a.length != b.length) {
|
|
68
64
|
return false;
|
|
@@ -92,25 +88,17 @@ function _base64urlDecode(str: string) {
|
|
|
92
88
|
return Buffer.from(str, "base64url").toString();
|
|
93
89
|
}
|
|
94
90
|
|
|
95
|
-
|
|
96
|
-
console.log("🟢 Auth check event:", event);
|
|
97
|
-
console.log("🔵 Auth check context:", context);
|
|
91
|
+
async function handler(event: AWSCloudFrontFunction.Event) {
|
|
98
92
|
const request = event.request;
|
|
99
93
|
const secret_key = await getSecret();
|
|
100
94
|
|
|
101
95
|
if (!secret_key) {
|
|
102
|
-
|
|
96
|
+
// It's not possible for us to validate requests without the secret key so we have no choice but to block all requests.
|
|
97
|
+
throw new Error("Error retrieving JWT secret key");
|
|
103
98
|
}
|
|
104
99
|
|
|
105
|
-
|
|
106
|
-
// console.log(request.cookies);
|
|
107
|
-
// console.log(request.cookies["auth-token"]);
|
|
108
|
-
// console.log(Object.keys(request.cookies));
|
|
109
|
-
const jwtToken = request.cookies["auth-token"]?.value;
|
|
110
|
-
console.log("jwtToken:", jwtToken);
|
|
111
|
-
// console.log(Object.keys(request.cookies));
|
|
100
|
+
const jwtToken = request.cookies["auth-token"] && request.cookies["auth-token"].value;
|
|
112
101
|
|
|
113
|
-
// If no JWT token, then generate HTTP redirect 401 response.
|
|
114
102
|
if (!jwtToken) {
|
|
115
103
|
log("Error: No JWT in the cookies");
|
|
116
104
|
return redirectResponse;
|
|
@@ -122,8 +110,6 @@ export const handler = async (event: AWSCloudFrontFunction.Event, context: AWSCl
|
|
|
122
110
|
return redirectResponse;
|
|
123
111
|
}
|
|
124
112
|
|
|
125
|
-
// //Remove the JWT from the query string if valid and return.
|
|
126
|
-
// delete request.querystring.jwt;
|
|
127
113
|
log("Valid JWT token");
|
|
128
114
|
return request;
|
|
129
115
|
}
|
|
@@ -139,8 +125,24 @@ async function getSecret() {
|
|
|
139
125
|
}
|
|
140
126
|
}
|
|
141
127
|
|
|
142
|
-
const log: typeof console.log = (
|
|
143
|
-
if (loggingEnabled)
|
|
144
|
-
|
|
128
|
+
const log: typeof console.log = function () {
|
|
129
|
+
if (!loggingEnabled) return;
|
|
130
|
+
|
|
131
|
+
// CloudFront Function runtime only prints first argument passed to console.log so add other args to the first one if given.
|
|
132
|
+
// eslint-disable-next-line prefer-rest-params -- We can't use spread or rest parameters in CloudFront Functions
|
|
133
|
+
let message = arguments[0]
|
|
134
|
+
if (arguments.length > 1) {
|
|
135
|
+
const otherArgs = [];
|
|
136
|
+
for (let i = 1; i < arguments.length; i++) {
|
|
137
|
+
// eslint-disable-next-line prefer-rest-params
|
|
138
|
+
otherArgs[i-1] = arguments[i];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
message += " - additional args: " + JSON.stringify(otherArgs);
|
|
145
142
|
}
|
|
143
|
+
console.log(message);
|
|
146
144
|
}
|
|
145
|
+
|
|
146
|
+
// This serves no purpose other than to make TypeScript and eslint happy by showing that that handler is used. We can't
|
|
147
|
+
// export handler as an alterative because CloudFront Functions don't support exports.
|
|
148
|
+
handler;
|
|
@@ -30,34 +30,31 @@ export const handler = addRequiredContext(
|
|
|
30
30
|
issuer: await Issuer.discover(oidcIssuerConfigUrl.href),
|
|
31
31
|
clientID: oidcClientId,
|
|
32
32
|
scope: oidcScope,
|
|
33
|
-
onSuccess: async (tokenset) => {
|
|
34
|
-
console.log("
|
|
35
|
-
|
|
36
|
-
// console.log("Config.jwtSecret:", jwtSecret);
|
|
37
|
-
|
|
33
|
+
onSuccess: async (tokenset, client) => {
|
|
34
|
+
console.log("🟢", client);
|
|
38
35
|
// Payload to include in the token
|
|
39
36
|
const payload = {
|
|
40
37
|
userID: tokenset.claims().sub,
|
|
41
38
|
};
|
|
42
|
-
|
|
43
|
-
// Options (optional)
|
|
44
|
-
const options = {
|
|
45
|
-
algorithm: "HS256",
|
|
46
|
-
expiresIn: "1h",
|
|
47
|
-
} as const;
|
|
39
|
+
const expiresInMs = 1000 * 60 * 60;
|
|
48
40
|
|
|
49
41
|
// Create the token
|
|
50
|
-
const token = jwt.sign(
|
|
51
|
-
|
|
52
|
-
|
|
42
|
+
const token = jwt.sign(
|
|
43
|
+
payload,
|
|
44
|
+
jwtSecret,
|
|
45
|
+
{
|
|
46
|
+
algorithm: "HS256",
|
|
47
|
+
expiresIn: expiresInMs / 1000,
|
|
48
|
+
}
|
|
53
49
|
);
|
|
50
|
+
const expiryDate = new Date(Date.now() + expiresInMs);
|
|
54
51
|
return {
|
|
55
52
|
statusCode: 302,
|
|
56
53
|
headers: {
|
|
57
54
|
location: "/",
|
|
58
55
|
},
|
|
59
56
|
cookies: [
|
|
60
|
-
`auth-token=${token}; HttpOnly; SameSite=None; Secure; Path=/; Expires=${
|
|
57
|
+
`auth-token=${token}; HttpOnly; SameSite=None; Secure; Path=/; Expires=${expiryDate}`,
|
|
61
58
|
],
|
|
62
59
|
};
|
|
63
60
|
},
|
|
@@ -195,7 +195,7 @@ export class CloudWatchOidcAuth extends Construct {
|
|
|
195
195
|
|
|
196
196
|
const edgeFuncAuthCheck = new CloudFront.Function(
|
|
197
197
|
scope,
|
|
198
|
-
`${this.id}
|
|
198
|
+
`${this.id}AuthCheckFunction`,
|
|
199
199
|
{
|
|
200
200
|
code: CloudFront.FunctionCode.fromInline(
|
|
201
201
|
fs.readFileSync(path.join(import.meta.dirname, "auth-check.js"), "utf8").replace("__placeholder-for-jwt-secret-key__", key),
|