I have defined several APIGateway endpoints in my microservice. As I am handling authentication with Auth0, I would have to create an authorizer which sends a request to Auth0’s authorization server to verify a user’s identity for each endpoint’s Lambda function.
While I am using LocalStack in my Docker container for testing deployments and running tests that send requests to the resources deployed to the container, I am working on bypassing the authorization step for local
stage (for testing only) at this stage of development.
// serverless.yml
service: myapp-users
custom:
localAuthorizer:
type: token
authorizerId:
Ref: AlwaysAllowAuthorizer
stageBasedJWTAuthorizer:
type: jwt
identitySource: method.request.header.Authorization
issuerUrl: https://myapp-${self:provider.stage}.jp.auth0.com
audience:
- https://myapp-${self:provider.stage}-auth0-authorizer
authorizers:
local: ${self:custom.localAuthorizer}
dev: ${self:custom.stageBasedJWTAuthorizer}
staging: ${self:custom.stageBasedJWTAuthorizer}
canary: ${self:custom.stageBasedJWTAuthorizer}
prod:
type: jwt
identitySource: method.request.header.Authorization
issuerUrl: https://myapp.jp.auth0.com
audience:
- https://myapp-auth0-authorizer
accountIds:
local: "000000000000" # Example AWS Account ID for local development
default: ${AWS::AccountId} # Use the AWS pseudo parameter for normal deployments
accountId: ${self:custom.accountIds.${self:provider.stage}, self:custom.accountIds.default}
localstack:
host: http://localhost
edgePort: 4566
stages:
- local
autostart: true
provider:
name: aws
endpointType: regional
runtime: nodejs20.x
region: ${opt:region, 'ap-east-1'} # Default to ap-east-1 if not specified
stage: ${opt:stage, 'dev'} # Default to dev if not specified
environment:
REGION: ${self:provider.region}
DYNAMODB_TABLE_NAME: myapp-users-${self:provider.stage}
deploymentBucket:
name: ${self:service}-${self:provider.stage}-deployments-${self:provider.region}
# logs:
# restApi: true
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- !Sub arn:aws:dynamodb:${self:provider.region}:${self:custom.accountId}:table/${self:provider.environment.DYNAMODB_TABLE_NAME}
- !Sub arn:aws:logs:${self:provider.region}:${self:custom.accountId}:log-group:/aws/lambda/${self:service}-*:*:*
plugins:
- serverless-localstack
functions:
alwaysAllowFunction:
handler: dist/tests/alwaysAllow.handler
createUser:
handler: dist/createUser.handler
events:
- http:
path: user/create
method: post
authorizer: ${self:custom.authorizers.${self:provider.stage}}
updateUser:
handler: dist/updateUser.handler
events:
- http:
path: user/{userId}/update
method: patch
authorizer: ${self:custom.authorizers.${self:provider.stage}}
deleteUser:
handler: dist/deleteUser.handler
events:
- http:
path: user/{userId}/delete
method: delete
authorizer: ${self:custom.authorizers.${self:provider.stage}}
getUser:
handler: dist/getUser.handler
events:
- http:
path: user
method: get
authorizer: ${self:custom.authorizers.${self:provider.stage}}
resources:
Conditions:
IsLocal: !Equals ["${self:provider.stage}", "local"]
Resources:
AlwaysAllowAuthorizer:
Type: "AWS::ApiGateway::Authorizer"
DependsOn: MyappUsersRestApi
Properties:
Name: "always-allow-authorizer"
Type: TOKEN
RestApiId:
Ref: MyappUsersRestApi
IdentitySource: "method.request.header.Authorization"
AuthorizerResultTtlInSeconds: 0
AuthorizerUri:
Fn::Join:
- ''
- - 'arn:aws:apigateway:'
- Ref: 'AWS::Region'
- ':lambda:path/2015-03-31/functions/arn:aws:lambda:'
- Ref: 'AWS::Region'
- ':'
- Ref: 'AWS::AccountId'
- ':function:alwaysAllowFunction/invocations'
IdentityValidationExpression: null
MyappUsersRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: ${self:service}-${self:provider.stage}
MyappUsersTable:
Type: "AWS::DynamoDB::Table"
Properties:
TableName: myapp-users-${self:provider.stage}
AttributeDefinitions:
......
// src/alwaysAllow.ts
import { APIGatewayTokenAuthorizerHandler } from "aws-lambda";
export const handler: APIGatewayTokenAuthorizerHandler = async (event) => {
return {
principalId: 'user',
policyDocument: {
Version: '2012-10-17',
Statement: [{
Action: 'execute-api:Invoke',
Effect: 'Allow',
Resource: event.methodArn
}]
}
};
};
When I run serverless deploy --stage local --region ap-east-1 --verbose
after yarn build
and aws --endpoint-url=http://localhost:4566 s3 mb s3://...
, the deployment is always stuck at the last moment where the stack creation fails:
Using serverless-localstack
Using serverless-localstack
serverless-localstack: Reconfigured endpoints
serverless-localstack: Reconfigured endpoints
Packaging
Excluding development dependencies for service package
Retrieving CloudFormation stack
Uploading
Uploading CloudFormation file to S3
Uploading State file to S3
Uploading service myapp-users.zip file to S3 (46.66 MB)
Skipping template validation: Unsupported in Localstack
Updating CloudFormation stack
Creating CloudFormation stack
Creating new change set
Waiting for new change set to be created
Executing created change set
CREATE_IN_PROGRESS - AWS::CloudFormation::Stack - myapp-users-local
CREATE_COMPLETE - AWS::Logs::LogGroup - AlwaysAllowFunctionLogGroup
CREATE_COMPLETE - AWS::Logs::LogGroup - CreateUserLogGroup
CREATE_COMPLETE - AWS::Logs::LogGroup - UpdateUserLogGroup
CREATE_COMPLETE - AWS::Logs::LogGroup - DeleteUserLogGroup
CREATE_COMPLETE - AWS::Logs::LogGroup - GetUserLogGroup
CREATE_COMPLETE - AWS::IAM::Role - IamRoleLambdaExecution
CREATE_COMPLETE - AWS::Lambda::Function - AlwaysAllowFunctionLambdaFunction
CREATE_COMPLETE - AWS::Lambda::Function - CreateUserLambdaFunction
CREATE_COMPLETE - AWS::Lambda::Function - UpdateUserLambdaFunction
CREATE_COMPLETE - AWS::Lambda::Function - DeleteUserLambdaFunction
CREATE_COMPLETE - AWS::Lambda::Function - GetUserLambdaFunction
CREATE_COMPLETE - AWS::Lambda::Version - AlwaysAllowFunctionLambdaVersionlbjwY5JAl1rBFpOEefd4Yclq3qqG2MAXzztIUgRa0
CREATE_COMPLETE - AWS::Lambda::Version - CreateUserLambdaVersionlMsoj4VDXZQHhFpgnPWfoRxHcQK6uAYE9UZsxpgUo
CREATE_COMPLETE - AWS::Lambda::Version - UpdateUserLambdaVersion0cdpf2VjTkP70G1Ua2jQemFsj3XuyTn7QyJs0uGSb8
CREATE_COMPLETE - AWS::Lambda::Version - DeleteUserLambdaVersiontkjw9aHaRwIKeyznYwwlE95tl5UrwvleLVw2vwAmjWs
CREATE_COMPLETE - AWS::Lambda::Version - GetUserLambdaVersionF1dbTgow5XudYkS2wIsf72O4fN2fNIKeIZFWBlFM
CREATE_COMPLETE - AWS::ApiGateway::RestApi - ApiGatewayRestApi
CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceUser
CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceUserCreate
CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceUserUseridVar
CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceUserUseridVarUpdate
CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceUserUseridVarDelete
CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceUserUseridVarLicensenumber
CREATE_COMPLETE - AWS::ApiGateway::Resource - ApiGatewayResourceUserUseridVarLicensenumberLicensenumberVar
CREATE_COMPLETE - AWS::Lambda::Permission - CreateUserLambdaPermissionApiGateway
CREATE_COMPLETE - AWS::Lambda::Permission - UpdateUserLambdaPermissionApiGateway
CREATE_COMPLETE - AWS::Lambda::Permission - DeleteUserLambdaPermissionApiGateway
CREATE_COMPLETE - AWS::Lambda::Permission - GetUserLambdaPermissionApiGateway
CREATE_COMPLETE - AWS::ApiGateway::RestApi - MyappUsersRestApi
CREATE_COMPLETE - AWS::DynamoDB::Table - MyappUsersTable
CREATE_COMPLETE - AWS::ApiGateway::Authorizer - AlwaysAllowAuthorizer
CREATE_FAILED - AWS::CloudFormation::Stack - myapp-users-local
Creating CloudFormation stack (36/42) (14630s)
I am not sure if I have configured incorrectly or there is incompatibility with the serverless-localstack
plugin. Could anyone provide me some guidance on this issue? I am also open to test the authorizer instead of bypassing it.