Skip to content

Commit e7c0c75

Browse files
authored
feat(aws-apigateway): add ability to include authorizer context in apigw sfn integration (#18892)
Implements #18891 This PR adds the ability to include custom authorizer context in the Step Functions API Gateway integration. This is useful if the custom authorizer sets custom context values, which can then be used downstream in the step function. This adds a `authorizer: { ... }` key to the state input. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent d3fdfe5 commit e7c0c75

File tree

7 files changed

+86
-14
lines changed

7 files changed

+86
-14
lines changed

packages/@aws-cdk/aws-apigateway/README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ Other metadata such as billing details, AWS account ID and resource ARNs are not
126126

127127
By default, a `prod` stage is provisioned.
128128

129-
In order to reduce the payload size sent to AWS Step Functions, `headers` are not forwarded to the Step Functions execution input. It is possible to choose whether `headers`, `requestContext`, `path` and `querystring` are included or not. By default, `headers` are excluded in all requests.
129+
In order to reduce the payload size sent to AWS Step Functions, `headers` are not forwarded to the Step Functions execution input. It is possible to choose whether `headers`, `requestContext`, `path`, `querystring`, and `authorizer` are included or not. By default, `headers` are excluded in all requests.
130130

131131
More details about AWS Step Functions payload limit can be found at https://docs.aws.amazon.com/step-functions/latest/dg/limits-overview.html#service-limits-task-executions.
132132

@@ -184,7 +184,7 @@ AWS Step Functions will receive the following execution input:
184184
}
185185
```
186186

187-
Additional information around the request such as the request context and headers can be included as part of the input
187+
Additional information around the request such as the request context, authorizer context, and headers can be included as part of the input
188188
forwarded to the state machine. The following example enables headers to be included in the input but not query string.
189189

190190
```ts fixture=stepfunctions
@@ -193,6 +193,7 @@ new apigateway.StepFunctionsRestApi(this, 'StepFunctionsRestApi', {
193193
headers: true,
194194
path: false,
195195
querystring: false,
196+
authorizer: false,
196197
requestContext: {
197198
caller: true,
198199
user: true,

packages/@aws-cdk/aws-apigateway/lib/integrations/stepfunctions.ts

+16
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,20 @@ export interface StepFunctionsExecutionIntegrationOptions extends IntegrationOpt
6868
* @default false
6969
*/
7070
readonly headers?: boolean;
71+
72+
/**
73+
* If the whole authorizer object, including custom context values should be in the execution input. The execution input will include a new key `authorizer`:
74+
*
75+
* {
76+
* "body": {},
77+
* "authorizer": {
78+
* "key": "value"
79+
* }
80+
* }
81+
*
82+
* @default false
83+
*/
84+
readonly authorizer?: boolean;
7185
}
7286

7387
/**
@@ -241,6 +255,7 @@ function templateString(
241255
const includeHeader = options.headers?? false;
242256
const includeQueryString = options.querystring?? true;
243257
const includePath = options.path?? true;
258+
const includeAuthorizer = options.authorizer ?? false;
244259

245260
if (options.requestContext && Object.keys(options.requestContext).length > 0) {
246261
requestContextStr = requestContext(options.requestContext);
@@ -251,6 +266,7 @@ function templateString(
251266
templateStr = templateStr.replace('%INCLUDE_HEADERS%', String(includeHeader));
252267
templateStr = templateStr.replace('%INCLUDE_QUERYSTRING%', String(includeQueryString));
253268
templateStr = templateStr.replace('%INCLUDE_PATH%', String(includePath));
269+
templateStr = templateStr.replace('%INCLUDE_AUTHORIZER%', String(includeAuthorizer));
254270
templateStr = templateStr.replace('%REQUESTCONTEXT%', requestContextStr);
255271

256272
return templateStr;

packages/@aws-cdk/aws-apigateway/lib/integrations/stepfunctions.vtl

+12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#set($includeHeaders = %INCLUDE_HEADERS%)
1010
#set($includeQueryString = %INCLUDE_QUERYSTRING%)
1111
#set($includePath = %INCLUDE_PATH%)
12+
#set($includeAuthorizer = %INCLUDE_AUTHORIZER%)
1213
#set($allParams = $input.params())
1314
{
1415
"stateMachineArn": "%STATEMACHINE%",
@@ -49,6 +50,17 @@
4950
#set($inputString = "$inputString }")
5051
#end
5152

53+
#if ($includeAuthorizer)
54+
#set($inputString = "$inputString, @@authorizer@@:{")
55+
#foreach($paramName in $context.authorizer.keySet())
56+
#set($inputString = "$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@")
57+
#if($foreach.hasNext)
58+
#set($inputString = "$inputString,")
59+
#end
60+
#end
61+
#set($inputString = "$inputString }")
62+
#end
63+
5264
#set($requestContext = "%REQUESTCONTEXT%")
5365
## Check if the request context should be included as part of the execution input
5466
#if($requestContext && !$requestContext.empty)

packages/@aws-cdk/aws-apigateway/lib/stepfunctions-api.ts

+21-6
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@ import { Model } from './model';
1111
*
1212
*/
1313
export interface StepFunctionsRestApiProps extends RestApiProps {
14-
/**
15-
* The default State Machine that handles all requests from this API.
16-
*
17-
* This stateMachine will be used as a the default integration for all methods in
18-
* this API, unless specified otherwise in `addMethod`.
19-
*/
14+
/**
15+
* The default State Machine that handles all requests from this API.
16+
*
17+
* This stateMachine will be used as a the default integration for all methods in
18+
* this API, unless specified otherwise in `addMethod`.
19+
*/
2020
readonly stateMachine: sfn.IStateMachine;
2121

2222
/**
@@ -75,6 +75,20 @@ export interface StepFunctionsRestApiProps extends RestApiProps {
7575
* @default false
7676
*/
7777
readonly headers?: boolean;
78+
79+
/**
80+
* If the whole authorizer object, including custom context values should be in the execution input. The execution input will include a new key `authorizer`:
81+
*
82+
* {
83+
* "body": {},
84+
* "authorizer": {
85+
* "key": "value"
86+
* }
87+
* }
88+
*
89+
* @default false
90+
*/
91+
readonly authorizer?: boolean;
7892
}
7993

8094
/**
@@ -96,6 +110,7 @@ export class StepFunctionsRestApi extends RestApi {
96110
path: props.path?? true,
97111
querystring: props.querystring?? true,
98112
headers: props.headers,
113+
authorizer: props.authorizer,
99114
});
100115

101116
super(scope, id, props);

packages/@aws-cdk/aws-apigateway/test/integ.stepfunctions-api.expected.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,11 @@
185185
"Fn::Join": [
186186
"",
187187
[
188-
"## Velocity Template used for API Gateway request mapping template\n##\n## This template forwards the request body, header, path, and querystring\n## to the execution input of the state machine.\n##\n## \"@@\" is used here as a placeholder for '\"' to avoid using escape characters.\n\n#set($inputString = '')\n#set($includeHeaders = true)\n#set($includeQueryString = false)\n#set($includePath = false)\n#set($allParams = $input.params())\n{\n \"stateMachineArn\": \"",
188+
"## Velocity Template used for API Gateway request mapping template\n##\n## This template forwards the request body, header, path, and querystring\n## to the execution input of the state machine.\n##\n## \"@@\" is used here as a placeholder for '\"' to avoid using escape characters.\n\n#set($inputString = '')\n#set($includeHeaders = true)\n#set($includeQueryString = false)\n#set($includePath = false)\n#set($includeAuthorizer = false)\n#set($allParams = $input.params())\n{\n \"stateMachineArn\": \"",
189189
{
190190
"Ref": "StateMachine2E01A3A5"
191191
},
192-
"\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #set($requestContext = \"{@@accountId@@:@@$context.identity.accountId@@,@@userArn@@:@@$context.identity.userArn@@}\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len))}\"\n}\n"
192+
"\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"{@@accountId@@:@@$context.identity.accountId@@,@@userArn@@:@@$context.identity.userArn@@}\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len))}\"\n}\n"
193193
]
194194
]
195195
}

packages/@aws-cdk/aws-apigateway/test/integrations/stepfunctions.test.ts

+30-2
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ describe('StepFunctionsIntegration', () => {
5252
'Fn::Join': [
5353
'',
5454
[
55-
"## Velocity Template used for API Gateway request mapping template\n##\n## This template forwards the request body, header, path, and querystring\n## to the execution input of the state machine.\n##\n## \"@@\" is used here as a placeholder for '\"' to avoid using escape characters.\n\n#set($inputString = '')\n#set($includeHeaders = false)\n#set($includeQueryString = true)\n#set($includePath = true)\n#set($allParams = $input.params())\n{\n \"stateMachineArn\": \"",
55+
"## Velocity Template used for API Gateway request mapping template\n##\n## This template forwards the request body, header, path, and querystring\n## to the execution input of the state machine.\n##\n## \"@@\" is used here as a placeholder for '\"' to avoid using escape characters.\n\n#set($inputString = '')\n#set($includeHeaders = false)\n#set($includeQueryString = true)\n#set($includePath = true)\n#set($includeAuthorizer = false)\n#set($allParams = $input.params())\n{\n \"stateMachineArn\": \"",
5656
{
5757
Ref: 'StateMachine2E01A3A5',
5858
},
59-
"\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len))}\"\n}\n",
59+
"\",\n\n #set($inputString = \"$inputString,@@body@@: $input.body\")\n\n #if ($includeHeaders)\n #set($inputString = \"$inputString, @@header@@:{\")\n #foreach($paramName in $allParams.header.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.header.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n \n #end\n\n #if ($includeQueryString)\n #set($inputString = \"$inputString, @@querystring@@:{\")\n #foreach($paramName in $allParams.querystring.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.querystring.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #if ($includePath)\n #set($inputString = \"$inputString, @@path@@:{\")\n #foreach($paramName in $allParams.path.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($allParams.path.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n \n #if ($includeAuthorizer)\n #set($inputString = \"$inputString, @@authorizer@@:{\")\n #foreach($paramName in $context.authorizer.keySet())\n #set($inputString = \"$inputString @@$paramName@@: @@$util.escapeJavaScript($context.authorizer.get($paramName))@@\")\n #if($foreach.hasNext)\n #set($inputString = \"$inputString,\")\n #end\n #end\n #set($inputString = \"$inputString }\")\n #end\n\n #set($requestContext = \"\")\n ## Check if the request context should be included as part of the execution input\n #if($requestContext && !$requestContext.empty)\n #set($inputString = \"$inputString,\")\n #set($inputString = \"$inputString @@requestContext@@: $requestContext\")\n #end\n\n #set($inputString = \"$inputString}\")\n #set($inputString = $inputString.replaceAll(\"@@\",'\"'))\n #set($len = $inputString.length() - 1)\n \"input\": \"{$util.escapeJavaScript($inputString.substring(1,$len))}\"\n}\n",
6060
],
6161
],
6262
},
@@ -264,6 +264,34 @@ describe('StepFunctionsIntegration', () => {
264264
});
265265
});
266266

267+
test('authorizer context is included when specified by the integration', () => {
268+
//GIVEN
269+
const { stack, api, stateMachine } = givenSetup();
270+
271+
//WHEN
272+
const integ = apigw.StepFunctionsIntegration.startExecution(stateMachine, {
273+
authorizer: true,
274+
});
275+
api.root.addMethod('GET', integ);
276+
277+
Template.fromStack(stack).hasResourceProperties('AWS::ApiGateway::Method', {
278+
Integration: {
279+
RequestTemplates: {
280+
'application/json': {
281+
'Fn::Join': [
282+
'',
283+
[
284+
Match.stringLikeRegexp('#set\\(\\$includeAuthorizer = true\\)'),
285+
{ Ref: 'StateMachine2E01A3A5' },
286+
Match.anyValue(),
287+
],
288+
],
289+
},
290+
},
291+
},
292+
});
293+
});
294+
267295
test('works for imported RestApi', () => {
268296
const stack = new cdk.Stack();
269297
const api = apigw.RestApi.fromRestApiAttributes(stack, 'RestApi', {

0 commit comments

Comments
 (0)