From cd582b2afcbb12193e0750a431eecd53e5dc9761 Mon Sep 17 00:00:00 2001 From: kaizen3031593 Date: Thu, 30 Sep 2021 16:00:49 -0400 Subject: [PATCH 1/7] add lambdaauthorizerconfig --- .../@aws-cdk/aws-appsync/lib/graphqlapi.ts | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts index 8b0252bed9eb4..e8bc6ea33c24c 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts @@ -1,5 +1,6 @@ import { IUserPool } from '@aws-cdk/aws-cognito'; import { ManagedPolicy, Role, IRole, ServicePrincipal, Grant, IGrantable } from '@aws-cdk/aws-iam'; +import { IFunction } from '@aws-cdk/aws-lambda'; import { CfnResource, Duration, Expiration, IResolvable, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnApiKey, CfnGraphQLApi, CfnGraphQLSchema } from './appsync.generated'; @@ -29,6 +30,10 @@ export enum AuthorizationType { * OpenID Connect authorization type */ OIDC = 'OPENID_CONNECT', + /** + * Lambda authorization type + */ + LAMBDA = 'AWS_LAMBDA', } /** @@ -58,6 +63,11 @@ export interface AuthorizationMode { * @default - none */ readonly openIdConnectConfig?: OpenIdConnectConfig; + /** + * If authorizationType is `AuthorizationType.LAMBDA`, this option is required. + * @default - none + */ + readonly lambdaConfig?: LambdaConfig; } /** @@ -150,6 +160,31 @@ export interface OpenIdConnectConfig { readonly oidcProvider: string; } +/** + * Configuration for Lambda authorization in AppSync + */ +export interface LambdaConfig { + /** + * The handler for the authorizer lambda function. + */ + readonly handler: IFunction; + + /** + * How long APIGateway should cache the results. Max 1 hour. + * Disable caching by setting this to 0. + * + * @default Duration.minutes(5) + */ + readonly resultsCacheTtl?: Duration; + + /** + * An optional regex to be matched against the authorization token. When matched the authorizer lambda is invoked, + * otherwise a 401 Unauthorized is returned to the client. + * + * @default - no regex filter will be applied. + */ + readonly validationRegex?: string; +} /** * Configuration of the API authorization modes. */ From fcf825f68924cb21223ad99dff663aebf64beb32 Mon Sep 17 00:00:00 2001 From: kaizen3031593 Date: Thu, 30 Sep 2021 16:45:23 -0400 Subject: [PATCH 2/7] wire lambdaconfig through graphql --- .../@aws-cdk/aws-appsync/lib/graphqlapi.ts | 35 ++++++++++++++----- .../aws-appsync/test/appsync-auth.test.ts | 4 +++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts index e8bc6ea33c24c..f465d91140c8c 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts @@ -1,6 +1,5 @@ import { IUserPool } from '@aws-cdk/aws-cognito'; import { ManagedPolicy, Role, IRole, ServicePrincipal, Grant, IGrantable } from '@aws-cdk/aws-iam'; -import { IFunction } from '@aws-cdk/aws-lambda'; import { CfnResource, Duration, Expiration, IResolvable, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnApiKey, CfnGraphQLApi, CfnGraphQLSchema } from './appsync.generated'; @@ -67,7 +66,7 @@ export interface AuthorizationMode { * If authorizationType is `AuthorizationType.LAMBDA`, this option is required. * @default - none */ - readonly lambdaConfig?: LambdaConfig; + readonly lambdaAuthorizerConfig?: lambdaAuthorizerConfig; } /** @@ -163,14 +162,21 @@ export interface OpenIdConnectConfig { /** * Configuration for Lambda authorization in AppSync */ -export interface LambdaConfig { +export interface lambdaAuthorizerConfig { /** - * The handler for the authorizer lambda function. + * The ARN for the authorizer lambda function. This may be a standard Lambda ARN, a version ARN (.../v3) or alias ARN. + * Note: This Lambda function must have the following resource-based policy assigned to it. + * When configuring Lambda authorizers in the console, this is done for you. + * To do so with the AWS CLI, run the following: + * + * `aws lambda add-permission --function-name "arn:aws:lambda:us-east-2:111122223333:function:my-function" --statement-id "appsync" --principal appsync.amazonaws.com --action lambda:InvokeFunction` + * + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-graphqlapi-lambdaauthorizerconfig.html */ - readonly handler: IFunction; + readonly functionArn: string; /** - * How long APIGateway should cache the results. Max 1 hour. + * How long the results are cached. * Disable caching by setting this to 0. * * @default Duration.minutes(5) @@ -178,13 +184,13 @@ export interface LambdaConfig { readonly resultsCacheTtl?: Duration; /** - * An optional regex to be matched against the authorization token. When matched the authorizer lambda is invoked, - * otherwise a 401 Unauthorized is returned to the client. + * A regular expression for validation of tokens before the Lambda function is called. * * @default - no regex filter will be applied. */ readonly validationRegex?: string; } + /** * Configuration of the API authorization modes. */ @@ -453,6 +459,7 @@ export class GraphqlApi extends GraphqlApiBase { logConfig: this.setupLogConfig(props.logConfig), openIdConnectConfig: this.setupOpenIdConnectConfig(defaultMode.openIdConnectConfig), userPoolConfig: this.setupUserPoolConfig(defaultMode.userPoolConfig), + lambdaAuthorizerConfig: this.setupLambdaAuthorizerConfig(defaultMode.lambdaAuthorizerConfig), additionalAuthenticationProviders: this.setupAdditionalAuthorizationModes(additionalModes), xrayEnabled: props.xrayEnabled, }); @@ -532,6 +539,9 @@ export class GraphqlApi extends GraphqlApiBase { if (mode.authorizationType === AuthorizationType.USER_POOL && !mode.userPoolConfig) { throw new Error('Missing User Pool Configuration'); } + if (mode.authorizationType === AuthorizationType.LAMBDA && !mode.lambdaAuthorizerConfig) { + throw new Error('Missing Lambda Configuration'); + } }); if (modes.filter((mode) => mode.authorizationType === AuthorizationType.API_KEY).length > 1) { throw new Error('You can\'t duplicate API_KEY configuration. See https://docs.aws.amazon.com/appsync/latest/devguide/security.html'); @@ -586,6 +596,15 @@ export class GraphqlApi extends GraphqlApiBase { }; } + private setupLambdaAuthorizerConfig(config?: lambdaAuthorizerConfig) { + if (!config) return undefined; + return { + authorizerResultTtlInSeconds: config.resultsCacheTtl?.toSeconds(), + authorizerUri: config.functionArn, + identityValidationExpression: config.validationRegex, + } + } + private setupAdditionalAuthorizationModes(modes?: AuthorizationMode[]) { if (!modes || modes.length === 0) return undefined; return modes.reduce((acc, mode) => [ diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts index a137e54b0423f..885a3597d597d 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts @@ -630,3 +630,7 @@ describe('AppSync OIDC Authorization', () => { }); }); }); + +describe('AppSync Lambda Authorization', () => { + test() +}); \ No newline at end of file From eb35dd4c78b6aa7168ad03332d71ce785dc77da2 Mon Sep 17 00:00:00 2001 From: kaizen3031593 Date: Thu, 30 Sep 2021 17:17:59 -0400 Subject: [PATCH 3/7] test lambda auth --- .../@aws-cdk/aws-appsync/lib/graphqlapi.ts | 1 + .../aws-appsync/test/appsync-auth.test.ts | 214 +++++++++++++++++- 2 files changed, 214 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts index f465d91140c8c..c27459879d21f 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts @@ -612,6 +612,7 @@ export class GraphqlApi extends GraphqlApiBase { authenticationType: mode.authorizationType, userPoolConfig: this.setupUserPoolConfig(mode.userPoolConfig), openIdConnectConfig: this.setupOpenIdConnectConfig(mode.openIdConnectConfig), + lambdaAuthorizerConfig: this.setupLambdaAuthorizerConfig(mode.lambdaAuthorizerConfig), }, ], []); } diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts index 885a3597d597d..88e5cb0d13a58 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts @@ -1,8 +1,10 @@ import * as path from 'path'; import { Template } from '@aws-cdk/assertions'; import * as cognito from '@aws-cdk/aws-cognito'; +import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; import * as appsync from '../lib'; +import { Duration } from '@aws-cdk/core'; // GIVEN let stack: cdk.Stack; @@ -632,5 +634,215 @@ describe('AppSync OIDC Authorization', () => { }); describe('AppSync Lambda Authorization', () => { - test() + let fn: lambda.Function; + beforeEach(() => { + fn = new lambda.Function(stack, 'auth-function', { + runtime: lambda.Runtime.NODEJS_12_X, + handler: 'index.handler', + code: lambda.Code.fromInline('/* lambda authentication code here.*/'), + }); + }); + + test('Lambda authorization configurable in default authorization has default configuration', () => { + // WHEN + new appsync.GraphqlApi(stack, 'api', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { + defaultAuthorization: { + authorizationType: appsync.AuthorizationType.LAMBDA, + lambdaAuthorizerConfig: { + functionArn: fn.functionArn, + }, + }, + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppSync::GraphQLApi', { + AuthenticationType: 'AWS_LAMBDA', + LambdaAuthorizerConfig: { + AuthorizerUri: { + 'Fn::GetAtt': [ + 'authfunction96361832', + 'Arn', + ], + }, + }, + }); + }); + + test('Lambda authorization configurable in default authorization', () => { + // WHEN + new appsync.GraphqlApi(stack, 'api', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { + defaultAuthorization: { + authorizationType: appsync.AuthorizationType.LAMBDA, + lambdaAuthorizerConfig: { + functionArn: fn.functionArn, + resultsCacheTtl: Duration.seconds(300), + validationRegex: 'abc', + }, + }, + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppSync::GraphQLApi', { + AuthenticationType: 'AWS_LAMBDA', + LambdaAuthorizerConfig: { + AuthorizerUri: { + 'Fn::GetAtt': [ + 'authfunction96361832', + 'Arn', + ], + }, + AuthorizerResultTtlInSeconds: 300, + IdentityValidationExpression: 'abc', + }, + }); + }); + + test('Lambda authorization configurable in additional authorization has default configuration', () => { + // WHEN + new appsync.GraphqlApi(stack, 'api', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { + additionalAuthorizationModes: [{ + authorizationType: appsync.AuthorizationType.LAMBDA, + lambdaAuthorizerConfig: { + functionArn: fn.functionArn, + }, + }], + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppSync::GraphQLApi', { + AdditionalAuthenticationProviders: [{ + AuthenticationType: 'AWS_LAMBDA', + LambdaAuthorizerConfig: { + AuthorizerUri: { + 'Fn::GetAtt': [ + 'authfunction96361832', + 'Arn', + ], + }, + }, + }], + }); + }); + + test('Lambda authorization configurable in additional authorization', () => { + // WHEN + new appsync.GraphqlApi(stack, 'api', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { + additionalAuthorizationModes: [{ + authorizationType: appsync.AuthorizationType.LAMBDA, + lambdaAuthorizerConfig: { + functionArn: fn.functionArn, + resultsCacheTtl: Duration.seconds(300), + validationRegex: 'abc', + }, + }], + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppSync::GraphQLApi', { + AdditionalAuthenticationProviders: [{ + AuthenticationType: 'AWS_LAMBDA', + LambdaAuthorizerConfig: { + AuthorizerUri: { + 'Fn::GetAtt': [ + 'authfunction96361832', + 'Arn', + ], + }, + AuthorizerResultTtlInSeconds: 300, + IdentityValidationExpression: 'abc', + }, + }], + }); + }); + + test('Lambda authorization configurable in with multiple authorization', () => { + // WHEN + new appsync.GraphqlApi(stack, 'api', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { + defaultAuthorization: { + authorizationType: appsync.AuthorizationType.LAMBDA, + lambdaAuthorizerConfig: { + functionArn: fn.functionArn, + }, + }, + additionalAuthorizationModes: [ + { + authorizationType: appsync.AuthorizationType.LAMBDA, + lambdaAuthorizerConfig: { + functionArn: fn.functionArn, + resultsCacheTtl: Duration.seconds(300), + validationRegex: 'abc', + }, + }, + { + authorizationType: appsync.AuthorizationType.LAMBDA, + lambdaAuthorizerConfig: { + functionArn: fn.functionArn, + resultsCacheTtl: Duration.minutes(0), + validationRegex: 'abc', + }, + }, + ], + }, + }); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::AppSync::GraphQLApi', { + AuthenticationType: 'AWS_LAMBDA', + LambdaAuthorizerConfig: { + AuthorizerUri: { + 'Fn::GetAtt': [ + 'authfunction96361832', + 'Arn', + ], + }, + }, + AdditionalAuthenticationProviders: [ + { + AuthenticationType: 'AWS_LAMBDA', + LambdaAuthorizerConfig: { + AuthorizerUri: { + 'Fn::GetAtt': [ + 'authfunction96361832', + 'Arn', + ], + }, + AuthorizerResultTtlInSeconds: 300, + IdentityValidationExpression: 'abc', + }, + }, + { + AuthenticationType: 'AWS_LAMBDA', + LambdaAuthorizerConfig: { + AuthorizerUri: { + 'Fn::GetAtt': [ + 'authfunction96361832', + 'Arn', + ], + }, + AuthorizerResultTtlInSeconds: 0, + IdentityValidationExpression: 'abc', + }, + }, + ], + }); + }); }); \ No newline at end of file From 91462851f4ed02716282c348028dba5366b42af4 Mon Sep 17 00:00:00 2001 From: kaizen3031593 Date: Fri, 1 Oct 2021 09:52:33 -0400 Subject: [PATCH 4/7] add Authorization to README and fix linter --- packages/@aws-cdk/aws-appsync/README.md | 36 ++++++++++++++++++- .../@aws-cdk/aws-appsync/lib/graphqlapi.ts | 4 +-- .../aws-appsync/test/appsync-auth.test.ts | 13 ++++--- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index 0145beae621f6..8f5b2154d7d91 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -28,7 +28,7 @@ APIs that use GraphQL. ### DynamoDB -Example of a GraphQL API with `AWS_IAM` authorization resolving into a DynamoDb +Example of a GraphQL API with `AWS_IAM` [authorization](#authorization) resolving into a DynamoDb backend data source. GraphQL schema file `schema.graphql`: @@ -345,6 +345,40 @@ If you don't specify `graphqlArn` in `fromXxxAttributes`, CDK will autogenerate the expected `arn` for the imported api, given the `apiId`. For creating data sources and resolvers, an `apiId` is sufficient. +## Authorization + +There are multiple authorization types available for GraphQL API to cater to different +access use cases. They are: + +- API Keys (`AuthorizationType.API_KEY`) +- Amazon Cognito User Pools (`AuthorizationType.USER_POOL`) +- OpenID Connect (`AuthorizationType.OPENID_CONNECT`) +- AWS Identity and Access Management (`AuthorizationType.AWS_IAM`) +- AWS Lambda (`AuthorizationType.AWS_LAMBDA`) + +These types can be used simultaneously in a single API, allowing different types of clients to +access data. When you specify an authorization type, you can also specify the corresponding +authorization mode to finish defining your authorization. For example, this is a GraphQL API +with AWS Lambda Authorization. + +```ts +authFunction = new lambda.Function(stack, 'auth-function', {}); + +new appsync.GraphqlApi(stack, 'api', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { + defaultAuthorization: { + authorizationType: appsync.AuthorizationType.LAMBDA, + lambdaAuthorizerConfig: { + functionArn: authFunction.functionArn, + // can also specify `resultsCacheTtl` and `validationRegex`. + }, + }, + }, +}); +``` + ## Permissions When using `AWS_IAM` as the authorization type for GraphQL API, an IAM Role diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts index c27459879d21f..4a195be4c5553 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts @@ -184,7 +184,7 @@ export interface lambdaAuthorizerConfig { readonly resultsCacheTtl?: Duration; /** - * A regular expression for validation of tokens before the Lambda function is called. + * A regular expression for validation of tokens before the Lambda function is called. * * @default - no regex filter will be applied. */ @@ -602,7 +602,7 @@ export class GraphqlApi extends GraphqlApiBase { authorizerResultTtlInSeconds: config.resultsCacheTtl?.toSeconds(), authorizerUri: config.functionArn, identityValidationExpression: config.validationRegex, - } + }; } private setupAdditionalAuthorizationModes(modes?: AuthorizationMode[]) { diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts index 88e5cb0d13a58..b5b923ea0b7aa 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts @@ -4,7 +4,6 @@ import * as cognito from '@aws-cdk/aws-cognito'; import * as lambda from '@aws-cdk/aws-lambda'; import * as cdk from '@aws-cdk/core'; import * as appsync from '../lib'; -import { Duration } from '@aws-cdk/core'; // GIVEN let stack: cdk.Stack; @@ -682,7 +681,7 @@ describe('AppSync Lambda Authorization', () => { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { functionArn: fn.functionArn, - resultsCacheTtl: Duration.seconds(300), + resultsCacheTtl: cdk.Duration.seconds(300), validationRegex: 'abc', }, }, @@ -746,7 +745,7 @@ describe('AppSync Lambda Authorization', () => { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { functionArn: fn.functionArn, - resultsCacheTtl: Duration.seconds(300), + resultsCacheTtl: cdk.Duration.seconds(300), validationRegex: 'abc', }, }], @@ -788,7 +787,7 @@ describe('AppSync Lambda Authorization', () => { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { functionArn: fn.functionArn, - resultsCacheTtl: Duration.seconds(300), + resultsCacheTtl: cdk.Duration.seconds(300), validationRegex: 'abc', }, }, @@ -796,7 +795,7 @@ describe('AppSync Lambda Authorization', () => { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { functionArn: fn.functionArn, - resultsCacheTtl: Duration.minutes(0), + resultsCacheTtl: cdk.Duration.minutes(0), validationRegex: 'abc', }, }, @@ -807,13 +806,13 @@ describe('AppSync Lambda Authorization', () => { // THEN Template.fromStack(stack).hasResourceProperties('AWS::AppSync::GraphQLApi', { AuthenticationType: 'AWS_LAMBDA', - LambdaAuthorizerConfig: { + LambdaAuthorizerConfig: { AuthorizerUri: { 'Fn::GetAtt': [ 'authfunction96361832', 'Arn', ], - }, + }, }, AdditionalAuthenticationProviders: [ { From b415fca406802b684983ec4cf1e9c7384eebf335 Mon Sep 17 00:00:00 2001 From: kaizen3031593 Date: Fri, 1 Oct 2021 11:36:42 -0400 Subject: [PATCH 5/7] add test for mode/type mismatch and single lambda clause --- .../@aws-cdk/aws-appsync/lib/graphqlapi.ts | 11 ++- .../aws-appsync/test/appsync-auth.test.ts | 87 ++++++++----------- 2 files changed, 45 insertions(+), 53 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts index 4a195be4c5553..356936b817027 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts @@ -66,7 +66,7 @@ export interface AuthorizationMode { * If authorizationType is `AuthorizationType.LAMBDA`, this option is required. * @default - none */ - readonly lambdaAuthorizerConfig?: lambdaAuthorizerConfig; + readonly lambdaAuthorizerConfig?: LambdaAuthorizerConfig; } /** @@ -160,9 +160,9 @@ export interface OpenIdConnectConfig { } /** - * Configuration for Lambda authorization in AppSync + * Configuration for Lambda authorization in AppSync. Note that you can only have a single AWS Lambda function configured to authorize your API. */ -export interface lambdaAuthorizerConfig { +export interface LambdaAuthorizerConfig { /** * The ARN for the authorizer lambda function. This may be a standard Lambda ARN, a version ARN (.../v3) or alias ARN. * Note: This Lambda function must have the following resource-based policy assigned to it. @@ -532,6 +532,9 @@ export class GraphqlApi extends GraphqlApiBase { } private validateAuthorizationProps(modes: AuthorizationMode[]) { + if (modes.filter((mode) => mode.authorizationType === AuthorizationType.LAMBDA).length > 1) { + throw new Error('You can only have a single AWS Lambda function configured to authorize your API.'); + } modes.map((mode) => { if (mode.authorizationType === AuthorizationType.OIDC && !mode.openIdConnectConfig) { throw new Error('Missing OIDC Configuration'); @@ -596,7 +599,7 @@ export class GraphqlApi extends GraphqlApiBase { }; } - private setupLambdaAuthorizerConfig(config?: lambdaAuthorizerConfig) { + private setupLambdaAuthorizerConfig(config?: LambdaAuthorizerConfig) { if (!config) return undefined; return { authorizerResultTtlInSeconds: config.resultsCacheTtl?.toSeconds(), diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts index b5b923ea0b7aa..21cf659ee4a41 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts @@ -682,7 +682,7 @@ describe('AppSync Lambda Authorization', () => { lambdaAuthorizerConfig: { functionArn: fn.functionArn, resultsCacheTtl: cdk.Duration.seconds(300), - validationRegex: 'abc', + validationRegex: 'custom-.*', }, }, }, @@ -699,7 +699,7 @@ describe('AppSync Lambda Authorization', () => { ], }, AuthorizerResultTtlInSeconds: 300, - IdentityValidationExpression: 'abc', + IdentityValidationExpression: 'custom-.*', }, }); }); @@ -746,7 +746,7 @@ describe('AppSync Lambda Authorization', () => { lambdaAuthorizerConfig: { functionArn: fn.functionArn, resultsCacheTtl: cdk.Duration.seconds(300), - validationRegex: 'abc', + validationRegex: 'custom-.*', }, }], }, @@ -764,15 +764,14 @@ describe('AppSync Lambda Authorization', () => { ], }, AuthorizerResultTtlInSeconds: 300, - IdentityValidationExpression: 'abc', + IdentityValidationExpression: 'custom-.*', }, }], }); }); - test('Lambda authorization configurable in with multiple authorization', () => { - // WHEN - new appsync.GraphqlApi(stack, 'api', { + test('Lambda authorization throws with multiple lambda authorization', () => { + expect(() => new appsync.GraphqlApi(stack, 'api', { name: 'api', schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), authorizationConfig: { @@ -788,60 +787,50 @@ describe('AppSync Lambda Authorization', () => { lambdaAuthorizerConfig: { functionArn: fn.functionArn, resultsCacheTtl: cdk.Duration.seconds(300), - validationRegex: 'abc', + validationRegex: 'custom-.*', }, }, + ], + }, + })).toThrow('You can only have a single AWS Lambda function configured to authorize your API.'); + + expect(() => new appsync.GraphqlApi(stack, 'api2', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { + defaultAuthorization: { authorizationType: appsync.AuthorizationType.IAM }, + additionalAuthorizationModes: [ { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { functionArn: fn.functionArn, - resultsCacheTtl: cdk.Duration.minutes(0), - validationRegex: 'abc', + resultsCacheTtl: cdk.Duration.seconds(300), + validationRegex: 'custom-.*', + }, + }, + { + authorizationType: appsync.AuthorizationType.LAMBDA, + lambdaAuthorizerConfig: { + functionArn: fn.functionArn, + resultsCacheTtl: cdk.Duration.seconds(300), + validationRegex: 'custom-.*', }, }, ], }, - }); + })).toThrow('You can only have a single AWS Lambda function configured to authorize your API.'); + }); - // THEN - Template.fromStack(stack).hasResourceProperties('AWS::AppSync::GraphQLApi', { - AuthenticationType: 'AWS_LAMBDA', - LambdaAuthorizerConfig: { - AuthorizerUri: { - 'Fn::GetAtt': [ - 'authfunction96361832', - 'Arn', - ], + test('throws if authorization type and mode do not match', () => { + expect(() => new appsync.GraphqlApi(stack, 'api', { + name: 'api', + schema: appsync.Schema.fromAsset(path.join(__dirname, 'appsync.test.graphql')), + authorizationConfig: { + defaultAuthorization: { + authorizationType: appsync.AuthorizationType.LAMBDA, + openIdConnectConfig: { oidcProvider: 'test' }, }, }, - AdditionalAuthenticationProviders: [ - { - AuthenticationType: 'AWS_LAMBDA', - LambdaAuthorizerConfig: { - AuthorizerUri: { - 'Fn::GetAtt': [ - 'authfunction96361832', - 'Arn', - ], - }, - AuthorizerResultTtlInSeconds: 300, - IdentityValidationExpression: 'abc', - }, - }, - { - AuthenticationType: 'AWS_LAMBDA', - LambdaAuthorizerConfig: { - AuthorizerUri: { - 'Fn::GetAtt': [ - 'authfunction96361832', - 'Arn', - ], - }, - AuthorizerResultTtlInSeconds: 0, - IdentityValidationExpression: 'abc', - }, - }, - ], - }); + })).toThrow('Missing Lambda Configuration'); }); }); \ No newline at end of file From f705b173059b2805277b5d11b62aeb7b4380cc15 Mon Sep 17 00:00:00 2001 From: kaizen3031593 Date: Fri, 1 Oct 2021 12:39:59 -0400 Subject: [PATCH 6/7] change property from 'functionArn' to 'handler' --- packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts | 7 ++++--- .../aws-appsync/test/appsync-auth.test.ts | 16 ++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts index 356936b817027..93c9078e32358 100644 --- a/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts +++ b/packages/@aws-cdk/aws-appsync/lib/graphqlapi.ts @@ -1,5 +1,6 @@ import { IUserPool } from '@aws-cdk/aws-cognito'; import { ManagedPolicy, Role, IRole, ServicePrincipal, Grant, IGrantable } from '@aws-cdk/aws-iam'; +import { IFunction } from '@aws-cdk/aws-lambda'; import { CfnResource, Duration, Expiration, IResolvable, Stack } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnApiKey, CfnGraphQLApi, CfnGraphQLSchema } from './appsync.generated'; @@ -164,7 +165,7 @@ export interface OpenIdConnectConfig { */ export interface LambdaAuthorizerConfig { /** - * The ARN for the authorizer lambda function. This may be a standard Lambda ARN, a version ARN (.../v3) or alias ARN. + * The authorizer lambda function. * Note: This Lambda function must have the following resource-based policy assigned to it. * When configuring Lambda authorizers in the console, this is done for you. * To do so with the AWS CLI, run the following: @@ -173,7 +174,7 @@ export interface LambdaAuthorizerConfig { * * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-appsync-graphqlapi-lambdaauthorizerconfig.html */ - readonly functionArn: string; + readonly handler: IFunction; /** * How long the results are cached. @@ -603,7 +604,7 @@ export class GraphqlApi extends GraphqlApiBase { if (!config) return undefined; return { authorizerResultTtlInSeconds: config.resultsCacheTtl?.toSeconds(), - authorizerUri: config.functionArn, + authorizerUri: config.handler.functionArn, identityValidationExpression: config.validationRegex, }; } diff --git a/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts b/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts index 21cf659ee4a41..67f12ae4bc13a 100644 --- a/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts +++ b/packages/@aws-cdk/aws-appsync/test/appsync-auth.test.ts @@ -651,7 +651,7 @@ describe('AppSync Lambda Authorization', () => { defaultAuthorization: { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { - functionArn: fn.functionArn, + handler: fn, }, }, }, @@ -680,7 +680,7 @@ describe('AppSync Lambda Authorization', () => { defaultAuthorization: { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { - functionArn: fn.functionArn, + handler: fn, resultsCacheTtl: cdk.Duration.seconds(300), validationRegex: 'custom-.*', }, @@ -713,7 +713,7 @@ describe('AppSync Lambda Authorization', () => { additionalAuthorizationModes: [{ authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { - functionArn: fn.functionArn, + handler: fn, }, }], }, @@ -744,7 +744,7 @@ describe('AppSync Lambda Authorization', () => { additionalAuthorizationModes: [{ authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { - functionArn: fn.functionArn, + handler: fn, resultsCacheTtl: cdk.Duration.seconds(300), validationRegex: 'custom-.*', }, @@ -778,14 +778,14 @@ describe('AppSync Lambda Authorization', () => { defaultAuthorization: { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { - functionArn: fn.functionArn, + handler: fn, }, }, additionalAuthorizationModes: [ { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { - functionArn: fn.functionArn, + handler: fn, resultsCacheTtl: cdk.Duration.seconds(300), validationRegex: 'custom-.*', }, @@ -803,7 +803,7 @@ describe('AppSync Lambda Authorization', () => { { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { - functionArn: fn.functionArn, + handler: fn, resultsCacheTtl: cdk.Duration.seconds(300), validationRegex: 'custom-.*', }, @@ -811,7 +811,7 @@ describe('AppSync Lambda Authorization', () => { { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { - functionArn: fn.functionArn, + handler: fn, resultsCacheTtl: cdk.Duration.seconds(300), validationRegex: 'custom-.*', }, From 4c09cbe6e11a18a953708cb7dd38b7bfbd5c8130 Mon Sep 17 00:00:00 2001 From: kaizen3031593 Date: Fri, 1 Oct 2021 12:43:49 -0400 Subject: [PATCH 7/7] README update too --- packages/@aws-cdk/aws-appsync/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-appsync/README.md b/packages/@aws-cdk/aws-appsync/README.md index 8f5b2154d7d91..a191b51a86483 100644 --- a/packages/@aws-cdk/aws-appsync/README.md +++ b/packages/@aws-cdk/aws-appsync/README.md @@ -371,7 +371,7 @@ new appsync.GraphqlApi(stack, 'api', { defaultAuthorization: { authorizationType: appsync.AuthorizationType.LAMBDA, lambdaAuthorizerConfig: { - functionArn: authFunction.functionArn, + handler: authFunction, // can also specify `resultsCacheTtl` and `validationRegex`. }, },