Yup! you heard it right. I don’t know how many of my readers have heard of AWS Cognito before so I will start with a brief intro about it. According to the official website —
Amazon Cognito lets you add user sign-up, sign-in, and access control to your web and mobile apps quickly and easily. Amazon Cognito scales to millions of users and supports sign-in with social identity providers, such as Apple, Facebook, Google, and Amazon, and enterprise identity providers via SAML 2.0 and OpenID Connect. (More about AWS Cognito)
So basically it’s a service provided by Amazon Web Services(AWS) to help developers setup authentication in their web/mobile apps with ease. We are living in an agile age where even most of the medium scale webapps use microservices architecture(atleast many of which I am aware of), because not only does that make the overall codebase clean and segregated, it also helps make it easier to maintain for the developers actually coding it. In this article I will be discussing on how to setup an authentication/authorization server using AWS Cognito in Node.js, following some simple steps. I must say though — AWS does make things simple but setting the entire thing up from scratch might be a little intimidating for a beginner considering the fact that the official documentation, although very well written is practically an ocean of information due to the plethora of services/functionalities provided by AWS!
Now let me give a brief idea about what we’ll be doing here — We’ll setup an authentication server which will handle the basic authentication stuff that pretty much every web/mobile app out there needs to have. We’ll also be using AWS SES(Simple Email Service) to send OTPs to users.
Note: The services used in the setup process here are NOT free! But it won’t cost you anything to make the basic setup as I have described here. That will depend on how you use this setup(or any similar setup) in your own webapp. Anyways I would suggest it is always a good practice to go through the pricing of the services once and also monitor your account billing once you start setting up(in case you’re setting this up over a span of couple of days or more for some reason)
Let me break down the setup process into a number of steps so that I can discuss the steps one after the other.
Basic steps to setup authentication with AWS Cognito in Node.js -
- We need to have a website of our own. Could be a static site on github pages — this is needed to setup AWS SES as we would have to request for production access since the default sandbox environment won’t allow us to send OTPs to any user email we want.
- Setup AWS SES by making a request to move our environment out of the sandbox.
- Setup a user pool in Cognito with the relevant settings — In this setup we are planning the following flow: (I) User tries to signup with an email, (II) He gets a verification code on his email, (III) He enters the verification code along with a valid password and signs up, (IV) He can then use those credentials to login. (V) He can change his password using the forgot password option. (VI) He can access a protected resource from the server using valid credentials. So for this flow we will need a rather simple setup on Cognito’s end — Don’t worry, I will walk you through the entire setup process along with relevant screenshots and documentation links as well.
- Setup an Express.js server and include the appropriate SDKs.
- Write routes/utilities/functions/middlewares for the functionalities discussed in step 3 above.
We now have an idea of what we’re gonna setup, so without further ado, let’s buckle up and get started!
Steps 1 and 2:
I assume at this point you already have an account on AWS and know the basics of the cloud console. Head over to SES and make sure the “region” selected on the top right corner is N.Virginia(or US East, Virginia). We need this because when setting up an emailing functionality with Cognito with the help of SES, AWS allows the email address to be from 3 regions (at the time this article was written) namely — US East(Virginia), US West(Oregon) and EU(Ireland). Now we need to verify an email address. Go to verified identities section and on this screen,
click Create Identity. Then on the next screen select “email” and type in your email like this:
Then click on create identity. This send a verification code to the mentioned email address. Click on the link mentioned in the email to verify it. Once that’s done, it’s time to request a production access for SES. For this we would need a static website and AWS demands all these details. Please follow the steps mentioned here — https://docs.aws.amazon.com/ses/latest/DeveloperGuide/request-production-access.html
You can keep the email type field as “Transactional”, I think the other option is “Promotional” which is not quite our use case here. And if you face any issue in setting this up, don’t hesitate to contact me personally. I will provide links to my profiles at the end of this article. Once your SES account is moved out of sandbox to production environment, you’ll receive a confirmation email stating the same.
Now that we have setup SES, let’s proceed further and setup a user pool in AWS Cognito. But before that we need an access key and a secret key from our AWS account which are needed for the AWS SDKs that we’ll be using. It’s actually pretty easy to setup so I won’t be mentioning details. Just head over to the IAM section and click on “Create Access Key” as below:
As mentioned before, we are using pretty basic setup here(like for example using only email addresses for signup, not enforcing too many constraints on the password field etc. as will be evident from the snapshots below). So go ahead and select the Cognito service from the list of services and follow along as shown(snapshots first, explanation below):
Click on create user pool.
Give a name to your user pool and then click on “Step through settings”.
Check/Select the fields as shown above and then click on next. Just for your information, this is the place where you can configure what all details you want from users during signup.
Then in the policies tab, we have to set the password restrictions.
This is an important step where you have to select the email only option as this is the only field on which we want to authenticate our users.
And then this is the step where we have to setup our email functionality. As is evident from the screenshot only 3 SES regions are allowed here. So your verified “from” email address must be verified in any of those 3 regions. Select the verified entity from the dropdown and then we can also do some customizations on the email format which is really upto you. You might set that up as you like or leave the defaults. And we’re done! Finally click on save and our user pool is ready.
Now let’s setup a basic express server. Here is what my final package.json file looks like:
As you can see, there are some basic/standard npm packages like dotenv, cors, express etc — besides that we’ve some packages to handle authentication with Cognito. And the following is my final version of app.js:
Now let me tell you from a technical point of view what all we’ll be doing. Once the user is authenticated, cognito issues 3 tokens — An IdToken, An AccessToken and a RefreshToken. In case of default settings, the id and access tokens are valid for an hour and the refresh token is valid for 30 days. So the idea is to get those tokens and access a protected resource on the server — In this flow we’ll be checking if the ID/Access token is expired we’ll issue a new token and pass them in the response headers. It’s the responsibility of the client to check if tokens are present in the response headers and if so, to update them on the client side(mostly through cookies).
Note: In a production scenario, even if we know that the token themselves are encrypted it’s a good practice to hash them using some algorithms and decode them as and when required(applicable for both client and server side). Now there are several ways to hash any data so I will leave it upto the readers to decide what package/method suits them the most.
I have used certain middlewares and utility functions in my code to make it more structured. If you go through the code you will also find a lot of implementations related to the AWS SDK we installed. Now going over the working of the every line of code will make this article insanely verbose and boring. So what I will do is, I will post the code here as gist files and provide the appropriate links to AWS docs containing the implementation details of the specific SDK utilities which were needed to write that function. So let’s start according to the flow which I described above. You can find the exact imports and other details from my repo link at the end of this article. Here is -
(I) The function for Signup:
(II) The function for Confirm Password(new password along with the verification code received after signup):
(III) The function for SignIn:
(IV) The function for forgot password(User get a verification code as soon as he opts for forgot password option):
(V) The function for confirmForgotPassword(User sets a new password along with the verification code received in the above step)
(VI) The function for logout:
Now as far as the functionality is concerned we’re all set. Now I will be showing you the screenshots of the postman requests which I made so that you can get an overview of the request and response payloads.
(I) The signup request:
(II) We got the verification code:
(III) Set up the password using the verification code:
(IV) Sign in:
(V) Forgot Password:
Note: For simplicity purposes I passed the email in the request body. It’s actually better to pass the id_token in the headers and extract the email from there.
(VI) Received the verification code for password reset:
(VII) Password reset using the verification code:
(VIII) Accessing a protected resource:
And that’s how it’s done! Pretty sweet huh?
Now that our auth server is setup and running quite good, let’s discuss some areas of concern/improvements that you should definitely consider on a production environment:
- As mentioned earlier when handling tokens, we should hash them using some algorithm and then pass them in headers/cookies.
- Passwords should be hashed too while sending the request from the client. It can be decrypted and validated on the server side.
- We should implement rate limiting for our server side APIs. This is important because malicious users/bots might try and make a DDoS(or similar kind of) attack on our APIs(especially the password reset ones) if they come to know of the request payloads somehow. Some of the popular rate limiting packages for Node.js and SpringBoot are express-rate-limit and Bucket4j respectively. Feel free to explore these and implement in your server-side frameworks.
- Typically in a production environment(most likely containers on k8s or docker or any other serverless solution even) we should make sure to handle the Access Keys and IDs carefully! These values should be encrypted and should definitely NOT be pushed along with the code to any version control. Some popular solutions are K8s configMap and Secrets, Github/Gitlab environment secrets(in case you’re using Github/Gitlab) for CI/CD etc.
Now as said earlier, this is the github repo containing all the code for the above implementation.
GitHub - lakshyajit165/node_cognito: Authentication/Authorization in Node.js using AWS Cognito
Authentication/Authorization in Node.js using AWS Cognito - GitHub - lakshyajit165/node_cognito…
If you go through the code you can see that I have implemented utilities and some middleware functions even. The way of implementation totally depends on the developers and overall app/business requirements. I had worked on Cognito before so thought of sharing my knowledge with others who might need it. Now Cognito also allows to setup authentication with social authentication providers like Google and Facebook. May be I will do it in another article.
Lakshyajit Laxmikant - Software Developer - Programming Pathshala | LinkedIn
View Lakshyajit Laxmikant's profile on LinkedIn, the world's largest professional community. Lakshyajit has 6 jobs…
This is the link to my linkedin profile. Feel free to connect(or comment here) in case of any issues with the setup and if you liked this article, do give it some claps share it with others too!