AWS Cognito logo (from freeicons.io)

Authentication with AWS Cognito, React and express.

Dimitris Dovinos
codefully.io
Published in
5 min readApr 11, 2019

--

At codefully.io we try to use as much as possible low cost (technically and economically) — high-performance and low maintenance solutions. Here is the setup and the background behind using AWS Cognito.

The need

We wanted to put together a React front end application which would initially be launched on a web client but would eventually work also with Android and iOS (React Native ). We decided to decouple the front end from the back end for maximum flexibility. The back end is based on node and express with sequelize for persistence, whereas the front end is a React application. While designing the application we were keeping in mind that eventually, we would need to support native clients. Having an API based back-end is a great solution towards that end.

An API backend allows for interaction with the application from any possible platform (web browser, native, another application etc). All the clients can be independent but all do require a single common piece of functionality: authentication. Here comes AWS’s Cognito to the rescue. Cognito manages the sign in and sign up process as well as any other aspect of authentication. It works for a javascript application (our case just now) as well as for an iOS or an Android App. It also integrates with Facebook and Google authentication (as well as Amazon’s own provider) and other SAML providers. It does all you may ever ask from an authentication system, and being an AWS product it comes at a low cost and guaranteed long term support.

A standard architecture for authenticating with Cognito

Cognito future proofed our design and allowed us to focus on the development of the business features rather than on crucial but without much value for the end customers aspects, such as the authentication. Nobody is going to give you praise for a safe and smooth authentication setup, yet everybody expects it and will complain if it does not work flawlessly.

How Cognito works for a web client based application.

The sign-up process is not that complicated and we can leave it for later (part II). The sign-in is more interesting. First you have to setup a User Pool. I will assume that you are familiar with some of the basics of Cognito. If not, then the AWS documentation is a good starting point. The only part where I struggled was setting up the App client portion. I am including a screenshot just in case there are others that that need some help.

Cognito Application Client settings

Sign-in

Once a user reaches your site then you will redirect them to the Cognito URL that is available in the Domain name section. This will be something like:

https://my-application.auth.us-east-1.amazoncognito.com

unless you have setup your own domain. Cognito will then present a sign in form, which can be customized via the UI customization section. You can see that in our case we have allowed for a logo and have performed other minor modifications to the card. Overall you do not have a lot of control but you have just enough to make the sign-in form look relevant to your application.

Cognito sign in

Provided that the user enters correctly their credentials then she will be redirected to your site. Cognito will call a URL on your site with a parameter that includes the token or code. For example:

https://my-website.com/auth/callback?code=3ba07b14-4e90-4e52-9309-7867b01dc8ed

Your application must figure out how to convert this code “3ba07b14–4e90–4e52–9309–7867b01dc8ed” into a user and log them in. You need to get the user information that is behind the code, which is available at the TOKEN endpoint. The next method does just that. It performs a POST call to the token endpoint using axios for the communication. The debug info can be of course ignored or removed. The URLs are based on a config file which could be fed with parameters from the environment (the config file can be found at the end).

The axios POST object will look something like this (straight from the log output):

AWS oauth2/token request parameters: {"method":"post","url":"https://my-cognito-url.auth.us-east-1.amazoncognito.com/oauth2/token","data":"grant_type=authorization_code&client_id=7uanuaesvjunxxxxxxxxxxx&code=3ba07b14-4e90-4e52-9309-7867b01dc8ed&scope=profile&redirect_uri=http%3A%2F%2Fmy-website.com%2Fauth%2Fcallback","auth":{"username":"7uanuaesvjunxxxxxxxxxxx","password":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}}

and the response will be similar, but much longer than this:

{"id_token":"LOTSANDLOTSOFCHARACTERSIDTOKEN",
"access_token":"LOTSANDLOTSOFCHARACTERS",
"refresh_token":"LOTSANDLOTSOFCHARACTERS",
"expires_in":3600,
"token_type":"Bearer"}

All the information regarding the user is inside the id_token. You can copy paste the contents of the id_token at jwt.io and you will see all the different pieces of information that come back from Cognito. At this point, you have the user but you have not verified that the sender of the information is indeed AWS. You can verify and extract the user information using the following method. The input to getEmailFromCode is the code that was sent to the callback URL (“3ba07b14–4e90–4e52–9309–7867b01dc8ed”) after the user successfully entered their credentials. getEmailFromCode includes a call to awsCallTokenEndpoint. (probably best to include both methods in the same file)

The above method still requires logic for handling failures in the verification process and for dealing with caching of calls to the jwksUrl. Neither is implemented here.

JWT token

With the user information, it is up to you to decide what to do next. In our case, we wanted to create a jwt token and forward it to the front end.

The user object would have been retrieved from your database based on the data that you got from getEmailFromCode. This last part is not really Cognito related, but it does give an idea of where the authentication process ends.

Finally, I am including the config file. Again this is not relevant to Cognito but it shows a way of accessing variables that should not be embedded in the code (passwords, API keys):

Case sensitivity — the (minor?) catch

Try registering with auser@codefully.io and then try again aUser@codefully.io. Horror of horrors!! Congnito has registered two different users. I am sure there is a standard that Cognito follows and that this is not a random decision. Nonetheless, it did shake my confidence when I figured this out and realized that I have to work around the issue. There are discussions on the matter with solutions of varying complexity, but I wish that AWS would make case sensitivity a setup option. I don’t see why I need to implement logic for dealing with case sensitivity of emails (or usernames) when everybody treats emails as case insensitive.

--

--