Created
May 16, 2018 15:19
-
-
Save honkskillet/39fc060ef533fab173af96b55f609d74 to your computer and use it in GitHub Desktop.
Serverless + AppSync + IAM/Cognito Auth + Lambda datasources (for mongoDB)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Keep all sensitive data in this file and add it to your .gitignore | |
dev: | |
awsAccountId: 1111111111 | |
mongoUrl: "url to dev monogDB" | |
region: us-east-1 #if not set will default to us-west-2 | |
# prod: | |
# awsAccountId: 1111111111 | |
# mongoUrl: "url to production monogDB " |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# NOTES | |
# This setup allows both authenticated and unauthenticated access to AppSync datasources via IAM. | |
# A cognito user pool stores the user info | |
# This AppSync uses lambda datasources (not dynamoDB) | |
# This is a WIP. Many of the policies allow overbroad access. | |
# There are 2 UserPoolClientWeb, one of which I believe is unneeded and could probably be removed. | |
service: MEMORYCARDS | |
# You can pin your service to only deploy with a specific Serverless version | |
# Check out our docs for more details | |
frameworkVersion: ">=1.21.0 <2.0.0" | |
package: | |
individually: true | |
provider: | |
name: aws | |
runtime: nodejs8.10 | |
stage: ${opt:stage, "dev"} | |
region: ${file(./serverless.env.yml):${self:provider.stage}.region, "us-west-2"} | |
iamRoleStatements: ##This adds policies to the default lambda function's roles created by serverless | |
- Effect: "Allow" | |
Action: | |
- "cognito-idp:*" | |
Resource: "arn:aws:cognito-idp:*" | |
#stackTags: # Optional CF stack tags | |
plugins: | |
- serverless-stack-output #provides output from deploy for use by frontend build: https://github.com/sbstjn/serverless-stack-output | |
custom: | |
accountId: ${file(./serverless.env.yml):${self:provider.stage}.awsAccountId} | |
appName: 'MEMORYCARDS' # ? remove or just reference service? | |
output: #config the serverless-stack-output plugin, output values come from resources.Outputs at bototm of file | |
handler: scripts/output.handler # Same syntax as you already know | |
file: .serverless/outputs.yaml # toml, yaml, yml, and json format is available | |
functions: | |
graphql: | |
handler: lambdaFunctions/resolver/handler.graphqlHandler | |
environment: | |
MONGO_URL: ${file(./serverless.env.yml):${self:provider.stage}.mongoUrl} | |
REGION: ${self:provider.region} | |
USER_POOL_ID: | |
Ref: UserPool | |
package: | |
exclude: #exclude all the files in the root directory | |
- '**' | |
include: #add back the files in the lambdas directory | |
- lambdaFunctions/resolver/** | |
addUserToGroup: | |
handler: lambdaFunctions/addUserToGroup/handler.addUserToGroup | |
environment: | |
GROUP_NAME: Users | |
package: | |
exclude: #exclude all the files in the root directory | |
- '**' | |
include: #add back the files in the lambdas directory | |
- lambdaFunctions/addUserToGroup/** | |
resources: | |
Resources: | |
############################## | |
######### IAM ROLES ########## | |
############################## | |
AppSyncLambdaServiceRole: | |
Type: "AWS::IAM::Role" | |
Properties: | |
RoleName: "${self:custom.appName}-Lambda-AppSyncServiceRole--${self:provider.region}-${self:provider.stage}" | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- | |
Effect: "Allow" | |
Principal: | |
Service: | |
- "appsync.amazonaws.com" | |
Action: | |
- "sts:AssumeRole" | |
Policies: | |
- | |
PolicyName: "Lambda-AppSyncServiceRole-Policy" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- | |
Effect: "Allow" | |
Action: | |
- "lambda:invokeFunction" | |
Resource: "*" #TODO: specify the lambda data source that is called | |
# - "arn:aws:lambda:us-west-2:*:function:serverless-graphql-appsync-lda-${self:provider.stage}-graphql" #previously production | |
# - "arn:aws:lambda:us-west-2:*:function:serverless-graphql-appsync-lda-${self:provider.stage}-graphql:*" #previously production | |
# Create a role for unauthorized acces to AWS resources. Control what your user can access. | |
CognitoUnAuthorizedRole: | |
Type: "AWS::IAM::Role" | |
Properties: | |
RoleName: "${self:custom.appName}-CognitoUnAuthorizedRole-${self:provider.region}-${self:provider.stage}" | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Principal: | |
Federated: "cognito-identity.amazonaws.com" | |
Action: | |
- "sts:AssumeRoleWithWebIdentity" | |
Condition: | |
StringEquals: | |
"cognito-identity.amazonaws.com:aud": | |
Ref: IdentityPool | |
"ForAnyValue:StringLike": | |
"cognito-identity.amazonaws.com:amr": unauthenticated | |
Policies: | |
- PolicyName: "CognitoUnauthorizedPolicy" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Action: | |
- "appsync:GraphQL" | |
Resource: "*" | |
#- "arn:aws:appsync:us-west-2:${self:custom.accountId}:apis/${self:custom.appSync.apiId}/*" | |
- Effect: "Allow" | |
Action: | |
- "appsync:GraphQL" #OLD | |
- "cognito-identity:*" #OLD | |
- "mobileanalytics:PutEvents" #NEW | |
- "cognito-sync:*" #NEW | |
Resource: "*" | |
# TODO: see which Actions Resources are needed and limit them to given resource | |
# Create a role for authorized acces to AWS resources. | |
CognitoAuthorizedRole: | |
Type: "AWS::IAM::Role" | |
Properties: | |
RoleName: "${self:custom.appName}-CognitoAuthorizedRole-${self:provider.region}-${self:provider.stage}" | |
AssumeRolePolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Principal: | |
Federated: "cognito-identity.amazonaws.com" | |
Action: | |
- "sts:AssumeRoleWithWebIdentity" | |
Condition: | |
StringEquals: | |
"cognito-identity.amazonaws.com:aud": | |
Ref: IdentityPool | |
# StringEquals: | |
# "cognito-identity.amazonaws.com:sub": | |
# Ref: IdentityPool | |
"ForAnyValue:StringLike": | |
"cognito-identity.amazonaws.com:amr": authenticated | |
Policies: | |
- PolicyName: "${self:custom.appName}_CognitoAuthorizedPolicy" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Action: | |
- "appsync:GraphQL" #OLD | |
# - "mobileanalytics:PutEvents" #NEW | |
# - "cognito-sync:*" #NEW | |
Resource: | |
Fn::Join : | |
- "" | |
- - "arn:aws:appsync:${self:provider.region}:${self:custom.accountId}:apis/" | |
- Fn::GetAtt: [ GraphQlApi, ApiId ] | |
- "/*" | |
- PolicyName: "${self:custom.appName}_SignInPolicy" | |
PolicyDocument: | |
Version: "2012-10-17" | |
Statement: | |
- Effect: "Allow" | |
Action: | |
- cognito-identity:GetId | |
Resource: | |
Fn::Join : | |
- "" | |
- - "arn:aws:cognito-identity:*:*:identityPool/" | |
- Ref: IdentityPool | |
- Effect: "Allow" | |
Action: | |
- cognito-idp:* | |
Resource: | |
'Fn::GetAtt': [ UserPool, Arn ] | |
# - Effect: "Allow" | |
# Action: | |
# - "lambda:InvokeFunction" #NEW | |
# Resource: "*" | |
# TODO: see which Actions and resources are needed and limit them to given resource | |
###################################### | |
######### COGNITO RESOURCES ########## | |
###################################### | |
UserPool: | |
Type: "AWS::Cognito::UserPool" | |
Properties: | |
UserPoolName: '${self:custom.appName}-${self:provider.stage}-user-pool' | |
SmsVerificationMessage: "Your verification code is {####}." | |
AliasAttributes: | |
- preferred_username | |
AutoVerifiedAttributes: | |
MfaConfiguration: "OFF" | |
EmailVerificationSubject: "Your ${self:custom.appName} verification code" | |
EmailVerificationMessage: "Your ${self:custom.appName} verification code is {####}." | |
SmsAuthenticationMessage: "Your ${self:custom.appName} authentication code is {####}." | |
LambdaConfig: | |
PostConfirmation: "arn:aws:lambda:${self:provider.region}:${self:custom.accountId}:function:${self:custom.appName}-${self:provider.stage}-addUserToGroup" | |
Schema: | |
- Name: family_name | |
AttributeDataType: String | |
Mutable: true | |
Required: false | |
- Name: given_name | |
AttributeDataType: String | |
Mutable: true | |
Required: false | |
- Name: email | |
AttributeDataType: String | |
Mutable: false | |
Required: true | |
Policies: | |
PasswordPolicy: | |
RequireLowercase: true | |
RequireSymbols: false | |
RequireNumbers: true | |
MinimumLength: 8 | |
RequireUppercase: true | |
AdminCreateUserConfig: | |
InviteMessageTemplate: | |
EmailMessage: "Your ${self:custom.appName} username is {username} and temporary password is {####}." | |
EmailSubject: "Your temporary ${self:custom.appName} password" | |
SMSMessage: "Your ${self:custom.appName} username is {username} and temporary password is {####}." | |
UnusedAccountValidityDays: 7 | |
AllowAdminCreateUserOnly: false | |
UserPoolTags: | |
'STAGE': '${self:provider.stage}' | |
UserPoolGroupUsers: | |
Type: "AWS::Cognito::UserPoolGroup" | |
Properties: | |
Description: "Group represents all normal users of the app" | |
GroupName: "Users" | |
# RoleArn: String | |
UserPoolId: | |
Ref: UserPool | |
# Creates a User Pool Client to be used by the identity pool | |
UserPoolClient: | |
Type: "AWS::Cognito::UserPoolClient" | |
Properties: | |
ClientName: ${self:custom.appName}-${self:provider.stage}-client | |
GenerateSecret: true | |
UserPoolId: | |
Ref: UserPool | |
UserPoolClientWeb: | |
Type: "AWS::Cognito::UserPoolClient" | |
Properties: | |
ClientName: "${self:custom.appName}-${self:provider.stage}-client web" | |
GenerateSecret: false | |
UserPoolId: | |
Ref: UserPool | |
# Creates a federeated Identity pool | |
IdentityPool: | |
Type: "AWS::Cognito::IdentityPool" | |
Properties: | |
IdentityPoolName: ${self:custom.appName}_${self:provider.stage}_identity | |
AllowUnauthenticatedIdentities: true | |
CognitoIdentityProviders: | |
- ClientId: | |
Ref: UserPoolClient | |
ProviderName: | |
'Fn::GetAtt': [ UserPool, ProviderName ] | |
- ClientId: | |
Ref: UserPoolClientWeb | |
ProviderName: | |
'Fn::GetAtt': [ UserPool, ProviderName ] | |
# Assigns the roles to the Identity Pool | |
IdentityPoolRoleMapping: | |
Type: "AWS::Cognito::IdentityPoolRoleAttachment" | |
Properties: | |
IdentityPoolId: | |
Ref: IdentityPool | |
Roles: | |
authenticated: | |
'Fn::GetAtt': [ CognitoAuthorizedRole, Arn ] | |
unauthenticated: | |
'Fn::GetAtt': [ CognitoUnAuthorizedRole, Arn ] | |
###################################### | |
######### GRAPHQL RESOURCES ########## | |
###################################### | |
GraphQlApi: | |
Type: 'AWS::AppSync::GraphQLApi' | |
Properties: | |
Name: '${self:custom.appName}_${self:provider.stage}' | |
AuthenticationType: 'AWS_IAM' ##'AMAZON_COGNITO_USER_POOLS' | |
# UserPoolConfig: | |
# AwsRegion: ${self:provider.region} | |
# DefaultAction: 'ALLOW' | |
# UserPoolId: | |
# Ref: UserPool | |
GraphQlSchema: | |
Type: 'AWS::AppSync::GraphQLSchema' | |
Properties: | |
Definition: '${file(schema.graphql)}' | |
ApiId: | |
Fn::GetAtt: [ GraphQlApi, ApiId ] | |
GraphQlLambdaDataSource: | |
Type: 'AWS::AppSync::DataSource' | |
Properties: | |
ApiId: | |
Fn::GetAtt: [ GraphQlApi, ApiId ] | |
Name: '${self:custom.appName}_LambdaDataSource' | |
Description: 'Lambda for MongoDB' | |
Type: "AWS_LAMBDA" | |
LambdaConfig: | |
LambdaFunctionArn: "arn:aws:lambda:${self:provider.region}:${self:custom.accountId}:function:${self:service}-${self:provider.stage}-graphql" | |
ServiceRoleArn: | |
Fn::GetAtt: [ AppSyncLambdaServiceRole, Arn ] | |
############################ | |
######## RESOLVERS ######### | |
######## QUERIES ########### | |
GraphQlAllPostsResolver: | |
Type: "AWS::AppSync::Resolver" | |
DependsOn: GraphQlSchema | |
Properties: | |
ApiId: | |
Fn::GetAtt: [GraphQlApi, ApiId] | |
TypeName: Query | |
FieldName: allPosts | |
DataSourceName: | |
Fn::GetAtt: [GraphQlLambdaDataSource, Name] | |
RequestMappingTemplate: | | |
{ | |
"version" : "2017-02-28", | |
"operation": "Invoke", | |
"payload": { | |
"field": "allPosts", | |
"context": $util.toJson($context) | |
} | |
} | |
ResponseMappingTemplate: '$util.toJson($context.result)' | |
GraphQlGetPostResolver: | |
Type: "AWS::AppSync::Resolver" | |
DependsOn: GraphQlSchema | |
Properties: | |
ApiId: | |
Fn::GetAtt: [GraphQlApi, ApiId] | |
TypeName: Query | |
FieldName: getPost | |
DataSourceName: | |
Fn::GetAtt: [GraphQlLambdaDataSource, Name] | |
RequestMappingTemplate: | | |
{ | |
"version" : "2017-02-28", | |
"operation": "Invoke", | |
"payload": { | |
"field": "getPost", | |
"context": $util.toJson($context) | |
} | |
} | |
ResponseMappingTemplate: '$util.toJson($context.result)' | |
####### MUTATIONS ############ | |
GraphQlAddPostResolver: | |
Type: "AWS::AppSync::Resolver" | |
DependsOn: GraphQlSchema | |
Properties: | |
ApiId: | |
Fn::GetAtt: [GraphQlApi, ApiId] | |
TypeName: Mutation | |
FieldName: addPost | |
DataSourceName: | |
Fn::GetAtt: [GraphQlLambdaDataSource, Name] | |
RequestMappingTemplate: | | |
{ | |
"version" : "2017-02-28", | |
"operation": "Invoke", | |
"payload": { | |
"field": "addPost", | |
"context": $util.toJson($context) | |
} | |
} | |
ResponseMappingTemplate: '$util.toJson($context.result)' | |
GraphQlUpdatePostResolver: | |
Type: "AWS::AppSync::Resolver" | |
DependsOn: GraphQlSchema | |
Properties: | |
ApiId: | |
Fn::GetAtt: [GraphQlApi, ApiId] | |
TypeName: Mutation | |
FieldName: updatePost | |
DataSourceName: | |
Fn::GetAtt: [GraphQlLambdaDataSource, Name] | |
RequestMappingTemplate: | | |
{ | |
"version" : "2017-02-28", | |
"operation": "Invoke", | |
"payload": { | |
"field": "updatePost", | |
"context": $util.toJson($context) | |
} | |
} | |
ResponseMappingTemplate: '$util.toJson($context.result)' | |
GraphQlDeletePostResolver: | |
Type: "AWS::AppSync::Resolver" | |
DependsOn: GraphQlSchema | |
Properties: | |
ApiId: | |
Fn::GetAtt: [GraphQlApi, ApiId] | |
TypeName: Mutation | |
FieldName: deletePost | |
DataSourceName: | |
Fn::GetAtt: [GraphQlLambdaDataSource, Name] | |
RequestMappingTemplate: | | |
{ | |
"version" : "2017-02-28", | |
"operation": "Invoke", | |
"payload": { | |
"field": "deletePost", | |
"context": $util.toJson($context) | |
} | |
} | |
ResponseMappingTemplate: '$util.toJson($context.result)' | |
#'${file(mapping-templates/TemplateTypes/create-response-mapping.txt)}' | |
############################ | |
######### OUTPUTS ########## | |
############################ | |
Outputs: | |
GraphQlApiEndpoint: | |
Description: The GraphQL API endpoint. | |
Value: | |
Fn::GetAtt: [ GraphQlApi, GraphQLUrl ] | |
AppSyncRegion: | |
Value: '${self:provider.region}' | |
CognitoIdentityPooId: | |
Value: | |
Ref: IdentityPool | |
UserPoolId: | |
Value: | |
Ref: UserPool | |
UserPoolsWebClienId: | |
Value: | |
Ref: UserPoolClientWeb | |
CognitoRegion: | |
Value: '${self:provider.region}' | |
AmplifySigInEnabled: | |
Description: For use with AWS Amplify config | |
Value: 'enable' | |
AmplifyUserPools: | |
Description: For use with AWS Amplify config | |
Value: 'enable' | |
AmplifyUserPoolsMfaType: | |
Description: For use with AWS Amplify config | |
Value: 'OFF' | |
# LambdaAppSyncServiceRole: | |
# Description: The AppSync service role created with permissions to AWS Lambda operations. | |
# Value: !GetAtt AppSyncServiceRole.Arn |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment