lambda-essentials-ts 4.1.5 → 5.1.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/CHANGELOG.md CHANGED
@@ -4,6 +4,30 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
6
 
7
+ ## [5.1.1] - 2022-09-05
8
+
9
+ ### Changed
10
+
11
+ HttpApi payload version 2.0 events supported for openApiWrapper.
12
+
13
+ ## [5.1.0] - 2022-09-05
14
+
15
+ ### Changed
16
+
17
+ Dependencies aren't pinned to a fixed version to allow users of the library to independently upgrade minor (devDependencies) and patch (dependencies) versions. This will simplify fixing security alerts faster than in this library, for example by applying `npm audit fix`.
18
+
19
+ ## [5.0.0] - 2022-02-22
20
+
21
+ ### Changed
22
+
23
+ - **[Breaking change]** `TokenProvider` was replaced by more specific `KmsTokenProvider` class.
24
+ The functionality and interface remains the same, the imports need to be changed.
25
+
26
+ ### Added
27
+
28
+ - New `SecretsManagerTokenProvider` that relies on AWS Secrets Manager to retrieve client ID and client secret.
29
+ The advantage of using AWS Secrets Manager is that it can be supplied with a secret rotation function.
30
+
7
31
  ## [4.1.5] - 2022-02-10
8
32
 
9
33
  ### Changed
package/README.md CHANGED
@@ -88,15 +88,55 @@ let axiosClient = axios.create({ timeout: 3000 });
88
88
  new HttpClient({ client: axiosClient });
89
89
  ```
90
90
 
91
- ### TokenProvider
91
+ ### SecretsManagerTokenProvider
92
+
93
+ It uses AWS Secrets Manager to retrieve the client ID and secret and then calls the specified token endpoint the retrieve JWT.
94
+
95
+ CloudFormation to create a secret. Also allow the lambda function to access the secret by attaching `AWSSecretsManagerGetSecretValuePolicy` IAM Policy.
96
+
97
+ ```yaml
98
+ Auth0Secret:
99
+ Type: AWS::SecretsManager::Secret
100
+ Properties:
101
+ Description: 'Auth0 Client ID/Secret'
102
+ GenerateSecretString:
103
+ SecretStringTemplate: '{"Auth0ClientID": "client_id", "Auth0ClientSecret": "client_secret"}'
104
+ GenerateStringKey: 'Auth0ClientSecret'
105
+ ```
106
+
107
+ ```typescript
108
+ import {
109
+ SecretsManagerTokenProvider,
110
+ SecretsManagerTokenConfiguration,
111
+ } from 'lambda-essentials-ts';
112
+
113
+ const configuration: SecretsManagerTokenConfiguration = {
114
+ clientSecretId: 'arn:aws:secretsmanager:eu-west-1:<aws_account_id>:secret:<secret_id>',
115
+ audience: 'https://example.com/',
116
+ tokenEndpoint: 'https://example.com/oauth/token',
117
+ };
118
+
119
+ const secretsManagerClient = new aws.SecretsManager({ region: 'eu-west-1' });
120
+ const tokenProvider = new SecretsManagerTokenProvider({
121
+ secretsManagerClient: secretsManagerClient,
122
+ tokenConfiguration: configuration,
123
+ });
124
+
125
+ // recommended way to retrieve token (utilizes caching and takes care of token expiration)
126
+ const accessToken = await tokenProvider.getToken();
127
+
128
+ // or bypass caching and get a new fresh token
129
+ const freshAccessToken = await tokenProvider.getTokenWithoutCache();
130
+ ```
131
+
132
+ ### KmsTokenProvider
92
133
 
93
134
  It uses AWS KMS to decrypt the client secret and then calls the specified token endpoint the retrieve JWT.
94
135
 
95
136
  ```typescript
96
- import axios from 'axios';
97
- import { TokenProvider, TokenConfiguration } from 'lambda-essentials-ts';
137
+ import { KmsTokenProvider, KmsTokenConfiguration } from 'lambda-essentials-ts';
98
138
 
99
- const configuration: TokenConfiguration = {
139
+ const configuration: KmsTokenConfiguration = {
100
140
  clientId: 'CLIENT_ID',
101
141
  encryptedClientSecret: 'BASE64_KMS_ENCRYPTED_CLIENT_SECRET',
102
142
  audience: 'https://example.com/',
@@ -104,17 +144,16 @@ const configuration: TokenConfiguration = {
104
144
  };
105
145
 
106
146
  const kmsClient = new aws.KMS({ region: 'eu-west-1' });
107
- const tokenProvider = new TokenProvider({
108
- httpClient: axios.create(),
147
+ const tokenProvider = new KmsTokenProvider({
109
148
  kmsClient: kmsClient,
110
149
  tokenConfiguration: configuration,
111
150
  });
112
151
 
113
152
  // recommended way to retrieve token (utilizes caching and takes care of token expiration)
114
- let accessToken = await tokenProvider.getToken();
153
+ const accessToken = await tokenProvider.getToken();
115
154
 
116
- // or bypass caching and get new token
117
- accessToken = await tokenProvider.getTokenWithoutCache();
155
+ // or bypass caching and get a new fresh token
156
+ const freshAccessToken = await tokenProvider.getTokenWithoutCache();
118
157
  ```
119
158
 
120
159
  ### Exceptions
@@ -6,7 +6,7 @@ class Exception extends Error {
6
6
  constructor(message, statusCode, details) {
7
7
  super(message);
8
8
  this.statusCode = statusCode;
9
- this.details = details ? util_1.serializeObject(details) : details;
9
+ this.details = details ? (0, util_1.serializeObject)(details) : details;
10
10
  }
11
11
  }
12
12
  exports.Exception = Exception;
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -14,7 +18,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
14
18
  var __importStar = (this && this.__importStar) || function (mod) {
15
19
  if (mod && mod.__esModule) return mod;
16
20
  var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
22
  __setModuleDefault(result, mod);
19
23
  return result;
20
24
  };
@@ -47,6 +51,7 @@ class HttpClient {
47
51
  /**
48
52
  * Create a new Instance of the HttpClient
49
53
  */
54
+ // eslint-disable-next-line complexity
50
55
  constructor(options) {
51
56
  var _a, _b, _c, _d, _e;
52
57
  // eslint-disable-next-line no-console
@@ -56,26 +61,27 @@ class HttpClient {
56
61
  this.correlationIdResolverFunction = options === null || options === void 0 ? void 0 : options.correlationIdResolver;
57
62
  this.enableCache = (_c = options === null || options === void 0 ? void 0 : options.enableCache) !== null && _c !== void 0 ? _c : false;
58
63
  this.enableRetry = (_d = options === null || options === void 0 ? void 0 : options.enableRetry) !== null && _d !== void 0 ? _d : false;
59
- this.client = (_e = options === null || options === void 0 ? void 0 : options.client) !== null && _e !== void 0 ? _e : axios_1.default.create({
60
- adapter: (() => {
61
- let adapters = axios_1.default.defaults.adapter;
62
- if (this.enableCache) {
63
- const cache = axios_cache_adapter_1.setupCache({
64
- maxAge: 5 * 60 * 1000,
65
- readHeaders: false,
66
- readOnError: true,
67
- exclude: {
68
- query: false,
69
- },
70
- ...options === null || options === void 0 ? void 0 : options.cacheOptions,
71
- key: (req) => HttpClient.generateCacheKey(req),
72
- });
73
- // debounce concurrent calls with the same cacheKey so that only one HTTP request is made
74
- adapters = deduplicateRequestAdapter_1.createDebounceRequestAdapter(cache.adapter, HttpClient.generateCacheKey);
75
- }
76
- return adapters;
77
- })(),
78
- });
64
+ this.client =
65
+ (_e = options === null || options === void 0 ? void 0 : options.client) !== null && _e !== void 0 ? _e : axios_1.default.create({
66
+ adapter: (() => {
67
+ let adapters = axios_1.default.defaults.adapter;
68
+ if (this.enableCache) {
69
+ const cache = (0, axios_cache_adapter_1.setupCache)({
70
+ maxAge: 5 * 60 * 1000,
71
+ readHeaders: false,
72
+ readOnError: true,
73
+ exclude: {
74
+ query: false, // also cache requests with query parameters
75
+ },
76
+ ...options === null || options === void 0 ? void 0 : options.cacheOptions,
77
+ key: (req) => HttpClient.generateCacheKey(req),
78
+ });
79
+ // debounce concurrent calls with the same cacheKey so that only one HTTP request is made
80
+ adapters = (0, deduplicateRequestAdapter_1.createDebounceRequestAdapter)(cache.adapter, HttpClient.generateCacheKey);
81
+ }
82
+ return adapters;
83
+ })(),
84
+ });
79
85
  if (this.enableRetry) {
80
86
  this.client.defaults.raxConfig = {
81
87
  ...options === null || options === void 0 ? void 0 : options.retryOptions,
@@ -88,7 +94,7 @@ class HttpClient {
88
94
  level: 'INFO',
89
95
  retryAttempt: (_b = (_a = err.config) === null || _a === void 0 ? void 0 : _a.raxConfig) === null || _b === void 0 ? void 0 : _b.currentRetryAttempt,
90
96
  ...HttpClient.extractRequestLogData(err.config),
91
- error: util_1.serializeAxiosError(err),
97
+ error: (0, util_1.serializeAxiosError)(err),
92
98
  });
93
99
  },
94
100
  };
@@ -109,7 +115,7 @@ class HttpClient {
109
115
  return config;
110
116
  }, (error) => {
111
117
  var _a;
112
- const serializedAxiosError = util_1.serializeAxiosError(error);
118
+ const serializedAxiosError = (0, util_1.serializeAxiosError)(error);
113
119
  this.logFunction({
114
120
  title: 'HTTP Request Error',
115
121
  level: 'WARN',
@@ -136,7 +142,7 @@ class HttpClient {
136
142
  if (error instanceof clientException_1.ClientException) {
137
143
  throw error;
138
144
  }
139
- const serializedAxiosError = util_1.serializeAxiosError(error);
145
+ const serializedAxiosError = (0, util_1.serializeAxiosError)(error);
140
146
  if (error.message === invalidToken) {
141
147
  this.logFunction({
142
148
  title: 'HTTP call skipped due to a token error',
@@ -166,18 +172,20 @@ class HttpClient {
166
172
  method: requestConfig.method,
167
173
  url: requestConfig.url,
168
174
  query: requestConfig.params,
169
- request: util_1.safeJsonParse(requestConfig.data, requestConfig.data),
175
+ request: (0, util_1.safeJsonParse)(requestConfig.data, requestConfig.data),
170
176
  correlationId: (_a = requestConfig.headers) === null || _a === void 0 ? void 0 : _a[shared_1.orionCorrelationIdRoot],
171
177
  };
172
178
  }
173
179
  // implemented based on https://github.com/RasCarlito/axios-cache-adapter/blob/master/src/cache.js#L77
174
180
  static generateCacheKey(req) {
175
181
  var _a, _b;
176
- const prefix = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a.Authorization) ? (_b = util_1.safeJwtCanonicalIdParse(req.headers.Authorization.replace('Bearer ', ''))) !== null && _b !== void 0 ? _b : uuid.v4() : 'shared';
182
+ const prefix = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a.Authorization)
183
+ ? (_b = (0, util_1.safeJwtCanonicalIdParse)(req.headers.Authorization.replace('Bearer ', ''))) !== null && _b !== void 0 ? _b : uuid.v4()
184
+ : 'shared';
177
185
  const url = `${req.baseURL ? req.baseURL : ''}${req.url}`;
178
186
  const query = req.params ? JSON.stringify(req.params) : ''; // possible improvement: optimize cache-hit ratio by sorting the query params
179
187
  const key = `${prefix}/${url}${query}`;
180
- return `${key}${req.data ? md5_1.default(req.data) : ''}`;
188
+ return `${key}${req.data ? (0, md5_1.default)(req.data) : ''}`;
181
189
  }
182
190
  /**
183
191
  * Resolves the token with the token provider and adds it to the headers
package/lib/index.d.ts CHANGED
@@ -3,7 +3,9 @@ import Logger, { LoggerConfiguration, SuggestedLogObject } from './logger/logger
3
3
  import OpenApiWrapper from './openApi/openApiWrapper';
4
4
  import { ApiResponse } from './openApi/apiResponseModel';
5
5
  import { ApiRequest, GetRequest, PostRequest, PutRequest } from './openApi/apiRequestModel';
6
- import TokenProvider, { TokenConfiguration, TokenProviderOptions, TokenProviderHttpClient } from './tokenProvider/tokenProvider';
6
+ import { TokenProviderHttpClient } from './tokenProvider/tokenProvider';
7
+ import KmsTokenProvider, { KmsTokenProviderOptions, KmsTokenConfiguration } from './tokenProvider/kmsTokenProvider';
8
+ import SecretsManagerTokenProvider, { SecretsManagerTokenProviderOptions, SecretsManagerTokenConfiguration } from './tokenProvider/secretsManagerTokenProvider';
7
9
  import { ClientException } from './exceptions/clientException';
8
10
  import { ConflictException } from './exceptions/conflictException';
9
11
  import { Exception } from './exceptions/exception';
@@ -13,5 +15,5 @@ import { InvalidDataException } from './exceptions/invalidDataException';
13
15
  import { NotFoundException } from './exceptions/notFoundException';
14
16
  import { ValidationException } from './exceptions/validationException';
15
17
  import { serializeObject, serializeAxiosError } from './util';
16
- export { Logger, OpenApiWrapper, ApiResponse, HttpClient, HttpLogType, TokenProvider, ValidationException, NotFoundException, InvalidDataException, InternalException, ForbiddenException, Exception, ConflictException, ClientException, serializeObject, serializeAxiosError, };
17
- export type { LoggerConfiguration, SuggestedLogObject, HttpClientOptions, HttpLogOptions, ApiRequest, GetRequest, PostRequest, PutRequest, TokenConfiguration, TokenProviderOptions, TokenProviderHttpClient, };
18
+ export { Logger, OpenApiWrapper, ApiResponse, HttpClient, HttpLogType, KmsTokenProvider, SecretsManagerTokenProvider, ValidationException, NotFoundException, InvalidDataException, InternalException, ForbiddenException, Exception, ConflictException, ClientException, serializeObject, serializeAxiosError, };
19
+ export type { LoggerConfiguration, SuggestedLogObject, HttpClientOptions, HttpLogOptions, ApiRequest, GetRequest, PostRequest, PutRequest, TokenProviderHttpClient, KmsTokenProviderOptions, KmsTokenConfiguration, SecretsManagerTokenProviderOptions, SecretsManagerTokenConfiguration, };
package/lib/index.js CHANGED
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -14,7 +18,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
14
18
  var __importStar = (this && this.__importStar) || function (mod) {
15
19
  if (mod && mod.__esModule) return mod;
16
20
  var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
22
  __setModuleDefault(result, mod);
19
23
  return result;
20
24
  };
@@ -22,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
22
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
23
27
  };
24
28
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.serializeAxiosError = exports.serializeObject = exports.ClientException = exports.ConflictException = exports.Exception = exports.ForbiddenException = exports.InternalException = exports.InvalidDataException = exports.NotFoundException = exports.ValidationException = exports.TokenProvider = exports.HttpLogType = exports.HttpClient = exports.ApiResponse = exports.OpenApiWrapper = exports.Logger = void 0;
29
+ exports.serializeAxiosError = exports.serializeObject = exports.ClientException = exports.ConflictException = exports.Exception = exports.ForbiddenException = exports.InternalException = exports.InvalidDataException = exports.NotFoundException = exports.ValidationException = exports.SecretsManagerTokenProvider = exports.KmsTokenProvider = exports.HttpLogType = exports.HttpClient = exports.ApiResponse = exports.OpenApiWrapper = exports.Logger = void 0;
26
30
  const httpClient_1 = __importStar(require("./httpClient/httpClient"));
27
31
  exports.HttpClient = httpClient_1.default;
28
32
  Object.defineProperty(exports, "HttpLogType", { enumerable: true, get: function () { return httpClient_1.HttpLogType; } });
@@ -32,8 +36,10 @@ const openApiWrapper_1 = __importDefault(require("./openApi/openApiWrapper"));
32
36
  exports.OpenApiWrapper = openApiWrapper_1.default;
33
37
  const apiResponseModel_1 = require("./openApi/apiResponseModel");
34
38
  Object.defineProperty(exports, "ApiResponse", { enumerable: true, get: function () { return apiResponseModel_1.ApiResponse; } });
35
- const tokenProvider_1 = __importDefault(require("./tokenProvider/tokenProvider"));
36
- exports.TokenProvider = tokenProvider_1.default;
39
+ const kmsTokenProvider_1 = __importDefault(require("./tokenProvider/kmsTokenProvider"));
40
+ exports.KmsTokenProvider = kmsTokenProvider_1.default;
41
+ const secretsManagerTokenProvider_1 = __importDefault(require("./tokenProvider/secretsManagerTokenProvider"));
42
+ exports.SecretsManagerTokenProvider = secretsManagerTokenProvider_1.default;
37
43
  const clientException_1 = require("./exceptions/clientException");
38
44
  Object.defineProperty(exports, "ClientException", { enumerable: true, get: function () { return clientException_1.ClientException; } });
39
45
  const conflictException_1 = require("./exceptions/conflictException");
@@ -2,7 +2,11 @@
2
2
  /* eslint-disable no-console */
3
3
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
4
  if (k2 === undefined) k2 = k;
5
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
6
10
  }) : (function(o, m, k, k2) {
7
11
  if (k2 === undefined) k2 = k;
8
12
  o[k2] = m[k];
@@ -15,7 +19,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
19
  var __importStar = (this && this.__importStar) || function (mod) {
16
20
  if (mod && mod.__esModule) return mod;
17
21
  var result = {};
18
- if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
19
23
  __setModuleDefault(result, mod);
20
24
  return result;
21
25
  };
@@ -70,8 +74,8 @@ class Logger {
70
74
  const truncateToken = (innerPayload) => {
71
75
  return innerPayload.replace(/(eyJ[a-zA-Z0-9_-]{5,}\.eyJ[a-zA-Z0-9_-]{5,})\.[a-zA-Z0-9_-]*/gi, (m, p1) => `${p1}.<sig>`);
72
76
  };
73
- const replacer = (key, value) => (is_error_1.default(value) ? Logger.errorToObject(value) : value);
74
- let stringifiedPayload = truncateToken(fast_safe_stringify_1.default(payload, replacer, this.jsonSpace));
77
+ const replacer = (key, value) => ((0, is_error_1.default)(value) ? Logger.errorToObject(value) : value);
78
+ let stringifiedPayload = truncateToken((0, fast_safe_stringify_1.default)(payload, replacer, this.jsonSpace));
75
79
  // https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/cloudwatch_limits_cwl.html 256KB => 32768 characters
76
80
  if (stringifiedPayload.length >= 32768) {
77
81
  const replacementPayload = {
@@ -82,7 +86,7 @@ class Logger {
82
86
  truncatedPayload: stringifiedPayload.substring(0, 10000),
83
87
  },
84
88
  };
85
- stringifiedPayload = fast_safe_stringify_1.default(replacementPayload, replacer, this.jsonSpace);
89
+ stringifiedPayload = (0, fast_safe_stringify_1.default)(replacementPayload, replacer, this.jsonSpace);
86
90
  }
87
91
  this.logFunction(stringifiedPayload);
88
92
  }
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
5
9
  }) : (function(o, m, k, k2) {
6
10
  if (k2 === undefined) k2 = k;
7
11
  o[k2] = m[k];
@@ -14,7 +18,7 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
14
18
  var __importStar = (this && this.__importStar) || function (mod) {
15
19
  if (mod && mod.__esModule) return mod;
16
20
  var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
22
  __setModuleDefault(result, mod);
19
23
  return result;
20
24
  };
@@ -37,14 +41,17 @@ class OpenApiWrapper {
37
41
  this.userPrincipal = this.notSet;
38
42
  this.requestId = this.notSet;
39
43
  this.correlationId = this.notSet;
44
+ // @ts-ignore Later Use the options Type from OpenApiFactory
40
45
  this.api = new openapi_factory_1.default({
41
46
  requestMiddleware: (request) => {
42
47
  var _a, _b, _c, _d, _e, _f;
43
48
  const correlationId = this.generateCorrelationId(request.headers);
44
49
  requestLogger.startInvocation(null, correlationId);
45
50
  // TODO: restrict the alternative way of resolving token and principal only for localhost
46
- this.userToken = (_b = (_a = request.requestContext.authorizer) === null || _a === void 0 ? void 0 : _a.jwt) !== null && _b !== void 0 ? _b : (_c = request.headers.Authorization) === null || _c === void 0 ? void 0 : _c.split(' ')[1];
47
- this.userPrincipal = (_f = (_e = (_d = request.requestContext.authorizer) === null || _d === void 0 ? void 0 : _d.canonicalId) !== null && _e !== void 0 ? _e : util_1.safeJwtCanonicalIdParse(this.userToken)) !== null && _f !== void 0 ? _f : 'unknown';
51
+ this.userToken =
52
+ (_b = (_a = request.requestContext.authorizer) === null || _a === void 0 ? void 0 : _a.jwt) !== null && _b !== void 0 ? _b : (_c = request.headers.Authorization) === null || _c === void 0 ? void 0 : _c.split(' ')[1];
53
+ this.userPrincipal =
54
+ (_f = (_e = (_d = request.requestContext.authorizer) === null || _d === void 0 ? void 0 : _d.canonicalId) !== null && _e !== void 0 ? _e : (0, util_1.safeJwtCanonicalIdParse)(this.userToken)) !== null && _f !== void 0 ? _f : 'unknown';
48
55
  this.requestId = request.requestContext.requestId;
49
56
  requestLogger.log({
50
57
  title: 'RequestLogger',
@@ -71,7 +78,7 @@ class OpenApiWrapper {
71
78
  errorMiddleware: (request, error) => {
72
79
  const { correlationId } = this;
73
80
  this.clearContext();
74
- const serializedError = util_1.serializeObject(error);
81
+ const serializedError = (0, util_1.serializeObject)(error);
75
82
  if (error instanceof exception_1.Exception) {
76
83
  if (error.statusCode === 500) {
77
84
  requestLogger.log({ title: 'ErrorLogger', level: 'ERROR', ...serializedError });
@@ -0,0 +1,28 @@
1
+ import { KMS } from 'aws-sdk';
2
+ import TokenProvider, { Auth0Secret, TokenConfiguration, TokenProviderOptions } from './tokenProvider';
3
+ export default class KmsTokenProvider extends TokenProvider {
4
+ private kmsClient;
5
+ private kmsConfiguration;
6
+ constructor(options: KmsTokenProviderOptions);
7
+ getClientSecret(): Promise<Auth0Secret | undefined>;
8
+ }
9
+ export interface KmsTokenProviderOptions extends TokenProviderOptions {
10
+ /**
11
+ * AWS KMS Client
12
+ */
13
+ kmsClient: KMS;
14
+ /**
15
+ * Configuration needed for the token
16
+ */
17
+ tokenConfiguration: KmsTokenConfiguration;
18
+ }
19
+ export interface KmsTokenConfiguration extends TokenConfiguration {
20
+ /**
21
+ * Username or ClientId
22
+ */
23
+ clientId: string;
24
+ /**
25
+ * KMS Encrypted client secret
26
+ */
27
+ encryptedClientSecret: string;
28
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const tokenProvider_1 = __importDefault(require("./tokenProvider"));
7
+ class KmsTokenProvider extends tokenProvider_1.default {
8
+ constructor(options) {
9
+ super(options);
10
+ this.kmsClient = options.kmsClient;
11
+ this.kmsConfiguration = options.tokenConfiguration;
12
+ }
13
+ async getClientSecret() {
14
+ const secret = await this.kmsClient
15
+ .decrypt({
16
+ CiphertextBlob: Buffer.from(this.kmsConfiguration.encryptedClientSecret, 'base64'),
17
+ })
18
+ .promise()
19
+ .then((data) => { var _a; return (_a = data.Plaintext) === null || _a === void 0 ? void 0 : _a.toString(); });
20
+ if (!secret) {
21
+ throw new Error('Request error: failed to decrypt secret using KMS');
22
+ }
23
+ return { Auth0ClientSecret: secret, Auth0ClientID: this.kmsConfiguration.clientId };
24
+ }
25
+ }
26
+ exports.default = KmsTokenProvider;
@@ -0,0 +1,29 @@
1
+ import { SecretsManager } from 'aws-sdk';
2
+ import TokenProvider, { Auth0Secret, TokenConfiguration, TokenProviderOptions } from './tokenProvider';
3
+ export default class SecretsManagerTokenProvider extends TokenProvider {
4
+ private secretsManagerClient;
5
+ private secretsManagerConfiguration;
6
+ constructor(options: SecretsManagerTokenProviderOptions);
7
+ getClientSecret(): Promise<Auth0Secret | undefined>;
8
+ }
9
+ export interface SecretsManagerTokenProviderOptions extends TokenProviderOptions {
10
+ /**
11
+ * AWS Secrets Manager Client
12
+ */
13
+ secretsManagerClient: SecretsManager;
14
+ /**
15
+ * Configuration needed for the token
16
+ */
17
+ tokenConfiguration: SecretsManagerTokenConfiguration;
18
+ }
19
+ export interface SecretsManagerTokenConfiguration extends TokenConfiguration {
20
+ /**
21
+ * The ID of a secret stored in AWS Secrets Manager.
22
+ * The expected secret format is:
23
+ * {
24
+ * Auth0ClientID: "string",
25
+ * Auth0ClientSecret: "string"
26
+ * }
27
+ */
28
+ clientSecretId: string;
29
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const tokenProvider_1 = __importDefault(require("./tokenProvider"));
7
+ class SecretsManagerTokenProvider extends tokenProvider_1.default {
8
+ constructor(options) {
9
+ super(options);
10
+ this.secretsManagerClient = options.secretsManagerClient;
11
+ this.secretsManagerConfiguration = options.tokenConfiguration;
12
+ }
13
+ async getClientSecret() {
14
+ const secret = await this.secretsManagerClient
15
+ .getSecretValue({ SecretId: this.secretsManagerConfiguration.clientSecretId })
16
+ .promise();
17
+ if (!(secret === null || secret === void 0 ? void 0 : secret.SecretString)) {
18
+ throw new Error('Request error: failed to retrieve secret from Secrets Manager');
19
+ }
20
+ return JSON.parse(secret.SecretString);
21
+ }
22
+ }
23
+ exports.default = SecretsManagerTokenProvider;
@@ -1,14 +1,12 @@
1
- import { KMS } from 'aws-sdk';
2
1
  import { AxiosRequestConfig, AxiosResponse } from 'axios';
3
- export default class TokenProvider {
2
+ export default abstract class TokenProvider {
4
3
  private httpClient;
5
- private kmsClient;
6
4
  private configuration;
7
5
  private currentTokenPromise?;
8
6
  /**
9
7
  * Create a new Instance of the TokenProvider
10
8
  */
11
- constructor(options: TokenProviderOptions);
9
+ protected constructor(options: TokenProviderOptions);
12
10
  /**
13
11
  * Get access token. Subsequent calls are cached and the token is renewed only if it is expired.
14
12
  */
@@ -17,30 +15,23 @@ export default class TokenProvider {
17
15
  * Get access token.
18
16
  */
19
17
  getTokenWithoutCache(): Promise<string>;
18
+ abstract getClientSecret(): Promise<Auth0Secret | undefined>;
19
+ }
20
+ export interface Auth0Secret {
21
+ Auth0ClientID: string;
22
+ Auth0ClientSecret: string;
20
23
  }
21
24
  export interface TokenProviderOptions {
22
25
  /**
23
26
  * Either an Axios instance or an @atsquad/httpclient instance, by default it creates an axios instance
24
27
  */
25
28
  httpClient?: TokenProviderHttpClient;
26
- /**
27
- * AWS KMS Client
28
- */
29
- kmsClient: KMS;
30
29
  /**
31
30
  * Configuration needed for the token
32
31
  */
33
32
  tokenConfiguration: TokenConfiguration;
34
33
  }
35
34
  export interface TokenConfiguration {
36
- /**
37
- * Username or ClientId
38
- */
39
- clientId: string;
40
- /**
41
- * KMS Encrypted client secret
42
- */
43
- encryptedClientSecret: string;
44
35
  audience: string;
45
36
  tokenEndpoint: string;
46
37
  }
@@ -12,24 +12,7 @@ class TokenProvider {
12
12
  constructor(options) {
13
13
  var _a;
14
14
  this.httpClient = (_a = options.httpClient) !== null && _a !== void 0 ? _a : axios_1.default.create();
15
- if (!options.kmsClient) {
16
- throw new Error('KMS Client is missing');
17
- }
18
- this.kmsClient = options.kmsClient;
19
- const configuration = options.tokenConfiguration;
20
- if (!(configuration === null || configuration === void 0 ? void 0 : configuration.clientId)) {
21
- throw new Error('Configuration error: missing required property "clientId"');
22
- }
23
- if (!(configuration === null || configuration === void 0 ? void 0 : configuration.encryptedClientSecret)) {
24
- throw new Error('Configuration error: missing required property "encryptedClientSecret"');
25
- }
26
- if (!(configuration === null || configuration === void 0 ? void 0 : configuration.audience)) {
27
- throw new Error('Configuration error: missing required property "audience"');
28
- }
29
- if (!(configuration === null || configuration === void 0 ? void 0 : configuration.tokenEndpoint)) {
30
- throw new Error('Configuration error: missing required property "tokenEndpoint"');
31
- }
32
- this.configuration = configuration;
15
+ this.configuration = options.tokenConfiguration;
33
16
  }
34
17
  /**
35
18
  * Get access token. Subsequent calls are cached and the token is renewed only if it is expired.
@@ -43,10 +26,10 @@ class TokenProvider {
43
26
  try {
44
27
  const jwtToken = await this.currentTokenPromise;
45
28
  // lower the token expiry time by 10s so that the returned token will be not immediately expired
46
- if (!jwtToken || ((_a = jsonwebtoken_1.default.decode(jwtToken)) === null || _a === void 0 ? void 0 : _a['exp']) < Date.now() / 1000 - 10) {
29
+ if (!jwtToken || ((_a = jsonwebtoken_1.default.decode(jwtToken)) === null || _a === void 0 ? void 0 : _a.exp) < Date.now() / 1000 - 10) {
47
30
  this.currentTokenPromise = this.getTokenWithoutCache();
48
31
  }
49
- return this.currentTokenPromise;
32
+ return await this.currentTokenPromise;
50
33
  }
51
34
  catch (error) {
52
35
  this.currentTokenPromise = this.getTokenWithoutCache();
@@ -57,14 +40,14 @@ class TokenProvider {
57
40
  * Get access token.
58
41
  */
59
42
  async getTokenWithoutCache() {
60
- const secret = await this.kmsClient
61
- .decrypt({ CiphertextBlob: Buffer.from(this.configuration.encryptedClientSecret, 'base64') })
62
- .promise()
63
- .then((data) => { var _a; return (_a = data.Plaintext) === null || _a === void 0 ? void 0 : _a.toString(); });
43
+ const secret = await this.getClientSecret();
44
+ if (!(secret === null || secret === void 0 ? void 0 : secret.Auth0ClientID) || !secret.Auth0ClientSecret) {
45
+ throw new Error('Request error: failed to retrieve Auth0 Client ID/Secret');
46
+ }
64
47
  const headers = { 'Content-Type': 'application/json' };
65
48
  const body = {
66
- client_id: this.configuration.clientId,
67
- client_secret: secret,
49
+ client_id: secret.Auth0ClientID,
50
+ client_secret: secret.Auth0ClientSecret,
68
51
  audience: this.configuration.audience,
69
52
  grant_type: 'client_credentials',
70
53
  };
package/lib/util.js CHANGED
@@ -43,7 +43,7 @@ function serializeAxiosError(error) {
43
43
  const { status, data } = error.response;
44
44
  return {
45
45
  status: (_a = data.originalStatusCode) !== null && _a !== void 0 ? _a : status,
46
- details: data.details ? data.details : data,
46
+ details: data.details ? data.details : data, // Prevent wrapping of Exception
47
47
  };
48
48
  }
49
49
  exports.serializeAxiosError = serializeAxiosError;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lambda-essentials-ts",
3
- "version": "4.1.5",
3
+ "version": "5.1.1",
4
4
  "description": "A selection of the finest modules supporting authorization, API routing, error handling, logging and sending HTTP requests.",
5
5
  "main": "lib/index.js",
6
6
  "private": false,
@@ -26,28 +26,28 @@
26
26
  },
27
27
  "homepage": "https://github.com/Cimpress-MCP/lambda-essentials-ts#readme",
28
28
  "dependencies": {
29
- "aws-sdk": "2.939.0",
30
- "axios": "0.21.1",
31
- "axios-cache-adapter": "2.7.3",
32
- "fast-safe-stringify": "2.0.7",
33
- "is-error": "2.2.2",
34
- "jsonwebtoken": "8.5.1",
35
- "md5": "2.3.0",
36
- "openapi-factory": "5.2.17",
37
- "retry-axios": "2.6.0",
38
- "uuid": "8.3.2"
29
+ "aws-sdk": "^2.1078.0",
30
+ "axios": "~0.21.3",
31
+ "axios-cache-adapter": "~2.7.3",
32
+ "fast-safe-stringify": "~2.0.7",
33
+ "is-error": "~2.2.2",
34
+ "jsonwebtoken": "~8.5.1",
35
+ "md5": "~2.3.0",
36
+ "openapi-factory": "5.2.38",
37
+ "retry-axios": "~2.6.0",
38
+ "uuid": "~8.3.2"
39
39
  },
40
40
  "devDependencies": {
41
- "@types/jest": "26.0.20",
42
- "eslint": "7.18.0",
43
- "eslint-config-cimpress-atsquad": "2.1.2",
44
- "husky": "4.2.5",
45
- "jest": "26.2.2",
46
- "lint-staged": "10.2.13",
47
- "prettier": "2.0.5",
48
- "ts-jest": "26.1.4",
49
- "ts-node": "8.10.2",
50
- "typescript": "3.9.7"
41
+ "@types/jest": "^26.0.20",
42
+ "eslint": "^7.18.0",
43
+ "eslint-config-cimpress-atsquad": "^2.1.2",
44
+ "husky": "^4.2.5",
45
+ "jest": "^26.2.2",
46
+ "lint-staged": "^10.2.13",
47
+ "prettier": "^2.7.1",
48
+ "ts-jest": "^26.1.4",
49
+ "ts-node": "^10.9.1",
50
+ "typescript": "^4.8.2"
51
51
  },
52
52
  "eslintConfig": {
53
53
  "extends": "cimpress-atsquad"