Skip to content

Commit

Permalink
feat(aws-apigatewayv2-authorizers): add payloadFormatVersion field to…
Browse files Browse the repository at this point in the history
… HttpLambdaAuthorizer
  • Loading branch information
yasamoka committed Jul 12, 2024
1 parent 1ac51fd commit b6faf0d
Show file tree
Hide file tree
Showing 2 changed files with 213 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,30 @@ export interface HttpLambdaAuthorizerProps {
readonly resultsCacheTtl?: Duration;

/**
* The type of response the lambda can return
* The payload format version
*
* @see https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.payload-format
*
* If set to HttpLambdaResponseType.SIMPLE then
* response format 2.0 will be used.
* Note: if `responseType` is set to simple, then
* setting this property to version 1.0 is not allowed.
*
* @default - If `responseType` is not set or is set to IAM,
* then payload format version is set to 1.0. If `responseType`
* is set to simple, then payload format version is set to 2.0.
*/
readonly payloadFormatVersion?: AuthorizerPayloadVersion;

/**
* The type of response the lambda can return
*
* @see https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.payload-format-response
*
* @default HttpLambdaResponseType.IAM
* Note: if `payloadFormatVersion` is set to version 1.0, then
* setting this property to simple is not allowed.
*
* @default - if `payloadFormatVersion` is not set or is set
* to 1.0, then response type is set to IAM. If `payloadFormatVersion`
* is set to 2.0, then response type is set to simple.
*/
readonly responseType?: HttpLambdaResponseType;
}
Expand All @@ -78,6 +94,9 @@ export class HttpLambdaAuthorizer implements IHttpRouteAuthorizer {
private readonly id: string,
private readonly handler: IFunction,
private readonly props: HttpLambdaAuthorizerProps = {}) {
if (props.payloadFormatVersion === AuthorizerPayloadVersion.VERSION_1_0 && props.responseType === HttpLambdaResponseType.SIMPLE) {
throw new Error('payload format version is set to 1.0 but response type is set to SIMPLE');
}
}

public bind(options: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig {
Expand All @@ -86,8 +105,25 @@ export class HttpLambdaAuthorizer implements IHttpRouteAuthorizer {
}

if (!this.authorizer) {
const responseType = this.props.responseType ?? HttpLambdaResponseType.IAM;
const enableSimpleResponses = responseType === HttpLambdaResponseType.SIMPLE || undefined;
let enableSimpleResponses: boolean;
let payloadFormatVersion: AuthorizerPayloadVersion;

const payloadFormatVersionInner = this.props.payloadFormatVersion;
const responseType = this.props.responseType;

if (payloadFormatVersionInner === undefined) {
enableSimpleResponses = responseType === HttpLambdaResponseType.SIMPLE;
payloadFormatVersion = enableSimpleResponses ? AuthorizerPayloadVersion.VERSION_2_0 : AuthorizerPayloadVersion.VERSION_1_0;
} else if (responseType === undefined) {
enableSimpleResponses = payloadFormatVersionInner === AuthorizerPayloadVersion.VERSION_2_0;
payloadFormatVersion = payloadFormatVersionInner;
} else {
// No need to check here whether payload format version is 1.0
// and response type is simple since this is already handled
// by the constructor
enableSimpleResponses = responseType === HttpLambdaResponseType.SIMPLE;
payloadFormatVersion = payloadFormatVersionInner;
}

this.httpApi = options.route.httpApi;
this.authorizer = new HttpAuthorizer(options.scope, this.id, {
Expand All @@ -98,7 +134,7 @@ export class HttpLambdaAuthorizer implements IHttpRouteAuthorizer {
type: HttpAuthorizerType.LAMBDA,
authorizerName: this.props.authorizerName ?? this.id,
enableSimpleResponses,
payloadFormatVersion: enableSimpleResponses ? AuthorizerPayloadVersion.VERSION_2_0 : AuthorizerPayloadVersion.VERSION_1_0,
payloadFormatVersion,
authorizerUri: lambdaAuthorizerArn(this.handler),
resultsCacheTtl: this.props.resultsCacheTtl ?? Duration.minutes(5),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { HttpLambdaAuthorizer, HttpLambdaResponseType } from './../../lib/http/l
import { DummyRouteIntegration } from './integration';
import { Duration, Stack } from '../../..';
import { Match, Template } from '../../../assertions';
import { HttpApi } from '../../../aws-apigatewayv2';
import { AuthorizerPayloadVersion, HttpApi } from '../../../aws-apigatewayv2';
import { Code, Function } from '../../../aws-lambda';
import * as lambda from '../../../aws-lambda';

Expand Down Expand Up @@ -34,6 +34,7 @@ describe('HttpLambdaAuthorizer', () => {
AuthorizerType: 'REQUEST',
AuthorizerResultTtlInSeconds: 300,
AuthorizerPayloadFormatVersion: '1.0',
EnableSimpleResponses: false,
IdentitySource: [
'$request.header.Authorization',
],
Expand All @@ -44,7 +45,7 @@ describe('HttpLambdaAuthorizer', () => {
});
});

test('should use format 2.0 and simple responses when simple response type is requested', () => {
test('should use payload format version 2.0 and simple responses when payload format version is undefined and simple response type is requested', () => {
// GIVEN
const stack = new Stack();
const api = new HttpApi(stack, 'HttpApi');
Expand Down Expand Up @@ -73,7 +74,7 @@ describe('HttpLambdaAuthorizer', () => {
});
});

test('should use format 1.0 when only IAM response type is requested', () => {
test('should use payload format version 2.0 and non-simple responses when payload format version is undefined and IAM response type is requested', () => {
// GIVEN
const stack = new Stack();
const api = new HttpApi(stack, 'HttpApi');
Expand All @@ -98,10 +99,175 @@ describe('HttpLambdaAuthorizer', () => {
// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Authorizer', {
AuthorizerPayloadFormatVersion: '1.0',
EnableSimpleResponses: Match.absent(),
EnableSimpleResponses: false,
});
});

test('should not use simple responses when payload format version is 1.0 and response type is undefined', () => {
// GIVEN
const stack = new Stack();
const api = new HttpApi(stack, 'HttpApi');

const handler = new Function(stack, 'auth-function', {
runtime: lambda.Runtime.NODEJS_LATEST,
code: Code.fromInline('exports.handler = () => {return true}'),
handler: 'index.handler',
});

const authorizer = new HttpLambdaAuthorizer('BooksAuthorizer', handler, {
payloadFormatVersion: AuthorizerPayloadVersion.VERSION_1_0,
});

// WHEN
api.addRoutes({
integration: new DummyRouteIntegration(),
path: '/books',
authorizer,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Authorizer', {
AuthorizerPayloadFormatVersion: '1.0',
EnableSimpleResponses: false,
});
});

test('should not use simple responses when payload format version is 1.0 and IAM response type is requested', () => {
// GIVEN
const stack = new Stack();
const api = new HttpApi(stack, 'HttpApi');

const handler = new Function(stack, 'auth-function', {
runtime: lambda.Runtime.NODEJS_LATEST,
code: Code.fromInline('exports.handler = () => {return true}'),
handler: 'index.handler',
});

const authorizer = new HttpLambdaAuthorizer('BooksAuthorizer', handler, {
payloadFormatVersion: AuthorizerPayloadVersion.VERSION_1_0,
responseType: HttpLambdaResponseType.IAM,
});

// WHEN
api.addRoutes({
integration: new DummyRouteIntegration(),
path: '/books',
authorizer,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Authorizer', {
AuthorizerPayloadFormatVersion: '1.0',
EnableSimpleResponses: false,
});
});

test('should use simple responses when payload format version 2.0 is specified and response type is undefined', () => {
// GIVEN
const stack = new Stack();
const api = new HttpApi(stack, 'HttpApi');

const handler = new Function(stack, 'auth-function', {
runtime: lambda.Runtime.NODEJS_LATEST,
code: Code.fromInline('exports.handler = () => {return true}'),
handler: 'index.handler',
});

const authorizer = new HttpLambdaAuthorizer('BooksAuthorizer', handler, {
payloadFormatVersion: AuthorizerPayloadVersion.VERSION_2_0,
});

// WHEN
api.addRoutes({
integration: new DummyRouteIntegration(),
path: '/books',
authorizer,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Authorizer', {
AuthorizerPayloadFormatVersion: '2.0',
EnableSimpleResponses: true,
});
});

test('should use simple responses when payload format version 2.0 is specified and simple response type is requested', () => {
// GIVEN
const stack = new Stack();
const api = new HttpApi(stack, 'HttpApi');

const handler = new Function(stack, 'auth-function', {
runtime: lambda.Runtime.NODEJS_LATEST,
code: Code.fromInline('exports.handler = () => {return true}'),
handler: 'index.handler',
});

const authorizer = new HttpLambdaAuthorizer('BooksAuthorizer', handler, {
payloadFormatVersion: AuthorizerPayloadVersion.VERSION_2_0,
responseType: HttpLambdaResponseType.SIMPLE,
});

// WHEN
api.addRoutes({
integration: new DummyRouteIntegration(),
path: '/books',
authorizer,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Authorizer', {
AuthorizerPayloadFormatVersion: '2.0',
EnableSimpleResponses: true,
});
});

test('should not use simple responses when payload format version is 2.0 and IAM response type is requested', () => {
// GIVEN
const stack = new Stack();
const api = new HttpApi(stack, 'HttpApi');

const handler = new Function(stack, 'auth-function', {
runtime: lambda.Runtime.NODEJS_LATEST,
code: Code.fromInline('exports.handler = () => {return true}'),
handler: 'index.handler',
});

const authorizer = new HttpLambdaAuthorizer('BooksAuthorizer', handler, {
payloadFormatVersion: AuthorizerPayloadVersion.VERSION_2_0,
responseType: HttpLambdaResponseType.IAM,
});

// WHEN
api.addRoutes({
integration: new DummyRouteIntegration(),
path: '/books',
authorizer,
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Authorizer', {
AuthorizerPayloadFormatVersion: '2.0',
EnableSimpleResponses: false,
});
});

test('error when payload format version 1.0 is specified and simple response type is requested', () => {
const stack = new Stack();

const handler = new Function(stack, 'auth-function', {
runtime: lambda.Runtime.NODEJS_LATEST,
code: Code.fromInline('exports.handler = () => {return true}'),
handler: 'index.handler',
});

expect(() => {
new HttpLambdaAuthorizer('BooksAuthorizer', handler, {
payloadFormatVersion: AuthorizerPayloadVersion.VERSION_1_0,
responseType: HttpLambdaResponseType.SIMPLE,
});
}).toThrow(/payload format version is set to 1.0 but response type is set to SIMPLE/);
});

test('can override cache ttl', () => {
// GIVEN
const stack = new Stack();
Expand Down

0 comments on commit b6faf0d

Please sign in to comment.