How to add Amazon Cognito Auth to a Web App (part 4)
Looking at the NodeJS logic that is run in lambda functions to power the Back End of Amazon Cognito Authentication.
Intro
In my last post, I explained how Amazon Cognito is integrated into the Front End, to allow content to become visible to users that have gone through the Auth process as shown in the below demo 👇
In this post, I’ll explain how this same process can be implemented in the Back End.
If you look at the right side of the above gif, you will see an open console log. Please note the ‘Hello World!! :D‘ message that appears in the logs once the user has successfully signed in.
This message is returned to users, that have logged into the Front End and then gone through the Auth process on the Back End.
Implementing Cognito Auth on the Back End
I have a Wrapper.js template that you can use to spin up an implementation of Cognito, here are some highlight files from that template:
serverless/serverless.common.yml
service: ${file(./../../../serverless.env.json):service_name}-${file(./../../../serverless.env.json):stage}
provider:
environment: ${file(./../../../serverless.env.json)}
name: aws
region: ${file(./../../../serverless.env.json):region}
runtime: nodejs12.x
stage: ${file(./../../../serverless.env.json):stage}
apiGateway: # Optional API Gateway global config
restApiId: ${file(./../../../serverless.env.json):api_gateway_rest_api_id} # REST API resource ID. Default is generated by the framework
restApiRootResourceId: ${file(./../../../serverless.env.json):api_gateway_root_resource_id} # Root resource ID, represent as / path
iam:
role:
statements:
- Effect: Allow
Action:
- dynamodb:Query
- dynamodb:Scan
- dynamodb:GetItem
- dynamodb:PutItem
- dynamodb:UpdateItem
- dynamodb:DeleteItem
Resource: "*"
- Effect: Allow
Action:
- "execute-api:ManageConnections"
Resource:
- "arn:aws:execute-api:*:*:**/@connections/*"
custom:
apiAuthorizer:
id : ${file(./../../../serverless.env.json):cognito_authorizer}
serverless-offline:
host: '0.0.0.0'
Serverless Framework in WrapperJS is organised into HTTP and Websocket services, each service has its own yaml configuration.
Variables that are exported from Terraform (the cognito details) are passed to the above file, which the individual services can then reference.
These variables are:
restApiId: the ID of the API Gateway that Serverless Framework should deploy lambda end points to
restApiIdRootResourceId: the root path of that API Gateway, that all lambda endpoints will be added to
apiAuthorizer: an API Authorizer that was created by Terraform from the ARN of the Cognito User Pool
serverless/services/http/users/serverless.yml
service: ${file(./../../../serverless.common.yml):service}-${self:custom.service}
useDotenv: true
provider: ${self:custom.common.provider}
functions:
- ${file(userData/lambda.yml)}
plugins:
- serverless-offline
custom:
service: users-service
common: ${file(./../../../serverless.common.yml)}
apiAuthorizer: ${self:custom.common.custom.apiAuthorizer.id}
serverless-offline: ${self:custom.common.custom.serverless-offline}
In lines 4 and 15 of the Users service’s yaml (our demo service), you can see the configurations declared in the previous file being set in this service.
serverless/services/http/users/userData/lambda.yml
getUserData:
handler: userData/index.handler
events:
- http:
path: users/data
method: get
integration: lambda
authorizer:
type: COGNITO_USER_POOLS
authorizerId: ${self:custom.apiAuthorizer}
cors:
origin: '*'
headers: # <-- Specify allowed headers
- Content-Type
- Authorization
The configuration for the users/data endpoint (our test endpoint) is configured to return data if the Front End provides the authorizer that has been defined in lines 8-10.
serverless/services/http/users/userData/index.js
'use strict';
module.exports.handler = async(event, context, callback) => {
const response = {
statusCode: 200,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true
},
body: 'Hello World!! :D'
};
callback(null, response);
};
Finally, this is the lambda that is executed when the end point has been called and the appropriate authorisation is provided.
The function returns a body of ‘Hello World!! :D‘.
Conclusion
So that is it, the end of the 4 part tutorial series!!!!
I hope this has been helpful for you in understanding how to use Amazon Cognito on the Back End for Lambda functions in Serverless Framework.
Feel free to use the template I created for Wrapper.js to spin this up and give it a go!!
In the meantime, go build cool stuff and have fun :D