serverless-offline 8.3.1 → 8.6.0

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.
Files changed (31) hide show
  1. package/README.md +83 -10
  2. package/dist/ServerlessOffline.js +19 -2
  3. package/dist/config/commandOptions.js +4 -0
  4. package/dist/config/defaultOptions.js +1 -0
  5. package/dist/events/{http/authCanExecuteResource.js → authCanExecuteResource.js} +0 -0
  6. package/dist/events/{http/authFunctionNameExtractor.js → authFunctionNameExtractor.js} +1 -1
  7. package/dist/events/{http/authMatchPolicyResource.js → authMatchPolicyResource.js} +0 -0
  8. package/dist/events/http/HttpServer.js +48 -10
  9. package/dist/events/http/createAuthScheme.js +3 -2
  10. package/dist/events/http/lambda-events/LambdaProxyIntegrationEvent.js +21 -1
  11. package/dist/events/http/lambda-events/LambdaProxyIntegrationEventV2.js +28 -2
  12. package/dist/events/websocket/WebSocketClients.js +259 -26
  13. package/dist/events/websocket/WebSocketServer.js +50 -4
  14. package/dist/events/websocket/lambda-events/WebSocketAuthorizerEvent.js +99 -0
  15. package/dist/events/websocket/lambda-events/index.js +8 -0
  16. package/dist/lambda/LambdaFunction.js +3 -2
  17. package/dist/lambda/handler-runner/HandlerRunner.js +9 -1
  18. package/dist/lambda/handler-runner/child-process-runner/ChildProcessRunner.js +9 -1
  19. package/dist/lambda/handler-runner/docker-runner/DockerContainer.js +4 -1
  20. package/dist/lambda/handler-runner/go-runner/GoRunner.js +211 -0
  21. package/dist/lambda/handler-runner/go-runner/index.js +15 -0
  22. package/dist/lambda/handler-runner/in-process-runner/InProcessRunner.js +28 -7
  23. package/dist/lambda/handler-runner/java-runner/JavaRunner.js +1 -2
  24. package/dist/lambda/handler-runner/ruby-runner/RubyRunner.js +0 -2
  25. package/dist/lambda/handler-runner/worker-thread-runner/WorkerThreadRunner.js +2 -0
  26. package/dist/lambda/handler-runner/worker-thread-runner/workerThreadHelper.js +3 -2
  27. package/dist/lambda/routes/invocations/invocationsRoute.js +2 -1
  28. package/dist/utils/checkGoVersion.js +27 -0
  29. package/dist/utils/index.js +8 -0
  30. package/dist/utils/splitHandlerPathAndName.js +13 -9
  31. package/package.json +48 -19
package/README.md CHANGED
@@ -31,7 +31,7 @@ To do so, it starts an HTTP server that handles the request's lifecycle like API
31
31
 
32
32
  **Features:**
33
33
 
34
- - [Node.js](https://nodejs.org), [Python](https://www.python.org), [Ruby](https://www.ruby-lang.org) <!-- and [Go](https://golang.org) --> λ runtimes.
34
+ - [Node.js](https://nodejs.org), [Python](https://www.python.org), [Ruby](https://www.ruby-lang.org) and [Go](https://golang.org) λ runtimes.
35
35
  - Velocity templates support.
36
36
  - Lazy loading of your handler files.
37
37
  - And more: integrations, authorizers, proxies, timeouts, responseParameters, HTTPS, CORS, etc...
@@ -49,6 +49,7 @@ This plugin is updated by its users, I just do maintenance and ensure that PRs a
49
49
  - [Custom authorizers](#custom-authorizers)
50
50
  - [Remote authorizers](#remote-authorizers)
51
51
  - [JWT authorizers](#jwt-authorizers)
52
+ - [Serverless plugin authorizers](#serverless-plugin-authorizers)
52
53
  - [Custom headers](#custom-headers)
53
54
  - [Environment variables](#environment-variables)
54
55
  - [AWS API Gateway Features](#aws-api-gateway-features)
@@ -115,6 +116,7 @@ All CLI options are optional:
115
116
  --corsDisallowCredentials When provided, the default Access-Control-Allow-Credentials header value will be passed as 'false'. Default: true
116
117
  --corsExposedHeaders Used as additional Access-Control-Exposed-Headers header value for responses. Delimit multiple values with commas. Default: 'WWW-Authenticate,Server-Authorization'
117
118
  --disableCookieValidation Used to disable cookie-validation on hapi.js-server
119
+ --disableScheduledEvents Disables all scheduled events. Overrides configurations in serverless.yml.
118
120
  --dockerHost The host name of Docker. Default: localhost
119
121
  --dockerHostServicePath Defines service path which is used by SLS running inside Docker container
120
122
  --dockerNetwork The network that the Docker container will connect to
@@ -242,7 +244,7 @@ to calling it via `aws-sdk`.
242
244
 
243
245
  ## The `process.env.IS_OFFLINE` variable
244
246
 
245
- Will be `"true"` in your handlers and thorough the plugin.
247
+ Will be `"true"` in your handlers and throughout the plugin.
246
248
 
247
249
  ## Docker and Layers
248
250
 
@@ -359,14 +361,36 @@ defined in the `serverless.yml` can be used to validate the token and scopes in
359
361
  the signature of the JWT is not validated with the defined issuer. Since this is a security risk, this feature is
360
362
  only enabled with the `--ignoreJWTSignature` flag. Make sure to only set this flag for local development work.
361
363
 
364
+ ## Serverless plugin authorizers
365
+
366
+ If your authentication needs are custom and not satisfied by the existing capabilities of the Serverless offline project, you can inject your own authentication strategy. To inject a custom strategy for Lambda invocation, you define a custom variable under `serverless-offline` called `authenticationProvider` in the serverless.yml file. The value of the custom variable will be used to `require(your authenticationProvider value)` where the location is expected to return a function with the following signature.
367
+
368
+ ```js
369
+ module.exports = function (endpoint, functionKey, method, path) {
370
+ return {
371
+ name: 'your strategy name',
372
+ scheme: 'your scheme name',
373
+
374
+ getAuthenticateFunction: () => ({
375
+ async authenticate(request, h) {
376
+ // your implementation
377
+ },
378
+ }),
379
+ }
380
+ }
381
+ ```
382
+
383
+ A working example of injecting a custom authorization provider can be found in the projects integration tests under the folder `custom-authentication`.
384
+
362
385
  ## Custom headers
363
386
 
364
387
  You are able to use some custom headers in your request to gain more control over the requestContext object.
365
388
 
366
- | Header | Event key |
367
- | ------------------------------- | ----------------------------------------------------------- |
368
- | cognito-identity-id | event.requestContext.identity.cognitoIdentityId |
369
- | cognito-authentication-provider | event.requestContext.identity.cognitoAuthenticationProvider |
389
+ | Header | Event key | Example |
390
+ | ------------------------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------- |
391
+ | cognito-identity-id | event.requestContext.identity.cognitoIdentityId | |
392
+ | cognito-authentication-provider | event.requestContext.identity.cognitoAuthenticationProvider | |
393
+ | sls-offline-authorizer-override | event.requestContext.authorizer | { "iam": {"cognitoUser": { "amr": ["unauthenticated"], "identityId": "abc123" }}} |
370
394
 
371
395
  By doing this you are now able to change those values using a custom header. This can help you with easier authentication or retrieving the userId from a `cognitoAuthenticationProvider` value.
372
396
 
@@ -507,8 +531,6 @@ Where the `event` is received in the lambda handler function.
507
531
 
508
532
  There's support for [websocketsApiRouteSelectionExpression](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-selection-expressions.html) in it's basic form: `$request.body.x.y.z`, where the default value is `$request.body.action`.
509
533
 
510
- Authorizers and wss:// are currently not supported.
511
-
512
534
  ## Usage with Webpack
513
535
 
514
536
  Use [serverless-webpack](https://github.com/serverless-heaven/serverless-webpack) to compile and bundle your ES-next code
@@ -572,7 +594,54 @@ For each debug run:
572
594
 
573
595
  The system will start in wait status. This will also automatically start the chrome browser and wait for you to set breakpoints for inspection. Set the breakpoints as needed and, then, click the play button for the debugging to continue.
574
596
 
575
- Depending on the breakpoint, you may need to call the URL path for your function in seperate browser window for your serverless function to be run and made available for debugging.
597
+ Depending on the breakpoint, you may need to call the URL path for your function in separate browser window for your serverless function to be run and made available for debugging.
598
+
599
+ ### Interactive Debugging with Visual Studio Code (VSC)
600
+
601
+ With newer versions of node (6.3+) the node inspector is already part of your node environment and you can take advantage of debugging inside your IDE with source-map support. Here is the example configuration to debug interactively with VSC. It has two steps.
602
+
603
+ #### Step 1 : Adding a launch configuration in IDE
604
+
605
+ Add a new [launch configuration](https://code.visualstudio.com/docs/editor/debugging) to VSC like this:
606
+
607
+ ```json
608
+ {
609
+
610
+ "type": "node",
611
+ "request": "launch",
612
+ "name": "Debug Serverless Offline",
613
+ "cwd": "${workspaceFolder}",
614
+ "runtimeExecutable": "npm",
615
+ "runtimeArgs": [
616
+ "run",
617
+ "debug"
618
+ ],
619
+ "sourceMaps": true
620
+ }
621
+
622
+ ```
623
+
624
+ #### Step2 : Adding a debug script
625
+
626
+ You will also need to add a `debug` script reference in your `package.json file`
627
+
628
+ Add this to the `scripts` section:
629
+
630
+ > Unix/Mac: `"debug" : "export SLS_DEBUG=* && node --inspect /usr/local/bin/serverless offline"`
631
+
632
+ > Windows: `"debug": "SET SLS_DEBUG=* && node --inspect node_modules\\serverless\\bin\\serverless offline"`
633
+
634
+ Example:
635
+
636
+ ```json
637
+ ....
638
+ "scripts": {
639
+ "debug" : "SET SLS_DEBUG=* && node --inspect node_modules\\serverless\\bin\\serverless offline"
640
+ }
641
+ ```
642
+
643
+ In VSC, you can, then, add breakpoints to your code. To start a debug sessions you can either start your script in `package.json` by clicking the hovering debug intellisense icon or by going to your debug pane and selecting the Debug Serverless Offline configuration.
644
+
576
645
 
577
646
  ## Resource permissions and AWS profile
578
647
 
@@ -743,6 +812,10 @@ We try to follow [Airbnb's JavaScript Style Guide](https://github.com/airbnb/jav
743
812
  | :------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------: |
744
813
  | [lteacher](https://github.com/lteacher) | [martinmicunda](https://github.com/martinmicunda) | [nori3tsu](https://github.com/nori3tsu) | [ppasmanik](https://github.com/ppasmanik) | [ryanzyy](https://github.com/ryanzyy) |
745
814
 
746
- | [<img alt="m0ppers" src="https://avatars3.githubusercontent.com/u/819421?v=4&s=117" width="117">](https://github.com/m0ppers) | [<img alt="footballencarta" src="https://avatars0.githubusercontent.com/u/1312258?v=4&s=117" width="117">](https://github.com/footballencarta) | [<img alt="bryanvaz" src="https://avatars0.githubusercontent.com/u/9157498?v=4&s=117" width="117">](https://github.com/bryanvaz) | [<img alt="njyjn" src="https://avatars.githubusercontent.com/u/10694375?v=4&s=117" width="117">](https://github.com/njyjn) |
815
+ | [<img alt="m0ppers" src="https://avatars3.githubusercontent.com/u/819421?v=4&s=117" width="117">](https://github.com/m0ppers) | [<img alt="footballencarta" src="https://avatars0.githubusercontent.com/u/1312258?v=4&s=117" width="117">](https://github.com/footballencarta) | [<img alt="bryanvaz" src="https://avatars0.githubusercontent.com/u/9157498?v=4&s=117" width="117">](https://github.com/bryanvaz) | [<img alt="njyjn" src="https://avatars.githubusercontent.com/u/10694375?v=4&s=117" width="117">](https://github.com/njyjn) | |
747
816
  | :---------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------: | ------------------------------------- |
748
817
  | [m0ppers](https://github.com/m0ppers) | [footballencarta](https://github.com/footballencarta) | [bryanvaz](https://github.com/bryanvaz) | [njyjn](https://github.com/njyjn) | [kdybicz](https://github.com/kdybicz) |
818
+
819
+ | [<img alt="ericctsf" src="https://avatars.githubusercontent.com/u/42775388?s=400&v=4" width="117">](https://github.com/ericctsf) | | | | |
820
+ | :------------------------------------------------------------------------------------------------------------------------------: | :-: | :-: | :-: | :-: |
821
+ | [ericctsf](https://github.com/erictsf) | | | | |
@@ -96,6 +96,10 @@ class ServerlessOffline {
96
96
  lifecycleEvents: ['init', 'ready', 'end'],
97
97
  options: _index2.commandOptions,
98
98
  usage: 'Simulates API Gateway to call your lambda functions offline using backward compatible initialization.'
99
+ },
100
+ functionsUpdated: {
101
+ type: 'entrypoint',
102
+ lifecycleEvents: ['cleanup']
99
103
  }
100
104
  },
101
105
  lifecycleEvents: ['start'],
@@ -106,6 +110,7 @@ class ServerlessOffline {
106
110
  this.hooks = {
107
111
  'offline:start:init': this.start.bind(this),
108
112
  'offline:start:ready': this.ready.bind(this),
113
+ 'offline:functionsUpdated:cleanup': this.cleanupFunctions.bind(this),
109
114
  'offline:start': this._startWithExplicitEnd.bind(this),
110
115
  'offline:start:end': this.end.bind(this)
111
116
  };
@@ -150,7 +155,7 @@ class ServerlessOffline {
150
155
  eventModules.push(this._createHttp(httpEvents));
151
156
  }
152
157
 
153
- if (scheduleEvents.length > 0) {
158
+ if (!_classPrivateFieldLooseBase(this, _options)[_options].disableScheduledEvents && scheduleEvents.length > 0) {
154
159
  eventModules.push(this._createSchedule(scheduleEvents));
155
160
  }
156
161
 
@@ -203,6 +208,13 @@ class ServerlessOffline {
203
208
  process.exit(0);
204
209
  }
205
210
  }
211
+
212
+ async cleanupFunctions() {
213
+ if (_classPrivateFieldLooseBase(this, _lambda)[_lambda]) {
214
+ (0, _serverlessLog.default)('Forcing cleanup of Lambda functions');
215
+ await _classPrivateFieldLooseBase(this, _lambda)[_lambda].cleanup();
216
+ }
217
+ }
206
218
  /**
207
219
  * Entry point for the plugin (sls offline) when running 'sls offline'
208
220
  * The call to this.end() would terminate the process before 'offline:start:end' could be consumed
@@ -398,7 +410,12 @@ class ServerlessOffline {
398
410
  }
399
411
 
400
412
  httpEvent.http.isHttpApi = true;
401
- httpEvent.http.payload = service.provider.httpApi && service.provider.httpApi.payload ? service.provider.httpApi.payload : '2.0';
413
+
414
+ if (functionDefinition.httpApi && functionDefinition.httpApi.payload) {
415
+ httpEvent.http.payload = functionDefinition.httpApi.payload;
416
+ } else {
417
+ httpEvent.http.payload = service.provider.httpApi && service.provider.httpApi.payload ? service.provider.httpApi.payload : '2.0';
418
+ }
402
419
  }
403
420
 
404
421
  if (http && http.private) {
@@ -29,6 +29,10 @@ var _default = {
29
29
  usage: 'Used to disable cookie-validation on hapi.js-server',
30
30
  type: 'boolean'
31
31
  },
32
+ disableScheduledEvents: {
33
+ usage: 'Disables all scheduled events. Overrides configurations in serverless.yml. Default: false',
34
+ type: 'boolean'
35
+ },
32
36
  enforceSecureCookies: {
33
37
  usage: 'Enforce secure cookies',
34
38
  type: 'boolean'
@@ -16,6 +16,7 @@ var _default = {
16
16
  corsAllowOrigin: '*',
17
17
  corsExposedHeaders: 'WWW-Authenticate,Server-Authorization',
18
18
  disableCookieValidation: false,
19
+ disableScheduledEvents: false,
19
20
  dockerHost: 'localhost',
20
21
  dockerHostServicePath: null,
21
22
  dockerNetwork: null,
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = authFunctionNameExtractor;
7
7
 
8
- var _serverlessLog2 = _interopRequireDefault(require("../../serverlessLog.js"));
8
+ var _serverlessLog2 = _interopRequireDefault(require("../serverlessLog.js"));
9
9
 
10
10
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
11
 
@@ -9,13 +9,15 @@ var _buffer = require("buffer");
9
9
 
10
10
  var _fs = require("fs");
11
11
 
12
- var _path = require("path");
12
+ var pathUtils = _interopRequireWildcard(require("path"));
13
13
 
14
14
  var _h2o = _interopRequireDefault(require("@hapi/h2o2"));
15
15
 
16
16
  var _hapi = require("@hapi/hapi");
17
17
 
18
- var _authFunctionNameExtractor = _interopRequireDefault(require("./authFunctionNameExtractor.js"));
18
+ var _module = require("module");
19
+
20
+ var _authFunctionNameExtractor = _interopRequireDefault(require("../authFunctionNameExtractor.js"));
19
21
 
20
22
  var _authJWTSettingsExtractor = _interopRequireDefault(require("./authJWTSettingsExtractor.js"));
21
23
 
@@ -39,12 +41,12 @@ var _index2 = require("../../utils/index.js");
39
41
 
40
42
  var _LambdaProxyIntegrationEventV = _interopRequireDefault(require("./lambda-events/LambdaProxyIntegrationEventV2.js"));
41
43
 
44
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
45
+
42
46
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
43
47
 
44
48
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
45
49
 
46
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
47
-
48
50
  function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; }
49
51
 
50
52
  var id = 0;
@@ -134,8 +136,8 @@ class HttpServer {
134
136
 
135
137
  if (typeof httpsProtocol === 'string' && httpsProtocol.length > 0) {
136
138
  serverOptions.tls = {
137
- cert: (0, _fs.readFileSync)((0, _path.resolve)(httpsProtocol, 'cert.pem'), 'ascii'),
138
- key: (0, _fs.readFileSync)((0, _path.resolve)(httpsProtocol, 'key.pem'), 'ascii')
139
+ cert: (0, _fs.readFileSync)((0, pathUtils.resolve)(httpsProtocol, 'cert.pem'), 'ascii'),
140
+ key: (0, _fs.readFileSync)((0, pathUtils.resolve)(httpsProtocol, 'key.pem'), 'ascii')
139
141
  };
140
142
  } // Hapijs server creation
141
143
 
@@ -428,6 +430,36 @@ class HttpServer {
428
430
  return authStrategyName;
429
431
  }
430
432
 
433
+ _setAuthorizationStrategy(endpoint, functionKey, method, path) {
434
+ var _customizations$offli;
435
+
436
+ /*
437
+ * The authentication strategy can be provided outside of this project
438
+ * by injecting the provider through a custom variable in the serverless.yml.
439
+ *
440
+ * see the example in the tests for more details
441
+ * /tests/integration/custom-authentication
442
+ */
443
+ const customizations = _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.custom;
444
+
445
+ if (customizations && (_customizations$offli = customizations.offline) !== null && _customizations$offli !== void 0 && _customizations$offli.customAuthenticationProvider) {
446
+ const root = pathUtils.resolve(_classPrivateFieldLooseBase(this, _serverless)[_serverless].serviceDir, 'require-resolver');
447
+ const customRequire = (0, _module.createRequire)(root);
448
+ const provider = customRequire(customizations.offline.customAuthenticationProvider);
449
+ const strategy = provider(endpoint, functionKey, method, path);
450
+
451
+ _classPrivateFieldLooseBase(this, _server)[_server].auth.scheme(strategy.scheme, strategy.getAuthenticateFunction);
452
+
453
+ _classPrivateFieldLooseBase(this, _server)[_server].auth.strategy(strategy.name, strategy.scheme);
454
+
455
+ return strategy.name;
456
+ } // If the endpoint has an authorization function, create an authStrategy for the route
457
+
458
+
459
+ const authStrategyName = _classPrivateFieldLooseBase(this, _options)[_options].noAuth ? null : this._configureJWTAuthorization(endpoint, functionKey, method, path) || this._configureAuthorization(endpoint, functionKey, method, path);
460
+ return authStrategyName;
461
+ }
462
+
431
463
  createRoutes(functionKey, httpEvent, handler) {
432
464
  const [handlerPath] = (0, _index2.splitHandlerPathAndName)(handler);
433
465
  let method;
@@ -455,7 +487,7 @@ class HttpServer {
455
487
  hapiPath = (0, _index2.generateHapiPath)(path, _classPrivateFieldLooseBase(this, _options)[_options], _classPrivateFieldLooseBase(this, _serverless)[_serverless]);
456
488
  }
457
489
 
458
- const endpoint = new _Endpoint.default((0, _path.join)(_classPrivateFieldLooseBase(this, _serverless)[_serverless].config.servicePath, handlerPath), httpEvent, this.v3Utils);
490
+ const endpoint = new _Endpoint.default((0, pathUtils.join)(_classPrivateFieldLooseBase(this, _serverless)[_serverless].config.servicePath, handlerPath), httpEvent, this.v3Utils);
459
491
  const stage = endpoint.isHttpApi ? '$default' : _classPrivateFieldLooseBase(this, _options)[_options].stage || _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.provider.stage;
460
492
  const protectedRoutes = [];
461
493
 
@@ -477,10 +509,10 @@ class HttpServer {
477
509
  server,
478
510
  stage: endpoint.isHttpApi || _classPrivateFieldLooseBase(this, _options)[_options].noPrependStageInUrl ? null : stage,
479
511
  invokePath: `/2015-03-31/functions/${functionKey}/invocations`
480
- }); // If the endpoint has an authorization function, create an authStrategy for the route
512
+ });
481
513
 
514
+ const authStrategyName = this._setAuthorizationStrategy(endpoint, functionKey, method, path);
482
515
 
483
- const authStrategyName = _classPrivateFieldLooseBase(this, _options)[_options].noAuth ? null : this._configureJWTAuthorization(endpoint, functionKey, method, path) || this._configureAuthorization(endpoint, functionKey, method, path);
484
516
  let cors = null;
485
517
 
486
518
  if (endpoint.cors) {
@@ -538,6 +570,12 @@ class HttpServer {
538
570
  };
539
571
  }
540
572
 
573
+ const additionalRequestContext = {};
574
+
575
+ if (httpEvent.operationId) {
576
+ additionalRequestContext.operationName = httpEvent.operationId;
577
+ }
578
+
541
579
  hapiOptions.tags = ['api'];
542
580
 
543
581
  const hapiHandler = async (request, h) => {
@@ -682,7 +720,7 @@ class HttpServer {
682
720
  }
683
721
  } else if (integration === 'AWS_PROXY') {
684
722
  const stageVariables = _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.custom ? _classPrivateFieldLooseBase(this, _serverless)[_serverless].service.custom.stageVariables : null;
685
- const lambdaProxyIntegrationEvent = endpoint.isHttpApi && endpoint.payload === '2.0' ? new _LambdaProxyIntegrationEventV.default(request, stage, endpoint.routeKey, stageVariables, this.v3Utils) : new _index.LambdaProxyIntegrationEvent(request, stage, requestPath, stageVariables, endpoint.isHttpApi ? endpoint.routeKey : null, this.v3Utils);
723
+ const lambdaProxyIntegrationEvent = endpoint.isHttpApi && endpoint.payload === '2.0' ? new _LambdaProxyIntegrationEventV.default(request, stage, endpoint.routeKey, stageVariables, additionalRequestContext, this.v3Utils) : new _index.LambdaProxyIntegrationEvent(request, stage, requestPath, stageVariables, endpoint.isHttpApi ? endpoint.routeKey : null, additionalRequestContext, this.v3Utils);
686
724
  event = lambdaProxyIntegrationEvent.create();
687
725
  }
688
726
 
@@ -7,7 +7,7 @@ exports.default = createAuthScheme;
7
7
 
8
8
  var _boom = _interopRequireDefault(require("@hapi/boom"));
9
9
 
10
- var _authCanExecuteResource = _interopRequireDefault(require("./authCanExecuteResource.js"));
10
+ var _authCanExecuteResource = _interopRequireDefault(require("../authCanExecuteResource.js"));
11
11
 
12
12
  var _debugLog = _interopRequireDefault(require("../../debugLog.js"));
13
13
 
@@ -112,7 +112,8 @@ function createAuthScheme(authorizerOptions, provider, lambda, {
112
112
  lambdaFunction.setEvent(event);
113
113
 
114
114
  try {
115
- const result = await lambdaFunction.runHandler(); // return processResponse(null, result)
115
+ const result = await lambdaFunction.runHandler();
116
+ if (result === 'Unauthorized') return _boom.default.unauthorized('Unauthorized'); // return processResponse(null, result)
116
117
 
117
118
  const policy = result; // Validate that the policy document has the principalId set
118
119
 
@@ -39,8 +39,10 @@ var _stage = /*#__PURE__*/_classPrivateFieldLooseKey("stage");
39
39
 
40
40
  var _stageVariables = /*#__PURE__*/_classPrivateFieldLooseKey("stageVariables");
41
41
 
42
+ var _additionalRequestContext = /*#__PURE__*/_classPrivateFieldLooseKey("additionalRequestContext");
43
+
42
44
  class LambdaProxyIntegrationEvent {
43
- constructor(request, stage, path, stageVariables, routeKey = null, v3Utils) {
45
+ constructor(request, stage, path, stageVariables, routeKey = null, additionalRequestContext = null, v3Utils) {
44
46
  Object.defineProperty(this, _path, {
45
47
  writable: true,
46
48
  value: null
@@ -61,11 +63,16 @@ class LambdaProxyIntegrationEvent {
61
63
  writable: true,
62
64
  value: null
63
65
  });
66
+ Object.defineProperty(this, _additionalRequestContext, {
67
+ writable: true,
68
+ value: null
69
+ });
64
70
  _classPrivateFieldLooseBase(this, _path)[_path] = path;
65
71
  _classPrivateFieldLooseBase(this, _routeKey)[_routeKey] = routeKey;
66
72
  _classPrivateFieldLooseBase(this, _request)[_request] = request;
67
73
  _classPrivateFieldLooseBase(this, _stage)[_stage] = stage;
68
74
  _classPrivateFieldLooseBase(this, _stageVariables)[_stageVariables] = stageVariables;
75
+ _classPrivateFieldLooseBase(this, _additionalRequestContext)[_additionalRequestContext] = additionalRequestContext || {};
69
76
 
70
77
  if (v3Utils) {
71
78
  this.log = v3Utils.log;
@@ -103,6 +110,18 @@ class LambdaProxyIntegrationEvent {
103
110
 
104
111
  const headers = (0, _index.parseHeaders)(rawHeaders || []) || {};
105
112
 
113
+ if (headers['sls-offline-authorizer-override']) {
114
+ try {
115
+ authAuthorizer = parse(headers['sls-offline-authorizer-override']);
116
+ } catch (error) {
117
+ if (this.log) {
118
+ this.log.error('Could not parse header sls-offline-authorizer-override, make sure it is correct JSON');
119
+ } else {
120
+ console.error('Serverless-offline: Could not parse header sls-offline-authorizer-override make sure it is correct JSON.');
121
+ }
122
+ }
123
+ }
124
+
106
125
  if (body) {
107
126
  if (typeof body !== 'string') {
108
127
  // this.#request.payload is NOT the same as the rawPayload
@@ -205,6 +224,7 @@ class LambdaProxyIntegrationEvent {
205
224
  userAgent: _headers['user-agent'] || '',
206
225
  userArn: 'offlineContext_userArn'
207
226
  },
227
+ operationName: _classPrivateFieldLooseBase(this, _additionalRequestContext)[_additionalRequestContext].operationName,
208
228
  path: _classPrivateFieldLooseBase(this, _path)[_path],
209
229
  protocol: 'HTTP/1.1',
210
230
  requestId: (0, _index.createUniqueId)(),
@@ -36,8 +36,10 @@ var _stage = /*#__PURE__*/_classPrivateFieldLooseKey("stage");
36
36
 
37
37
  var _stageVariables = /*#__PURE__*/_classPrivateFieldLooseKey("stageVariables");
38
38
 
39
+ var _additionalRequestContext = /*#__PURE__*/_classPrivateFieldLooseKey("additionalRequestContext");
40
+
39
41
  class LambdaProxyIntegrationEventV2 {
40
- constructor(request, stage, routeKey, stageVariables, v3Utils) {
42
+ constructor(request, stage, routeKey, stageVariables, additionalRequestContext, v3Utils) {
41
43
  Object.defineProperty(this, _routeKey, {
42
44
  writable: true,
43
45
  value: null
@@ -54,10 +56,15 @@ class LambdaProxyIntegrationEventV2 {
54
56
  writable: true,
55
57
  value: null
56
58
  });
59
+ Object.defineProperty(this, _additionalRequestContext, {
60
+ writable: true,
61
+ value: null
62
+ });
57
63
  _classPrivateFieldLooseBase(this, _routeKey)[_routeKey] = routeKey;
58
64
  _classPrivateFieldLooseBase(this, _request)[_request] = request;
59
65
  _classPrivateFieldLooseBase(this, _stage)[_stage] = stage;
60
66
  _classPrivateFieldLooseBase(this, _stageVariables)[_stageVariables] = stageVariables;
67
+ _classPrivateFieldLooseBase(this, _additionalRequestContext)[_additionalRequestContext] = additionalRequestContext || {};
61
68
 
62
69
  if (v3Utils) {
63
70
  this.log = v3Utils.log;
@@ -92,6 +99,18 @@ class LambdaProxyIntegrationEventV2 {
92
99
 
93
100
  const headers = (0, _index.parseHeaders)(rawHeaders || []) || {};
94
101
 
102
+ if (headers['sls-offline-authorizer-override']) {
103
+ try {
104
+ authAuthorizer = parse(headers['sls-offline-authorizer-override']);
105
+ } catch (error) {
106
+ if (this.log) {
107
+ this.log.error('Could not parse header sls-offline-authorizer-override, make sure it is correct JSON');
108
+ } else {
109
+ console.error('Serverless-offline: Could not parse header sls-offline-authorizer-override make sure it is correct JSON.');
110
+ }
111
+ }
112
+ }
113
+
95
114
  if (body) {
96
115
  if (typeof body !== 'string') {
97
116
  // this.#request.payload is NOT the same as the rawPayload
@@ -150,7 +169,13 @@ class LambdaProxyIntegrationEventV2 {
150
169
  const httpMethod = method.toUpperCase();
151
170
  const requestTime = (0, _index.formatToClfTime)(received);
152
171
  const requestTimeEpoch = received;
153
- const cookies = Object.entries(_classPrivateFieldLooseBase(this, _request)[_request].state).map(([key, value]) => `${key}=${value}`);
172
+ const cookies = Object.entries(_classPrivateFieldLooseBase(this, _request)[_request].state).flatMap(([key, value]) => {
173
+ if (Array.isArray(value)) {
174
+ return value.map(v => `${key}=${v}`);
175
+ }
176
+
177
+ return `${key}=${value}`;
178
+ });
154
179
  return {
155
180
  version: '2.0',
156
181
  routeKey: _classPrivateFieldLooseBase(this, _routeKey)[_routeKey],
@@ -177,6 +202,7 @@ class LambdaProxyIntegrationEventV2 {
177
202
  sourceIp: remoteAddress,
178
203
  userAgent: _headers['user-agent'] || ''
179
204
  },
205
+ operationName: _classPrivateFieldLooseBase(this, _additionalRequestContext)[_additionalRequestContext].operationName,
180
206
  requestId: 'offlineContext_resourceId',
181
207
  routeKey: _classPrivateFieldLooseBase(this, _routeKey)[_routeKey],
182
208
  stage: _classPrivateFieldLooseBase(this, _stage)[_stage],