@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.
@@ -1,10 +1,2 @@
1
- /// <reference types="node" resolution-mode="require"/>
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":";AA8FA,eAAO,MAAM,OAAO,UAAiB,2BAA2B,WAAW,sBAAsB,OAAO;;;;;;;kCAkCvG,CAAA"}
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
- // set to true to enable console logging
13
- const loggingEnabled = true;
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
- export const handler = async (event, context) => {
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
- return redirectResponse;
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
- // console.log(request);
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 = (...args) => {
120
- if (loggingEnabled) {
121
- console.log(...args);
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,4CAyCnB,CAAC"}
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("tokenset", tokenset, tokenset.claims());
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
- // Options (optional)
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, options);
41
- const expires = new Date(Date.now() + 1000 * 60 * 60 * 24 * 7);
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=${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}EdgeFunctionAuthCheck`, {
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.5",
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
- // set to true to enable console logging
16
- const loggingEnabled = true;
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
- export const handler = async (event: AWSCloudFrontFunction.Event, context: AWSCloudFrontFunction.Context) => {
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
- return redirectResponse;
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
- // console.log(request);
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 = (...args) => {
143
- if (loggingEnabled) {
144
- console.log(...args);
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("tokenset", tokenset, tokenset.claims());
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(payload, jwtSecret, options);
51
- const expires = new Date(
52
- Date.now() + 1000 * 60 * 60 * 24 * 7,
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=${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}EdgeFunctionAuthCheck`,
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),