API Gateway V2 API Authorizers Enabled
Overview
This check verifies that your Amazon API Gateway HTTP APIs and WebSocket APIs have at least one authorizer configured. Authorizers control access to your API routes by authenticating requests before they reach your backend services. Without an authorizer, anyone can call your API endpoints.
Risk
Without proper authorization on your API Gateway V2 APIs:
- Data exposure: Sensitive data can be accessed by anyone who discovers your API endpoint
- Unauthorized actions: Attackers can invoke backend operations and modify system state
- Cost overruns: Unprotected APIs are vulnerable to abuse, causing unexpected traffic spikes and charges
- Service disruption: Automated attacks can overwhelm your backend services
Remediation Steps
Prerequisites
- AWS account access with permissions to modify API Gateway configurations
- For JWT authorizers: An identity provider like Amazon Cognito or any OpenID Connect (OIDC) provider
- For Lambda authorizers: A Lambda function to handle authentication logic
AWS Console Method
- Sign in to the AWS Management Console
- Navigate to API Gateway
- Select your HTTP API or WebSocket API from the list
- In the left navigation, click Authorization
- Click Manage authorizers, then Create
- Choose one of the following options:
Option A: JWT Authorizer (recommended for HTTP APIs with Cognito or OIDC)
- Name: Enter a descriptive name (e.g.,
my-jwt-authorizer) - Identity source: Leave as
$request.header.Authorization - Issuer URL: Enter your identity provider URL (e.g.,
https://cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxxx) - Audience: Enter your app client ID
- Click Create
Option B: Lambda Authorizer (for custom authentication logic)
- Name: Enter a descriptive name (e.g.,
my-lambda-authorizer) - Lambda function: Select your authorizer function
- Identity source: Enter
$request.header.Authorization - Click Create
- Return to Authorization in the left navigation
- Select a route (e.g.,
GET /items) - Click Attach authorizer and select your newly created authorizer
- Repeat for all routes that require protection
- Your changes are applied automatically (no deployment step needed for HTTP APIs)
AWS CLI (optional)
List your HTTP/WebSocket APIs:
aws apigatewayv2 get-apis --region us-east-1
Check existing authorizers for an API:
aws apigatewayv2 get-authorizers \
--api-id <your-api-id> \
--region us-east-1
Create a JWT authorizer (for Cognito or OIDC):
aws apigatewayv2 create-authorizer \
--api-id <your-api-id> \
--name my-jwt-authorizer \
--authorizer-type JWT \
--identity-source '$request.header.Authorization' \
--jwt-configuration Audience=<your-app-client-id>,Issuer=https://cognito-idp.us-east-1.amazonaws.com/us-east-1_<pool-id> \
--region us-east-1
Create a Lambda authorizer:
aws apigatewayv2 create-authorizer \
--api-id <your-api-id> \
--name my-lambda-authorizer \
--authorizer-type REQUEST \
--authorizer-uri 'arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:<account-id>:function:<function-name>/invocations' \
--identity-source '$request.header.Authorization' \
--authorizer-payload-format-version 2.0 \
--enable-simple-responses \
--region us-east-1
Attach the authorizer to a route:
aws apigatewayv2 update-route \
--api-id <your-api-id> \
--route-id <route-id> \
--authorization-type JWT \
--authorizer-id <authorizer-id> \
--region us-east-1
For Lambda authorizers, use --authorization-type CUSTOM instead of JWT.
CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: API Gateway HTTP API with JWT Authorizer
Parameters:
CognitoUserPoolId:
Type: String
Description: Cognito User Pool ID (e.g., us-east-1_xxxxx)
CognitoAppClientId:
Type: String
Description: Cognito App Client ID
Resources:
HttpApi:
Type: AWS::ApiGatewayV2::Api
Properties:
Name: my-secure-http-api
Description: HTTP API with JWT authorization
ProtocolType: HTTP
JwtAuthorizer:
Type: AWS::ApiGatewayV2::Authorizer
Properties:
ApiId: !Ref HttpApi
AuthorizerType: JWT
Name: cognito-jwt-authorizer
IdentitySource:
- $request.header.Authorization
JwtConfiguration:
Audience:
- !Ref CognitoAppClientId
Issuer: !Sub 'https://cognito-idp.${AWS::Region}.amazonaws.com/${CognitoUserPoolId}'
SecureRoute:
Type: AWS::ApiGatewayV2::Route
Properties:
ApiId: !Ref HttpApi
RouteKey: GET /secure-resource
AuthorizationType: JWT
AuthorizerId: !Ref JwtAuthorizer
Target: !Sub 'integrations/${HttpIntegration}'
HttpIntegration:
Type: AWS::ApiGatewayV2::Integration
Properties:
ApiId: !Ref HttpApi
IntegrationType: HTTP_PROXY
IntegrationUri: https://httpbin.org/get
IntegrationMethod: GET
PayloadFormatVersion: '1.0'
DefaultStage:
Type: AWS::ApiGatewayV2::Stage
Properties:
ApiId: !Ref HttpApi
StageName: $default
AutoDeploy: true
Outputs:
ApiEndpoint:
Description: HTTP API endpoint URL
Value: !Sub 'https://${HttpApi}.execute-api.${AWS::Region}.amazonaws.com'
Deploy the template:
aws cloudformation deploy \
--template-file http-api-authorizer.yaml \
--stack-name http-api-authorizer-stack \
--parameter-overrides \
CognitoUserPoolId=us-east-1_xxxxx \
CognitoAppClientId=your-app-client-id \
--region us-east-1
Terraform (optional)
resource "aws_apigatewayv2_api" "http_api" {
name = "my-secure-http-api"
description = "HTTP API with JWT authorization"
protocol_type = "HTTP"
}
resource "aws_apigatewayv2_authorizer" "jwt" {
api_id = aws_apigatewayv2_api.http_api.id
authorizer_type = "JWT"
name = "cognito-jwt-authorizer"
identity_sources = ["$request.header.Authorization"]
jwt_configuration {
audience = [var.cognito_app_client_id]
issuer = "https://cognito-idp.${var.aws_region}.amazonaws.com/${var.cognito_user_pool_id}"
}
}
resource "aws_apigatewayv2_integration" "example" {
api_id = aws_apigatewayv2_api.http_api.id
integration_type = "HTTP_PROXY"
integration_uri = "https://httpbin.org/get"
integration_method = "GET"
}
resource "aws_apigatewayv2_route" "secure_route" {
api_id = aws_apigatewayv2_api.http_api.id
route_key = "GET /secure-resource"
authorization_type = "JWT"
authorizer_id = aws_apigatewayv2_authorizer.jwt.id
target = "integrations/${aws_apigatewayv2_integration.example.id}"
}
resource "aws_apigatewayv2_stage" "default" {
api_id = aws_apigatewayv2_api.http_api.id
name = "$default"
auto_deploy = true
}
variable "cognito_user_pool_id" {
description = "Cognito User Pool ID"
type = string
}
variable "cognito_app_client_id" {
description = "Cognito App Client ID"
type = string
}
variable "aws_region" {
description = "AWS region"
type = string
default = "us-east-1"
}
output "api_endpoint" {
value = aws_apigatewayv2_stage.default.invoke_url
}
For a Lambda authorizer instead:
resource "aws_apigatewayv2_authorizer" "lambda" {
api_id = aws_apigatewayv2_api.http_api.id
authorizer_type = "REQUEST"
name = "lambda-authorizer"
authorizer_uri = aws_lambda_function.authorizer.invoke_arn
identity_sources = ["$request.header.Authorization"]
authorizer_payload_format_version = "2.0"
enable_simple_responses = true
}
resource "aws_apigatewayv2_route" "secure_route" {
api_id = aws_apigatewayv2_api.http_api.id
route_key = "GET /secure-resource"
authorization_type = "CUSTOM"
authorizer_id = aws_apigatewayv2_authorizer.lambda.id
target = "integrations/${aws_apigatewayv2_integration.example.id}"
}
Verification
After configuring authorization, verify it is working:
- Go to API Gateway in the AWS Console
- Select your HTTP API or WebSocket API
- Click Authorization in the left navigation
- Confirm your authorizer appears under Manage authorizers
- Check that your routes show the authorizer attached (not "None")
- Test by calling an endpoint without a valid token - it should return a 401 Unauthorized response
CLI verification commands
List all authorizers for your API:
aws apigatewayv2 get-authorizers \
--api-id <your-api-id> \
--region us-east-1
Expected output shows your authorizer:
{
"Items": [
{
"AuthorizerId": "abc123",
"AuthorizerType": "JWT",
"Name": "my-jwt-authorizer",
"IdentitySource": [
"$request.header.Authorization"
],
"JwtConfiguration": {
"Audience": ["your-app-client-id"],
"Issuer": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_xxxxx"
}
}
]
}
Check a specific route's authorization:
aws apigatewayv2 get-route \
--api-id <your-api-id> \
--route-id <route-id> \
--region us-east-1
Verify AuthorizationType is not NONE:
{
"RouteId": "abc123",
"RouteKey": "GET /secure-resource",
"AuthorizationType": "JWT",
"AuthorizerId": "xyz789"
}
Additional Resources
- Controlling Access to HTTP APIs with JWT Authorizers
- Lambda Authorizers for HTTP APIs
- Working with AWS Lambda Authorizers
- Amazon Cognito User Pools
Notes
- HTTP APIs vs REST APIs: This check applies to API Gateway V2 (HTTP APIs and WebSocket APIs). For REST APIs, see
apigateway_restapi_authorizers_enabled - Authorizer types for HTTP APIs:
- JWT: Best for Cognito or any OIDC provider - validates tokens without invoking a Lambda function
- Lambda (REQUEST): Best for custom authentication logic or non-JWT tokens
- No deployment needed: HTTP APIs with auto-deploy enabled apply changes immediately
- Route-level authorization: Each route can have its own authorizer or inherit from API-level settings
- Token caching: JWT authorizers validate tokens on every request. Lambda authorizers can cache results (configure
AuthorizerResultTtlInSeconds) - Simple responses: For Lambda authorizers, enable simple responses to return a boolean instead of an IAM policy (simpler to implement)