Search code examples
aws-amplifyvelocityaws-appsync

Can I get the user email within a Velocity template of AWS Amplify?


When I query a resolver in my GraphQL API, in which I have added a $util.error($ctx) to return the context object, I get the following result (removed unnecessary values).

{
  "data": {
    "listXData": null
  },
  "errors": [
    {
      "message": {
        "arguments": {},
        "args": {},
        "info": {
          "fieldName": "listXData",
          "variables": {},
          "parentTypeName": "Query",
          "selectionSetList": [
            "items",
            "items/id",
            "items/createdAt",
            "items/updatedAt",
            "nextToken"
          ],
          "selectionSetGraphQL": "{\n  items {\n    id\n    createdAt\n    updatedAt\n  }\n  nextToken\n}"
        },
        "request": {...},
        "identity": {
          "sub": "",
          "issuer": "",
          "username": "013fe9d2-95f7-4885-83ec-b7e2e0a1423f",
          "sourceIp": "",
          "claims": {
            "origin_jti": "",
            "sub": "",
            "event_id": "",
            "token_use": "",
            "scope": "",
            "auth_time": ,
            "iss": "",
            "exp": ,
            "iat": ,
            "jti": "",
            "client_id": "",
            "username": "013fe9d2-95f7-4885-83ec-b7e2e0a1423f"
          },
          "defaultAuthStrategy": "ALLOW"
        },
        "stash": {},
        "source": null,
        "result": {
          "items": [],
          "scannedCount": 0,
          "nextToken": null
        },
        "error": null,
        "prev": {
          "result": {}
        }
      },
      "errorType": null,
      "data": null,
      "errorInfo": null,
      "path": [
        "listXData"
      ],
      "locations": [
        {
          "line": 2,
          "column": 3,
          "sourceName": "GraphQL request"
        }
      ]
    }
  ]
}

As you can see, the username is an ID, however I would prefer to (also) have the email. Is it possible to get the user email (within the Velocity template)?

Let me know if I need to add more details or if my question is unclear.


Solution

  • At this point, it seems that it is not possible to do this purely by vtl.

    I have implemented it using a lambda function, as follow:

    Lambda function (node):

    /* Amplify Params - DO NOT EDIT
        ENV
        REGION
    Amplify Params - DO NOT EDIT */
    
    const aws = require('aws-sdk')
    
    const cognitoidentityserviceprovider = new aws.CognitoIdentityServiceProvider({
      apiVersion: '2016-04-18',
      region: 'eu-west-1'
    })
    
    exports.handler = async (context, event, callback) => {
      if (!context.identity?.username) {
        callback('Not signed in')
      }
      const params = {
        'AccessToken': context.request.headers.authorization
      }
      const result = await cognitoidentityserviceprovider.getUser(params).promise()
      const email = result.UserAttributes.find(attribute => attribute.Name === 'email')
      callback(null, JSON.stringify({ email }))
    }
    

    CustomResources.json

    {
      "AWSTemplateFormatVersion": "2010-09-09",
      "Description": "An auto-generated nested stack.",
      "Metadata": {...},
      "Parameters": {...},
      "Resources": {
        "GetEmailLambdaDataSourceRole": {
          "Type": "AWS::IAM::Role",
          "Properties": {
            "RoleName": {
              "Fn::If": [
                "HasEnvironmentParameter",
                {
                  "Fn::Join": [
                    "-",
                    [
                      "GetEmail17ec",
                      {
                        "Ref": "GetAttGraphQLAPIApiId"
                      },
                      {
                        "Ref": "env"
                      }
                    ]
                  ]
                },
                {
                  "Fn::Join": [
                    "-",
                    [
                      "GetEmail17ec",
                      {
                        "Ref": "GetAttGraphQLAPIApiId"
                      }
                    ]
                  ]
                }
              ]
            },
            "AssumeRolePolicyDocument": {
              "Version": "2012-10-17",
              "Statement": [
                {
                  "Effect": "Allow",
                  "Principal": {
                    "Service": "appsync.amazonaws.com"
                  },
                  "Action": "sts:AssumeRole"
                }
              ]
            },
            "Policies": [
              {
                "PolicyName": "InvokeLambdaFunction",
                "PolicyDocument": {
                  "Version": "2012-10-17",
                  "Statement": [
                    {
                      "Effect": "Allow",
                      "Action": [
                        "lambda:InvokeFunction"
                      ],
                      "Resource": {
                        "Fn::If": [
                          "HasEnvironmentParameter",
                          {
                            "Fn::Sub": [
                              "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:GetEmail-${env}",
                              {
                                "env": {
                                  "Ref": "env"
                                }
                              }
                            ]
                          },
                          {
                            "Fn::Sub": [
                              "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:GetEmail",
                              {}
                            ]
                          }
                        ]
                      }
                    }
                  ]
                }
              }
            ]
          }
        },
        "GetEmailLambdaDataSource": {
          "Type": "AWS::AppSync::DataSource",
          "Properties": {
            "ApiId": {
              "Ref": "AppSyncApiId"
            },
            "Name": "GetEmailLambdaDataSource",
            "Type": "AWS_LAMBDA",
            "ServiceRoleArn": {
              "Fn::GetAtt": [
                "GetEmailLambdaDataSourceRole",
                "Arn"
              ]
            },
            "LambdaConfig": {
              "LambdaFunctionArn": {
                "Fn::If": [
                  "HasEnvironmentParameter",
                  {
                    "Fn::Sub": [
                      "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:GetEmail-${env}",
                      {
                        "env": {
                          "Ref": "env"
                        }
                      }
                    ]
                  },
                  {
                    "Fn::Sub": [
                      "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:GetEmail",
                      {}
                    ]
                  }
                ]
              }
            }
          },
          "DependsOn": "GetEmailLambdaDataSourceRole"
        },
        "InvokeGetEmailLambdaDataSource": {
          "Type": "AWS::AppSync::FunctionConfiguration",
          "Properties": {
            "ApiId": {
              "Ref": "AppSyncApiId"
            },
            "Name": "InvokeGetEmailLambdaDataSource",
            "DataSourceName": "GetEmailLambdaDataSource",
            "FunctionVersion": "2018-05-29",
            "RequestMappingTemplateS3Location": {
              "Fn::Sub": [
                "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/pipelineFunctions/${ResolverFileName}",
                {
                  "S3DeploymentBucket": {
                    "Ref": "S3DeploymentBucket"
                  },
                  "S3DeploymentRootKey": {
                    "Ref": "S3DeploymentRootKey"
                  },
                  "ResolverFileName": {
                    "Fn::Join": [
                      ".",
                      [
                        "InvokeGetEmailLambdaDataSource",
                        "req",
                        "vtl"
                      ]
                    ]
                  }
                }
              ]
            },
            "ResponseMappingTemplateS3Location": {
              "Fn::Sub": [
                "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/pipelineFunctions/${ResolverFileName}",
                {
                  "S3DeploymentBucket": {
                    "Ref": "S3DeploymentBucket"
                  },
                  "S3DeploymentRootKey": {
                    "Ref": "S3DeploymentRootKey"
                  },
                  "ResolverFileName": {
                    "Fn::Join": [
                      ".",
                      [
                        "InvokeGetEmailLambdaDataSource",
                        "res",
                        "vtl"
                      ]
                    ]
                  }
                }
              ]
            }
          },
          "DependsOn": "GetEmailLambdaDataSource"
        },                
        "IsOrganizationMember": {
          "Type": "AWS::AppSync::FunctionConfiguration",
          "Properties": {
            "FunctionVersion": "2018-05-29",
            "ApiId": {
              "Ref": "AppSyncApiId"
            },
            "Name": "IsOrganizationMember",
            "DataSourceName": "PermissionsPerOrganizationTable",
            "RequestMappingTemplateS3Location": {
              "Fn::Sub": [
                "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.isOrganizationMember.req.vtl",
                {
                  "S3DeploymentBucket": {
                    "Ref": "S3DeploymentBucket"
                  },
                  "S3DeploymentRootKey": {
                    "Ref": "S3DeploymentRootKey"
                  }
                }
              ]
            },
            "ResponseMappingTemplateS3Location": {
              "Fn::Sub": [
                "s3://${S3DeploymentBucket}/${S3DeploymentRootKey}/resolvers/Query.isOrganizationMember.res.vtl",
                {
                  "S3DeploymentBucket": {
                    "Ref": "S3DeploymentBucket"
                  },
                  "S3DeploymentRootKey": {
                    "Ref": "S3DeploymentRootKey"
                  }
                }
              ]
            }
          }
        },
        "OrganizationAccessPipeline": {
          "Type": "AWS::AppSync::Resolver",
          "Properties": {
            "ApiId": {
              "Ref": "AppSyncApiId"
            },
            "TypeName": "Query",
            "Kind": "PIPELINE",
            "FieldName": "listXData",
            "PipelineConfig": {
              "Functions": [
                {
                  "Fn::GetAtt": [
                    "InvokeGetEmailLambdaDataSource",
                    "FunctionId"
                  ]
                },
                {
                  "Fn::GetAtt": [
                    "IsOrganizationMember",
                    "FunctionId"
                  ]
                }
              ]
            },
            "RequestMappingTemplate": "{}",
            "ResponseMappingTemplate": "$util.toJson($ctx.result)"
          }
        }
      },
      "Conditions": {...},
      "Outputs": {...}
    }
    
    

    The lambda is created with the CLI and IsOrganizationMember is a regular VTL which has the user email in the $context.prev.result.