Skip to content

Commit

Permalink
feat(apigatewayv2): http api - default authorizer options (#13172)
Browse files Browse the repository at this point in the history
Allows setting an authorizer and authorization scopes that will be applied to all routes of the api.

@nija-at this is the bit we forgot In the first PR.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
iRoachie authored Mar 17, 2021
1 parent 9b1c786 commit 53d9661
Show file tree
Hide file tree
Showing 12 changed files with 391 additions and 152 deletions.
90 changes: 86 additions & 4 deletions packages/@aws-cdk/aws-apigatewayv2-authorizers/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# AWS APIGatewayv2 Authorizers

<!--BEGIN STABILITY BANNER-->

---
Expand All @@ -17,24 +18,105 @@

## Table of Contents

- [Introduction](#introduction)
- [HTTP APIs](#http-apis)
- [Default Authorization](#default-authorization)
- [Route Authorization](#route-authorization)
- [JWT Authorizers](#jwt-authorizers)
- [User Pool Authorizer](#user-pool-authorizer)

## HTTP APIs
## Introduction

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).

## HTTP APIs

Access control for Http Apis is managed by restricting which routes can be invoked via.

Authorizers, and scopes can either be applied to the api, or specifically for each route.

### Default Authorization

When using default authorization, all routes of the api will inherit the configuration.

In the example below, all routes will require the `manage:books` scope present in order to invoke the integration.

```ts
const authorizer = new HttpJwtAuthorizer({
...
});

const api = new HttpApi(stack, 'HttpApi', {
defaultAuthorizer: authorizer,
defaultAuthorizationScopes: ['manage:books'],
});
```

### Route Authorization

Authorization can also configured for each Route. When a route authorization is configured, it takes precedence over default authorization.

The example below showcases default authorization, along with route authorization. It also shows how to remove authorization entirely for a route.

- `GET /books` and `GET /books/{id}` use the default authorizer settings on the api
- `POST /books` will require the [write:books] scope
- `POST /login` removes the default authorizer (unauthenticated route)

```ts
const authorizer = new HttpJwtAuthorizer({
...
});

const api = new HttpApi(stack, 'HttpApi', {
defaultAuthorizer: authorizer,
defaultAuthorizationScopes: ['read:books'],
});

api.addRoutes({
...
path: '/books',
method: 'get',
});

api.addRoutes({
...
path: '/books/{id}',
method: 'get',
});

api.addRoutes({
...
path: '/books',
method: 'post',
authorizationScopes: ['write:books']
});

api.addRoutes({
...
path: '/login',
method: 'post',
authorizer: new NoneAuthorizer(),
});
```

## 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.

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.
When configured, API Gateway validates the JWT submitted by the client, and allows or denies access based on its content.

The location of the token is defined by the `identitySource` which defaults to 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).

Clients that fail authorization are presented with either 2 responses:

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).
- `401 - Unauthorized` - When the JWT validation fails
- `403 - Forbidden` - When the JWT validation is successful but the required scopes are not met

```ts
const authorizer = new HttpJwtAuthorizer({
Expand All @@ -58,7 +140,7 @@ 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
They must then use this token in the specified `identitySource` for 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,29 @@
}
}
},
"MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31": {
"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 /",
"AuthorizationScopes": [],
"AuthorizationType": "JWT",
"AuthorizerId": {
"Ref": "MyHttpApiUserPoolAuthorizer8754262B"
Expand All @@ -79,22 +94,6 @@
}
}
},
"MyHttpApiGETHttpIntegration6f095b8469365f72e33fa33d9711b140516EBE31": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
"Ref": "MyHttpApi8AEAAC21"
},
"IntegrationType": "AWS_PROXY",
"IntegrationUri": {
"Fn::GetAtt": [
"lambda8B5974B5",
"Arn"
]
},
"PayloadFormatVersion": "2.0"
}
},
"MyHttpApiUserPoolAuthorizer8754262B": {
"Type": "AWS::ApiGatewayV2::Authorizer",
"Properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -608,14 +608,31 @@
"ProtocolType": "HTTP"
}
},
"HttpProxyPrivateApiDefaultRouteHttpIntegration1a580b19954e4317026ffbce1f7d5ade7A32685B": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
"Ref": "HttpProxyPrivateApiA55E154D"
},
"IntegrationType": "HTTP_PROXY",
"ConnectionId": {
"Ref": "HttpProxyPrivateApiVpcLink190366CAE"
},
"ConnectionType": "VPC_LINK",
"IntegrationMethod": "ANY",
"IntegrationUri": {
"Ref": "lblistener657ADDEC"
},
"PayloadFormatVersion": "1.0"
}
},
"HttpProxyPrivateApiDefaultRoute1BDCA252": {
"Type": "AWS::ApiGatewayV2::Route",
"Properties": {
"ApiId": {
"Ref": "HttpProxyPrivateApiA55E154D"
},
"RouteKey": "$default",
"AuthorizationScopes": [],
"Target": {
"Fn::Join": [
"",
Expand Down Expand Up @@ -647,24 +664,6 @@
"SecurityGroupIds": []
}
},
"HttpProxyPrivateApiDefaultRouteHttpIntegration1a580b19954e4317026ffbce1f7d5ade7A32685B": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
"Ref": "HttpProxyPrivateApiA55E154D"
},
"IntegrationType": "HTTP_PROXY",
"ConnectionId": {
"Ref": "HttpProxyPrivateApiVpcLink190366CAE"
},
"ConnectionType": "VPC_LINK",
"IntegrationMethod": "ANY",
"IntegrationUri": {
"Ref": "lblistener657ADDEC"
},
"PayloadFormatVersion": "1.0"
}
},
"HttpProxyPrivateApiDefaultStage18B3706E": {
"Type": "AWS::ApiGatewayV2::Stage",
"Properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,29 @@
}
}
},
"LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
"Ref": "LambdaProxyApi67594471"
},
"IntegrationType": "AWS_PROXY",
"IntegrationUri": {
"Fn::GetAtt": [
"AlwaysSuccess099EAB05",
"Arn"
]
},
"PayloadFormatVersion": "2.0"
}
},
"LambdaProxyApiDefaultRoute1EB30A46": {
"Type": "AWS::ApiGatewayV2::Route",
"Properties": {
"ApiId": {
"Ref": "LambdaProxyApi67594471"
},
"RouteKey": "$default",
"AuthorizationScopes": [],
"Target": {
"Fn::Join": [
"",
Expand All @@ -115,22 +130,6 @@
}
}
},
"LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
"Ref": "LambdaProxyApi67594471"
},
"IntegrationType": "AWS_PROXY",
"IntegrationUri": {
"Fn::GetAtt": [
"AlwaysSuccess099EAB05",
"Arn"
]
},
"PayloadFormatVersion": "2.0"
}
},
"LambdaProxyApiDefaultStage07C38681": {
"Type": "AWS::ApiGatewayV2::Stage",
"Properties": {
Expand All @@ -148,27 +147,6 @@
"ProtocolType": "HTTP"
}
},
"HttpProxyApiDefaultRoute8AF66B5C": {
"Type": "AWS::ApiGatewayV2::Route",
"Properties": {
"ApiId": {
"Ref": "HttpProxyApiD0217C67"
},
"RouteKey": "$default",
"AuthorizationScopes": [],
"Target": {
"Fn::Join": [
"",
[
"integrations/",
{
"Ref": "HttpProxyApiDefaultRouteHttpIntegration8eeecf9ecdb91f31bebf6bd54fb711a41921AB82"
}
]
]
}
}
},
"HttpProxyApiDefaultRouteHttpIntegration8eeecf9ecdb91f31bebf6bd54fb711a41921AB82": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
Expand Down Expand Up @@ -200,6 +178,26 @@
"PayloadFormatVersion": "1.0"
}
},
"HttpProxyApiDefaultRoute8AF66B5C": {
"Type": "AWS::ApiGatewayV2::Route",
"Properties": {
"ApiId": {
"Ref": "HttpProxyApiD0217C67"
},
"RouteKey": "$default",
"Target": {
"Fn::Join": [
"",
[
"integrations/",
{
"Ref": "HttpProxyApiDefaultRouteHttpIntegration8eeecf9ecdb91f31bebf6bd54fb711a41921AB82"
}
]
]
}
}
},
"HttpProxyApiDefaultStageA88F9DE3": {
"Type": "AWS::ApiGatewayV2::Stage",
"Properties": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,29 @@
}
}
},
"LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
"Ref": "LambdaProxyApi67594471"
},
"IntegrationType": "AWS_PROXY",
"IntegrationUri": {
"Fn::GetAtt": [
"AlwaysSuccess099EAB05",
"Arn"
]
},
"PayloadFormatVersion": "2.0"
}
},
"LambdaProxyApiDefaultRoute1EB30A46": {
"Type": "AWS::ApiGatewayV2::Route",
"Properties": {
"ApiId": {
"Ref": "LambdaProxyApi67594471"
},
"RouteKey": "$default",
"AuthorizationScopes": [],
"Target": {
"Fn::Join": [
"",
Expand All @@ -115,22 +130,6 @@
}
}
},
"LambdaProxyApiDefaultRouteHttpIntegration70df0ec52c3e3b6bbc96e64ce3a05f24EE575CBA": {
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {
"ApiId": {
"Ref": "LambdaProxyApi67594471"
},
"IntegrationType": "AWS_PROXY",
"IntegrationUri": {
"Fn::GetAtt": [
"AlwaysSuccess099EAB05",
"Arn"
]
},
"PayloadFormatVersion": "2.0"
}
},
"LambdaProxyApiDefaultStage07C38681": {
"Type": "AWS::ApiGatewayV2::Stage",
"Properties": {
Expand Down
Loading

0 comments on commit 53d9661

Please sign in to comment.