@sebspark/gcp-iam 0.3.1 → 0.4.1

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/README.md CHANGED
@@ -1,3 +1,10 @@
1
1
  # `@sebspark/gcp-iam`
2
2
 
3
3
  Google IAM utilities. This package is intended to be run in GCP contexts, such as running under a GKE service account using Workload Identity.
4
+
5
+
6
+ # Explanation
7
+
8
+ It generates the JWT that we put into the proxy-authorization header. That JWT is signed by the GCP service account and the aud part of the JWT is set to the URL we are going to call in the API Gateway.
9
+
10
+ Generating and signing the JWT uses a bunch of CPU cycles, so it makes sense to cache them for a short time.
package/dist/index.d.mts CHANGED
@@ -8,6 +8,11 @@ import { Logger } from 'winston';
8
8
  * @param logger An optional logger to use for logging.
9
9
  * @returns A JWT.
10
10
  */
11
- declare const getApiGatewayToken: (apiURL: string, logger?: Logger) => Promise<string>;
11
+ declare const getApiGatewayToken: ({ apiURL, key, ttl, logger, }: {
12
+ apiURL: string;
13
+ key?: string;
14
+ ttl?: number;
15
+ logger?: Logger;
16
+ }) => Promise<string>;
12
17
 
13
18
  export { getApiGatewayToken };
package/dist/index.d.ts CHANGED
@@ -8,6 +8,11 @@ import { Logger } from 'winston';
8
8
  * @param logger An optional logger to use for logging.
9
9
  * @returns A JWT.
10
10
  */
11
- declare const getApiGatewayToken: (apiURL: string, logger?: Logger) => Promise<string>;
11
+ declare const getApiGatewayToken: ({ apiURL, key, ttl, logger, }: {
12
+ apiURL: string;
13
+ key?: string;
14
+ ttl?: number;
15
+ logger?: Logger;
16
+ }) => Promise<string>;
12
17
 
13
18
  export { getApiGatewayToken };
package/dist/index.js CHANGED
@@ -18,11 +18,11 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
20
  // src/index.ts
21
- var src_exports = {};
22
- __export(src_exports, {
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
23
  getApiGatewayToken: () => getApiGatewayToken
24
24
  });
25
- module.exports = __toCommonJS(src_exports);
25
+ module.exports = __toCommonJS(index_exports);
26
26
 
27
27
  // src/apiGatewayToken.ts
28
28
  var import_iam_credentials = require("@google-cloud/iam-credentials");
@@ -32,17 +32,17 @@ var import_google_auth_library = require("google-auth-library");
32
32
  var LruCache = class {
33
33
  values = /* @__PURE__ */ new Map();
34
34
  maxEntries = 1e4;
35
- ttl = 1e3 * 60 * 10;
36
- // 1 minute
35
+ defaultTTL = 1e3 * 10;
36
+ // 10 seconds
37
37
  constructor(props) {
38
- this.ttl = (props == null ? void 0 : props.ttl) ?? this.ttl;
39
- this.maxEntries = (props == null ? void 0 : props.maxEntries) ?? this.maxEntries;
38
+ this.defaultTTL = props?.ttl ?? this.defaultTTL;
39
+ this.maxEntries = props?.maxEntries ?? this.maxEntries;
40
40
  }
41
41
  get(key) {
42
42
  const hasKey = this.values.has(key);
43
43
  if (hasKey) {
44
44
  const entry = this.values.get(key);
45
- if (Date.now() - entry.timestamp > this.ttl) {
45
+ if (Date.now() - entry.timestamp > entry.ttl) {
46
46
  this.values.delete(key);
47
47
  return void 0;
48
48
  }
@@ -51,22 +51,29 @@ var LruCache = class {
51
51
  return entry.data;
52
52
  }
53
53
  }
54
- put(key, value) {
54
+ put(key, value, ttl) {
55
55
  if (this.values.size >= this.maxEntries) {
56
56
  const keyToDelete = this.values.keys().next().value;
57
57
  this.values.delete(keyToDelete);
58
58
  }
59
- this.values.set(key, { data: value, timestamp: Date.now() });
59
+ this.values.set(key, {
60
+ data: value,
61
+ timestamp: Date.now(),
62
+ ttl: ttl || this.defaultTTL
63
+ });
60
64
  }
61
65
  };
62
66
 
63
67
  // src/apiGatewayToken.ts
64
68
  var expInSeconds = 60 * 60;
65
- var apiGatewayJwtCache = new LruCache({
66
- ttl: 1e3 * expInSeconds / 2
67
- });
68
- var getApiGatewayToken = async (apiURL, logger) => {
69
- const cachedJwt = apiGatewayJwtCache.get(apiURL);
69
+ var apiGatewayJwtCache = new LruCache();
70
+ var getApiGatewayToken = async ({
71
+ apiURL,
72
+ key,
73
+ ttl,
74
+ logger
75
+ }) => {
76
+ const cachedJwt = apiGatewayJwtCache.get(key || apiURL);
70
77
  if (cachedJwt) {
71
78
  return cachedJwt;
72
79
  }
@@ -78,7 +85,7 @@ var getApiGatewayToken = async (apiURL, logger) => {
78
85
  if (!serviceAccountEmail) {
79
86
  throw new Error("No service account e-mail could be found.");
80
87
  }
81
- logger == null ? void 0 : logger.info(`Serice account e-mail beeing used: ${serviceAccountEmail}`);
88
+ logger?.info(`Serice account e-mail beeing used: ${serviceAccountEmail}`);
82
89
  const header = {
83
90
  alg: "RS256",
84
91
  typ: "JWT"
@@ -106,16 +113,17 @@ var getApiGatewayToken = async (apiURL, logger) => {
106
113
  "signBlob(...) returned an empty response. Cannot sign JWT."
107
114
  );
108
115
  }
116
+ console.log("IAM KeyID", response.keyId);
109
117
  const signature = Buffer.from(response.signedBlob).toString("base64");
110
118
  const signedJWT = `${unsignedJWT}.${signature}`;
111
- apiGatewayJwtCache.put(apiURL, signedJWT);
119
+ apiGatewayJwtCache.put(key || apiURL, signedJWT, ttl);
112
120
  return signedJWT;
113
121
  } catch (error) {
114
122
  if (process.env.GCP_IAM_SOFT_FAIL === "true") {
115
- logger == null ? void 0 : logger.warn("Soft fail enabled, returning empty JWT");
123
+ logger?.warn("Soft fail enabled, returning empty JWT");
116
124
  return "";
117
125
  }
118
- logger == null ? void 0 : logger.error("Error generating system JWT", error);
126
+ logger?.error("Error generating system JWT", error);
119
127
  throw new Error(`Error generating system JWT: ${JSON.stringify(error)}`);
120
128
  }
121
129
  };
package/dist/index.mjs CHANGED
@@ -6,17 +6,17 @@ import { GoogleAuth } from "google-auth-library";
6
6
  var LruCache = class {
7
7
  values = /* @__PURE__ */ new Map();
8
8
  maxEntries = 1e4;
9
- ttl = 1e3 * 60 * 10;
10
- // 1 minute
9
+ defaultTTL = 1e3 * 10;
10
+ // 10 seconds
11
11
  constructor(props) {
12
- this.ttl = (props == null ? void 0 : props.ttl) ?? this.ttl;
13
- this.maxEntries = (props == null ? void 0 : props.maxEntries) ?? this.maxEntries;
12
+ this.defaultTTL = props?.ttl ?? this.defaultTTL;
13
+ this.maxEntries = props?.maxEntries ?? this.maxEntries;
14
14
  }
15
15
  get(key) {
16
16
  const hasKey = this.values.has(key);
17
17
  if (hasKey) {
18
18
  const entry = this.values.get(key);
19
- if (Date.now() - entry.timestamp > this.ttl) {
19
+ if (Date.now() - entry.timestamp > entry.ttl) {
20
20
  this.values.delete(key);
21
21
  return void 0;
22
22
  }
@@ -25,22 +25,29 @@ var LruCache = class {
25
25
  return entry.data;
26
26
  }
27
27
  }
28
- put(key, value) {
28
+ put(key, value, ttl) {
29
29
  if (this.values.size >= this.maxEntries) {
30
30
  const keyToDelete = this.values.keys().next().value;
31
31
  this.values.delete(keyToDelete);
32
32
  }
33
- this.values.set(key, { data: value, timestamp: Date.now() });
33
+ this.values.set(key, {
34
+ data: value,
35
+ timestamp: Date.now(),
36
+ ttl: ttl || this.defaultTTL
37
+ });
34
38
  }
35
39
  };
36
40
 
37
41
  // src/apiGatewayToken.ts
38
42
  var expInSeconds = 60 * 60;
39
- var apiGatewayJwtCache = new LruCache({
40
- ttl: 1e3 * expInSeconds / 2
41
- });
42
- var getApiGatewayToken = async (apiURL, logger) => {
43
- const cachedJwt = apiGatewayJwtCache.get(apiURL);
43
+ var apiGatewayJwtCache = new LruCache();
44
+ var getApiGatewayToken = async ({
45
+ apiURL,
46
+ key,
47
+ ttl,
48
+ logger
49
+ }) => {
50
+ const cachedJwt = apiGatewayJwtCache.get(key || apiURL);
44
51
  if (cachedJwt) {
45
52
  return cachedJwt;
46
53
  }
@@ -52,7 +59,7 @@ var getApiGatewayToken = async (apiURL, logger) => {
52
59
  if (!serviceAccountEmail) {
53
60
  throw new Error("No service account e-mail could be found.");
54
61
  }
55
- logger == null ? void 0 : logger.info(`Serice account e-mail beeing used: ${serviceAccountEmail}`);
62
+ logger?.info(`Serice account e-mail beeing used: ${serviceAccountEmail}`);
56
63
  const header = {
57
64
  alg: "RS256",
58
65
  typ: "JWT"
@@ -80,16 +87,17 @@ var getApiGatewayToken = async (apiURL, logger) => {
80
87
  "signBlob(...) returned an empty response. Cannot sign JWT."
81
88
  );
82
89
  }
90
+ console.log("IAM KeyID", response.keyId);
83
91
  const signature = Buffer.from(response.signedBlob).toString("base64");
84
92
  const signedJWT = `${unsignedJWT}.${signature}`;
85
- apiGatewayJwtCache.put(apiURL, signedJWT);
93
+ apiGatewayJwtCache.put(key || apiURL, signedJWT, ttl);
86
94
  return signedJWT;
87
95
  } catch (error) {
88
96
  if (process.env.GCP_IAM_SOFT_FAIL === "true") {
89
- logger == null ? void 0 : logger.warn("Soft fail enabled, returning empty JWT");
97
+ logger?.warn("Soft fail enabled, returning empty JWT");
90
98
  return "";
91
99
  }
92
- logger == null ? void 0 : logger.error("Error generating system JWT", error);
100
+ logger?.error("Error generating system JWT", error);
93
101
  throw new Error(`Error generating system JWT: ${JSON.stringify(error)}`);
94
102
  }
95
103
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sebspark/gcp-iam",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "license": "Apache-2.0",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -20,6 +20,6 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@google-cloud/iam-credentials": "3.3.0",
23
- "google-auth-library": "^9.15.0"
23
+ "google-auth-library": "9.15.1"
24
24
  }
25
25
  }