I just implemented a lambda resolver in AWS AppSync. The lambda and AppSync live in different projects; The template that provisions the function writes the function ARN to SSM and the template that builds AppSync pulls that SSM parameter down and assigns that ARN to an AdditionalAuthenticationProvider
.
The deploy process goes in order synchronously; Lambda (create auth function, set ARN to SSM param) -> AppSync (create API, retrieve SSM param and assign to authorization provider). When I examine the console, I can see the correct function ARN is assigned as the authentication provider to AppSync.
The problem: when I go to issue a request, the lambda is never invoked, I can check CloudWatch and verify no invocations - I am just met with the response.
{
"errors" : [ {
"errorType" : "BadRequestException"
} ]
}
If I do not provide a value to the authorization
header, I get a 401
- which is the expected behavior of the lambda authorization directive, rejecting any requests without a value in that header before proceeding to the function.
So it would appear that something isn't plumbed correctly, something is missing that I can't find in a doc to allow invocation.
The gotcha: if I go into the console and assign this same function ARN manually, everything works fine and stays working fine. It would seem that, perhaps, the console is doing something behind the scenes that my deploy is not, but I cannot seem to correctly identify what is missing.
I've been following this document https://docs.aws.amazon.com/appsync/latest/devguide/security-authz.html#aws-lambda-authorization and one note gives me pause - and I have set these trust permissions, AFAIK.
Lambda functions used for authorization require a principal policy for appsync.amazonaws.com to be applied on them to allow AWS AppSync to call them. This action is done automatically in the AWS AppSync console
Here is the SAM template (without input params)
Resources:
ServiceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: [ lambda.amazonaws.com, appsync.amazonaws.com ]
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: logs
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: "*"
- Effect: Allow
Action:
- xray:*
Resource: "*"
- PolicyName: ssm
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action: ssm:*
Resource: "*"
LambdaPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref AuthorizationFunction
Action: lambda:Invoke
Principal: appsync.amazonaws.com
AuthorizationFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: <code-uri>
Handler: app.lambda
Runtime: nodejs14.x
Role: !GetAtt ServiceRole.Arn
Tracing: Active
FunctionARNParameter:
Type: AWS::SSM::Parameter
Properties:
Type: String
Name: <name>
Value: !GetAtt AuthorizationFunction.Arn
Maybe typing it out my problem was just what I needed. The last thing I tried, LambdaPermission
was the key - but the action was incorrect and needed to be InvokeFunction
.
I also chose to assign the FunctionName
as the lambda ARN instead of the name
LambdaPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt PPSAuthorizationFunction.Arn
Action: lambda: InvokeFunction # <--
Principal: appsync.amazonaws.com
Hope this is useful to someone!