Skip to content

Commit f397071

Browse files
authored
fix(apigatewayv2): defaultAuthorizer cannot be applied to HttpRoute (#27576)
This PR fixes a bug that `defaultAuthorizer` cannot be applied to `HttpRoute` without an authorizer. Closes #27436. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent f6bf5cc commit f397071

File tree

13 files changed

+1536
-304
lines changed

13 files changed

+1536
-304
lines changed

packages/@aws-cdk/aws-apigatewayv2-alpha/lib/http/api.ts

+23-4
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,29 @@ import { DomainMappingOptions } from '../common/stage';
1717
export interface IHttpApi extends IApi {
1818
/**
1919
* The identifier of this API Gateway HTTP API.
20+
*
2021
* @attribute
2122
* @deprecated - use apiId instead
2223
*/
2324
readonly httpApiId: string;
2425

26+
/**
27+
* Default Authorizer applied to all routes in the gateway.
28+
*
29+
* @attribute
30+
* @default - no default authorizer
31+
*/
32+
readonly defaultAuthorizer?: IHttpRouteAuthorizer;
33+
34+
/**
35+
* Default OIDC scopes attached to all routes in the gateway, unless explicitly configured on the route.
36+
* The scopes are used with a COGNITO_USER_POOLS authorizer to authorize the method invocation.
37+
*
38+
* @attribute
39+
* @default - no default authorization scopes
40+
*/
41+
readonly defaultAuthorizationScopes?: string[];
42+
2543
/**
2644
* Metric for the number of client-side errors captured in a given period.
2745
*
@@ -125,14 +143,15 @@ export interface HttpApiProps {
125143
readonly disableExecuteApiEndpoint?: boolean;
126144

127145
/**
128-
* Default Authorizer to applied to all routes in the gateway
146+
* Default Authorizer applied to all routes in the gateway.
129147
*
130-
* @default - No authorizer
148+
* @default - no default authorizer
131149
*/
132150
readonly defaultAuthorizer?: IHttpRouteAuthorizer;
133151

134152
/**
135153
* Default OIDC scopes attached to all routes in the gateway, unless explicitly configured on the route.
154+
* The scopes are used with a COGNITO_USER_POOLS authorizer to authorize the method invocation.
136155
*
137156
* @default - no default authorization scopes
138157
*/
@@ -340,8 +359,8 @@ export class HttpApi extends HttpApiBase {
340359

341360
private readonly _apiEndpoint: string;
342361

343-
private readonly defaultAuthorizer?: IHttpRouteAuthorizer;
344-
private readonly defaultAuthorizationScopes?: string[];
362+
public readonly defaultAuthorizer?: IHttpRouteAuthorizer;
363+
public readonly defaultAuthorizationScopes?: string[];
345364

346365
constructor(scope: Construct, id: string, props?: HttpApiProps) {
347366
super(scope, id);

packages/@aws-cdk/aws-apigatewayv2-alpha/lib/http/route.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,8 @@ export class HttpRoute extends Resource implements IHttpRoute {
193193
scope: this,
194194
});
195195

196-
this.authBindResult = props.authorizer?.bind({
196+
const authorizer = props.authorizer ?? this.httpApi.defaultAuthorizer;
197+
this.authBindResult = authorizer?.bind({
197198
route: this,
198199
scope: this.httpApi instanceof Construct ? this.httpApi : this, // scope under the API if it's not imported
199200
});
@@ -204,10 +205,10 @@ export class HttpRoute extends Resource implements IHttpRoute {
204205

205206
let authorizationScopes = this.authBindResult?.authorizationScopes;
206207

207-
if (this.authBindResult && props.authorizationScopes) {
208+
if (this.authBindResult && (props.authorizationScopes || this.httpApi.defaultAuthorizationScopes)) {
208209
authorizationScopes = Array.from(new Set([
209210
...authorizationScopes ?? [],
210-
...props.authorizationScopes,
211+
...props.authorizationScopes ?? this.httpApi.defaultAuthorizationScopes ?? [],
211212
]));
212213
}
213214

packages/@aws-cdk/aws-apigatewayv2-alpha/test/http/route.test.ts

+90
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,96 @@ describe('HttpRoute', () => {
329329
});
330330
});
331331

332+
test('can create route without an authorizer when api has defaultAuthorizer', () => {
333+
const stack = new Stack();
334+
335+
const authorizer = new DummyAuthorizer();
336+
const httpApi = new HttpApi(stack, 'HttpApi', {
337+
defaultAuthorizer: authorizer,
338+
defaultAuthorizationScopes: ['read:books'],
339+
});
340+
341+
const route = new HttpRoute(stack, 'HttpRoute', {
342+
httpApi,
343+
integration: new DummyIntegration(),
344+
routeKey: HttpRouteKey.with('/books', HttpMethod.GET),
345+
});
346+
347+
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Integration', {
348+
ApiId: stack.resolve(httpApi.apiId),
349+
IntegrationType: 'HTTP_PROXY',
350+
PayloadFormatVersion: '2.0',
351+
IntegrationUri: 'some-uri',
352+
});
353+
354+
Template.fromStack(stack).resourceCountIs('AWS::ApiGatewayV2::Authorizer', 1);
355+
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Route', {
356+
AuthorizerId: stack.resolve(authorizer.bind({ scope: stack, route: route }).authorizerId),
357+
AuthorizationType: 'JWT',
358+
AuthorizationScopes: ['read:books'],
359+
});
360+
});
361+
362+
test('authorizationScopes can be applied to route without authorizer when api has defaultAuthorizer', () => {
363+
const stack = new Stack();
364+
365+
const authorizer = new DummyAuthorizer();
366+
const httpApi = new HttpApi(stack, 'HttpApi', {
367+
defaultAuthorizer: authorizer,
368+
});
369+
370+
const route = new HttpRoute(stack, 'HttpRoute', {
371+
httpApi,
372+
integration: new DummyIntegration(),
373+
routeKey: HttpRouteKey.with('/books', HttpMethod.GET),
374+
authorizationScopes: ['read:books'],
375+
});
376+
377+
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Integration', {
378+
ApiId: stack.resolve(httpApi.apiId),
379+
IntegrationType: 'HTTP_PROXY',
380+
PayloadFormatVersion: '2.0',
381+
IntegrationUri: 'some-uri',
382+
});
383+
384+
Template.fromStack(stack).resourceCountIs('AWS::ApiGatewayV2::Authorizer', 1);
385+
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Route', {
386+
AuthorizerId: stack.resolve(authorizer.bind({ scope: stack, route: route }).authorizerId),
387+
AuthorizationType: 'JWT',
388+
AuthorizationScopes: ['read:books'],
389+
});
390+
});
391+
392+
test('defaultAuthorizationScopes can be applied to route', () => {
393+
const stack = new Stack();
394+
395+
const authorizer = new DummyAuthorizer();
396+
const httpApi = new HttpApi(stack, 'HttpApi', {
397+
defaultAuthorizationScopes: ['read:books'],
398+
});
399+
400+
const route = new HttpRoute(stack, 'HttpRoute', {
401+
httpApi,
402+
integration: new DummyIntegration(),
403+
routeKey: HttpRouteKey.with('/books', HttpMethod.GET),
404+
authorizer,
405+
});
406+
407+
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Integration', {
408+
ApiId: stack.resolve(httpApi.apiId),
409+
IntegrationType: 'HTTP_PROXY',
410+
PayloadFormatVersion: '2.0',
411+
IntegrationUri: 'some-uri',
412+
});
413+
414+
Template.fromStack(stack).resourceCountIs('AWS::ApiGatewayV2::Authorizer', 1);
415+
Template.fromStack(stack).hasResourceProperties('AWS::ApiGatewayV2::Route', {
416+
AuthorizerId: stack.resolve(authorizer.bind({ scope: stack, route: route }).authorizerId),
417+
AuthorizationType: 'JWT',
418+
AuthorizationScopes: ['read:books'],
419+
});
420+
});
421+
332422
test('can attach additional scopes to a route with an authorizer attached', () => {
333423
const stack = new Stack();
334424
const httpApi = new HttpApi(stack, 'HttpApi');

packages/@aws-cdk/aws-apigatewayv2-authorizers-alpha/test/http/integ.lambda.js.snapshot/AuthorizerInteg.assets.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)