I am trying to set up a private API with a single lambda function using a sam template, but whatever I do the API gateway logs API_CONFIGURATION_ERROR. I have no idea what I do wrong. The docs isn't very specific about it. According to this example I think my template looks alright.
My code uses axios and aws4, I have also tried fetch instead of axios. I'm also getting the same error when I use Postman so I don't really think it is something wrong with my request. Everything works perfectly fine if I just disable the IAM authorization.
This is my template for the api and the lambda function I try to invoke.
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
iam-api-example
Parameters:
SamplePrivateApiStageName:
Type: String
Default: endpoint
SampleTableName:
Type: String
Default: SampleTable
Globals:
Function:
Runtime: nodejs14.x
Timeout: 10
MemorySize: 128
Handler: app.lambdaHandler
Resources:
SamplePrivateApi:
Type: AWS::Serverless::Api
Properties:
Name: SamplePrivateApi
StageName: !Ref SamplePrivateApiStageName
Auth:
DefaultAuthorizer: AWS_IAM
AccessLogSetting:
DestinationArn: !GetAtt SamplePrivateApiAccessLogs.Arn
Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "path":"$context.path", "status":"$context.status","protocol":"$context.protocol", "responseLength":"$context.responseLength", "errorMessage":"$context.error.message", "responseType":"$context.error.responseType" }'
SamplePrivateApiAccessLogs:
Type: AWS::Logs::LogGroup
GetQuestionFn:
Type: AWS::Serverless::Function
Properties:
Policies:
- AmazonDynamoDBFullAccess
CodeUri: build/lambdas/private/advertisement/get-question
Environment:
Variables:
TABLE_NAME: !Ref SampleTableName
Events:
GetQuestionFnEvent:
Type: Api
Properties:
Path: /questions
Method: GET
RestApiId: !Ref SamplePrivateApi
#Auth:
#Authorizer: "NONE"
If I stop using the defaultAuthorizer by uncommenting the last two auth-lines (i.e. using Authorizer: "NONE") in the lambda function everything works fine. So it only fails when I leave the defaultAuthorizer in charge.
The error I get in the API log is:
{
"requestId": "12311ec0-732d-4088-a641-71f05e4bd418",
"ip": "x.xx.xxx.xxx",
"requestTime": "16/Jul/2021:12:37:01 +0000",
"httpMethod": "GET",
"routeKey": "-",
"path": "/endpoint/questions",
"status": "500",
"protocol": "HTTP/1.1",
"responseLength": "36",
"errorMessage": "Internal server error",
"responseType": "API_CONFIGURATION_ERROR"
}
In Postman, I use AWS signature authorization and the access key and secret key from an IAM user that has AmazonAPIGatewayInvokeFullAccess policy. The regions should be correct as well.
My axios code I use in another lambda function (which also has AmazonAPIGatewayInvokeFullAccess policy) currently looks like this.
import AWS from 'aws-sdk'
const aws4 = require('aws4')
const fetchQuestion = async () => {
// Host is domain without https://
const urlParts = QUESTION_API_DOMAIN.split('://')
const host = urlParts.length > 1 ? urlParts[1] : ''
if (!host) {
console.error('No host could be extracted.')
return null
}
const request = {
host,
method: 'GET',
url: `${QUESTION_API_DOMAIN}/endpoint/questions`,
path: `/endpoint/questions`,
}
const secretAccessKey = AWS.config.credentials?.secretAccessKey
const accessKeyId = AWS.config.credentials?.accessKeyId
if (!secretAccessKey || !accessKeyId) {
console.error('No credentials could be found.')
return null
}
let signedRequest = aws4.sign(request, {
secretAccessKey: secretAccessKey,
accessKeyId: accessKeyId,
sessionToken: AWS.config.credentials?.sessionToken,
})
// delete signedRequest.headers['Host']
// delete signedRequest.headers['Content-Length']
try {
const response = await axios(signedRequest)
return response.data
} catch (err) {
console.error('Error while retrieving question:', err)
}
}
No idea left what could be wrong.
Your API Gateway needs to have a role assigned to it which allows it to invoke the lambda function (authorizer)
Try using the InvokeRole
property in the API Auth section (https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-apiauth.html).
--- Updated ----
The invoke role arn must point to a role arn and not to a user see example below
Type: AWS::Serverless::Api
Properties:
Name: SamplePrivateApi
StageName: !Ref SamplePrivateApiStageName
Auth:
DefaultAuthorizer: AWS_IAM
InvokeRole: !GetAtt APIGatewayRole.Arn
AccessLogSetting:
DestinationArn: !GetAtt SamplePrivateApiAccessLogs.Arn
Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "path":"$context.path", "status":"$context.status","protocol":"$context.protocol", "responseLength":"$context.responseLength", "errorMessage":"$context.error.message", "responseType":"$context.error.responseType" }'
SamplePrivateApiAccessLogs:
Type: AWS::Logs::LogGroup
APIGatewayRole:
Type: "AWS::IAM::Role"
Properties:
Path: "/"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
Policies:
- PolicyName: "authorizerLambdaInvokeAccess"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- lambda:InvokeAsync
- lambda:InvokeFunction
Resource: !Sub ${AuthorizerLambdaFunction.Arn}
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "AllowApiGatewayServiceToAssumeRole"
Effect: "Allow"
Action:
- "sts:AssumeRole"
Principal:
Service:
- "apigateway.amazonaws.com" ```