From 6db0e2d937aea96095e1c24ac7d2dc7e60c26c1f Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Tue, 20 Oct 2020 03:05:51 -0400 Subject: [PATCH 01/47] feat(apigatewayv2): Authorizer L2 Support Part 1 of the work needed to make #10534 happen. --- .../aws-apigatewayv2/lib/common/authorizer.ts | 33 +++++ .../aws-apigatewayv2/lib/common/index.ts | 1 + .../aws-apigatewayv2/lib/http/authorizer.ts | 124 ++++++++++++++++++ .../aws-apigatewayv2/lib/http/index.ts | 1 + .../test/http/authorizer.test.ts | 49 +++++++ 5 files changed, 208 insertions(+) create mode 100644 packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts create mode 100644 packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts create mode 100644 packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts new file mode 100644 index 0000000000000..85e208548d1e6 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts @@ -0,0 +1,33 @@ +import { IResource } from '@aws-cdk/core'; + +/** + * Represents an Authorizer. + */ +export interface IHttpAuthorizer extends IResource { + /** + * Id of the Authorizer + * @attribute + */ + readonly authorizerId: string + + /** + * A human friendly name for this Authorizer + * @attribute + */ + readonly authorizerName: string +} + +/** + * Reference to an http authorizer + */ +export interface HttpAuthorizerAttributes { + /** + * Id of the Authorizer + */ + readonly authorizerId: string + + /** + * A human friendly name for this Authorizer + */ + readonly authorizerName: string +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts index d727436b86c99..80a9e1680bcd2 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts @@ -3,3 +3,4 @@ export * from './route'; export * from './stage'; export * from './domain-name'; export * from './api-mapping'; +export * from './authorizer'; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts new file mode 100644 index 0000000000000..f02297e91b524 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -0,0 +1,124 @@ +import { Resource, Lazy } from '@aws-cdk/core'; +import { Construct } from 'constructs'; +import { CfnAuthorizer } from '../apigatewayv2.generated'; + +import { HttpAuthorizerAttributes, IHttpAuthorizer } from '../common'; +import { IHttpApi } from './api'; + +/** + * Supported HTTP methods + */ +export enum HttpAuthorizerType { + /** JSON Web Tokens */ + JWT = 'JWT', + /** Lambda Authorizer */ + REQUEST = 'DELETE', +} + +/** + * Specifies the configuration of a JWT authorizer + */ +export interface HttpJwtConfiguration { + /** + * A list of the intended recipients of the JWT + */ + readonly audience: string[] + + /** + * The base domain of the identity provider that issues JSON Web Tokens. + */ + readonly issuer: string; +} + + +/** + * Properties to initialize an instance of `HttpAuthorizer`. + */ +export interface HttpAuthorizerProps { + /** + * Name of the authorizer + * + * @default - id of the HttpAuthorizer construct. + */ + readonly authorizerName?: string + + /** + * HTTP Api to attach the authorizer to + */ + readonly httpApi: IHttpApi + + /** + * The authorizer type. Specify REQUEST for a Lambda function using incoming request parameters. + * Specify JWT to use JSON Web Tokens (supported only for HTTP APIs). + * + * @default JWT + */ + readonly type?: HttpAuthorizerType; + + /** + * The identity source for which authorization is requested. + * + * @default ['$request.header.Authorization'] + */ + readonly identitySource?: string[], + + /** + * Configuration of a JWT authorizer. + * Required for the JWT authorizer type. Supported only for HTTP APIs. + * + * @default - No configuration + */ + readonly jwtConfiguration?: HttpJwtConfiguration +} + +/** + * Create a new Authorizer for Http APIs + * @resource AWS::ApiGatewayV2::Authorizer + */ +export class HttpAuthorizer extends Resource implements IHttpAuthorizer { + + /** + * Import an existing HTTP Authorizer into this CDK app. + */ + public static fromHttpAuthorizerAttributes(scope: Construct, id: string, attrs: HttpAuthorizerAttributes): IHttpAuthorizer { + class Import extends Resource implements IHttpAuthorizer { + public readonly authorizerId = attrs.authorizerId; + public readonly authorizerName = attrs.authorizerName; + } + return new Import(scope, id); + } + + /** + * A human friendly name for this Authorizer + */ + public readonly authorizerName: string + + public readonly authorizerId: string + + constructor(scope: Construct, id: string, props: HttpAuthorizerProps) { + super(scope, id, { + physicalName: props.authorizerName || Lazy.stringValue({ produce: () => this.node.uniqueId }), + }); + + this.authorizerName = props.authorizerName ?? id; + + const authorizerType = props.type ?? HttpAuthorizerType.JWT; + const identitySource = props.identitySource ?? ['$request.header.Authorization']; + const jwtConfiguration = props.jwtConfiguration; + + if (authorizerType === HttpAuthorizerType.JWT && !jwtConfiguration) { + throw new Error('jwtConfiguration is required for authorizer type of JWT'); + } + + const resource = new CfnAuthorizer(this, 'Resource', { + name: this.authorizerName, + apiId: props.httpApi.httpApiId, + authorizerType, + identitySource, + jwtConfiguration, + }); + + this.authorizerId = resource.ref; + } +} + diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts index c42e089aa1d08..9b027e4bf1e0f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts @@ -4,3 +4,4 @@ export * from './integration'; export * from './integrations'; export * from './stage'; export * from './api-mapping'; +export * from './authorizer'; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts new file mode 100644 index 0000000000000..a7e035b8705a2 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts @@ -0,0 +1,49 @@ +import '@aws-cdk/assert/jest'; +import { Stack } from '@aws-cdk/core'; +import { + HttpApi, HttpAuthorizer, +} from '../../lib'; + +describe('HttpAuthorizer', () => { + test('default', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + new HttpAuthorizer(stack, 'HttpAuthorizer', { + httpApi, + jwtConfiguration: { + audience: ['cognito-pool'], + issuer: 'http://congnito.aws', + }, + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + ApiId: stack.resolve(httpApi.httpApiId), + Name: 'HttpAuthorizer', + AuthorizerType: 'JWT', + IdentitySource: ['$request.header.Authorization'], + JwtConfiguration: { + Audience: ['cognito-pool'], + Issuer: 'http://congnito.aws', + }, + }); + }); + + test('can set authorizer name', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + new HttpAuthorizer(stack, 'HttpAuthorizer', { + httpApi, + authorizerName: 'my-authorizer', + jwtConfiguration: { + audience: ['cognito-pool'], + issuer: 'http://congnito.aws', + }, + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + Name: 'my-authorizer', + }); + }); +}); From 2bf79b8dbef1e5722d06f5f24ad62315aea9b525 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Tue, 20 Oct 2020 03:13:05 -0400 Subject: [PATCH 02/47] style: line endings --- packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts | 2 +- packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts | 2 +- packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts index 85e208548d1e6..2e3b506965de0 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts @@ -30,4 +30,4 @@ export interface HttpAuthorizerAttributes { * A human friendly name for this Authorizer */ readonly authorizerName: string -} \ No newline at end of file +} diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts index 80a9e1680bcd2..eeb237a4e7f84 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/index.ts @@ -3,4 +3,4 @@ export * from './route'; export * from './stage'; export * from './domain-name'; export * from './api-mapping'; -export * from './authorizer'; \ No newline at end of file +export * from './authorizer'; diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts index 9b027e4bf1e0f..8c2dde43a8a99 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/index.ts @@ -4,4 +4,4 @@ export * from './integration'; export * from './integrations'; export * from './stage'; export * from './api-mapping'; -export * from './authorizer'; \ No newline at end of file +export * from './authorizer'; From d7f2930b2bd5f8d5dbd42c8f37400cd7249b0495 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Tue, 20 Oct 2020 09:35:20 -0400 Subject: [PATCH 03/47] Add example for authorizer --- packages/@aws-cdk/aws-apigatewayv2/README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index e5cf86886aa3d..6bbe2c017eff6 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -200,6 +200,23 @@ with 3 API mapping resources across different APIs and Stages. | api | beta | `https://${domainName}/bar` | | apiDemo | $default | `https://${domainName}/demo` | +### Managing access to HTTP APIs + +API Gateway supports multiple mechanisms for [controlling and managing access to your HTTP API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control.html) through authorizers. Here's an example with using a Cognito User Pool as the identity source. + +```ts +const userPool = new UserPool(stack, 'my-pool'); +const httpApi = new HttpApi(stack, 'HttpApi'); + +const authorizer = new Authorizer(stack, { + httpApi, + jwtConfiguration: { + audience: [userPool.userPoolId], + issuer: `https://cognito-idp.${scope.region}.amazonaws.com/${userPool.userPoolId}`, + }, +}); +``` + ## Metrics The API Gateway v2 service sends metrics around the performance of HTTP APIs to Amazon CloudWatch. From 8dad3ade341935c15869a8134981b38dc51d651c Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 21 Oct 2020 10:11:44 -0400 Subject: [PATCH 04/47] docs: Add authorizer to TOC --- packages/@aws-cdk/aws-apigatewayv2/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 6bbe2c017eff6..3e1f1aadc4ec2 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -23,6 +23,7 @@ - [Cross Origin Resource Sharing (CORS)](#cross-origin-resource-sharing-cors) - [Publishing HTTP APIs](#publishing-http-apis) - [Custom Domain](#custom-domain) + - [Managing access to HTTP APIs](#managing-access-to-http-apis) - [Metrics](#metrics) ## Introduction From 1c598271244bb6f7559cb61bfc1ea190fda2d8c8 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 21 Oct 2020 10:23:12 -0400 Subject: [PATCH 05/47] Make suggested changes from review --- .../aws-apigatewayv2/lib/common/authorizer.ts | 23 +-------------- .../aws-apigatewayv2/lib/http/authorizer.ts | 29 +++++++------------ 2 files changed, 11 insertions(+), 41 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts index 2e3b506965de0..609d469a572fa 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts @@ -3,31 +3,10 @@ import { IResource } from '@aws-cdk/core'; /** * Represents an Authorizer. */ -export interface IHttpAuthorizer extends IResource { +export interface IAuthorizer extends IResource { /** * Id of the Authorizer * @attribute */ readonly authorizerId: string - - /** - * A human friendly name for this Authorizer - * @attribute - */ - readonly authorizerName: string -} - -/** - * Reference to an http authorizer - */ -export interface HttpAuthorizerAttributes { - /** - * Id of the Authorizer - */ - readonly authorizerId: string - - /** - * A human friendly name for this Authorizer - */ - readonly authorizerName: string } diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index f02297e91b524..2547f89320b82 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -1,8 +1,8 @@ -import { Resource, Lazy } from '@aws-cdk/core'; +import { Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnAuthorizer } from '../apigatewayv2.generated'; -import { HttpAuthorizerAttributes, IHttpAuthorizer } from '../common'; +import { IAuthorizer } from '../common'; import { IHttpApi } from './api'; /** @@ -12,7 +12,7 @@ export enum HttpAuthorizerType { /** JSON Web Tokens */ JWT = 'JWT', /** Lambda Authorizer */ - REQUEST = 'DELETE', + REQUEST = 'LAMBDA', } /** @@ -75,33 +75,24 @@ export interface HttpAuthorizerProps { * Create a new Authorizer for Http APIs * @resource AWS::ApiGatewayV2::Authorizer */ -export class HttpAuthorizer extends Resource implements IHttpAuthorizer { +export class HttpAuthorizer extends Resource implements IAuthorizer { /** * Import an existing HTTP Authorizer into this CDK app. */ - public static fromHttpAuthorizerAttributes(scope: Construct, id: string, attrs: HttpAuthorizerAttributes): IHttpAuthorizer { - class Import extends Resource implements IHttpAuthorizer { - public readonly authorizerId = attrs.authorizerId; - public readonly authorizerName = attrs.authorizerName; + public static fromAuthorizerId(scope: Construct, id: string, authorizerId: string): IAuthorizer { + class Import extends Resource implements IAuthorizer { + public readonly authorizerId = authorizerId; } return new Import(scope, id); } - /** - * A human friendly name for this Authorizer - */ - public readonly authorizerName: string - public readonly authorizerId: string constructor(scope: Construct, id: string, props: HttpAuthorizerProps) { - super(scope, id, { - physicalName: props.authorizerName || Lazy.stringValue({ produce: () => this.node.uniqueId }), - }); - - this.authorizerName = props.authorizerName ?? id; + super(scope, id); + const authorizerName = props.authorizerName ?? id; const authorizerType = props.type ?? HttpAuthorizerType.JWT; const identitySource = props.identitySource ?? ['$request.header.Authorization']; const jwtConfiguration = props.jwtConfiguration; @@ -111,7 +102,7 @@ export class HttpAuthorizer extends Resource implements IHttpAuthorizer { } const resource = new CfnAuthorizer(this, 'Resource', { - name: this.authorizerName, + name: authorizerName, apiId: props.httpApi.httpApiId, authorizerType, identitySource, From 23b5bb499fa409547e803c7bcfdf8eb5d743ee16 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 21 Oct 2020 10:45:58 -0400 Subject: [PATCH 06/47] Expose authorizerType --- .../aws-apigatewayv2/lib/common/authorizer.ts | 7 +++++ .../aws-apigatewayv2/lib/http/authorizer.ts | 31 +++++++++++++++---- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts index 609d469a572fa..c596616114b6b 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts @@ -1,4 +1,5 @@ import { IResource } from '@aws-cdk/core'; +import { HttpAuthorizerType } from '../http'; /** * Represents an Authorizer. @@ -9,4 +10,10 @@ export interface IAuthorizer extends IResource { * @attribute */ readonly authorizerId: string + + /** + * Type of authorizer + * @attribute + */ + readonly authorizerType: HttpAuthorizerType } diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index 2547f89320b82..5d8b98641837f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -30,6 +30,21 @@ export interface HttpJwtConfiguration { readonly issuer: string; } +/** + * Reference to an http authorizer + */ +export interface HttpAuthorizerAttributes { + /** + * Id of the Authorizer + */ + readonly authorizerId: string + + /** + * Type of authorizer + */ + readonly authorizerType: HttpAuthorizerType +} + /** * Properties to initialize an instance of `HttpAuthorizer`. @@ -80,31 +95,35 @@ export class HttpAuthorizer extends Resource implements IAuthorizer { /** * Import an existing HTTP Authorizer into this CDK app. */ - public static fromAuthorizerId(scope: Construct, id: string, authorizerId: string): IAuthorizer { + public static fromAuthorizerAttributes(scope: Construct, id: string, attrs: HttpAuthorizerAttributes): IAuthorizer { class Import extends Resource implements IAuthorizer { - public readonly authorizerId = authorizerId; + public readonly authorizerId = attrs.authorizerId; + public readonly authorizerType = attrs.authorizerType; } return new Import(scope, id); } - public readonly authorizerId: string + public readonly authorizerId: string; + + public readonly authorizerType: HttpAuthorizerType; constructor(scope: Construct, id: string, props: HttpAuthorizerProps) { super(scope, id); + this.authorizerType = props.type ?? HttpAuthorizerType.JWT; + const authorizerName = props.authorizerName ?? id; - const authorizerType = props.type ?? HttpAuthorizerType.JWT; const identitySource = props.identitySource ?? ['$request.header.Authorization']; const jwtConfiguration = props.jwtConfiguration; - if (authorizerType === HttpAuthorizerType.JWT && !jwtConfiguration) { + if (this.authorizerType === HttpAuthorizerType.JWT && !jwtConfiguration) { throw new Error('jwtConfiguration is required for authorizer type of JWT'); } const resource = new CfnAuthorizer(this, 'Resource', { name: authorizerName, apiId: props.httpApi.httpApiId, - authorizerType, + authorizerType: this.authorizerType, identitySource, jwtConfiguration, }); From 1d1c5dbbc562dcb3076999277081cd3ac8d4ae2e Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 21 Oct 2020 10:50:23 -0400 Subject: [PATCH 07/47] feat: Allow attaching authorizer to Route --- .../aws-apigatewayv2/lib/http/route.ts | 12 +++++++- .../aws-apigatewayv2/test/http/route.test.ts | 30 ++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts index 6f13c46d9e3d5..6e3c49d3d7ed1 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts @@ -1,7 +1,7 @@ import { Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnRoute, CfnRouteProps } from '../apigatewayv2.generated'; -import { IRoute } from '../common'; +import { IAuthorizer, IRoute } from '../common'; import { IHttpApi } from './api'; import { HttpIntegration, IHttpRouteIntegration } from './integration'; @@ -103,6 +103,12 @@ export interface HttpRouteProps extends BatchHttpRouteOptions { * The key to this route. This is a combination of an HTTP method and an HTTP path. */ readonly routeKey: HttpRouteKey; + + /** + * Authorizer for a WebSocket API or an HTTP API. + * @default - No authorizer + */ + readonly authorizer?: IAuthorizer; } /** @@ -137,6 +143,10 @@ export class HttpRoute extends Resource implements IHttpRoute { apiId: props.httpApi.httpApiId, routeKey: props.routeKey.key, target: `integrations/${integration.integrationId}`, + ...props.authorizer ? { + authorizerId: props.authorizer.authorizerId, + authorizationType: props.authorizer.authorizerType, + }: {}, }; const route = new CfnRoute(this, 'Resource', routeProps); diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts index 6d125b5eadc6e..4f848abacc40c 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts @@ -1,7 +1,7 @@ import '@aws-cdk/assert/jest'; import { Stack } from '@aws-cdk/core'; import { - HttpApi, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteIntegrationConfig, HttpRouteKey, IHttpRouteIntegration, + HttpApi, HttpAuthorizer, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteIntegrationConfig, HttpRouteKey, IHttpRouteIntegration, PayloadFormatVersion, } from '../../lib'; @@ -77,6 +77,34 @@ describe('HttpRoute', () => { })).toThrowError(/path must always start with a "\/" and not end with a "\/"/); }); + test('can create route with an authorizer attached', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + const authorizer = new HttpAuthorizer(stack, 'HttpAuthorizer', { + httpApi, + jwtConfiguration: { + audience: ['cognito-pool'], + issuer: 'http://congnito.aws', + }, + }); + + new HttpRoute(stack, 'HttpRoute', { + httpApi, + integration: new DummyIntegration(), + routeKey: HttpRouteKey.with('/books', HttpMethod.GET), + authorizer, + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Integration', { + ApiId: stack.resolve(httpApi.httpApiId), + IntegrationType: 'HTTP_PROXY', + PayloadFormatVersion: '2.0', + IntegrationUri: 'some-uri', + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer'); + }); }); class DummyIntegration implements IHttpRouteIntegration { From ff72b0a35fd2f4487d2b02ba00f027c771acf498 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 21 Oct 2020 11:03:12 -0400 Subject: [PATCH 08/47] test: Update test to check for authorizer on route --- packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts index 4f848abacc40c..26bf4d718cda8 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts @@ -104,6 +104,11 @@ describe('HttpRoute', () => { }); expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer'); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizerId: stack.resolve(authorizer.authorizerId), + AuthorizationType: 'JWT', + }); }); }); From dd579a0b4892f93b4526cd55f8f7cad68e33886a Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 21 Oct 2020 11:05:31 -0400 Subject: [PATCH 09/47] feat: Attach authorizer via api.addRoutes --- packages/@aws-cdk/aws-apigatewayv2/README.md | 7 ++++ .../@aws-cdk/aws-apigatewayv2/lib/http/api.ts | 8 ++++ .../aws-apigatewayv2/test/http/api.test.ts | 37 ++++++++++++++++++- 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 3e1f1aadc4ec2..34f17c52f2a6f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -216,6 +216,13 @@ const authorizer = new Authorizer(stack, { issuer: `https://cognito-idp.${scope.region}.amazonaws.com/${userPool.userPoolId}`, }, }); + +httpApi.addRoutes({ + path: '/books', + methods: [ HttpMethod.GET ], + integration: getBooksIntegration, + authorizer, // Protecting this route via our authorizer +}); ``` ## Metrics diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts index ff75808b5a8d6..5df5e235cd588 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts @@ -2,6 +2,7 @@ import { Metric, MetricOptions } from '@aws-cdk/aws-cloudwatch'; import { Duration, IResource, Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnApi, CfnApiProps } from '../apigatewayv2.generated'; +import { IAuthorizer } from '../common'; import { DefaultDomainMappingOptions } from '../http/stage'; import { IHttpRouteIntegration } from './integration'; import { BatchHttpRouteOptions, HttpMethod, HttpRoute, HttpRouteKey } from './route'; @@ -173,6 +174,12 @@ export interface AddRoutesOptions extends BatchHttpRouteOptions { * @default HttpMethod.ANY */ readonly methods?: HttpMethod[]; + + /** + * Authorizer for a WebSocket API or an HTTP API. + * @default - No authorizer + */ + readonly authorizer?: IAuthorizer; } abstract class HttpApiBase extends Resource implements IHttpApi { // note that this is not exported @@ -335,6 +342,7 @@ export class HttpApi extends HttpApiBase { httpApi: this, routeKey: HttpRouteKey.with(options.path, method), integration: options.integration, + authorizer: options.authorizer, })); } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts index 13ee4e120945d..9cad80cfa8711 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -3,7 +3,7 @@ import { ABSENT } from '@aws-cdk/assert'; import { Metric } from '@aws-cdk/aws-cloudwatch'; import { Code, Function, Runtime } from '@aws-cdk/aws-lambda'; import { Duration, Stack } from '@aws-cdk/core'; -import { HttpApi, HttpMethod, LambdaProxyIntegration } from '../../lib'; +import { HttpApi, HttpMethod, LambdaProxyIntegration, HttpAuthorizer } from '../../lib'; describe('HttpApi', () => { test('default', () => { @@ -233,4 +233,39 @@ describe('HttpApi', () => { Description: 'My Api', }); }); + + test('can attach authorizer to route', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'api'); + + const authorizer = new HttpAuthorizer(stack, 'HttpAuthorizer', { + httpApi, + jwtConfiguration: { + audience: ['cognito-pool'], + issuer: 'http://congnito.aws', + }, + }); + + httpApi.addRoutes({ + path: '/pets', + integration: new LambdaProxyIntegration({ + handler: new Function(stack, 'fn', { + code: Code.fromInline('foo'), + runtime: Runtime.NODEJS_12_X, + handler: 'index.handler', + }), + }), + authorizer, + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Api', { + Name: 'api', + ProtocolType: 'HTTP', + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizerId: stack.resolve(authorizer.authorizerId), + AuthorizationType: 'JWT', + }); + }); }); From 85a9d61ea69ae76e612c708dab8dab7fd9c82983 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 21 Oct 2020 12:55:00 -0400 Subject: [PATCH 10/47] Add integration test --- .../@aws-cdk/aws-apigatewayv2/package.json | 2 + .../test/http/integ.authorizer.expected.json | 261 ++++++++++++++++++ .../http/integ.authorizer.handler/index.ts | 23 ++ .../test/http/integ.authorizer.ts | 34 +++ 4 files changed, 320 insertions(+) create mode 100644 packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.expected.json create mode 100644 packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.handler/index.ts create mode 100644 packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts diff --git a/packages/@aws-cdk/aws-apigatewayv2/package.json b/packages/@aws-cdk/aws-apigatewayv2/package.json index 7c079b71baab1..38cc9e32184aa 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2/package.json @@ -84,6 +84,7 @@ "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-cloudwatch": "0.0.0", + "@aws-cdk/aws-cognito": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.0.4" }, @@ -92,6 +93,7 @@ "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-certificatemanager": "0.0.0", "@aws-cdk/aws-cloudwatch": "0.0.0", + "@aws-cdk/aws-cognito": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.0.4" }, diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.expected.json b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.expected.json new file mode 100644 index 0000000000000..ce4e8ae53b7b6 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.expected.json @@ -0,0 +1,261 @@ +{ + "Resources": { + "MyHttpApi8AEAAC21": { + "Type": "AWS::ApiGatewayV2::Api", + "Properties": { + "Name": "MyHttpApi", + "ProtocolType": "HTTP" + } + }, + "MyHttpApiDefaultStageDCB9BC49": { + "Type": "AWS::ApiGatewayV2::Stage", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "StageName": "$default", + "AutoDeploy": true + } + }, + "MyHttpApiGETAuthorizerIntegMyHttpApiGET16D02385PermissionBB02EBFE": { + "Type": "AWS::Lambda::Permission", + "Properties": { + "Action": "lambda:InvokeFunction", + "FunctionName": { + "Fn::GetAtt": [ + "lambda8B5974B5", + "Arn" + ] + }, + "Principal": "apigateway.amazonaws.com", + "SourceArn": { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":execute-api:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":", + { + "Ref": "MyHttpApi8AEAAC21" + }, + "/*/*/" + ] + ] + } + } + }, + "MyHttpApiGETGETIntegration0052AA15": { + "Type": "AWS::ApiGatewayV2::Integration", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "IntegrationType": "AWS_PROXY", + "IntegrationUri": { + "Fn::GetAtt": [ + "lambda8B5974B5", + "Arn" + ] + }, + "PayloadFormatVersion": "2.0" + } + }, + "MyHttpApiGETE0EFC6F8": { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "RouteKey": "GET /", + "AuthorizationType": "JWT", + "AuthorizerId": { + "Ref": "MyAuthorizer6575980E" + }, + "Target": { + "Fn::Join": [ + "", + [ + "integrations/", + { + "Ref": "MyHttpApiGETGETIntegration0052AA15" + } + ] + ] + } + } + }, + "userpool0AC4AA96": { + "Type": "AWS::Cognito::UserPool", + "Properties": { + "AccountRecoverySetting": { + "RecoveryMechanisms": [ + { + "Name": "verified_phone_number", + "Priority": 1 + }, + { + "Name": "verified_email", + "Priority": 2 + } + ] + }, + "AdminCreateUserConfig": { + "AllowAdminCreateUserOnly": true + }, + "EmailVerificationMessage": "The verification code to your new account is {####}", + "EmailVerificationSubject": "Verify your new account", + "SmsVerificationMessage": "The verification code to your new account is {####}", + "VerificationMessageTemplate": { + "DefaultEmailOption": "CONFIRM_WITH_CODE", + "EmailMessage": "The verification code to your new account is {####}", + "EmailSubject": "Verify your new account", + "SmsMessage": "The verification code to your new account is {####}" + } + } + }, + "MyAuthorizer6575980E": { + "Type": "AWS::ApiGatewayV2::Authorizer", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "AuthorizerType": "JWT", + "IdentitySource": [ + "$request.header.Authorization" + ], + "Name": "MyAuthorizer", + "JwtConfiguration": { + "Audience": [ + { + "Ref": "userpool0AC4AA96" + } + ], + "Issuer": { + "Fn::Join": [ + "", + [ + "https://cognito-idp.", + { + "Ref": "AWS::Region" + }, + ".amazonaws.com/", + { + "Ref": "userpool0AC4AA96" + } + ] + ] + } + } + } + }, + "lambdaServiceRole494E4CA6": { + "Type": "AWS::IAM::Role", + "Properties": { + "AssumeRolePolicyDocument": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "Service": "lambda.amazonaws.com" + } + } + ], + "Version": "2012-10-17" + }, + "ManagedPolicyArns": [ + { + "Fn::Join": [ + "", + [ + "arn:", + { + "Ref": "AWS::Partition" + }, + ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" + ] + ] + } + ] + } + }, + "lambda8B5974B5": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdS3Bucket0AFE1748" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdS3VersionKey8E654BCC" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdS3VersionKey8E654BCC" + } + ] + } + ] + } + ] + ] + } + }, + "Handler": "index.handler", + "Role": { + "Fn::GetAtt": [ + "lambdaServiceRole494E4CA6", + "Arn" + ] + }, + "Runtime": "nodejs10.x" + }, + "DependsOn": [ + "lambdaServiceRole494E4CA6" + ] + } + }, + "Parameters": { + "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdS3Bucket0AFE1748": { + "Type": "String", + "Description": "S3 bucket for asset \"7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cd\"" + }, + "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdS3VersionKey8E654BCC": { + "Type": "String", + "Description": "S3 key for asset version \"7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cd\"" + }, + "AssetParameters7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cdArtifactHashC4761AE9": { + "Type": "String", + "Description": "Artifact hash for asset \"7410bbb25893071ddf955447cf906ac518465ea509469e6b012c28dde8f8b5cd\"" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.handler/index.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.handler/index.ts new file mode 100644 index 0000000000000..afedb7efe3311 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.handler/index.ts @@ -0,0 +1,23 @@ +/* eslint-disable no-console */ + +export const handler = async (event: any, _context: any = {}): Promise => { + const authToken: string = event.authorizationToken; + console.log(`event.authorizationToken = ${authToken}`); + if (authToken === 'allow' || authToken === 'deny') { + return { + principalId: 'user', + policyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: 'execute-api:Invoke', + Effect: authToken, + Resource: event.methodArn, + }, + ], + }, + }; + } else { + throw new Error('Unauthorized'); + } +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts new file mode 100644 index 0000000000000..63a52e5d02ae5 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts @@ -0,0 +1,34 @@ +/// !cdk-integ pragma:ignore-assets +import * as path from 'path'; +import * as cognito from '@aws-cdk/aws-cognito'; +import * as lambda from '@aws-cdk/aws-lambda'; +import { App, Stack } from '@aws-cdk/core'; +import { HttpApi, HttpAuthorizer, HttpMethod, LambdaProxyIntegration } from '../../lib'; + +const app = new App(); +const stack = new Stack(app, 'AuthorizerInteg'); + +const httpApi = new HttpApi(stack, 'MyHttpApi'); + +const userPool = new cognito.UserPool(stack, 'userpool'); + +const authorizer = new HttpAuthorizer(stack, 'MyAuthorizer', { + httpApi, + jwtConfiguration: { + audience: [userPool.userPoolId], + issuer: `https://cognito-idp.${stack.region}.amazonaws.com/${userPool.userPoolId}`, + }, +}); + +const handler = new lambda.Function(stack, 'lambda', { + runtime: lambda.Runtime.NODEJS_10_X, + handler: 'index.handler', + code: lambda.AssetCode.fromAsset(path.join(__dirname, 'integ.authorizer.handler')), +}); + +httpApi.addRoutes({ + path: '/', + methods: [HttpMethod.GET], + integration: new LambdaProxyIntegration({ handler }), + authorizer, +}); From 3abdeb8a8550ac0a5979749b84eb0390df576b84 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 21 Oct 2020 12:56:04 -0400 Subject: [PATCH 11/47] Reference stack in docs --- packages/@aws-cdk/aws-apigatewayv2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 34f17c52f2a6f..33221e9c29677 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -213,7 +213,7 @@ const authorizer = new Authorizer(stack, { httpApi, jwtConfiguration: { audience: [userPool.userPoolId], - issuer: `https://cognito-idp.${scope.region}.amazonaws.com/${userPool.userPoolId}`, + issuer: `https://cognito-idp.${stack.region}.amazonaws.com/${userPool.userPoolId}`, }, }); From 25c78724140e50358377da6143487069123aa0ce Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 21 Oct 2020 12:58:23 -0400 Subject: [PATCH 12/47] Add verification steps --- .../aws-apigatewayv2/test/http/integ.authorizer.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts index 63a52e5d02ae5..c5b16cf233680 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts @@ -5,6 +5,13 @@ import * as lambda from '@aws-cdk/aws-lambda'; import { App, Stack } from '@aws-cdk/core'; import { HttpApi, HttpAuthorizer, HttpMethod, LambdaProxyIntegration } from '../../lib'; +/* + * Stack verification steps: + * * `curl -s -o /dev/null -w "%{http_code}" ` should return 401 + * * `curl -s -o /dev/null -w "%{http_code}" -H 'Authorization: deny' ` should return 403 + * * `curl -s -o /dev/null -w "%{http_code}" -H 'Authorization: allow' ` should return 200 + */ + const app = new App(); const stack = new Stack(app, 'AuthorizerInteg'); From a3dc8b987cfaa0c0016f4e25302faba818ef2cc7 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Thu, 22 Oct 2020 14:38:33 -0400 Subject: [PATCH 13/47] feat: Add authorizationScopes to Route --- packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts | 9 +++++++++ packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts | 9 +++++++++ packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts | 2 ++ .../@aws-cdk/aws-apigatewayv2/test/http/route.test.ts | 2 ++ 4 files changed, 22 insertions(+) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts index 5df5e235cd588..da6c809229cad 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts @@ -180,6 +180,14 @@ export interface AddRoutesOptions extends BatchHttpRouteOptions { * @default - No authorizer */ readonly authorizer?: IAuthorizer; + + /** + * Scopes required to access this route + * + * Useless if no authorizer is passed in + * @default - No scopes + */ + readonly authorizationScopes?: string[]; } abstract class HttpApiBase extends Resource implements IHttpApi { // note that this is not exported @@ -343,6 +351,7 @@ export class HttpApi extends HttpApiBase { routeKey: HttpRouteKey.with(options.path, method), integration: options.integration, authorizer: options.authorizer, + authorizationScopes: options.authorizationScopes, })); } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts index 6e3c49d3d7ed1..4bbe0b8602798 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts @@ -109,6 +109,14 @@ export interface HttpRouteProps extends BatchHttpRouteOptions { * @default - No authorizer */ readonly authorizer?: IAuthorizer; + + /** + * Scopes required to access this route + * + * Useless if no authorizer is passed in + * @default - No scopes + */ + readonly authorizationScopes?: string[]; } /** @@ -146,6 +154,7 @@ export class HttpRoute extends Resource implements IHttpRoute { ...props.authorizer ? { authorizerId: props.authorizer.authorizerId, authorizationType: props.authorizer.authorizerType, + authorizationScopes: props.authorizationScopes, }: {}, }; diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts index 9cad80cfa8711..e6fbc6172234b 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -256,6 +256,7 @@ describe('HttpApi', () => { }), }), authorizer, + authorizationScopes: ['pets:read'], }); expect(stack).toHaveResource('AWS::ApiGatewayV2::Api', { @@ -266,6 +267,7 @@ describe('HttpApi', () => { expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { AuthorizerId: stack.resolve(authorizer.authorizerId), AuthorizationType: 'JWT', + AuthorizationScopes: ['pets:read'], }); }); }); diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts index 26bf4d718cda8..42dfe58019409 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts @@ -94,6 +94,7 @@ describe('HttpRoute', () => { integration: new DummyIntegration(), routeKey: HttpRouteKey.with('/books', HttpMethod.GET), authorizer, + authorizationScopes: ['books:read'], }); expect(stack).toHaveResource('AWS::ApiGatewayV2::Integration', { @@ -108,6 +109,7 @@ describe('HttpRoute', () => { expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { AuthorizerId: stack.resolve(authorizer.authorizerId), AuthorizationType: 'JWT', + AuthorizationScopes: ['books:read'], }); }); }); From 4261a978e368f19ad4590d2383a4e1589feb34ee Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Fri, 23 Oct 2020 15:35:12 -0400 Subject: [PATCH 14/47] Update example to correct audience --- packages/@aws-cdk/aws-apigatewayv2/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 33221e9c29677..7232fe080b93a 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -209,10 +209,12 @@ API Gateway supports multiple mechanisms for [controlling and managing access to const userPool = new UserPool(stack, 'my-pool'); const httpApi = new HttpApi(stack, 'HttpApi'); +const appClient = userPool.addClient('my-app-client'); + const authorizer = new Authorizer(stack, { httpApi, jwtConfiguration: { - audience: [userPool.userPoolId], + audience: [appClient.userPoolClientId], issuer: `https://cognito-idp.${stack.region}.amazonaws.com/${userPool.userPoolId}`, }, }); From 5fe2645f0fd09876c6463c25b0c3a33761598708 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Fri, 23 Oct 2020 15:45:50 -0400 Subject: [PATCH 15/47] Update integ test to include app client --- .../test/http/integ.authorizer.expected.json | 28 ++++++++++++++++++- .../test/http/integ.authorizer.ts | 4 ++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.expected.json b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.expected.json index ce4e8ae53b7b6..e29474ca6dcbc 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.expected.json @@ -123,6 +123,32 @@ } } }, + "userpoolmyclientFAD947AB": { + "Type": "AWS::Cognito::UserPoolClient", + "Properties": { + "UserPoolId": { + "Ref": "userpool0AC4AA96" + }, + "AllowedOAuthFlows": [ + "implicit", + "code" + ], + "AllowedOAuthFlowsUserPoolClient": true, + "AllowedOAuthScopes": [ + "profile", + "phone", + "email", + "openid", + "aws.cognito.signin.user.admin" + ], + "CallbackURLs": [ + "https://example.com" + ], + "SupportedIdentityProviders": [ + "COGNITO" + ] + } + }, "MyAuthorizer6575980E": { "Type": "AWS::ApiGatewayV2::Authorizer", "Properties": { @@ -137,7 +163,7 @@ "JwtConfiguration": { "Audience": [ { - "Ref": "userpool0AC4AA96" + "Ref": "userpoolmyclientFAD947AB" } ], "Issuer": { diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts index c5b16cf233680..a187a3b96c846 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts @@ -19,10 +19,12 @@ const httpApi = new HttpApi(stack, 'MyHttpApi'); const userPool = new cognito.UserPool(stack, 'userpool'); +const client = userPool.addClient('my-client'); + const authorizer = new HttpAuthorizer(stack, 'MyAuthorizer', { httpApi, jwtConfiguration: { - audience: [userPool.userPoolId], + audience: [client.userPoolClientId], issuer: `https://cognito-idp.${stack.region}.amazonaws.com/${userPool.userPoolId}`, }, }); From 729c67980fb8c8b4590662f1c66cdce7e8d22bae Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 28 Oct 2020 15:20:59 +0000 Subject: [PATCH 16/47] Update packages/@aws-cdk/aws-apigatewayv2/README.md Co-authored-by: Niranjan Jayakar --- packages/@aws-cdk/aws-apigatewayv2/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 7232fe080b93a..cb229709d7c3f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -203,7 +203,9 @@ with 3 API mapping resources across different APIs and Stages. ### Managing access to HTTP APIs -API Gateway supports multiple mechanisms for [controlling and managing access to your HTTP API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control.html) through authorizers. Here's an example with using a Cognito User Pool as the identity source. +API Gateway supports multiple mechanisms for [controlling and managing access to your HTTP +API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control.html) through authorizers. +Here's an example with using a Cognito User Pool as the identity source. ```ts const userPool = new UserPool(stack, 'my-pool'); From af3c47ca28c8098335f4db3ef7babc91aced2d08 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 28 Oct 2020 11:21:34 -0400 Subject: [PATCH 17/47] Use HttpAuthorizer for example --- packages/@aws-cdk/aws-apigatewayv2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index cb229709d7c3f..9f3e57905e7a3 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -213,7 +213,7 @@ const httpApi = new HttpApi(stack, 'HttpApi'); const appClient = userPool.addClient('my-app-client'); -const authorizer = new Authorizer(stack, { +const authorizer = new HttpAuthorizer(stack, { httpApi, jwtConfiguration: { audience: [appClient.userPoolClientId], From 71305f536a8eded4e87385cd2708baf992971093 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 28 Oct 2020 15:22:45 +0000 Subject: [PATCH 18/47] Update packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts Co-authored-by: Niranjan Jayakar --- packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index 5d8b98641837f..c7b4e98d8be60 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -12,7 +12,7 @@ export enum HttpAuthorizerType { /** JSON Web Tokens */ JWT = 'JWT', /** Lambda Authorizer */ - REQUEST = 'LAMBDA', + LAMBDA = 'REQUEST', } /** @@ -131,4 +131,3 @@ export class HttpAuthorizer extends Resource implements IAuthorizer { this.authorizerId = resource.ref; } } - From d1745ae3436b6895852fca7c842a005fc3cebe7d Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 28 Oct 2020 15:23:04 +0000 Subject: [PATCH 19/47] Update packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts Co-authored-by: Niranjan Jayakar --- packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts index da6c809229cad..34978bfdfe59f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts @@ -176,7 +176,7 @@ export interface AddRoutesOptions extends BatchHttpRouteOptions { readonly methods?: HttpMethod[]; /** - * Authorizer for a WebSocket API or an HTTP API. + * Authorizer to be associated to these routes. * @default - No authorizer */ readonly authorizer?: IAuthorizer; From 3e8674e1f9a1fd9ac65e6c93c1aaf4df7afea040 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 28 Oct 2020 14:30:12 -0400 Subject: [PATCH 20/47] Update based on feedback (not finished) --- packages/@aws-cdk/aws-apigatewayv2/README.md | 2 +- .../aws-apigatewayv2/lib/common/authorizer.ts | 7 -- .../aws-apigatewayv2/lib/http/authorizer.ts | 84 ++++++++----------- .../test/http/authorizer.test.ts | 8 +- 4 files changed, 41 insertions(+), 60 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 9f3e57905e7a3..fca6084611237 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -213,7 +213,7 @@ const httpApi = new HttpApi(stack, 'HttpApi'); const appClient = userPool.addClient('my-app-client'); -const authorizer = new HttpAuthorizer(stack, { +const authorizer = new HttpJwtAuthorizer(stack, { httpApi, jwtConfiguration: { audience: [appClient.userPoolClientId], diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts index c596616114b6b..609d469a572fa 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/common/authorizer.ts @@ -1,5 +1,4 @@ import { IResource } from '@aws-cdk/core'; -import { HttpAuthorizerType } from '../http'; /** * Represents an Authorizer. @@ -10,10 +9,4 @@ export interface IAuthorizer extends IResource { * @attribute */ readonly authorizerId: string - - /** - * Type of authorizer - * @attribute - */ - readonly authorizerType: HttpAuthorizerType } diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index c7b4e98d8be60..53dd009fd5024 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -11,65 +11,58 @@ import { IHttpApi } from './api'; export enum HttpAuthorizerType { /** JSON Web Tokens */ JWT = 'JWT', + /** Lambda Authorizer */ LAMBDA = 'REQUEST', } /** - * Specifies the configuration of a JWT authorizer + * Properties to initialize an instance of `HttpAuthorizer`. */ -export interface HttpJwtConfiguration { +export interface HttpAuthorizerProps { /** - * A list of the intended recipients of the JWT + * Name of the authorizer + * + * @default - id of the HttpAuthorizer construct. */ - readonly audience: string[] + readonly authorizerName?: string /** - * The base domain of the identity provider that issues JSON Web Tokens. + * HTTP Api to attach the authorizer to */ - readonly issuer: string; + readonly httpApi: IHttpApi } /** - * Reference to an http authorizer + * Describes an instance of `IHttpAuthorizer` */ -export interface HttpAuthorizerAttributes { - /** - * Id of the Authorizer - */ - readonly authorizerId: string - +export interface IHttpAuthorizer extends IAuthorizer { /** * Type of authorizer + * @attribute */ - readonly authorizerType: HttpAuthorizerType + readonly authorizerType: HttpAuthorizerType; } - /** - * Properties to initialize an instance of `HttpAuthorizer`. + * Specifies the configuration of a JWT authorizer */ -export interface HttpAuthorizerProps { +export interface JwtConfiguration { /** - * Name of the authorizer - * - * @default - id of the HttpAuthorizer construct. - */ - readonly authorizerName?: string - - /** - * HTTP Api to attach the authorizer to + * A list of the intended recipients of the JWT */ - readonly httpApi: IHttpApi + readonly audience: string[] /** - * The authorizer type. Specify REQUEST for a Lambda function using incoming request parameters. - * Specify JWT to use JSON Web Tokens (supported only for HTTP APIs). - * - * @default JWT + * The base domain of the identity provider that issues JSON Web Tokens. */ - readonly type?: HttpAuthorizerType; + readonly issuer: string; +} +/** + * Properties to initialize an instance of `HttpAuthorizer`. + */ +export interface HttpJwtAuthorizerProps extends HttpAuthorizerProps { /** * The identity source for which authorization is requested. * @@ -83,49 +76,44 @@ export interface HttpAuthorizerProps { * * @default - No configuration */ - readonly jwtConfiguration?: HttpJwtConfiguration + readonly jwtConfiguration: JwtConfiguration } /** - * Create a new Authorizer for Http APIs + * Create a new JwtAuthorizer for Http APIs * @resource AWS::ApiGatewayV2::Authorizer */ -export class HttpAuthorizer extends Resource implements IAuthorizer { +export class HttpJwtAuthorizer extends Resource implements IHttpAuthorizer { /** * Import an existing HTTP Authorizer into this CDK app. */ - public static fromAuthorizerAttributes(scope: Construct, id: string, attrs: HttpAuthorizerAttributes): IAuthorizer { + public static fromAuthorizerId(scope: Construct, id: string, authorizerId: string): IAuthorizer { class Import extends Resource implements IAuthorizer { - public readonly authorizerId = attrs.authorizerId; - public readonly authorizerType = attrs.authorizerType; + public readonly authorizerId = authorizerId; } return new Import(scope, id); } public readonly authorizerId: string; + public readonly authorizerType: HttpAuthorizerType = HttpAuthorizerType.JWT; + public readonly identitySource: string[] = ['$request.header.Authorization'] - public readonly authorizerType: HttpAuthorizerType; - - constructor(scope: Construct, id: string, props: HttpAuthorizerProps) { + constructor(scope: Construct, id: string, props: HttpJwtAuthorizerProps) { super(scope, id); - this.authorizerType = props.type ?? HttpAuthorizerType.JWT; - const authorizerName = props.authorizerName ?? id; - const identitySource = props.identitySource ?? ['$request.header.Authorization']; - const jwtConfiguration = props.jwtConfiguration; - if (this.authorizerType === HttpAuthorizerType.JWT && !jwtConfiguration) { - throw new Error('jwtConfiguration is required for authorizer type of JWT'); + if (props.identitySource) { + this.identitySource = props.identitySource; } const resource = new CfnAuthorizer(this, 'Resource', { name: authorizerName, apiId: props.httpApi.httpApiId, authorizerType: this.authorizerType, - identitySource, - jwtConfiguration, + identitySource: this.identitySource, + jwtConfiguration: props.jwtConfiguration, }); this.authorizerId = resource.ref; diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts index a7e035b8705a2..95c032abe1ca3 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts @@ -1,15 +1,15 @@ import '@aws-cdk/assert/jest'; import { Stack } from '@aws-cdk/core'; import { - HttpApi, HttpAuthorizer, + HttpApi, HttpJwtAuthorizer, } from '../../lib'; -describe('HttpAuthorizer', () => { +describe('JwtAuthorizer', () => { test('default', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'HttpApi'); - new HttpAuthorizer(stack, 'HttpAuthorizer', { + new HttpJwtAuthorizer(stack, 'HttpAuthorizer', { httpApi, jwtConfiguration: { audience: ['cognito-pool'], @@ -33,7 +33,7 @@ describe('HttpAuthorizer', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'HttpApi'); - new HttpAuthorizer(stack, 'HttpAuthorizer', { + new HttpJwtAuthorizer(stack, 'HttpAuthorizer', { httpApi, authorizerName: 'my-authorizer', jwtConfiguration: { From 422f07de39f4adce0e0105ab72ba2e4e9b44885b Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 28 Oct 2020 14:52:27 -0400 Subject: [PATCH 21/47] Update test --- packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts | 4 ++-- packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts | 5 +++-- packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts | 4 ++-- .../@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts | 4 ++-- packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts index 34978bfdfe59f..faf4a9464d1f7 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts @@ -2,8 +2,8 @@ import { Metric, MetricOptions } from '@aws-cdk/aws-cloudwatch'; import { Duration, IResource, Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnApi, CfnApiProps } from '../apigatewayv2.generated'; -import { IAuthorizer } from '../common'; import { DefaultDomainMappingOptions } from '../http/stage'; +import { IHttpAuthorizer } from './authorizer'; import { IHttpRouteIntegration } from './integration'; import { BatchHttpRouteOptions, HttpMethod, HttpRoute, HttpRouteKey } from './route'; import { HttpStage, HttpStageOptions } from './stage'; @@ -179,7 +179,7 @@ export interface AddRoutesOptions extends BatchHttpRouteOptions { * Authorizer to be associated to these routes. * @default - No authorizer */ - readonly authorizer?: IAuthorizer; + readonly authorizer?: IHttpAuthorizer; /** * Scopes required to access this route diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts index 4bbe0b8602798..9ba93ce08ee61 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts @@ -1,8 +1,9 @@ import { Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnRoute, CfnRouteProps } from '../apigatewayv2.generated'; -import { IAuthorizer, IRoute } from '../common'; +import { IRoute } from '../common'; import { IHttpApi } from './api'; +import { IHttpAuthorizer } from './authorizer'; import { HttpIntegration, IHttpRouteIntegration } from './integration'; /** @@ -108,7 +109,7 @@ export interface HttpRouteProps extends BatchHttpRouteOptions { * Authorizer for a WebSocket API or an HTTP API. * @default - No authorizer */ - readonly authorizer?: IAuthorizer; + readonly authorizer?: IHttpAuthorizer; /** * Scopes required to access this route diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts index e6fbc6172234b..cf43177a0b0d0 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -3,7 +3,7 @@ import { ABSENT } from '@aws-cdk/assert'; import { Metric } from '@aws-cdk/aws-cloudwatch'; import { Code, Function, Runtime } from '@aws-cdk/aws-lambda'; import { Duration, Stack } from '@aws-cdk/core'; -import { HttpApi, HttpMethod, LambdaProxyIntegration, HttpAuthorizer } from '../../lib'; +import { HttpApi, HttpMethod, LambdaProxyIntegration, HttpJwtAuthorizer } from '../../lib'; describe('HttpApi', () => { test('default', () => { @@ -238,7 +238,7 @@ describe('HttpApi', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'api'); - const authorizer = new HttpAuthorizer(stack, 'HttpAuthorizer', { + const authorizer = new HttpJwtAuthorizer(stack, 'HttpAuthorizer', { httpApi, jwtConfiguration: { audience: ['cognito-pool'], diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts index a187a3b96c846..0681fa93dc4ef 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import * as cognito from '@aws-cdk/aws-cognito'; import * as lambda from '@aws-cdk/aws-lambda'; import { App, Stack } from '@aws-cdk/core'; -import { HttpApi, HttpAuthorizer, HttpMethod, LambdaProxyIntegration } from '../../lib'; +import { HttpApi, HttpJwtAuthorizer, HttpMethod, LambdaProxyIntegration } from '../../lib'; /* * Stack verification steps: @@ -21,7 +21,7 @@ const userPool = new cognito.UserPool(stack, 'userpool'); const client = userPool.addClient('my-client'); -const authorizer = new HttpAuthorizer(stack, 'MyAuthorizer', { +const authorizer = new HttpJwtAuthorizer(stack, 'MyAuthorizer', { httpApi, jwtConfiguration: { audience: [client.userPoolClientId], diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts index 42dfe58019409..b16269135871d 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts @@ -1,7 +1,7 @@ import '@aws-cdk/assert/jest'; import { Stack } from '@aws-cdk/core'; import { - HttpApi, HttpAuthorizer, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteIntegrationConfig, HttpRouteKey, IHttpRouteIntegration, + HttpApi, HttpJwtAuthorizer, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteIntegrationConfig, HttpRouteKey, IHttpRouteIntegration, PayloadFormatVersion, } from '../../lib'; @@ -81,7 +81,7 @@ describe('HttpRoute', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'HttpApi'); - const authorizer = new HttpAuthorizer(stack, 'HttpAuthorizer', { + const authorizer = new HttpJwtAuthorizer(stack, 'HttpAuthorizer', { httpApi, jwtConfiguration: { audience: ['cognito-pool'], From d6c3a520447905e64b3b64b0189b92d069dc2190 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 28 Oct 2020 14:57:30 -0400 Subject: [PATCH 22/47] Update from Authorizer --- packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index 53dd009fd5024..cae988aab94f7 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -88,9 +88,10 @@ export class HttpJwtAuthorizer extends Resource implements IHttpAuthorizer { /** * Import an existing HTTP Authorizer into this CDK app. */ - public static fromAuthorizerId(scope: Construct, id: string, authorizerId: string): IAuthorizer { - class Import extends Resource implements IAuthorizer { + public static fromAuthorizerId(scope: Construct, id: string, authorizerId: string): IHttpAuthorizer { + class Import extends Resource implements IHttpAuthorizer { public readonly authorizerId = authorizerId; + public readonly authorizerType = HttpAuthorizerType.JWT; } return new Import(scope, id); } From e9ccada94a515b8d24e4b2e9306b28e0049f779e Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 28 Oct 2020 15:04:50 -0400 Subject: [PATCH 23/47] Spelling error --- packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index cae988aab94f7..e7403dfa14c2d 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -6,7 +6,7 @@ import { IAuthorizer } from '../common'; import { IHttpApi } from './api'; /** - * Supported HTTP methods + * Supported Authorizer types */ export enum HttpAuthorizerType { /** JSON Web Tokens */ From 175fab69c8ff96ff34119d1523e8c6065fb26ef6 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 28 Oct 2020 15:24:37 -0400 Subject: [PATCH 24/47] Don't make identitySource public --- packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index e7403dfa14c2d..99fa70b0c930d 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -98,22 +98,19 @@ export class HttpJwtAuthorizer extends Resource implements IHttpAuthorizer { public readonly authorizerId: string; public readonly authorizerType: HttpAuthorizerType = HttpAuthorizerType.JWT; - public readonly identitySource: string[] = ['$request.header.Authorization'] constructor(scope: Construct, id: string, props: HttpJwtAuthorizerProps) { super(scope, id); const authorizerName = props.authorizerName ?? id; - if (props.identitySource) { - this.identitySource = props.identitySource; - } + const identitySource = props.identitySource ?? ['$request.header.Authorization']; const resource = new CfnAuthorizer(this, 'Resource', { name: authorizerName, apiId: props.httpApi.httpApiId, authorizerType: this.authorizerType, - identitySource: this.identitySource, + identitySource, jwtConfiguration: props.jwtConfiguration, }); From 3315fbd42b876ce2edb26a380689509d0c14bf7a Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Tue, 3 Nov 2020 10:16:23 -0400 Subject: [PATCH 25/47] Fix formatting --- packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts | 2 +- packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts index 906373ad98151..245ba6aa379c8 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -280,7 +280,7 @@ describe('HttpApi', () => { expect(api.apiEndpoint).toBeDefined(); }); - + test('can attach authorizer to route', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'api'); diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts index 575b6021022a8..e28b049c6afe4 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts @@ -2,7 +2,7 @@ import '@aws-cdk/assert/jest'; import { Stack } from '@aws-cdk/core'; import { HttpApi, HttpConnectionType, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteIntegrationConfig, HttpRouteKey, IHttpRouteIntegration, - PayloadFormatVersion, HttpJwtAuthorizer + PayloadFormatVersion, HttpJwtAuthorizer, } from '../../lib'; describe('HttpRoute', () => { @@ -113,7 +113,7 @@ describe('HttpRoute', () => { }); expect(stack).not.toHaveResource('AWS::ApiGatewayV2::VpcLink'); }); - + test('can create route with an authorizer attached', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'HttpApi'); @@ -148,6 +148,7 @@ describe('HttpRoute', () => { AuthorizationType: 'JWT', AuthorizationScopes: ['books:read'], }); + }); }); class DummyIntegration implements IHttpRouteIntegration { From 5e6fb78194eabacc2c3794b0ee073d7cdb03ea82 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Fri, 6 Nov 2020 15:15:48 -0400 Subject: [PATCH 26/47] Update packages/@aws-cdk/aws-apigatewayv2/README.md Co-authored-by: Niranjan Jayakar --- packages/@aws-cdk/aws-apigatewayv2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 018a6cd1b6fc6..e960a01951435 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -23,7 +23,7 @@ - [Cross Origin Resource Sharing (CORS)](#cross-origin-resource-sharing-cors) - [Publishing HTTP APIs](#publishing-http-apis) - [Custom Domain](#custom-domain) - - [Managing access to HTTP APIs](#managing-access-to-http-apis) + - [Managing access](#managing-access) - [Metrics](#metrics) - [VPC Link](#vpc-link) - [Private Integration](#private-integration) From fcc1565eec0c84ccc7d90fa89372308fca90b8dd Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Fri, 6 Nov 2020 15:16:02 -0400 Subject: [PATCH 27/47] Update packages/@aws-cdk/aws-apigatewayv2/README.md Co-authored-by: Niranjan Jayakar --- packages/@aws-cdk/aws-apigatewayv2/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index e960a01951435..d25159165cbde 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -205,7 +205,7 @@ with 3 API mapping resources across different APIs and Stages. | api | beta | `https://${domainName}/bar` | | apiDemo | $default | `https://${domainName}/demo` | -### Managing access to HTTP APIs +### Managing access API Gateway supports multiple mechanisms for [controlling and managing access to your HTTP API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control.html) through authorizers. From 69151c2209c5f6310cf49993d3bae32aab039a21 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Fri, 6 Nov 2020 15:16:25 -0400 Subject: [PATCH 28/47] Update packages/@aws-cdk/aws-apigatewayv2/README.md Co-authored-by: Niranjan Jayakar --- packages/@aws-cdk/aws-apigatewayv2/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index d25159165cbde..a4713f84847ae 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -209,7 +209,17 @@ with 3 API mapping resources across different APIs and Stages. API Gateway supports multiple mechanisms for [controlling and managing access to your HTTP API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control.html) through authorizers. -Here's an example with using a Cognito User Pool as the identity source. + +#### JWT Authorizer + +The JWT authorizer authorizes access via JSON Web Tokens (JWT) as part of the [OpenId +Connect](https://openid.net/specs/openid-connect-core-1_0.html) and [OAuth](https://oauth.net/2/) +frameworks. + +When configured on a route, the API Gateway service validates the JWTs submitted by the client, and allows or denies +access based on its content. + +The following sets up a JWT authorizer using a Cognito user pool as the identity source. ```ts const userPool = new UserPool(stack, 'my-pool'); From f0c78deeb9ec4008e0e2e4986e84be6b81daba01 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Fri, 6 Nov 2020 15:17:02 -0400 Subject: [PATCH 29/47] Update packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts Co-authored-by: Niranjan Jayakar --- .../@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts index 0681fa93dc4ef..bb08c9dd2edae 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts @@ -30,7 +30,7 @@ const authorizer = new HttpJwtAuthorizer(stack, 'MyAuthorizer', { }); const handler = new lambda.Function(stack, 'lambda', { - runtime: lambda.Runtime.NODEJS_10_X, + runtime: lambda.Runtime.NODEJS_12_X, handler: 'index.handler', code: lambda.AssetCode.fromAsset(path.join(__dirname, 'integ.authorizer.handler')), }); From fa244147e03a8aaf0d986ae241f78baf7351c581 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Sun, 8 Nov 2020 01:38:38 -0400 Subject: [PATCH 30/47] Move cognito to devDependencies --- packages/@aws-cdk/aws-apigatewayv2/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/package.json b/packages/@aws-cdk/aws-apigatewayv2/package.json index c855ea6a7b7a1..c8f00c60a5335 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2/package.json @@ -74,6 +74,7 @@ "license": "Apache-2.0", "devDependencies": { "@aws-cdk/assert": "0.0.0", + "@aws-cdk/aws-cognito": "0.0.0", "cdk-build-tools": "0.0.0", "cdk-integ-tools": "0.0.0", "cfn2ts": "0.0.0", @@ -85,7 +86,6 @@ "@aws-cdk/aws-iam": "0.0.0", "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-cloudwatch": "0.0.0", - "@aws-cdk/aws-cognito": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.2.0" }, @@ -95,7 +95,6 @@ "@aws-cdk/aws-lambda": "0.0.0", "@aws-cdk/aws-certificatemanager": "0.0.0", "@aws-cdk/aws-cloudwatch": "0.0.0", - "@aws-cdk/aws-cognito": "0.0.0", "@aws-cdk/core": "0.0.0", "constructs": "^3.2.0" }, From 52372277d6062f28909f3c85d536537397d80a46 Mon Sep 17 00:00:00 2001 From: Niranjan Jayakar Date: Tue, 8 Dec 2020 14:21:20 +0000 Subject: [PATCH 31/47] authorizer bind pattern + separate package A couple of changes done here - * use a better bind() pattern and model `userpoolauthorizer`. * move authorizers into its own package --- .../aws-apigatewayv2-authorizers/.eslintrc.js | 3 + .../aws-apigatewayv2-authorizers/.gitignore | 17 ++ .../aws-apigatewayv2-authorizers/.npmignore | 28 +++ .../aws-apigatewayv2-authorizers/LICENSE | 201 ++++++++++++++++++ .../aws-apigatewayv2-authorizers/NOTICE | 2 + .../aws-apigatewayv2-authorizers/README.md | 33 +++ .../jest.config.js | 10 + .../lib/http/index.ts | 1 + .../lib/http/user-pool.ts | 69 ++++++ .../aws-apigatewayv2-authorizers/lib/index.ts | 1 + .../aws-apigatewayv2-authorizers/package.json | 100 +++++++++ .../test/http/integ.user-pool.expected.json} | 0 .../test/http/integ.user-pool.ts} | 17 +- .../test/http/user-pool.test.ts | 83 ++++++++ .../test/integ.user-pool.handler}/index.ts | 0 .../@aws-cdk/aws-apigatewayv2/lib/http/api.ts | 13 +- .../aws-apigatewayv2/lib/http/authorizer.ts | 126 +++++++---- .../aws-apigatewayv2/lib/http/route.ts | 25 +-- .../aws-apigatewayv2/test/http/api.test.ts | 34 ++- .../test/http/authorizer.test.ts | 66 ++++-- .../aws-apigatewayv2/test/http/route.test.ts | 25 +-- 21 files changed, 724 insertions(+), 130 deletions(-) create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/.eslintrc.js create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/.gitignore create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/.npmignore create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/LICENSE create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/NOTICE create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/jest.config.js create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/user-pool.ts create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/index.ts create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json rename packages/@aws-cdk/{aws-apigatewayv2/test/http/integ.authorizer.expected.json => aws-apigatewayv2-authorizers/test/http/integ.user-pool.expected.json} (100%) rename packages/@aws-cdk/{aws-apigatewayv2/test/http/integ.authorizer.ts => aws-apigatewayv2-authorizers/test/http/integ.user-pool.ts} (67%) create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/user-pool.test.ts rename packages/@aws-cdk/{aws-apigatewayv2/test/http/integ.authorizer.handler => aws-apigatewayv2-authorizers/test/integ.user-pool.handler}/index.ts (100%) diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/.eslintrc.js b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.eslintrc.js new file mode 100644 index 0000000000000..61dd8dd001f63 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.eslintrc.js @@ -0,0 +1,3 @@ +const baseConfig = require('cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/.gitignore b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.gitignore new file mode 100644 index 0000000000000..becda34c45624 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.gitignore @@ -0,0 +1,17 @@ +*.d.ts +*.generated.ts +*.js +*.js.map +*.snk +.jsii +.LAST_BUILD +.LAST_PACKAGE +nyc.config.js +.nyc_output +coverage +dist +tsconfig.json +!.eslintrc.js +!jest.config.js + +junit.xml \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/.npmignore b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.npmignore new file mode 100644 index 0000000000000..093c734b1bd2f --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/.npmignore @@ -0,0 +1,28 @@ +# The basics +*.ts +*.tgz +*.snk +!*.d.ts +!*.js +**/cdk.out + +# Coverage +coverage +.nyc_output +.nycrc + +# Build gear +dist +.LAST_BUILD +.LAST_PACKAGE + +*.tsbuildinfo +tsconfig.json +!.jsii +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +test/ \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/LICENSE b/packages/@aws-cdk/aws-apigatewayv2-authorizers/LICENSE new file mode 100644 index 0000000000000..b71ec1688783a --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/NOTICE b/packages/@aws-cdk/aws-apigatewayv2-authorizers/NOTICE new file mode 100644 index 0000000000000..bfccac9a7f69c --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md new file mode 100644 index 0000000000000..4599d3a2d9abf --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md @@ -0,0 +1,33 @@ +## AWS APIGatewayv2 Authorizers + +--- + +![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) + +> The APIs of higher level constructs in this module are experimental and under active development. They are subject to non-backward compatible changes or removal in any future version. These are not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be announced in the release notes. This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package. + +--- + + +## Table of Contents + +- [HTTP APIs](#http-apis) +- [JWT Authorizers](#jwt-authorizers) + - [User Pool Authorizer](#user-pool-authorizer) + +## HTTP APIs + +API Gateway supports multiple mechanisms for controlling and managing access to your HTTP API. They are mainly +classified into Lambda Authorizers, JWT authorizers and standard AWS IAM roles and policies. More information is +available at [Controlling and managing access to an HTTP +API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control.html). + +## JWT Authorizers + +JWT authorizers allow the use of JSON Web Tokens (JWTs) as part of [OpenID +Connect](https://openid.net/specs/openid-connect-core-1_0.html) and [OAuth 2.0](https://oauth.net/2/) frameworks to +allow and restrict clients from accessing HTTP APIs. + +### User Pool Authorizer + + \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/jest.config.js b/packages/@aws-cdk/aws-apigatewayv2-authorizers/jest.config.js new file mode 100644 index 0000000000000..b5ccdecc15ee0 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/jest.config.js @@ -0,0 +1,10 @@ +const baseConfig = require('cdk-build-tools/config/jest.config'); +module.exports = { + ...baseConfig, + coverageThreshold: { + global: { + ...baseConfig.coverageThreshold.global, + branches: 70, + }, + }, +}; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts new file mode 100644 index 0000000000000..523c972ba246f --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts @@ -0,0 +1 @@ +export * from './user-pool'; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/user-pool.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/user-pool.ts new file mode 100644 index 0000000000000..4a251b8eb7406 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/user-pool.ts @@ -0,0 +1,69 @@ +import { HttpAuthorizer, HttpAuthorizerType, HttpRouteAuthorizerBindOptions, HttpRouteAuthorizerConfig, IHttpRouteAuthorizer } from '@aws-cdk/aws-apigatewayv2'; +import { IUserPool, IUserPoolClient } from '@aws-cdk/aws-cognito'; +import { Stack, Token } from '@aws-cdk/core'; + +/** + * Properties to initialize UserPoolAuthorizer. + */ +export interface UserPoolAuthorizerProps { + /** + * The user pool client that should be used to authorize requests with the user pool. + */ + readonly userPoolClient: IUserPoolClient; + + /** + * The associated user pool + */ + readonly userPool: IUserPool; + + /** + * The AWS region in which the user pool is present + * @default - same region as the Route the authorizer is attached to. + */ + readonly userPoolRegion?: string; + + /** + * The name of the authorizer + * @default 'UserPoolAuthorizer' + */ + readonly authorizerName?: string; + + /** + * The identity source for which authorization is requested. + * + * @default ['$request.header.Authorization'] + */ + readonly identitySource?: string[], +} + +/** + * Authorize Http Api routes on whether the requester is registered as part of + * an AWS Cognito user pool. + */ +export class HttpUserPoolAuthorizer implements IHttpRouteAuthorizer { + private authorizer?: HttpAuthorizer; + + constructor(private readonly props: UserPoolAuthorizerProps) { + } + + public bind(options: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { + if (!this.authorizer) { + const id = this.props.authorizerName && !Token.isUnresolved(this.props.authorizerName) ? + this.props.authorizerName : 'UserPoolAuthorizer'; + const region = this.props.userPoolRegion ?? Stack.of(options.scope).region; + this.authorizer = new HttpAuthorizer(options.scope, id, { + httpApi: options.route.httpApi, + identitySource: this.props.identitySource ?? ['$request.header.Authorization'], + type: HttpAuthorizerType.JWT, + authorizerName: this.props.authorizerName, + jwtAudience: [this.props.userPoolClient.userPoolClientId], + jwtIssuer: `https://cognito-idp.${region}.amazonaws.com/${this.props.userPool.userPoolId}`, + }); + } + + return { + authorizerId: this.authorizer.authorizerId, + authorizationType: HttpAuthorizerType.JWT, + }; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/index.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/index.ts new file mode 100644 index 0000000000000..c202386ae710e --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/index.ts @@ -0,0 +1 @@ +export * from './http'; diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json new file mode 100644 index 0000000000000..e22641d729d02 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json @@ -0,0 +1,100 @@ +{ + "name": "@aws-cdk/aws-apigatewayv2-authorizers", + "version": "0.0.0", + "description": "Authorizers for AWS APIGateway V2", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "jsii": { + "outdir": "dist", + "targets": { + "dotnet": { + "namespace": "Amazon.CDK.AWS.APIGatewayv2.Authorizers", + "packageId": "Amazon.CDK.AWS.APIGatewayv2.Authorizers", + "signAssembly": true, + "assemblyOriginatorKeyFile": "../../key.snk", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/master/logo/default-256-dark.png" + }, + "java": { + "package": "software.amazon.awscdk.services.apigatewayv2.authorizers", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "apigatewayv2-authorizers" + } + }, + "python": { + "distName": "aws-cdk.aws-apigatewayv2-authorizers", + "module": "aws_cdk.aws_apigatewayv2_authorizers", + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 1" + ] + } + }, + "projectReferences": true + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/aws-apigatewayv2-authorizers" + }, + "homepage": "https://github.com/aws/aws-cdk", + "scripts": { + "build": "cdk-build", + "integ": "cdk-integ", + "lint": "cdk-lint", + "package": "cdk-package", + "awslint": "cdk-awslint", + "pkglint": "pkglint -f", + "test": "cdk-test", + "watch": "cdk-watch", + "compat": "cdk-compat", + "build+test": "npm run build && npm test", + "build+test+package": "npm run build+test && npm run package" + }, + "cdk-build": { + "jest": true, + "env": { + "AWSLINT_BASE_CONSTRUCT": true + } + }, + "keywords": [ + "aws", + "cdk", + "constructs", + "apigateway" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "@aws-cdk/assert": "0.0.0", + "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", + "@aws-cdk/aws-lambda": "0.0.0", + "cdk-build-tools": "0.0.0", + "cdk-integ-tools": "0.0.0", + "pkglint": "0.0.0" + }, + "dependencies": { + "@aws-cdk/aws-apigatewayv2": "0.0.0", + "@aws-cdk/aws-cognito": "0.0.0", + "@aws-cdk/core": "0.0.0", + "constructs": "^3.2.0" + }, + "peerDependencies": { + "@aws-cdk/aws-apigatewayv2": "0.0.0", + "@aws-cdk/aws-cognito": "0.0.0", + "@aws-cdk/core": "0.0.0", + "constructs": "^3.2.0" + }, + "engines": { + "node": ">= 10.13.0 <13 || >=13.7.0" + }, + "stability": "experimental", + "maturity": "experimental", + "awscdkio": { + "announce": false + } +} diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.expected.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.expected.json similarity index 100% rename from packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.expected.json rename to packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.expected.json diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.ts similarity index 67% rename from packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts rename to packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.ts index bb08c9dd2edae..005c03d688935 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.ts @@ -1,9 +1,11 @@ /// !cdk-integ pragma:ignore-assets import * as path from 'path'; +import { HttpApi, HttpMethod } from '@aws-cdk/aws-apigatewayv2'; +import { LambdaProxyIntegration } from '@aws-cdk/aws-apigatewayv2-integrations'; import * as cognito from '@aws-cdk/aws-cognito'; import * as lambda from '@aws-cdk/aws-lambda'; import { App, Stack } from '@aws-cdk/core'; -import { HttpApi, HttpJwtAuthorizer, HttpMethod, LambdaProxyIntegration } from '../../lib'; +import { HttpUserPoolAuthorizer } from '../../lib'; /* * Stack verification steps: @@ -19,20 +21,17 @@ const httpApi = new HttpApi(stack, 'MyHttpApi'); const userPool = new cognito.UserPool(stack, 'userpool'); -const client = userPool.addClient('my-client'); +const userPoolClient = userPool.addClient('my-client'); -const authorizer = new HttpJwtAuthorizer(stack, 'MyAuthorizer', { - httpApi, - jwtConfiguration: { - audience: [client.userPoolClientId], - issuer: `https://cognito-idp.${stack.region}.amazonaws.com/${userPool.userPoolId}`, - }, +const authorizer = new HttpUserPoolAuthorizer({ + userPool, + userPoolClient, }); const handler = new lambda.Function(stack, 'lambda', { runtime: lambda.Runtime.NODEJS_12_X, handler: 'index.handler', - code: lambda.AssetCode.fromAsset(path.join(__dirname, 'integ.authorizer.handler')), + code: lambda.AssetCode.fromAsset(path.join(__dirname, 'integ.user-pool.handler')), }); httpApi.addRoutes({ diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/user-pool.test.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/user-pool.test.ts new file mode 100644 index 0000000000000..c12e00a342acd --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/user-pool.test.ts @@ -0,0 +1,83 @@ +import '@aws-cdk/assert/jest'; +import { HttpApi, HttpIntegrationType, HttpRouteIntegrationBindOptions, IHttpRouteIntegration, PayloadFormatVersion } from '@aws-cdk/aws-apigatewayv2'; +import { UserPool } from '@aws-cdk/aws-cognito'; +import { Stack } from '@aws-cdk/core'; +import { HttpUserPoolAuthorizer } from '../../lib'; + +describe('HttpUserPoolAuthorizer', () => { + test('default', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + const userPool = new UserPool(stack, 'UserPool'); + const userPoolClient = userPool.addClient('UserPoolClient'); + const authorizer = new HttpUserPoolAuthorizer({ + userPool, + userPoolClient, + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + AuthorizerType: 'JWT', + IdentitySource: ['$request.header.Authorization'], + JwtConfiguration: { + Audience: [stack.resolve(userPoolClient.userPoolClientId)], + Issuer: { + 'Fn::Join': [ + '', + [ + 'https://cognito-idp.', + { Ref: 'AWS::Region' }, + '.amazonaws.com/', + stack.resolve(userPool.userPoolId), + ], + ], + }, + }, + }); + }); + + test('same authorizer is used when bound to multiple routes', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + const userPool = new UserPool(stack, 'UserPool'); + const userPoolClient = userPool.addClient('UserPoolClient'); + const authorizer = new HttpUserPoolAuthorizer({ + userPool, + userPoolClient, + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/pets', + authorizer, + }); + + // THEN + expect(stack).toCountResources('AWS::ApiGatewayV2::Authorizer', 1); + }); +}); + +class DummyRouteIntegration implements IHttpRouteIntegration { + public bind(_: HttpRouteIntegrationBindOptions) { + return { + payloadFormatVersion: PayloadFormatVersion.VERSION_2_0, + type: HttpIntegrationType.HTTP_PROXY, + uri: 'some-uri', + }; + } +} diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.handler/index.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/integ.user-pool.handler/index.ts similarity index 100% rename from packages/@aws-cdk/aws-apigatewayv2/test/http/integ.authorizer.handler/index.ts rename to packages/@aws-cdk/aws-apigatewayv2-authorizers/test/integ.user-pool.handler/index.ts diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts index 248da8343b4dd..a8f429f427e31 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts @@ -3,7 +3,7 @@ import { Duration, IResource, Resource } from '@aws-cdk/core'; import { Construct } from 'constructs'; import { CfnApi, CfnApiProps } from '../apigatewayv2.generated'; import { DefaultDomainMappingOptions } from '../http/stage'; -import { IHttpAuthorizer } from './authorizer'; +import { IHttpRouteAuthorizer } from './authorizer'; import { IHttpRouteIntegration } from './integration'; import { BatchHttpRouteOptions, HttpMethod, HttpRoute, HttpRouteKey } from './route'; import { HttpStage, HttpStageOptions } from './stage'; @@ -185,15 +185,7 @@ export interface AddRoutesOptions extends BatchHttpRouteOptions { * Authorizer to be associated to these routes. * @default - No authorizer */ - readonly authorizer?: IHttpAuthorizer; - - /** - * Scopes required to access this route - * - * Useless if no authorizer is passed in - * @default - No scopes - */ - readonly authorizationScopes?: string[]; + readonly authorizer?: IHttpRouteAuthorizer; } abstract class HttpApiBase extends Resource implements IHttpApi { // note that this is not exported @@ -378,7 +370,6 @@ export class HttpApi extends HttpApiBase { routeKey: HttpRouteKey.with(options.path, method), integration: options.integration, authorizer: options.authorizer, - authorizationScopes: options.authorizationScopes, })); } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index 99fa70b0c930d..5698c93a51ed8 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -4,6 +4,7 @@ import { CfnAuthorizer } from '../apigatewayv2.generated'; import { IAuthorizer } from '../common'; import { IHttpApi } from './api'; +import { IHttpRoute } from './route'; /** * Supported Authorizer types @@ -22,7 +23,6 @@ export enum HttpAuthorizerType { export interface HttpAuthorizerProps { /** * Name of the authorizer - * * @default - id of the HttpAuthorizer construct. */ readonly authorizerName?: string @@ -31,89 +31,121 @@ export interface HttpAuthorizerProps { * HTTP Api to attach the authorizer to */ readonly httpApi: IHttpApi -} -/** - * Describes an instance of `IHttpAuthorizer` - */ -export interface IHttpAuthorizer extends IAuthorizer { /** - * Type of authorizer - * @attribute + * The type of authorizer */ - readonly authorizerType: HttpAuthorizerType; -} + readonly type: HttpAuthorizerType; -/** - * Specifies the configuration of a JWT authorizer - */ -export interface JwtConfiguration { /** - * A list of the intended recipients of the JWT + * The identity source for which authorization is requested. + * @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigatewayv2-authorizer.html#cfn-apigatewayv2-authorizer-identitysource */ - readonly audience: string[] + readonly identitySource: string[]; /** - * The base domain of the identity provider that issues JSON Web Tokens. + * A list of the intended recipients of the JWT. + * A valid JWT must provide an aud that matches at least one entry in this list. + * @default - required for JWT authorizer typess. */ - readonly issuer: string; -} + readonly jwtAudience?: string[] -/** - * Properties to initialize an instance of `HttpAuthorizer`. - */ -export interface HttpJwtAuthorizerProps extends HttpAuthorizerProps { /** - * The identity source for which authorization is requested. - * - * @default ['$request.header.Authorization'] + * The base domain of the identity provider that issues JWT. + * @default - required for JWT authorizer types. */ - readonly identitySource?: string[], + readonly jwtIssuer?: string; +} - /** - * Configuration of a JWT authorizer. - * Required for the JWT authorizer type. Supported only for HTTP APIs. - * - * @default - No configuration - */ - readonly jwtConfiguration: JwtConfiguration +/** + * An authorizer for HTTP APIs + */ +export interface IHttpAuthorizer extends IAuthorizer { } /** - * Create a new JwtAuthorizer for Http APIs + * An authorizer for Http Apis * @resource AWS::ApiGatewayV2::Authorizer */ -export class HttpJwtAuthorizer extends Resource implements IHttpAuthorizer { - +export class HttpAuthorizer extends Resource implements IHttpAuthorizer { /** * Import an existing HTTP Authorizer into this CDK app. */ public static fromAuthorizerId(scope: Construct, id: string, authorizerId: string): IHttpAuthorizer { class Import extends Resource implements IHttpAuthorizer { public readonly authorizerId = authorizerId; - public readonly authorizerType = HttpAuthorizerType.JWT; } return new Import(scope, id); } public readonly authorizerId: string; - public readonly authorizerType: HttpAuthorizerType = HttpAuthorizerType.JWT; - constructor(scope: Construct, id: string, props: HttpJwtAuthorizerProps) { + constructor(scope: Construct, id: string, props: HttpAuthorizerProps) { super(scope, id); - const authorizerName = props.authorizerName ?? id; - - const identitySource = props.identitySource ?? ['$request.header.Authorization']; + if (props.type === HttpAuthorizerType.JWT && (!props.jwtAudience || props.jwtAudience.length === 0 || !props.jwtIssuer)) { + throw new Error('jwtAudience and jwtIssuer are mandatory for JWT authorizers'); + } const resource = new CfnAuthorizer(this, 'Resource', { - name: authorizerName, + name: props.authorizerName ?? id, apiId: props.httpApi.httpApiId, - authorizerType: this.authorizerType, - identitySource, - jwtConfiguration: props.jwtConfiguration, + authorizerType: props.type, + identitySource: props.identitySource, + jwtConfiguration: undefinedIfNoKeys({ + audience: props.jwtAudience, + issuer: props.jwtIssuer, + }), }); this.authorizerId = resource.ref; } } + +/** + * Input to the bind() operation, that binds an authorizer to a route. + */ +export interface HttpRouteAuthorizerBindOptions { + /** + * The route to which the authorizer is being bound. + */ + readonly route: IHttpRoute; + /** + * The scope for any constructs created as part of the bind. + */ + readonly scope: Construct; +} + +/** + * Results of binding an authorizer to an http route. + */ +export interface HttpRouteAuthorizerConfig { + /** + * The authorizer id + */ + readonly authorizerId: string; + /** + * The type of authorization + */ + readonly authorizationType: HttpAuthorizerType; + /** + * The list of OIDC scopes to include in the authorization. + * @default - no authorization scopes + */ + readonly authorizationScopes?: string[]; +} + +/** + * An authorizer that can attach to an Http Route. + */ +export interface IHttpRouteAuthorizer { + /** + * Bind this authorizer to a specified Http route. + */ + bind(options: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig; +} + +function undefinedIfNoKeys(obj: A): A | undefined { + const allUndefined = Object.values(obj).every(val => val === undefined); + return allUndefined ? undefined : obj; +} diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts index 36c0ad35e8275..f59d6e9ab7351 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts @@ -3,7 +3,7 @@ import { Construct } from 'constructs'; import { CfnRoute, CfnRouteProps } from '../apigatewayv2.generated'; import { IRoute } from '../common'; import { IHttpApi } from './api'; -import { IHttpAuthorizer } from './authorizer'; +import { IHttpRouteAuthorizer } from './authorizer'; import { HttpIntegration, IHttpRouteIntegration } from './integration'; /** @@ -109,15 +109,7 @@ export interface HttpRouteProps extends BatchHttpRouteOptions { * Authorizer for a WebSocket API or an HTTP API. * @default - No authorizer */ - readonly authorizer?: IHttpAuthorizer; - - /** - * Scopes required to access this route - * - * Useless if no authorizer is passed in - * @default - No scopes - */ - readonly authorizationScopes?: string[]; + readonly authorizer?: IHttpRouteAuthorizer; } /** @@ -150,15 +142,18 @@ export class HttpRoute extends Resource implements IHttpRoute { payloadFormatVersion: config.payloadFormatVersion, }); + const authBindResult = props.authorizer ? props.authorizer.bind({ + route: this, + scope: this.httpApi instanceof Construct ? this.httpApi : this, // scope under the API if it's not imported + }) : undefined; + const routeProps: CfnRouteProps = { apiId: props.httpApi.httpApiId, routeKey: props.routeKey.key, target: `integrations/${integration.integrationId}`, - ...props.authorizer ? { - authorizerId: props.authorizer.authorizerId, - authorizationType: props.authorizer.authorizerType, - authorizationScopes: props.authorizationScopes, - }: {}, + authorizerId: authBindResult?.authorizerId, + authorizationType: authBindResult?.authorizationType, + authorizationScopes: authBindResult?.authorizationScopes, }; const route = new CfnRoute(this, 'Resource', routeProps); diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts index 3ba3874e6bd4f..ff4419f81902d 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -3,7 +3,10 @@ import { ABSENT } from '@aws-cdk/assert'; import { Metric } from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; import { Duration, Stack } from '@aws-cdk/core'; -import { HttpApi, HttpIntegrationType, HttpMethod, HttpRouteIntegrationBindOptions, HttpRouteIntegrationConfig, IHttpRouteIntegration, PayloadFormatVersion, HttpJwtAuthorizer } from '../../lib'; +import { + HttpApi, HttpAuthorizerType, HttpIntegrationType, HttpMethod, HttpRouteAuthorizerBindOptions, HttpRouteAuthorizerConfig, + HttpRouteIntegrationBindOptions, HttpRouteIntegrationConfig, IHttpRouteAuthorizer, IHttpRouteIntegration, PayloadFormatVersion, +} from '../../lib'; describe('HttpApi', () => { test('default', () => { @@ -266,25 +269,12 @@ describe('HttpApi', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'api'); - const authorizer = new HttpJwtAuthorizer(stack, 'HttpAuthorizer', { - httpApi, - jwtConfiguration: { - audience: ['cognito-pool'], - issuer: 'http://congnito.aws', - }, - }); + const authorizer = new DummyAuthorizer(); httpApi.addRoutes({ path: '/pets', - integration: new LambdaProxyIntegration({ - handler: new Function(stack, 'fn', { - code: Code.fromInline('foo'), - runtime: Runtime.NODEJS_12_X, - handler: 'index.handler', - }), - }), + integration: new DummyRouteIntegration(), authorizer, - authorizationScopes: ['pets:read'], }); expect(stack).toHaveResource('AWS::ApiGatewayV2::Api', { @@ -293,9 +283,8 @@ describe('HttpApi', () => { }); expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { - AuthorizerId: stack.resolve(authorizer.authorizerId), + AuthorizerId: 'auth-1234', AuthorizationType: 'JWT', - AuthorizationScopes: ['pets:read'], }); }); }); @@ -308,4 +297,13 @@ class DummyRouteIntegration implements IHttpRouteIntegration { uri: 'some-uri', }; } +} + +class DummyAuthorizer implements IHttpRouteAuthorizer { + public bind(_: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { + return { + authorizerId: 'auth-1234', + authorizationType: HttpAuthorizerType.JWT, + }; + } } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts index 95c032abe1ca3..91d36607d00c3 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts @@ -1,49 +1,79 @@ import '@aws-cdk/assert/jest'; +import { ABSENT } from '@aws-cdk/assert'; import { Stack } from '@aws-cdk/core'; import { - HttpApi, HttpJwtAuthorizer, + HttpApi, HttpAuthorizer, HttpAuthorizerType, } from '../../lib'; -describe('JwtAuthorizer', () => { +describe('HttpAuthorizer', () => { test('default', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'HttpApi'); - new HttpJwtAuthorizer(stack, 'HttpAuthorizer', { + new HttpAuthorizer(stack, 'HttpAuthorizer', { httpApi, - jwtConfiguration: { - audience: ['cognito-pool'], - issuer: 'http://congnito.aws', - }, + identitySource: ['identitysource.1', 'identitysource.2'], + type: HttpAuthorizerType.JWT, }); expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { ApiId: stack.resolve(httpApi.httpApiId), Name: 'HttpAuthorizer', AuthorizerType: 'JWT', - IdentitySource: ['$request.header.Authorization'], - JwtConfiguration: { - Audience: ['cognito-pool'], - Issuer: 'http://congnito.aws', - }, + IdentitySource: ['identitysource.1', 'identitysource.2'], }); }); - test('can set authorizer name', () => { + test('authorizer name', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'HttpApi'); - new HttpJwtAuthorizer(stack, 'HttpAuthorizer', { + new HttpAuthorizer(stack, 'HttpAuthorizer', { httpApi, authorizerName: 'my-authorizer', - jwtConfiguration: { - audience: ['cognito-pool'], - issuer: 'http://congnito.aws', - }, + identitySource: ['identitysource.1', 'identitysource.2'], + type: HttpAuthorizerType.JWT, }); expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { Name: 'my-authorizer', }); }); + + describe('jwt configuration', () => { + test('audience and issuer', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + new HttpAuthorizer(stack, 'HttpAuthorizer', { + httpApi, + identitySource: ['identitysource.1', 'identitysource.2'], + type: HttpAuthorizerType.JWT, + jwtAudience: ['audience.1', 'audience.2'], + jwtIssuer: 'issuer', + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + JwtConfiguration: { + Audience: ['audience.1', 'audience.2'], + Issuer: 'issuer', + }, + }); + }); + + test('absent when audience and issuer are unspecified', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + new HttpAuthorizer(stack, 'HttpAuthorizer', { + httpApi, + identitySource: ['identitysource.1', 'identitysource.2'], + type: HttpAuthorizerType.JWT, + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + JwtConfiguration: ABSENT, + }); + }); + }); }); diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts index e28b049c6afe4..778a93ff581d7 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts @@ -1,8 +1,8 @@ import '@aws-cdk/assert/jest'; import { Stack } from '@aws-cdk/core'; import { - HttpApi, HttpConnectionType, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteIntegrationConfig, HttpRouteKey, IHttpRouteIntegration, - PayloadFormatVersion, HttpJwtAuthorizer, + HttpApi, HttpAuthorizerType, HttpConnectionType, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteAuthorizerBindOptions, + HttpRouteAuthorizerConfig, HttpRouteIntegrationConfig, HttpRouteKey, IHttpRouteAuthorizer, IHttpRouteIntegration, PayloadFormatVersion, } from '../../lib'; describe('HttpRoute', () => { @@ -118,20 +118,13 @@ describe('HttpRoute', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'HttpApi'); - const authorizer = new HttpJwtAuthorizer(stack, 'HttpAuthorizer', { - httpApi, - jwtConfiguration: { - audience: ['cognito-pool'], - issuer: 'http://congnito.aws', - }, - }); + const authorizer = new DummyAuthorizer(); new HttpRoute(stack, 'HttpRoute', { httpApi, integration: new DummyIntegration(), routeKey: HttpRouteKey.with('/books', HttpMethod.GET), authorizer, - authorizationScopes: ['books:read'], }); expect(stack).toHaveResource('AWS::ApiGatewayV2::Integration', { @@ -144,9 +137,8 @@ describe('HttpRoute', () => { expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer'); expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { - AuthorizerId: stack.resolve(authorizer.authorizerId), + AuthorizerId: 'auth-1234', AuthorizationType: 'JWT', - AuthorizationScopes: ['books:read'], }); }); }); @@ -160,4 +152,13 @@ class DummyIntegration implements IHttpRouteIntegration { method: HttpMethod.DELETE, }; } +} + +class DummyAuthorizer implements IHttpRouteAuthorizer { + public bind(_: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { + return { + authorizerId: 'auth-1234', + authorizationType: HttpAuthorizerType.JWT, + }; + } } \ No newline at end of file From a343a3b5724f8334d509582f75061f1c5894fd73 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Sun, 24 Jan 2021 17:27:45 -0400 Subject: [PATCH 32/47] lint fixes from fast-forward --- .../@aws-cdk/aws-apigatewayv2/lib/http/api.ts | 2 +- .../aws-apigatewayv2/lib/http/route.ts | 2 +- .../aws-apigatewayv2/test/http/api.test.ts | 2 +- yarn.lock | 102 +++++++++++++++++- 4 files changed, 102 insertions(+), 6 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts index 0b7271bc91e4a..94861d414329f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts @@ -5,7 +5,7 @@ import { Construct } from 'constructs'; import { CfnApi, CfnApiProps } from '../apigatewayv2.generated'; import { DefaultDomainMappingOptions } from '../http/stage'; import { IHttpRouteAuthorizer } from './authorizer'; -import { IHttpRouteIntegration, HttpIntegration, HttpRouteIntegrationConfig, } from './integration'; +import { IHttpRouteIntegration, HttpIntegration, HttpRouteIntegrationConfig } from './integration'; import { BatchHttpRouteOptions, HttpMethod, HttpRoute, HttpRouteKey } from './route'; import { HttpStage, HttpStageOptions } from './stage'; import { VpcLink, VpcLinkProps } from './vpc-link'; diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts index 66e09ded80c17..90fd2a529a7bd 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts @@ -4,7 +4,7 @@ import { CfnRoute, CfnRouteProps } from '../apigatewayv2.generated'; import { IRoute } from '../common'; import { IHttpApi } from './api'; import { IHttpRouteAuthorizer } from './authorizer'; -import { IHttpRouteIntegration, HttpIntegration } from './integration'; +import { IHttpRouteIntegration } from './integration'; /** * Represents a Route for an HTTP API. diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts index a184a2f4e41f0..318fffc0179cf 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -299,7 +299,7 @@ describe('HttpApi', () => { AuthorizationType: 'JWT', }); }); - + test('throws when accessing apiEndpoint and disableExecuteApiEndpoint is true', () => { const stack = new Stack(); const api = new HttpApi(stack, 'api', { diff --git a/yarn.lock b/yarn.lock index 04ec4d7447fea..a0abba5bf0ddd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2068,6 +2068,11 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +app-root-path@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a" + integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA== + append-transform@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-1.0.0.tgz#046a52ae582a228bd72f58acfbe2967c678759ab" @@ -2310,6 +2315,21 @@ aws-sdk-mock@^5.1.0: sinon "^9.0.1" traverse "^0.6.6" +aws-sdk@^2.596.0: + version "2.831.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.831.0.tgz#02607cc911a2136e5aabe624c1282e821830aef2" + integrity sha512-lrOjbGFpjk2xpESyUx2PGsTZgptCy5xycZazPeakNbFO19cOoxjHx3xyxOHsMCYb3pQwns35UvChQT60B4u6cw== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.15.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + uuid "3.3.2" + xml2js "0.4.19" + aws-sdk@^2.637.0, aws-sdk@^2.830.0: version "2.830.0" resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.830.0.tgz#1d3631d573d18c48373046da7ad92855a7fd1636" @@ -3758,6 +3778,16 @@ dot-prop@^5.1.0: dependencies: is-obj "^2.0.0" +dotenv-json@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dotenv-json/-/dotenv-json-1.0.0.tgz#fc7f672aafea04bed33818733b9f94662332815c" + integrity sha512-jAssr+6r4nKhKRudQ0HOzMskOFFi9+ubXWwmrSGJFgTvpjyPXCXsCsYbjif6mXp7uxA7xY3/LGaiTQukZzSbOQ== + +dotenv@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" + integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== + dotgitignore@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/dotgitignore/-/dotgitignore-2.1.0.tgz#a4b15a4e4ef3cf383598aaf1dfa4a04bcc089b7b" @@ -3981,6 +4011,11 @@ escodegen@^1.14.1, escodegen@^1.8.1: optionalDependencies: source-map "~0.6.1" +eslint-config-standard@^14.1.1: + version "14.1.1" + resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-14.1.1.tgz#830a8e44e7aef7de67464979ad06b406026c56ea" + integrity sha512-Z9B+VR+JIXRxz21udPTL9HpFMyoMUEeX1G251EQ6e05WD9aPVtVBn09XUmZ259wCMlCDmYDSZG62Hhm+ZTJcUg== + eslint-import-resolver-node@^0.3.4: version "0.3.4" resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz#85ffa81942c25012d8231096ddf679c03042c717" @@ -4008,6 +4043,14 @@ eslint-module-utils@^2.6.0: debug "^2.6.9" pkg-dir "^2.0.0" +eslint-plugin-es@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" + integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== + dependencies: + eslint-utils "^2.0.0" + regexpp "^3.0.0" + eslint-plugin-import@^2.22.1: version "2.22.1" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz#0896c7e6a0cf44109a2d97b95903c2bb689d7702" @@ -4034,11 +4077,33 @@ eslint-plugin-jest@^24.1.3: dependencies: "@typescript-eslint/experimental-utils" "^4.0.1" +eslint-plugin-node@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" + integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== + dependencies: + eslint-plugin-es "^3.0.0" + eslint-utils "^2.0.0" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-plugin-promise@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" + integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== + eslint-plugin-rulesdir@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-rulesdir/-/eslint-plugin-rulesdir-0.1.0.tgz#ad144d7e98464fda82963eff3fab331aecb2bf08" integrity sha1-rRRNfphGT9qClj7/P6szGuyyvwg= +eslint-plugin-standard@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-4.1.0.tgz#0c3bf3a67e853f8bbbc580fb4945fbf16f41b7c5" + integrity sha512-ZL7+QRixjTR6/528YNGyDotyffm5OQst/sGxKDwGb9Uqs4In5Egi4+jbobhqJoyoCM6/7v/1A5fhQ7ScMtDjaQ== + eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -5149,7 +5214,7 @@ ignore@^4.0.3, ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -ignore@^5.1.4, ignore@^5.1.8, ignore@~5.1.8: +ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8, ignore@~5.1.8: version "5.1.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== @@ -5335,7 +5400,7 @@ is-ci@^2.0.0: dependencies: ci-info "^2.0.0" -is-core-module@^2.0.0: +is-core-module@^2.0.0, is-core-module@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== @@ -6425,6 +6490,24 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +lambda-leak@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/lambda-leak/-/lambda-leak-2.0.0.tgz#771985d3628487f6e885afae2b54510dcfb2cd7e" + integrity sha1-dxmF02KEh/boha+uK1RRDc+yzX4= + +lambda-tester@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/lambda-tester/-/lambda-tester-3.6.0.tgz#ceb7d4f4f0da768487a05cff37dcd088508b5247" + integrity sha512-F2ZTGWCLyIR95o/jWK46V/WnOCFAEUG/m/V7/CLhPJ7PCM+pror1rZ6ujP3TkItSGxUfpJi0kqwidw+M/nEqWw== + dependencies: + app-root-path "^2.2.1" + dotenv "^8.0.0" + dotenv-json "^1.0.0" + lambda-leak "^2.0.0" + semver "^6.1.1" + uuid "^3.3.2" + vandium-utils "^1.1.1" + lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" @@ -8605,6 +8688,14 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.1, resolve@^1.13.1, resolve@^1.17 dependencies: path-parse "^1.0.6" +resolve@^1.10.1: + version "1.19.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" + integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + dependencies: + is-core-module "^2.1.0" + path-parse "^1.0.6" + resolve@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" @@ -8762,7 +8853,7 @@ semver@7.x, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.0, semver@^6.1.1, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -10128,6 +10219,11 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +vandium-utils@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/vandium-utils/-/vandium-utils-1.2.0.tgz#44735de4b7641a05de59ebe945f174e582db4f59" + integrity sha1-RHNd5LdkGgXeWevpRfF05YLbT1k= + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" From d4fb6b54e7d8469b43a9cc656a2c72348ee66840 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Sun, 24 Jan 2021 20:10:35 -0400 Subject: [PATCH 33/47] feat: add fromHttpAuthorizerAttributes --- .../aws-apigatewayv2/lib/http/authorizer.ts | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index 5698c93a51ed8..eeaf19fa461eb 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -63,6 +63,21 @@ export interface HttpAuthorizerProps { export interface IHttpAuthorizer extends IAuthorizer { } +/** + * Reference to an http authorizer + */ +export interface HttpAuthorizerAttributes { + /** + * Id of the Authorizer + */ + readonly authorizerId: string + + /** + * Type of authorizer + */ + readonly authorizerType: HttpAuthorizerType +} + /** * An authorizer for Http Apis * @resource AWS::ApiGatewayV2::Authorizer @@ -71,15 +86,18 @@ export class HttpAuthorizer extends Resource implements IHttpAuthorizer { /** * Import an existing HTTP Authorizer into this CDK app. */ - public static fromAuthorizerId(scope: Construct, id: string, authorizerId: string): IHttpAuthorizer { + public static fromHttpAuthorizerAttributes(scope: Construct, id: string, attrs: HttpAuthorizerAttributes): IHttpAuthorizer { class Import extends Resource implements IHttpAuthorizer { - public readonly authorizerId = authorizerId; + public readonly authorizerId = attrs.authorizerId; + public readonly authorizerType = attrs.authorizerType; } return new Import(scope, id); } public readonly authorizerId: string; + public readonly authorizerType: HttpAuthorizerType; + constructor(scope: Construct, id: string, props: HttpAuthorizerProps) { super(scope, id); @@ -99,6 +117,7 @@ export class HttpAuthorizer extends Resource implements IHttpAuthorizer { }); this.authorizerId = resource.ref; + this.authorizerType = props.type; } } From b158d4d6d8805eda74538dca454a0c97151dfc43 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Sun, 24 Jan 2021 22:32:52 -0400 Subject: [PATCH 34/47] feat: allow adding scopes via api.addRoutes --- .../@aws-cdk/aws-apigatewayv2/lib/http/api.ts | 9 ++++ .../aws-apigatewayv2/lib/http/authorizer.ts | 3 -- .../aws-apigatewayv2/lib/http/route.ts | 19 ++++++++- .../aws-apigatewayv2/test/http/api.test.ts | 25 +++++++++++ .../aws-apigatewayv2/test/http/route.test.ts | 42 ++++++++++++++++--- 5 files changed, 89 insertions(+), 9 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts index 94861d414329f..339efed0707fb 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/api.ts @@ -208,6 +208,14 @@ export interface AddRoutesOptions extends BatchHttpRouteOptions { * @default - No authorizer */ readonly authorizer?: IHttpRouteAuthorizer; + + /** + * The list of OIDC scopes to include in the authorization. + * + * These scopes will be merged with the scopes from the attached authorizer + * @default - no additional authorization scopes + */ + readonly authorizationScopes?: string[]; } abstract class HttpApiBase extends Resource implements IHttpApi { // note that this is not exported @@ -454,6 +462,7 @@ export class HttpApi extends HttpApiBase { routeKey: HttpRouteKey.with(options.path, method), integration: options.integration, authorizer: options.authorizer, + authorizationScopes: options.authorizationScopes, })); } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index eeaf19fa461eb..cf720f99cc49c 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -96,8 +96,6 @@ export class HttpAuthorizer extends Resource implements IHttpAuthorizer { public readonly authorizerId: string; - public readonly authorizerType: HttpAuthorizerType; - constructor(scope: Construct, id: string, props: HttpAuthorizerProps) { super(scope, id); @@ -117,7 +115,6 @@ export class HttpAuthorizer extends Resource implements IHttpAuthorizer { }); this.authorizerId = resource.ref; - this.authorizerType = props.type; } } diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts index 90fd2a529a7bd..dac962f7bfee4 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/route.ts @@ -110,6 +110,14 @@ export interface HttpRouteProps extends BatchHttpRouteOptions { * @default - No authorizer */ readonly authorizer?: IHttpRouteAuthorizer; + + /** + * The list of OIDC scopes to include in the authorization. + * + * These scopes will be merged with the scopes from the attached authorizer + * @default - no additional authorization scopes + */ + readonly authorizationScopes?: string[]; } /** @@ -139,13 +147,22 @@ export class HttpRoute extends Resource implements IHttpRoute { scope: this.httpApi instanceof Construct ? this.httpApi : this, // scope under the API if it's not imported }) : undefined; + let authorizationScopes = authBindResult?.authorizationScopes ?? []; + + if (authBindResult && props.authorizationScopes) { + authorizationScopes = Array.from(new Set([ + ...authorizationScopes, + ...props.authorizationScopes, + ])); + } + const routeProps: CfnRouteProps = { apiId: props.httpApi.httpApiId, routeKey: props.routeKey.key, target: `integrations/${integration.integrationId}`, authorizerId: authBindResult?.authorizerId, authorizationType: authBindResult?.authorizationType, - authorizationScopes: authBindResult?.authorizationScopes, + authorizationScopes, }; const route = new CfnRoute(this, 'Resource', routeProps); diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts index 318fffc0179cf..53a192fdce057 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -300,6 +300,31 @@ describe('HttpApi', () => { }); }); + test('can attach custom scopes to authorizer route', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'api'); + + const authorizer = new DummyAuthorizer(); + + httpApi.addRoutes({ + path: '/pets', + integration: new DummyRouteIntegration(), + authorizer, + authorizationScopes: ['read:scopes'], + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Api', { + Name: 'api', + ProtocolType: 'HTTP', + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizerId: 'auth-1234', + AuthorizationType: 'JWT', + AuthorizationScopes: ['read:scopes'], + }); + }); + test('throws when accessing apiEndpoint and disableExecuteApiEndpoint is true', () => { const stack = new Stack(); const api = new HttpApi(stack, 'api', { diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts index 6a1944f8873b2..bfeb036a3fa72 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/route.test.ts @@ -1,7 +1,7 @@ import '@aws-cdk/assert/jest'; import { Stack } from '@aws-cdk/core'; import { - HttpApi, HttpAuthorizerType, HttpConnectionType, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteAuthorizerBindOptions, + HttpApi, HttpAuthorizer, HttpAuthorizerType, HttpConnectionType, HttpIntegrationType, HttpMethod, HttpRoute, HttpRouteAuthorizerBindOptions, HttpRouteAuthorizerConfig, HttpRouteIntegrationConfig, HttpRouteKey, IHttpRouteAuthorizer, IHttpRouteIntegration, PayloadFormatVersion, } from '../../lib'; @@ -180,7 +180,7 @@ describe('HttpRoute', () => { const authorizer = new DummyAuthorizer(); - new HttpRoute(stack, 'HttpRoute', { + const route = new HttpRoute(stack, 'HttpRoute', { httpApi, integration: new DummyIntegration(), routeKey: HttpRouteKey.with('/books', HttpMethod.GET), @@ -197,10 +197,29 @@ describe('HttpRoute', () => { expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer'); expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { - AuthorizerId: 'auth-1234', + AuthorizerId: stack.resolve(authorizer.bind({ scope: stack, route: route }).authorizerId), AuthorizationType: 'JWT', }); }); + + test('can attach additional scopes to a route with an authorizer attached', () => { + const stack = new Stack(); + const httpApi = new HttpApi(stack, 'HttpApi'); + + const authorizer = new DummyAuthorizer(); + + new HttpRoute(stack, 'HttpRoute', { + httpApi, + integration: new DummyIntegration(), + routeKey: HttpRouteKey.with('/books', HttpMethod.GET), + authorizer, + authorizationScopes: ['read:books'], + }); + + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizationScopes: ['read:books'], + }); + }); }); class DummyIntegration implements IHttpRouteIntegration { @@ -215,9 +234,22 @@ class DummyIntegration implements IHttpRouteIntegration { } class DummyAuthorizer implements IHttpRouteAuthorizer { - public bind(_: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { + private authorizer?: HttpAuthorizer; + + public bind(options: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { + if (!this.authorizer) { + + this.authorizer = new HttpAuthorizer(options.scope, 'auth-1234', { + httpApi: options.route.httpApi, + identitySource: ['identitysource.1', 'identitysource.2'], + type: HttpAuthorizerType.JWT, + jwtAudience: ['audience.1', 'audience.2'], + jwtIssuer: 'issuer', + }); + } + return { - authorizerId: 'auth-1234', + authorizerId: this.authorizer.authorizerId, authorizationType: HttpAuthorizerType.JWT, }; } From 3f4eda73248d80a140b616672b4c15545d6c0b47 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Sun, 24 Jan 2021 22:48:05 -0400 Subject: [PATCH 35/47] test: Update authorizer tests Remove redundant test for if issuer and audience not provided when creating a jwt type authorizer. We already do a check that prevents the resource from being created if these are missing. --- .../test/http/authorizer.test.ts | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts index 91d36607d00c3..bee2f7d05be1b 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/authorizer.test.ts @@ -1,5 +1,4 @@ import '@aws-cdk/assert/jest'; -import { ABSENT } from '@aws-cdk/assert'; import { Stack } from '@aws-cdk/core'; import { HttpApi, HttpAuthorizer, HttpAuthorizerType, @@ -14,6 +13,8 @@ describe('HttpAuthorizer', () => { httpApi, identitySource: ['identitysource.1', 'identitysource.2'], type: HttpAuthorizerType.JWT, + jwtAudience: ['audience.1', 'audience.2'], + jwtIssuer: 'issuer', }); expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { @@ -33,6 +34,8 @@ describe('HttpAuthorizer', () => { authorizerName: 'my-authorizer', identitySource: ['identitysource.1', 'identitysource.2'], type: HttpAuthorizerType.JWT, + jwtAudience: ['audience.1', 'audience.2'], + jwtIssuer: 'issuer', }); expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { @@ -60,20 +63,5 @@ describe('HttpAuthorizer', () => { }, }); }); - - test('absent when audience and issuer are unspecified', () => { - const stack = new Stack(); - const httpApi = new HttpApi(stack, 'HttpApi'); - - new HttpAuthorizer(stack, 'HttpAuthorizer', { - httpApi, - identitySource: ['identitysource.1', 'identitysource.2'], - type: HttpAuthorizerType.JWT, - }); - - expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { - JwtConfiguration: ABSENT, - }); - }); }); }); From afd09d7d9fedf6377e1fcfea60917b92f5d22fe6 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Sun, 24 Jan 2021 23:36:29 -0400 Subject: [PATCH 36/47] fix pkglint errors --- .../@aws-cdk/aws-apigatewayv2-authorizers/LICENSE | 2 +- .../@aws-cdk/aws-apigatewayv2-authorizers/NOTICE | 2 +- .../@aws-cdk/aws-apigatewayv2-authorizers/README.md | 12 +++++++++--- .../aws-apigatewayv2-authorizers/package.json | 5 +++-- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/LICENSE b/packages/@aws-cdk/aws-apigatewayv2-authorizers/LICENSE index b71ec1688783a..28e4bdcec77ec 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/LICENSE +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/NOTICE b/packages/@aws-cdk/aws-apigatewayv2-authorizers/NOTICE index bfccac9a7f69c..5fc3826926b5b 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/NOTICE +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/NOTICE @@ -1,2 +1,2 @@ AWS Cloud Development Kit (AWS CDK) -Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. +Copyright 2018-2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md index 4599d3a2d9abf..4b0e2e4be9407 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md @@ -1,12 +1,18 @@ -## AWS APIGatewayv2 Authorizers +# AWS APIGatewayv2 Authorizers + --- ![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) -> The APIs of higher level constructs in this module are experimental and under active development. They are subject to non-backward compatible changes or removal in any future version. These are not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be announced in the release notes. This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package. +> The APIs of higher level constructs in this module are experimental and under active development. +> They are subject to non-backward compatible changes or removal in any future version. These are +> not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be +> announced in the release notes. This means that while you may use them, you may need to update +> your source code when upgrading to a newer version of this package. --- + ## Table of Contents @@ -30,4 +36,4 @@ allow and restrict clients from accessing HTTP APIs. ### User Pool Authorizer - \ No newline at end of file + diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json index e22641d729d02..de6dffe1edc61 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/package.json @@ -48,8 +48,9 @@ "test": "cdk-test", "watch": "cdk-watch", "compat": "cdk-compat", - "build+test": "npm run build && npm test", - "build+test+package": "npm run build+test && npm run package" + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "rosetta:extract": "yarn --silent jsii-rosetta extract" }, "cdk-build": { "jest": true, From eb636c4d0d3250a170e134d17b9c54c0f8a8e4aa Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Mon, 25 Jan 2021 00:34:10 -0400 Subject: [PATCH 37/47] feat: add jwt authorizer --- .../lib/http/index.ts | 3 +- .../lib/http/jwt.ts | 95 +++++++++++++++ .../test/http/integ.user-pool.expected.json | 109 +++++++++--------- .../test/http/integ.user-pool.ts | 2 +- .../test/http/jwt.test.ts | 96 +++++++++++++++ 5 files changed, 249 insertions(+), 56 deletions(-) create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts create mode 100644 packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/jwt.test.ts diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts index 523c972ba246f..9f9ad94c6a4b7 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/index.ts @@ -1 +1,2 @@ -export * from './user-pool'; \ No newline at end of file +export * from './user-pool'; +export * from './jwt'; \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts new file mode 100644 index 0000000000000..f4477d4c32fcc --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts @@ -0,0 +1,95 @@ +import { + HttpAuthorizer, + HttpAuthorizerType, + HttpRouteAuthorizerBindOptions, + HttpRouteAuthorizerConfig, + IHttpAuthorizer, + IHttpRouteAuthorizer, +} from '@aws-cdk/aws-apigatewayv2'; +import { Resource, Token } from '@aws-cdk/core'; +import { Construct } from 'constructs'; + +/** + * Properties to initialize HttpJwtAuthorizer. + */ +export interface HttpJwtAuthorizerProps { + + /** + * The name of the authorizer + * @default 'JwtAuthorizer' + */ + readonly authorizerName?: string; + + /** + * The identity source for which authorization is requested. + * + * @default ['$request.header.Authorization'] + */ + readonly identitySource?: string[], + + /** + * A list of the intended recipients of the JWT. + * A valid JWT must provide an aud that matches at least one entry in this list. + */ + readonly jwtAudience: string[] + + /** + * The base domain of the identity provider that issues JWT. + */ + readonly jwtIssuer: string; +} + +/** + * Authorize Http Api routes on whether the requester is registered as part of + * an AWS Cognito user pool. + */ +export class HttpJwtAuthorizer implements IHttpRouteAuthorizer { + /** + * Import an existing HTTP Jwt Authorizer into this CDK app. + */ + public static fromHttpJwtAuthorizerId(scope: Construct, id: string, authorizerId: string): IHttpRouteAuthorizer { + class Import extends Resource implements IHttpRouteAuthorizer { + private authorizer?: IHttpAuthorizer; + + public bind(): HttpRouteAuthorizerConfig { + + if (!this.authorizer) { + this.authorizer = HttpAuthorizer.fromHttpAuthorizerAttributes(scope, 'authorizer', { authorizerId, authorizerType: HttpAuthorizerType.JWT }); + } + + return { + authorizerId: this.authorizer.authorizerId, + authorizationType: HttpAuthorizerType.JWT, + }; + } + } + + return new Import(scope, id); + } + + private authorizer?: HttpAuthorizer; + + constructor(private readonly props: HttpJwtAuthorizerProps) { + } + + public bind(options: HttpRouteAuthorizerBindOptions): HttpRouteAuthorizerConfig { + if (!this.authorizer) { + const id = this.props.authorizerName && !Token.isUnresolved(this.props.authorizerName) ? + this.props.authorizerName : 'JwtAuthorizer'; + + this.authorizer = new HttpAuthorizer(options.scope, id, { + httpApi: options.route.httpApi, + identitySource: this.props.identitySource ?? ['$request.header.Authorization'], + type: HttpAuthorizerType.JWT, + authorizerName: this.props.authorizerName, + jwtAudience: this.props.jwtAudience, + jwtIssuer: this.props.jwtIssuer, + }); + } + + return { + authorizerId: this.authorizer.authorizerId, + authorizationType: HttpAuthorizerType.JWT, + }; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.expected.json b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.expected.json index e29474ca6dcbc..ccdbd26a3cbd2 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.expected.json @@ -54,7 +54,32 @@ } } }, - "MyHttpApiGETGETIntegration0052AA15": { + "MyHttpApiGETE0EFC6F8": { + "Type": "AWS::ApiGatewayV2::Route", + "Properties": { + "ApiId": { + "Ref": "MyHttpApi8AEAAC21" + }, + "RouteKey": "GET /", + "AuthorizationScopes": [], + "AuthorizationType": "JWT", + "AuthorizerId": { + "Ref": "MyHttpApiUserPoolAuthorizer8754262B" + }, + "Target": { + "Fn::Join": [ + "", + [ + "integrations/", + { + "Ref": "MyHttpApiHttpIntegration6b0135d91888d914a9a0945a69f42c05C76E7A88" + } + ] + ] + } + } + }, + "MyHttpApiHttpIntegration6b0135d91888d914a9a0945a69f42c05C76E7A88": { "Type": "AWS::ApiGatewayV2::Integration", "Properties": { "ApiId": { @@ -70,27 +95,38 @@ "PayloadFormatVersion": "2.0" } }, - "MyHttpApiGETE0EFC6F8": { - "Type": "AWS::ApiGatewayV2::Route", + "MyHttpApiUserPoolAuthorizer8754262B": { + "Type": "AWS::ApiGatewayV2::Authorizer", "Properties": { "ApiId": { "Ref": "MyHttpApi8AEAAC21" }, - "RouteKey": "GET /", - "AuthorizationType": "JWT", - "AuthorizerId": { - "Ref": "MyAuthorizer6575980E" - }, - "Target": { - "Fn::Join": [ - "", - [ - "integrations/", - { - "Ref": "MyHttpApiGETGETIntegration0052AA15" - } + "AuthorizerType": "JWT", + "IdentitySource": [ + "$request.header.Authorization" + ], + "Name": "UserPoolAuthorizer", + "JwtConfiguration": { + "Audience": [ + { + "Ref": "userpoolmyclientFAD947AB" + } + ], + "Issuer": { + "Fn::Join": [ + "", + [ + "https://cognito-idp.", + { + "Ref": "AWS::Region" + }, + ".amazonaws.com/", + { + "Ref": "userpool0AC4AA96" + } + ] ] - ] + } } } }, @@ -149,41 +185,6 @@ ] } }, - "MyAuthorizer6575980E": { - "Type": "AWS::ApiGatewayV2::Authorizer", - "Properties": { - "ApiId": { - "Ref": "MyHttpApi8AEAAC21" - }, - "AuthorizerType": "JWT", - "IdentitySource": [ - "$request.header.Authorization" - ], - "Name": "MyAuthorizer", - "JwtConfiguration": { - "Audience": [ - { - "Ref": "userpoolmyclientFAD947AB" - } - ], - "Issuer": { - "Fn::Join": [ - "", - [ - "https://cognito-idp.", - { - "Ref": "AWS::Region" - }, - ".amazonaws.com/", - { - "Ref": "userpool0AC4AA96" - } - ] - ] - } - } - } - }, "lambdaServiceRole494E4CA6": { "Type": "AWS::IAM::Role", "Properties": { @@ -256,14 +257,14 @@ ] } }, - "Handler": "index.handler", "Role": { "Fn::GetAtt": [ "lambdaServiceRole494E4CA6", "Arn" ] }, - "Runtime": "nodejs10.x" + "Handler": "index.handler", + "Runtime": "nodejs12.x" }, "DependsOn": [ "lambdaServiceRole494E4CA6" diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.ts index 005c03d688935..edf455f4a787c 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.ts +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/integ.user-pool.ts @@ -31,7 +31,7 @@ const authorizer = new HttpUserPoolAuthorizer({ const handler = new lambda.Function(stack, 'lambda', { runtime: lambda.Runtime.NODEJS_12_X, handler: 'index.handler', - code: lambda.AssetCode.fromAsset(path.join(__dirname, 'integ.user-pool.handler')), + code: lambda.AssetCode.fromAsset(path.join(__dirname, '../integ.user-pool.handler')), }); httpApi.addRoutes({ diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/jwt.test.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/jwt.test.ts new file mode 100644 index 0000000000000..54d9170cda533 --- /dev/null +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/jwt.test.ts @@ -0,0 +1,96 @@ +import '@aws-cdk/assert/jest'; +import { HttpApi, HttpIntegrationType, HttpRouteIntegrationBindOptions, IHttpRouteIntegration, PayloadFormatVersion } from '@aws-cdk/aws-apigatewayv2'; +import { Stack } from '@aws-cdk/core'; +import { HttpJwtAuthorizer } from '../../lib'; + +describe('HttpJwtAuthorizer', () => { + test('default', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const authorizer = new HttpJwtAuthorizer({ + jwtAudience: ['3131231'], + jwtIssuer: 'https://test.us.auth0.com', + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Authorizer', { + AuthorizerType: 'JWT', + IdentitySource: ['$request.header.Authorization'], + JwtConfiguration: { + Audience: ['3131231'], + Issuer: 'https://test.us.auth0.com', + }, + }); + }); + + test('same authorizer is used when bound to multiple routes', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const authorizer = new HttpJwtAuthorizer({ + jwtAudience: ['3131231'], + jwtIssuer: 'https://test.us.auth0.com', + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/pets', + authorizer, + }); + + // THEN + expect(stack).toCountResources('AWS::ApiGatewayV2::Authorizer', 1); + }); + + test('can import authorizer using authorizerId', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const authorizer = HttpJwtAuthorizer.fromHttpJwtAuthorizerId(stack, 'auth', '12345'); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/pets', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizerId: '12345', + }); + }); +}); + +class DummyRouteIntegration implements IHttpRouteIntegration { + public bind(_: HttpRouteIntegrationBindOptions) { + return { + payloadFormatVersion: PayloadFormatVersion.VERSION_2_0, + type: HttpIntegrationType.HTTP_PROXY, + uri: 'some-uri', + }; + } +} From ebae46583bfb38d2ddeb492d5dded436f3e098ca Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Fri, 5 Feb 2021 02:18:32 -0400 Subject: [PATCH 38/47] feat: change import signature for HttpAuthorizer Remove fromHttpJwtAuthorizerId from HttpJwtAuthorizer and implement on the base HttpAuthorizer class --- .../lib/http/jwt.ts | 27 +--------------- .../test/http/jwt.test.ts | 26 ---------------- .../aws-apigatewayv2/lib/http/authorizer.ts | 11 +++++-- .../aws-apigatewayv2/test/http/api.test.ts | 31 ++++++++++++++++++- 4 files changed, 40 insertions(+), 55 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts index f4477d4c32fcc..afb5f10ac07f8 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/lib/http/jwt.ts @@ -3,11 +3,9 @@ import { HttpAuthorizerType, HttpRouteAuthorizerBindOptions, HttpRouteAuthorizerConfig, - IHttpAuthorizer, IHttpRouteAuthorizer, } from '@aws-cdk/aws-apigatewayv2'; -import { Resource, Token } from '@aws-cdk/core'; -import { Construct } from 'constructs'; +import { Token } from '@aws-cdk/core'; /** * Properties to initialize HttpJwtAuthorizer. @@ -44,29 +42,6 @@ export interface HttpJwtAuthorizerProps { * an AWS Cognito user pool. */ export class HttpJwtAuthorizer implements IHttpRouteAuthorizer { - /** - * Import an existing HTTP Jwt Authorizer into this CDK app. - */ - public static fromHttpJwtAuthorizerId(scope: Construct, id: string, authorizerId: string): IHttpRouteAuthorizer { - class Import extends Resource implements IHttpRouteAuthorizer { - private authorizer?: IHttpAuthorizer; - - public bind(): HttpRouteAuthorizerConfig { - - if (!this.authorizer) { - this.authorizer = HttpAuthorizer.fromHttpAuthorizerAttributes(scope, 'authorizer', { authorizerId, authorizerType: HttpAuthorizerType.JWT }); - } - - return { - authorizerId: this.authorizer.authorizerId, - authorizationType: HttpAuthorizerType.JWT, - }; - } - } - - return new Import(scope, id); - } - private authorizer?: HttpAuthorizer; constructor(private readonly props: HttpJwtAuthorizerProps) { diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/jwt.test.ts b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/jwt.test.ts index 54d9170cda533..8b27dc312a1a3 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/jwt.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/test/http/jwt.test.ts @@ -57,32 +57,6 @@ describe('HttpJwtAuthorizer', () => { // THEN expect(stack).toCountResources('AWS::ApiGatewayV2::Authorizer', 1); }); - - test('can import authorizer using authorizerId', () => { - // GIVEN - const stack = new Stack(); - const api = new HttpApi(stack, 'HttpApi'); - - const authorizer = HttpJwtAuthorizer.fromHttpJwtAuthorizerId(stack, 'auth', '12345'); - - // WHEN - api.addRoutes({ - integration: new DummyRouteIntegration(), - path: '/books', - authorizer, - }); - - api.addRoutes({ - integration: new DummyRouteIntegration(), - path: '/pets', - authorizer, - }); - - // THEN - expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { - AuthorizerId: '12345', - }); - }); }); class DummyRouteIntegration implements IHttpRouteIntegration { diff --git a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts index cf720f99cc49c..aadfb630ba276 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/lib/http/authorizer.ts @@ -86,10 +86,17 @@ export class HttpAuthorizer extends Resource implements IHttpAuthorizer { /** * Import an existing HTTP Authorizer into this CDK app. */ - public static fromHttpAuthorizerAttributes(scope: Construct, id: string, attrs: HttpAuthorizerAttributes): IHttpAuthorizer { - class Import extends Resource implements IHttpAuthorizer { + public static fromHttpAuthorizerAttributes(scope: Construct, id: string, attrs: HttpAuthorizerAttributes): IHttpRouteAuthorizer { + class Import extends Resource implements IHttpRouteAuthorizer { public readonly authorizerId = attrs.authorizerId; public readonly authorizerType = attrs.authorizerType; + + public bind(): HttpRouteAuthorizerConfig { + return { + authorizerId: attrs.authorizerId, + authorizationType: attrs.authorizerType, + }; + } } return new Import(scope, id); } diff --git a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts index 53a192fdce057..01252be7d84f1 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts +++ b/packages/@aws-cdk/aws-apigatewayv2/test/http/api.test.ts @@ -4,7 +4,7 @@ import { Metric } from '@aws-cdk/aws-cloudwatch'; import * as ec2 from '@aws-cdk/aws-ec2'; import { Duration, Stack } from '@aws-cdk/core'; import { - HttpApi, HttpAuthorizerType, HttpIntegrationType, HttpMethod, HttpRouteAuthorizerBindOptions, HttpRouteAuthorizerConfig, + HttpApi, HttpAuthorizer, HttpAuthorizerType, HttpIntegrationType, HttpMethod, HttpRouteAuthorizerBindOptions, HttpRouteAuthorizerConfig, HttpRouteIntegrationBindOptions, HttpRouteIntegrationConfig, IHttpRouteAuthorizer, IHttpRouteIntegration, PayloadFormatVersion, } from '../../lib'; @@ -300,6 +300,35 @@ describe('HttpApi', () => { }); }); + test('can import existing authorizer and attach to route', () => { + // GIVEN + const stack = new Stack(); + const api = new HttpApi(stack, 'HttpApi'); + + const authorizer = HttpAuthorizer.fromHttpAuthorizerAttributes(stack, 'auth', { + authorizerId: '12345', + authorizerType: HttpAuthorizerType.JWT, + }); + + // WHEN + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/books', + authorizer, + }); + + api.addRoutes({ + integration: new DummyRouteIntegration(), + path: '/pets', + authorizer, + }); + + // THEN + expect(stack).toHaveResource('AWS::ApiGatewayV2::Route', { + AuthorizerId: '12345', + }); + }); + test('can attach custom scopes to authorizer route', () => { const stack = new Stack(); const httpApi = new HttpApi(stack, 'api'); From e6dd0797e25c678786216a371f11e022df7cd6a7 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Fri, 5 Feb 2021 02:46:56 -0400 Subject: [PATCH 39/47] docs: add info on user pool authorizer --- .../aws-apigatewayv2-authorizers/README.md | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md index 4b0e2e4be9407..a20efd0c10bd5 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md @@ -36,4 +36,24 @@ allow and restrict clients from accessing HTTP APIs. ### User Pool Authorizer - +User Pool Authorizer is a type of JWT Authorizer that uses a Cognito user pool and app client to control who can access your Api. After a successful authorization from the app client, the generated access token will be used as the JWT. + +```ts +const userPool = new UserPool(stack, 'UserPool'); +const userPoolClient = userPool.addClient('UserPoolClient'); + +const authorizer = new HttpUserPoolAuthorizer({ + userPool, + userPoolClient, +}); + +const api = new HttpApi(stack, 'HttpApi'); + +api.addRoutes({ + integration: new HttpProxyIntegration({ + url: 'https://get-books-proxy.myproxy.internal', + }), + path: '/books', + authorizer, +}); +``` \ No newline at end of file From d6868a67fe2c7c5023765fe47f4717ad93a4b254 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Fri, 5 Feb 2021 02:56:13 -0400 Subject: [PATCH 40/47] docs: refer to authorizers package --- .../aws-apigatewayv2-authorizers/README.md | 23 +++++++++++-- packages/@aws-cdk/aws-apigatewayv2/README.md | 33 +------------------ 2 files changed, 21 insertions(+), 35 deletions(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md index a20efd0c10bd5..60dbc7d336672 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md @@ -30,9 +30,26 @@ API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-acces ## JWT Authorizers -JWT authorizers allow the use of JSON Web Tokens (JWTs) as part of [OpenID -Connect](https://openid.net/specs/openid-connect-core-1_0.html) and [OAuth 2.0](https://oauth.net/2/) frameworks to -allow and restrict clients from accessing HTTP APIs. +JWT authorizers allow the use of JSON Web Tokens (JWTs) as part of [OpenID Connect](https://openid.net/specs/openid-connect-core-1_0.html) and [OAuth 2.0](https://oauth.net/2/) frameworks to allow and restrict clients from accessing HTTP APIs. + +When configured on a route, the API Gateway service validates the JWTs submitted by the client, and allows or denies access based on its content. + +```ts +const authorizer = new HttpJwtAuthorizer({ + jwtAudience: ['3131231'], + jwtIssuer: 'https://test.us.auth0.com', +}); + +const api = new HttpApi(stack, 'HttpApi'); + +api.addRoutes({ + integration: new HttpProxyIntegration({ + url: 'https://get-books-proxy.myproxy.internal', + }), + path: '/books', + authorizer, +}); +``` ### User Pool Authorizer diff --git a/packages/@aws-cdk/aws-apigatewayv2/README.md b/packages/@aws-cdk/aws-apigatewayv2/README.md index 8f950b8f20c8d..4da900f271e8f 100644 --- a/packages/@aws-cdk/aws-apigatewayv2/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2/README.md @@ -228,38 +228,7 @@ with 3 API mapping resources across different APIs and Stages. API Gateway supports multiple mechanisms for [controlling and managing access to your HTTP API](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-access-control.html) through authorizers. -#### JWT Authorizer - -The JWT authorizer authorizes access via JSON Web Tokens (JWT) as part of the [OpenId -Connect](https://openid.net/specs/openid-connect-core-1_0.html) and [OAuth](https://oauth.net/2/) -frameworks. - -When configured on a route, the API Gateway service validates the JWTs submitted by the client, and allows or denies -access based on its content. - -The following sets up a JWT authorizer using a Cognito user pool as the identity source. - -```ts -const userPool = new UserPool(stack, 'my-pool'); -const httpApi = new HttpApi(stack, 'HttpApi'); - -const appClient = userPool.addClient('my-app-client'); - -const authorizer = new HttpJwtAuthorizer(stack, { - httpApi, - jwtConfiguration: { - audience: [appClient.userPoolClientId], - issuer: `https://cognito-idp.${stack.region}.amazonaws.com/${userPool.userPoolId}`, - }, -}); - -httpApi.addRoutes({ - path: '/books', - methods: [ HttpMethod.GET ], - integration: getBooksIntegration, - authorizer, // Protecting this route via our authorizer -}); -``` +These authorizers can be found in the [APIGatewayV2-Authorizers](https://docs.aws.amazon.com/cdk/api/latest/docs/aws-apigatewayv2-authorizers-readme.html) constructs library. ## Metrics From ce218c1cc28a534a71b3717998bac972b90a9c6e Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Tue, 9 Feb 2021 20:32:18 -0400 Subject: [PATCH 41/47] Update packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md Co-authored-by: Niranjan Jayakar --- packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md index 60dbc7d336672..a2e79f12dc821 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md @@ -55,6 +55,9 @@ api.addRoutes({ User Pool Authorizer is a type of JWT Authorizer that uses a Cognito user pool and app client to control who can access your Api. After a successful authorization from the app client, the generated access token will be used as the JWT. +Clients accessing an API that uses a user pool authorizer must first sign in to a user pool and obtain an identity or access token. +They must then use this token in the `Authorization` header of the API call. More information is available at [using Amazon Cognito user +pools as authorizer](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html). ```ts const userPool = new UserPool(stack, 'UserPool'); const userPoolClient = userPool.addClient('UserPoolClient'); @@ -73,4 +76,4 @@ api.addRoutes({ path: '/books', authorizer, }); -``` \ No newline at end of file +``` From 27bc22b3441f0939ac127c06430ca3c95dde5250 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Tue, 9 Feb 2021 20:34:10 -0400 Subject: [PATCH 42/47] spacing nit --- packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md index a2e79f12dc821..7fcce81c2af88 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md @@ -58,6 +58,7 @@ User Pool Authorizer is a type of JWT Authorizer that uses a Cognito user pool a Clients accessing an API that uses a user pool authorizer must first sign in to a user pool and obtain an identity or access token. They must then use this token in the `Authorization` header of the API call. More information is available at [using Amazon Cognito user pools as authorizer](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-integrate-with-cognito.html). + ```ts const userPool = new UserPool(stack, 'UserPool'); const userPoolClient = userPool.addClient('UserPoolClient'); From 43625af78486bcaaea2e0d9534d98a1f3a8ca5f0 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Tue, 9 Feb 2021 20:50:54 -0400 Subject: [PATCH 43/47] docs: add info on the token validation process --- packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md index 7fcce81c2af88..9591ecfe4ffde 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md +++ b/packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md @@ -34,6 +34,8 @@ JWT authorizers allow the use of JSON Web Tokens (JWTs) as part of [OpenID Conne When configured on a route, the API Gateway service validates the JWTs submitted by the client, and allows or denies access based on its content. +API gateway uses the `identitySource` to determine where to look for the token. By default it checks the http `Authorization` header. However it also [supports a number of other options](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html#http-api-lambda-authorizer.identity-sources). It then decodes the JWT and validates the signature and claims, against the options defined in the authorizer and route (scopes). For more information check the [JWT Authorizer documentation](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-jwt-authorizer.html). + ```ts const authorizer = new HttpJwtAuthorizer({ jwtAudience: ['3131231'], From fc1d3c0db5f2bb988ad9370808c30a174b0a8863 Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Tue, 9 Feb 2021 22:45:33 -0400 Subject: [PATCH 44/47] test: update integrations --- .../test/http/integ.alb.expected.json | 1 + .../test/http/integ.http-proxy.expected.json | 2 ++ .../test/http/integ.lambda-proxy.expected.json | 1 + .../test/http/integ.nlb.expected.json | 1 + .../test/http/integ.service-discovery.expected.json | 1 + 5 files changed, 6 insertions(+) diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.alb.expected.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.alb.expected.json index d96b67d1582f9..cc969ddfa4684 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.alb.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.alb.expected.json @@ -615,6 +615,7 @@ "Ref": "HttpProxyPrivateApiA55E154D" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.http-proxy.expected.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.http-proxy.expected.json index 1c2bc834f7f48..709d1871e780e 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.http-proxy.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.http-proxy.expected.json @@ -101,6 +101,7 @@ "Ref": "LambdaProxyApi67594471" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", @@ -154,6 +155,7 @@ "Ref": "HttpProxyApiD0217C67" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.lambda-proxy.expected.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.lambda-proxy.expected.json index c2661f62833a8..99547f333ade2 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.lambda-proxy.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.lambda-proxy.expected.json @@ -101,6 +101,7 @@ "Ref": "LambdaProxyApi67594471" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.nlb.expected.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.nlb.expected.json index a29f632928938..5fa3047213025 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.nlb.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.nlb.expected.json @@ -580,6 +580,7 @@ "Ref": "HttpProxyPrivateApiA55E154D" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", diff --git a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.service-discovery.expected.json b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.service-discovery.expected.json index d815552e3107d..39b8d98fafe0a 100644 --- a/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.service-discovery.expected.json +++ b/packages/@aws-cdk/aws-apigatewayv2-integrations/test/http/integ.service-discovery.expected.json @@ -581,6 +581,7 @@ "Ref": "HttpProxyPrivateApiA55E154D" }, "RouteKey": "$default", + "AuthorizationScopes": [], "Target": { "Fn::Join": [ "", From cf03de5014e5a5160617932e6992312a18ccbdca Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Tue, 9 Feb 2021 23:26:17 -0400 Subject: [PATCH 45/47] feat: add authorizer package to decdk --- packages/decdk/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/decdk/package.json b/packages/decdk/package.json index e030318880208..41d11481f3108 100644 --- a/packages/decdk/package.json +++ b/packages/decdk/package.json @@ -36,6 +36,7 @@ "@aws-cdk/aws-apigateway": "0.0.0", "@aws-cdk/aws-apigatewayv2": "0.0.0", "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", + "@aws-cdk/aws-apigatewayv2-authorizers": "0.0.0", "@aws-cdk/aws-appconfig": "0.0.0", "@aws-cdk/aws-appflow": "0.0.0", "@aws-cdk/aws-applicationautoscaling": "0.0.0", From f89041b0be6a5608e5f657f8e9bc1f0d2fb1543a Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 10 Feb 2021 01:40:53 -0400 Subject: [PATCH 46/47] feat: add authorizer package to monocdk --- packages/monocdk/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/monocdk/package.json b/packages/monocdk/package.json index f06d5a236d5e3..883fa9169c512 100644 --- a/packages/monocdk/package.json +++ b/packages/monocdk/package.json @@ -113,6 +113,7 @@ "@aws-cdk/aws-amplify": "0.0.0", "@aws-cdk/aws-apigateway": "0.0.0", "@aws-cdk/aws-apigatewayv2": "0.0.0", + "@aws-cdk/aws-apigatewayv2-authorizers": "0.0.0", "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", "@aws-cdk/aws-appconfig": "0.0.0", "@aws-cdk/aws-appflow": "0.0.0", From 9a46843c58d12aeef8e7469a878b2018961cec2c Mon Sep 17 00:00:00 2001 From: Kyle Roach Date: Wed, 10 Feb 2021 01:42:45 -0400 Subject: [PATCH 47/47] feat: add authorizer package to aws-cdk-lib --- packages/aws-cdk-lib/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/aws-cdk-lib/package.json b/packages/aws-cdk-lib/package.json index fda319d7104db..70a29966650f1 100644 --- a/packages/aws-cdk-lib/package.json +++ b/packages/aws-cdk-lib/package.json @@ -108,6 +108,7 @@ "@aws-cdk/aws-amplify": "0.0.0", "@aws-cdk/aws-apigateway": "0.0.0", "@aws-cdk/aws-apigatewayv2": "0.0.0", + "@aws-cdk/aws-apigatewayv2-authorizers": "0.0.0", "@aws-cdk/aws-apigatewayv2-integrations": "0.0.0", "@aws-cdk/aws-appconfig": "0.0.0", "@aws-cdk/aws-appflow": "0.0.0",