AWS Cognito JS Example (No React, Vue, Angular Etc)

This article will look at an AWS Cognito pure JS example. We will not use any front-end frameworks like React, Vue, or Angular. This might be useful to you if your application front end does not need a full UI framework. But, at the same time, you want to delegate all the complex and boilerplate “user management” stuff to AWS Cognito.

Bad News First: There Is No CDN Link

So, in an ideal world, there would just be a CDN link and docs. But, sadly there is no CDN link.

There is a GitHub repo that is a sub-repo of AWS Amplify. And in the docs of that repo is a section that talks about taking amazon-cognito-identity.min.js file from the “dist” folder. But, there is no such file to be found when we search the repo.

Cognito minfied lib file is not found in the GitHub Repo

From this, I gather that the AWS team wants us to use the Amplify Library. But, the Amplify library depends on module bundlers like Webpack, etc. And all we are looking for is a nice simple JS file to drop into our project.

Don’t worry. We are going to make our own JS file to drop into our project.

Create An AWS Cognito User Pool

Before we start to set things up, let’s go through the process of setting up a User Pool on AWS Cognito. I have made a small video that goes over the process from the AWS web console.

The video below does not cover all the options and what they mean and how to use them. However, it goes over the standard “user management” type setup.

How To Use Webpack To Make Your Own JS File With Cognito Functionality (To be used without Webpack)

Since AWS Amplify is built to work well with Webpack etc, we are going to use that to create our JS file. We are not going to use Webpack for our final project. We just going to generate and build a JS file with the help of Webpack and then include that into our project like we would any other JS file.

Just in case you are not sure what Webpack is, here is a good into:

Setting Up The Webpack Project

The steps below are taken from the official Amplify docs. But, I have just dropped the steps we don’t need.

First, let’s create the needed folder structure and files, etc.

# Lets start by creating a folder and sub folder
$> mkdir -p amplify-js-app/src

# Go ito the created folder and sub-folder
$> cd amplify-js-app

# Create some files in which we will put things
$> touch src/app.js webpack.config.js

Next, we are going to install all the required packages:

# Initialize A New Node App, Create Package.json etc
$> npm init

# Install aws-amplify package (production mode)
$> npm install aws-amplify

# Install some tooling for development
$> npm install webpack webpack-cli --save-dev

Add the following to the package.json file…

"scripts": {
    "build": "webpack"
}

The whole package.json file will look something like this…

{
  "name": "cognito_js",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "aws-amplify": "^5.0.10"
  },
  "devDependencies": {
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.1"
  }
}

Finally, copy the following into the webpack.config.js

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/app.js',
  output: {
    filename: '[name].bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/
      }
    ]
  }
};

At this point, you are done with the project setup.

Configuring Cognito

Next, we need some pieces of information from the AWS console. We are looking for 3 pieces of information specifically. (The above video on AWS Pool Setup covers how to get all these 3):

  1. The region for the user pool we have created
  2. The user pool ID
  3. The user pool web client ID

Once you have this information, you can go to the “src/app.js” file that you have just created in the last step.

In that file, you can paste the following code.

// Importing Amplify & Auth From the AWS Amplify Lib
import { Amplify, Auth } from 'aws-amplify';

// Here we are going to put in the info we got after
// creating the user pool from the AWS Console.
// See the above video if you are not clear about how
// to get this information.
Amplify.configure({
    Auth: {
        region: 'ap-south-1',
        identityPoolRegion: 'ap-south-1',
        userPoolId: 'ap-south-1_POOL_ID',
        userPoolWebClientId: 'WEB_CLIENT_ID'
    }
});

// Here we call the configure method.
const currentConfig = Auth.configure();

// Logging out the result to the console.
// This will help us to see that everything is working
// as expected
console.log(currentConfig);

You have to update lines 10 to 13 with all the information that you have taken from the AWS console.

Now, before we proceed, let’s give this whole thing a dry run.

Let’s get Webpack to compile the JS in the above file, bring in all the dependencies and make a single JS file that we can use as needed.

Go to the terminal, in the folder where we are working, and run:

$> npm run build

You should get an output that looks something like this:

> amplify-js-app@1.0.0 build
> webpack

asset main.bundle.js 1.76 MiB [compared for emit] (name: main)
orphan modules 12 MiB [orphan] 2097 modules
runtime modules 1.25 KiB 6 modules
cacheable modules 1.41 MiB
  modules by path ./node_modules/@aws-sdk/ 793 KiB 122 modules
  modules by path ./node_modules/@aws-amplify/ 246 KiB 27 modules
  modules by path ./node_modules/@aws-crypto/ 115 KiB 27 modules
  modules by path ./node_modules/amazon-cognito-identity-js/es/ 138 KiB 21 modules
  modules by path ./node_modules/uuid/ 6.07 KiB 5 modules
  modules by path ./node_modules/querystring/*.js 4.51 KiB 3 modules
  modules by path ./node_modules/universal-cookie/es6/*.js 4.94 KiB 3 modules
  modules by path ./node_modules/url/*.js 23.1 KiB
    ./node_modules/url/url.js 22.8 KiB [built] 
    ./node_modules/url/util.js 314 bytes [built] 
  + 13 modules
webpack 5.75.0 compiled successfully in 4687 ms

Webpack has created a "dist" folder. And in that folder, there is a file called: main.bundle.js

This is our very own JS file that we can drop into any HTML project. So, let's test it out before we move ahead.

Let's create a really simple HTML file like so (Update the script reference to wherever your JS file is):

<html>
    <body>
        <h1>Hello!</h1>
    </body>
    <script src="./dist/main.bundle.js"></script> 
</html>

When you render the above HTML file in the browser, and you open up the browser console, you should see something like this:

{
  "region": "ap-south-1",
  "identityPoolRegion": "ap-south-1",
  "userPoolId": "ap-south-1_ieXhlE_userPoolId",
  "userPoolWebClientId": "16em4vun_userPoolWebClientId",
  "Auth": {
    "region": "ap-south-1",
    "identityPoolRegion": "ap-south-1",
    "userPoolId": "ap-south-1_ieXhlE_userPoolId",
    "userPoolWebClientId": "16em4vun_userPoolWebClientId"
  }
}

If you see this, you know you are on the right track.

Next, we need to add some more code to the app.js file, in order to expose all the Cognito functionality to the rest of the app.

Making Functions Available To All JS Scripts

All functions you see below are taken from the official Amplify Docs. You might need to look up other functions from the official docs depending on your needs. All the usual functions that you would need are already set up below.

Update your "app.js" file to the following code:

// Importing Amplify & Auth From the AWS Amplify Lib
// Importing Amplify & Auth From the AWS Amplify Lib
import { Amplify, Auth } from 'aws-amplify';

// Here we are going to put in the info we got after
// creating the user pool from the AWS Console.
// See the above video if you are not clear about how
// to get this information.
Amplify.configure({
    Auth: {
        region: 'ap-south-1',
        identityPoolRegion: 'ap-south-1',
        userPoolId: 'ap-south-1_xxxxxx',
        userPoolWebClientId: '6rxxxxxxxxxxxxg08'
    },
    authenticationFlowType: 'USER_PASSWORD_AUTH'
});

// Create New User
const createUserOnCognito = async (username, password) => {
    try {
        const { user } = await Auth.signUp({
            username,
            password
        });
        return user;
    } catch (error) {
        console.log('error signing up:', error);
    }
}

// Get Current User
const getCurrentUser = async () => {
    const user = await Auth.currentAuthenticatedUser({
        bypassCache: false // Optional, By default is false. If set to true, this call will send a request to Cognito to get the latest user data
    })
    return user;
}

// User will put in a code that he/she
// gets on their email. This code combined with the username
// will be used to confirm the signup. The username can be setup
// to be the email ID
const confirmSignUp = async (username, code) => {
    try {
        console.log([username, code]);
        return await Auth.confirmSignUp(username, code);
    } catch (error) {
        console.log('error confirming sign up', error);
    }
}

// In case the user has not got the confirmation 
// code email ID. Then this function can be used to re-send the code
const resendConfirmationCode = async (username) => {
    try {
        console.log('code resent successfully');
        return await Auth.resendSignUp(username);
    } catch (err) {
        console.log('error resending code: ', err);
    }
}

// Sing in the user based on the user name and password
const signIn = async (username, password) => {
    try {
        const user = await Auth.signIn(username, password);
        return user;
    } catch (error) {
        console.log('error signing in', error);
    }
}

// Sign out the currently signed in user
const signOut = async () => {
    try {
        await Auth.signOut();
    } catch (error) {
        console.log('error signing out: ', error);
    }
}

// Creating an object with all the Cognito
// helper functions. Name this carefully
// so that it does not conflict with any other names
// we are going to make it available to all
// other scripts via the "window" object
const cognitoAuthHelpers = {
    createUserOnCognito,
    getCurrentUser,
    confirmSignUp,
    resendConfirmationCode,
    signIn,
    signOut
}

// Attaching the above created object to the
// window object so that all other scrips can
// use this functionality
window.cognitoAuthHelpers = cognitoAuthHelpers;

The above code is well-commented. On the top, you will have to update the config info with yours.

Lines 17 to 77 are helper functions to perform various actions. And finally, from line 86 onwards, we are making all the helper functions available to the rest of the JS scripts on the page via the window object.

With all the code in place, you can run: npm run build once more on the terminal and generate the final JS file we will use. Once again, a file should get generated and put into your "dist" folder with the name "main.bundle.js". This is the final file we need. We are going to use it in the next step.

Connecting The UI To Cognito

Now that we have our bundle JS file, let's use it in the context of a simple HTML application. I have created a single page on which the user can sign-up and log in and confirm his/her email.

It's all in one place and looks a little odd. In an actual app, these would be on their own individual pages. Have a look at the video to see it all in action:

Here is the HTML of the page:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>Only JS Cognito</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet"
        integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body>
    <div class="app">
        <div class="container">
            <div class="row mt-3">
                <!-- Sign Up UI -->
                <div class="col-6 card p-5">
                    <h3>Sign Up</h3>
                    <div class="mb-3 row">
                        <label for="staticEmail" class="col-sm-2 col-form-label">Email</label>
                        <div class="col-sm-10">
                            <input type="email" class="form-control" id="email"
                                value="">
                        </div>
                    </div>
                    <div class="mb-3 row">
                        <label for="inputPassword" class="col-sm-2 col-form-label">Password</label>
                        <div class="col-sm-10">
                            <input type="password" class="form-control" id="password">
                        </div>
                    </div>
                    <button type="button" class="btn btn-primary" id="signup_button">
                        Sign Up
                    </button>
                    </p>
                </div>
                <!-- Verify Email UI -->
                <div class="col-6 card p-5">
                    <h3>Verify Email</h3>
                    <div class="mb-3 row">
                        <label for="staticEmail" class="col-sm-12 col-form-label">Email</label>
                        <div class="col-sm-10">
                            <input type="email" class="form-control" id="email_for_varification"
                                value="">
                        </div>
                    </div>
                    <div class="mb-3 row">
                        <label for="staticEmail" class="col-sm-12 col-form-label">Enter Email Verification Code</label>
                        <div class="col-sm-10">
                            <input type="number" class="form-control" id="code"
                                value="">
                        </div>
                    </div>
                    <button type="button" class="btn btn-primary" id="verify_email">
                        Verify Email
                    </button>
                </div>
                <!-- Log In UI -->
                <div class="col-6 card p-5">
                    <h3>Log In</h3>
                    <div class="mb-3 row">
                        <label for="staticEmail" class="col-sm-2 col-form-label">Email</label>
                        <div class="col-sm-10">
                            <input type="email" class="form-control" 
                                    id="login_email" value="">
                        </div>
                    </div>
                    <div class="mb-3 row">
                        <label for="inputPassword" class="col-sm-2 col-form-label">Password</label>
                        <div class="col-sm-10">
                            <input type="password" class="form-control" id="login_password">
                        </div>
                    </div>
                    <button type="button" class="btn btn-primary" id="login_button">
                        Login
                    </button>
                </div>
                <!-- This is not exactly a UI, but just a way to
                explore the way in which you can query the current user. -->
                <div class="col-6 card p-5">
                    <h3>Who Am I?</h3>
                    <button type="button" class="btn btn-primary" id="get_current_user">
                        Get Current User
                    </button>
                </div>
                <!-- Log Out UI -->
                <div class="col-6 card p-5">
                    <h3>Log Out</h3>
                    <button type="button" class="btn btn-primary" id="logout">
                        Logout
                    </button>
                </div>
            </div>
        </div>
    </div>
    <!-- Bring in the generated JS file via webpack -->
    <script src="./dist/main.bundle.2.js"></script> 
    
    <script>
        // A helper function so that we don't have to type
        // document.querySelector(selector) too many times
        const $ = (selector) => {
            return document.querySelector(selector);
        }
        document.addEventListener("DOMContentLoaded", function(event) {
            
            // Functions that connect the UI to the functions exposed
            // via webpack by attaching to the window object
            $("#signup_button").addEventListener('click', async () => {
                const emailAsUserName = $("#email").value;
                const password = $("#password").value;
                const responseFromCognito = await cognitoAuthHelpers.createUserOnCognito(emailAsUserName, password);
                console.log(responseFromCognito);
            })

            $("#verify_email").addEventListener('click', async () => {
                const emailAsUserNameForVarification = $("#email_for_varification").value;
                const code = $("#code").value;
                const responseFromCognito = await cognitoAuthHelpers.confirmSignUp(emailAsUserNameForVarification, code);
                console.log(responseFromCognito);
            })
            
            $("#login_button").addEventListener('click', async () => {
                const emailForLogin = $("#login_email").value;
                const passwordForLogin = $("#login_password").value;
                console.log([emailForLogin, passwordForLogin]);
                const responseFromCognito = await cognitoAuthHelpers.signIn(emailForLogin, passwordForLogin);
                console.log(responseFromCognito);
            })
            
            $("#get_current_user").addEventListener('click', async () => {
                const responseFromCognito = await cognitoAuthHelpers.getCurrentUser();
                console.log(responseFromCognito);
            })
            
            $("#logout").addEventListener('click', async () => {
                const responseFromCognito = await cognitoAuthHelpers.signOut();
                console.log(responseFromCognito);
            })
        });
    </script>
</body>
</html>

As you can see above, the UI is constructed and made to look presentable using Bootstrap. However, that part is not important from our perspective. You can construct the UI any way you would like.

Now, the JS calls the Cognito functions we exposed via the Webpack bundle in the JS at the bottom. Let me place the JS once again here for clarity...

// A helper function so that we don't have to type
// document.querySelector(selector) too many times
const $ = (selector) => {
    return document.querySelector(selector);
}
document.addEventListener("DOMContentLoaded", function(event) {
    
    // Functions that connect the UI to the functions exposed
    // via webpack by attaching to the window object
    $("#signup_button").addEventListener('click', async () => {
        const emailAsUserName = $("#email").value;
        const password = $("#password").value;
        const responseFromCognito = await cognitoAuthHelpers.createUserOnCognito(emailAsUserName, password);
        console.log(responseFromCognito);
    })

    $("#verify_email").addEventListener('click', async () => {
        const emailAsUserNameForVarification = $("#email_for_varification").value;
        const code = $("#code").value;
        const responseFromCognito = await cognitoAuthHelpers.confirmSignUp(emailAsUserNameForVarification, code);
        console.log(responseFromCognito);
    })
    
    $("#login_button").addEventListener('click', async () => {
        const emailForLogin = $("#login_email").value;
        const passwordForLogin = $("#login_password").value;
        console.log([emailForLogin, passwordForLogin]);
        const responseFromCognito = await cognitoAuthHelpers.signIn(emailForLogin, passwordForLogin);
        console.log(responseFromCognito);
    })
    
    $("#get_current_user").addEventListener('click', async () => {
        const responseFromCognito = await cognitoAuthHelpers.getCurrentUser();
        console.log(responseFromCognito);
    })
    
    $("#logout").addEventListener('click', async () => {
        const responseFromCognito = await cognitoAuthHelpers.signOut();
        console.log(responseFromCognito);
    })
});

Looking at the above code, things should be fairly clear. It's simple glue JS to connect the UI to Cognito. I have just console-logged the response from Cognito. But, you will surely want to do something better like give user feedback and save data in a DB, etc.

Special Note On Identifying The Current User & Showing Their Private Info

The whole reason to set up authentication is to identify the current user and only show the user their own information. So, we need to do this securely once the user is logged in. In order to help with this we have created the following JS above:

$("#get_current_user").addEventListener('click', async () => { 
   const responseFromCognito = await cognitoAuthHelpers.getCurrentUser(); 
   console.log(responseFromCognito); 
})

Now, when you run this JS, you will see that a massive object gets printed on the console. Below is that object.

{
  "username": "dev.livefire@gmail.com",
  "pool": {
    "userPoolId": "ap-south-1_zmfycIeeT",
    "clientId": "6r06ofd7sjm9kehdshk40bog08",
    "client": {
      "endpoint": "https://cognito-idp.ap-south-1.amazonaws.com/",
      "fetchOptions": {}
    },
    "advancedSecurityDataCollectionFlag": true,
    "storage": {
      "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.dev.livefire@gmail.com.refreshToken": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.EGt0Kjy8JW076zvYCEy_qfvKjmGnyruZ6GsR4BGp4dusfAkGjNDCLP07rHk2OMfj5FamEsVxNHBSNFV4rLfu4XTwXN7nxODm_q9i9TSZ6NaSnv75S5eywECv_QE0oEqDzo4ZgomdcOkWfHSPRprP4eN7uhv0yX_SmIMm6byRPWNU9Fy07CJejWcw4NRg8ehcvh31hm2w8pBchVMsxHlk8fKe9Bswg1_RhdjPoqzW9U3CRPl6ITS9I60dDwmMgo86qszToWDKBvF63d9kz9PiyLCY9ByL8vVeGq7ko7Tk9KyPkqGmTwW2c5t8lWVu_EdD4ENBzZyXtMOW42Cdx8qNmQ.FtJSf5JnVSZ5omcm.9zmVEdFn8zAl2z_YEhnZb_Ycj7MLwC3xCWDDd1FmAExh2eGhPNGNDONA13n0ldAuQuY6_Z1_G50j-MoagPYh5U-Iepd6zE8UMK_Lo2E3QI6UTj4xzhSSmeYY1tda8q1hZ92W0JCLtV7Dv5kWW7DbuhA95OF76nKtL9iJei63OrTgWrPKYziINhvjJwAiiAyAUqrR2fgqm_7J0c06qTm_PQzzeB3DZOs-XiKjCP_W-CBZdrSbjsbviL9sHyR9dOExeiqWSDpqX8mi5QbXnDN1C1GRhx-oi4HDSYfhA5LSJ9V6y_MOX7kEKyMRAtyYqUGjsrUXJe9-s0vzUcShYY39gM1G3X8YSQAoR64D9W_Fgq7sXvb2U5HZR7ax_N5k_imZkSeBtBpxF8gh8ZVJTksxEzmXMQm39J8yRD9W7lp5lYAsWVYyLyIzQIZmMwxTRDt4r0fSpEhJtD1eb1R_tcI8rvHtb6BSh0asH33sTdl2mx4Xv6aNu4iWGBJDiAPcYRtcReXb547RM-8urtsYyO20vDKquRL7VjBUPdOwNBSrSP1m612un5mm9EVhA6maJxUdwJDBcTf-PVpivjnN2UTA9ENwPChsLM4OXqr68NWNxdkDiMZ6AwdHXLT7MrRxp_2bdlQmzZoH6MSEkZQO0MOi4jqNhr4KGEcn4L1nDlhxU3Kd80mfWYwcfzCPX9r0l8n3fNGiC3LDsJw1xkvJqaI2OkNb0JfrOZ0Q4q7JX1wHQXpGnJqRPC4T09GugGJCTiUbgdqozPHNOo555wZNIEqp9p6Mk5Z4usrJnTcnYP8yHSH426ApZQpjGJpZUzFOqbNipb9V9BnE03DswJVJRtPRhT2Sx8sTxsnjNG4eU7sq-MiGvWQUWYtFu7FHlrpWIWnYnyjymfmJA6XQ6G_RDbUcopb5P02vBa9h3dXJC0QnI0aTPOKTBVC5S9ZVjHuikWHK9yLkZ1w6wbHARxJlrMCr5_GF0t56I4CwOl8XEkhouhvaXPwZI8SD-rw86zy9fBJe8HAHJZ0gqB1fY4kZRE_spl05nOQUCBUVGdIta1iqmDTVm2OpOxjb800_nDvKaPJh2FDgAVxiYIOO63ATEaCLxDN6stG62u__tXyYMPqyljtPOMk58rMX8VVARFFXoVg5NLFEYUnrkD4Gi5hAowm12TO3y10qnpKYoZeNgoRBmaLlCa0z8R0XNUwE_5dthlHCQDW7qOA-PbF54j4x9ulvfRakF-foEA9m_055WaF5kqUwfpS_wGA9OFtAAmutIDgsiahXXGbNmDLzfo7muOWKacOMYdkP-pe3QlM3donbu-gqf_hcw24bSqv88I_5eg.31FI95_1Rr3Z0pWZ3isfog",
      "amplify-signin-with-hostedUI": "false",
      "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.dev.livefire@gmail.com.userData": "{\"UserAttributes\":[{\"Name\":\"sub\",\"Value\":\"6d6335e1-cbd5-4fa6-9a77-8a7913bbd6ec\"},{\"Name\":\"email_verified\",\"Value\":\"true\"},{\"Name\":\"email\",\"Value\":\"dev.livefire@gmail.com\"}],\"Username\":\"6d6335e1-cbd5-4fa6-9a77-8a7913bbd6ec\"}",
      "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.dev.livefire@gmail.com.idToken": "eyJraWQiOiJ2TFBCbndwaFJqdE1RTHRqUGJlZWFmblhuYWhreWh2U3hxRzJjSllUcklZPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI2ZDYzMzVlMS1jYmQ1LTRmYTYtOWE3Ny04YTc5MTNiYmQ2ZWMiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLmFwLXNvdXRoLTEuYW1hem9uYXdzLmNvbVwvYXAtc291dGgtMV96bWZ5Y0llZVQiLCJjb2duaXRvOnVzZXJuYW1lIjoiNmQ2MzM1ZTEtY2JkNS00ZmE2LTlhNzctOGE3OTEzYmJkNmVjIiwib3JpZ2luX2p0aSI6ImFkMWYzMDc5LTZlODAtNGM3Yy04MmI4LWY3MjVlOWY2NWFlOCIsImF1ZCI6IjZyMDZvZmQ3c2ptOWtlaGRzaGs0MGJvZzA4IiwiZXZlbnRfaWQiOiJjNWJkYmI1ZC05YjA0LTQ0MmEtYTZjMC0zOTJlM2UzMzc5M2YiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTY3NTc3ODU2OSwiZXhwIjoxNjc1NzgyMTY5LCJpYXQiOjE2NzU3Nzg1NjksImp0aSI6IjRkZWQyYTQ4LTQwODMtNGJiOS1hYzI4LWExOGIwNGRkOThhYyIsImVtYWlsIjoiZGV2LmxpdmVmaXJlQGdtYWlsLmNvbSJ9.q_0_SXMdslkF-hY55JvU0WwgG3hk0Lh5UgKyOwK7GwgcTwCBv26UMAGk_nA9uuw30ygCdeijfqjPicskPyk9iTwZZz3DTxSVjrUpiMZRej-8h6DFEPlAaO0kZNyof-06lb4T7yo6VpgnHPqhuK1bsCw-ZXwAgpNU2kY6b5vRqp6uVUvz3-M5tUiZIJ9kd9FAZO0NuuleYvgLsS5-X5HU1UPdJvGM34Z8ivmN4ESHgEhDNggZ0_rFgQP5-27r4gnVfn2aIIShWANNvedKqTxU4bd5msu0PaMYErJENZ7cc-L4BfYiLAZzSccYztBaFvMTzoRiZ48jGFqKmSc8eYzlsw",
      "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.dev.livefire@gmail.com.clockDrift": "0",
      "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.dev.livefire@gmail.com.accessToken": "eyJraWQiOiJNYWMrUk9ZMFdFTXNaYmpGaHRYUEpJZ1hFV3ZOc1dteUNJZ240YUR3WUdNPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI2ZDYzMzVlMS1jYmQ1LTRmYTYtOWE3Ny04YTc5MTNiYmQ2ZWMiLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAuYXAtc291dGgtMS5hbWF6b25hd3MuY29tXC9hcC1zb3V0aC0xX3ptZnljSWVlVCIsImNsaWVudF9pZCI6IjZyMDZvZmQ3c2ptOWtlaGRzaGs0MGJvZzA4Iiwib3JpZ2luX2p0aSI6ImFkMWYzMDc5LTZlODAtNGM3Yy04MmI4LWY3MjVlOWY2NWFlOCIsImV2ZW50X2lkIjoiYzViZGJiNWQtOWIwNC00NDJhLWE2YzAtMzkyZTNlMzM3OTNmIiwidG9rZW5fdXNlIjoiYWNjZXNzIiwic2NvcGUiOiJhd3MuY29nbml0by5zaWduaW4udXNlci5hZG1pbiIsImF1dGhfdGltZSI6MTY3NTc3ODU2OSwiZXhwIjoxNjc1NzgyMTY5LCJpYXQiOjE2NzU3Nzg1NjksImp0aSI6IjMyOTAyYjQzLTI5NDAtNDQwNS04MWIyLTY0YWFjMDQ3YWMwZSIsInVzZXJuYW1lIjoiNmQ2MzM1ZTEtY2JkNS00ZmE2LTlhNzctOGE3OTEzYmJkNmVjIn0.uh7o-3vdkxZi3Nc2PqahoKKA-mKrpdL0GbiiYWjlzQfzGerPtvi9dMjkNp4jDsNsTCHL9YlRRQ1LMIGHmO4H8SymabyAmtvwEkKKjYTItUshNXJtSuyBeziZhg6TZ9qlAQet13yIofmrFQd6_3_0d2QHNT8w4fAkjg89j3EALIhGE3rogOceQ7No0ssphGm7oO32wocxKjyPZe-d_vMe6gKATVNqGFOQvEubtJ7qdX8zq2yRzLxXanpPQMEQtyv-ukJDqvh-lAcbitJScL52Y93Gg7dAgHKKl1g_CfgU0LL9osaP2ANd6E_j937PiLrkg1axqttmJVBWqKd5j7LegA",
      "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.LastAuthUser": "dev.livefire@gmail.com"
    }
  },
  "Session": null,
  "client": {
    "endpoint": "https://cognito-idp.ap-south-1.amazonaws.com/",
    "fetchOptions": {}
  },
  "signInUserSession": {
    "idToken": {
      "jwtToken": "eyJraWQiOiJ2TFBCbndwaFJqdE1RTHRqUGJlZWFmblhuYWhreWh2U3hxRzJjSllUcklZPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI2ZDYzMzVlMS1jYmQ1LTRmYTYtOWE3Ny04YTc5MTNiYmQ2ZWMiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLmFwLXNvdXRoLTEuYW1hem9uYXdzLmNvbVwvYXAtc291dGgtMV96bWZ5Y0llZVQiLCJjb2duaXRvOnVzZXJuYW1lIjoiNmQ2MzM1ZTEtY2JkNS00ZmE2LTlhNzctOGE3OTEzYmJkNmVjIiwib3JpZ2luX2p0aSI6ImFkMWYzMDc5LTZlODAtNGM3Yy04MmI4LWY3MjVlOWY2NWFlOCIsImF1ZCI6IjZyMDZvZmQ3c2ptOWtlaGRzaGs0MGJvZzA4IiwiZXZlbnRfaWQiOiJjNWJkYmI1ZC05YjA0LTQ0MmEtYTZjMC0zOTJlM2UzMzc5M2YiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTY3NTc3ODU2OSwiZXhwIjoxNjc1NzgyMTY5LCJpYXQiOjE2NzU3Nzg1NjksImp0aSI6IjRkZWQyYTQ4LTQwODMtNGJiOS1hYzI4LWExOGIwNGRkOThhYyIsImVtYWlsIjoiZGV2LmxpdmVmaXJlQGdtYWlsLmNvbSJ9.q_0_SXMdslkF-hY55JvU0WwgG3hk0Lh5UgKyOwK7GwgcTwCBv26UMAGk_nA9uuw30ygCdeijfqjPicskPyk9iTwZZz3DTxSVjrUpiMZRej-8h6DFEPlAaO0kZNyof-06lb4T7yo6VpgnHPqhuK1bsCw-ZXwAgpNU2kY6b5vRqp6uVUvz3-M5tUiZIJ9kd9FAZO0NuuleYvgLsS5-X5HU1UPdJvGM34Z8ivmN4ESHgEhDNggZ0_rFgQP5-27r4gnVfn2aIIShWANNvedKqTxU4bd5msu0PaMYErJENZ7cc-L4BfYiLAZzSccYztBaFvMTzoRiZ48jGFqKmSc8eYzlsw",
      "payload": {
        "sub": "6d6335e1-cbd5-4fa6-9a77-8a7913bbd6ec",
        "email_verified": true,
        "iss": "https://cognito-idp.ap-south-1.amazonaws.com/ap-south-1_zmfycIeeT",
        "cognito:username": "6d6335e1-cbd5-4fa6-9a77-8a7913bbd6ec",
        "origin_jti": "ad1f3079-6e80-4c7c-82b8-f725e9f65ae8",
        "aud": "6r06ofd7sjm9kehdshk40bog08",
        "event_id": "c5bdbb5d-9b04-442a-a6c0-392e3e33793f",
        "token_use": "id",
        "auth_time": 1675778569,
        "exp": 1675782169,
        "iat": 1675778569,
        "jti": "4ded2a48-4083-4bb9-ac28-a18b04dd98ac",
        "email": "dev.livefire@gmail.com"
      }
    },
    "refreshToken": {
      "token": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.EGt0Kjy8JW076zvYCEy_qfvKjmGnyruZ6GsR4BGp4dusfAkGjNDCLP07rHk2OMfj5FamEsVxNHBSNFV4rLfu4XTwXN7nxODm_q9i9TSZ6NaSnv75S5eywECv_QE0oEqDzo4ZgomdcOkWfHSPRprP4eN7uhv0yX_SmIMm6byRPWNU9Fy07CJejWcw4NRg8ehcvh31hm2w8pBchVMsxHlk8fKe9Bswg1_RhdjPoqzW9U3CRPl6ITS9I60dDwmMgo86qszToWDKBvF63d9kz9PiyLCY9ByL8vVeGq7ko7Tk9KyPkqGmTwW2c5t8lWVu_EdD4ENBzZyXtMOW42Cdx8qNmQ.FtJSf5JnVSZ5omcm.9zmVEdFn8zAl2z_YEhnZb_Ycj7MLwC3xCWDDd1FmAExh2eGhPNGNDONA13n0ldAuQuY6_Z1_G50j-MoagPYh5U-Iepd6zE8UMK_Lo2E3QI6UTj4xzhSSmeYY1tda8q1hZ92W0JCLtV7Dv5kWW7DbuhA95OF76nKtL9iJei63OrTgWrPKYziINhvjJwAiiAyAUqrR2fgqm_7J0c06qTm_PQzzeB3DZOs-XiKjCP_W-CBZdrSbjsbviL9sHyR9dOExeiqWSDpqX8mi5QbXnDN1C1GRhx-oi4HDSYfhA5LSJ9V6y_MOX7kEKyMRAtyYqUGjsrUXJe9-s0vzUcShYY39gM1G3X8YSQAoR64D9W_Fgq7sXvb2U5HZR7ax_N5k_imZkSeBtBpxF8gh8ZVJTksxEzmXMQm39J8yRD9W7lp5lYAsWVYyLyIzQIZmMwxTRDt4r0fSpEhJtD1eb1R_tcI8rvHtb6BSh0asH33sTdl2mx4Xv6aNu4iWGBJDiAPcYRtcReXb547RM-8urtsYyO20vDKquRL7VjBUPdOwNBSrSP1m612un5mm9EVhA6maJxUdwJDBcTf-PVpivjnN2UTA9ENwPChsLM4OXqr68NWNxdkDiMZ6AwdHXLT7MrRxp_2bdlQmzZoH6MSEkZQO0MOi4jqNhr4KGEcn4L1nDlhxU3Kd80mfWYwcfzCPX9r0l8n3fNGiC3LDsJw1xkvJqaI2OkNb0JfrOZ0Q4q7JX1wHQXpGnJqRPC4T09GugGJCTiUbgdqozPHNOo555wZNIEqp9p6Mk5Z4usrJnTcnYP8yHSH426ApZQpjGJpZUzFOqbNipb9V9BnE03DswJVJRtPRhT2Sx8sTxsnjNG4eU7sq-MiGvWQUWYtFu7FHlrpWIWnYnyjymfmJA6XQ6G_RDbUcopb5P02vBa9h3dXJC0QnI0aTPOKTBVC5S9ZVjHuikWHK9yLkZ1w6wbHARxJlrMCr5_GF0t56I4CwOl8XEkhouhvaXPwZI8SD-rw86zy9fBJe8HAHJZ0gqB1fY4kZRE_spl05nOQUCBUVGdIta1iqmDTVm2OpOxjb800_nDvKaPJh2FDgAVxiYIOO63ATEaCLxDN6stG62u__tXyYMPqyljtPOMk58rMX8VVARFFXoVg5NLFEYUnrkD4Gi5hAowm12TO3y10qnpKYoZeNgoRBmaLlCa0z8R0XNUwE_5dthlHCQDW7qOA-PbF54j4x9ulvfRakF-foEA9m_055WaF5kqUwfpS_wGA9OFtAAmutIDgsiahXXGbNmDLzfo7muOWKacOMYdkP-pe3QlM3donbu-gqf_hcw24bSqv88I_5eg.31FI95_1Rr3Z0pWZ3isfog"
    },
    "accessToken": {
      "jwtToken": "eyJraWQiOiJNYWMrUk9ZMFdFTXNaYmpGaHRYUEpJZ1hFV3ZOc1dteUNJZ240YUR3WUdNPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI2ZDYzMzVlMS1jYmQ1LTRmYTYtOWE3Ny04YTc5MTNiYmQ2ZWMiLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAuYXAtc291dGgtMS5hbWF6b25hd3MuY29tXC9hcC1zb3V0aC0xX3ptZnljSWVlVCIsImNsaWVudF9pZCI6IjZyMDZvZmQ3c2ptOWtlaGRzaGs0MGJvZzA4Iiwib3JpZ2luX2p0aSI6ImFkMWYzMDc5LTZlODAtNGM3Yy04MmI4LWY3MjVlOWY2NWFlOCIsImV2ZW50X2lkIjoiYzViZGJiNWQtOWIwNC00NDJhLWE2YzAtMzkyZTNlMzM3OTNmIiwidG9rZW5fdXNlIjoiYWNjZXNzIiwic2NvcGUiOiJhd3MuY29nbml0by5zaWduaW4udXNlci5hZG1pbiIsImF1dGhfdGltZSI6MTY3NTc3ODU2OSwiZXhwIjoxNjc1NzgyMTY5LCJpYXQiOjE2NzU3Nzg1NjksImp0aSI6IjMyOTAyYjQzLTI5NDAtNDQwNS04MWIyLTY0YWFjMDQ3YWMwZSIsInVzZXJuYW1lIjoiNmQ2MzM1ZTEtY2JkNS00ZmE2LTlhNzctOGE3OTEzYmJkNmVjIn0.uh7o-3vdkxZi3Nc2PqahoKKA-mKrpdL0GbiiYWjlzQfzGerPtvi9dMjkNp4jDsNsTCHL9YlRRQ1LMIGHmO4H8SymabyAmtvwEkKKjYTItUshNXJtSuyBeziZhg6TZ9qlAQet13yIofmrFQd6_3_0d2QHNT8w4fAkjg89j3EALIhGE3rogOceQ7No0ssphGm7oO32wocxKjyPZe-d_vMe6gKATVNqGFOQvEubtJ7qdX8zq2yRzLxXanpPQMEQtyv-ukJDqvh-lAcbitJScL52Y93Gg7dAgHKKl1g_CfgU0LL9osaP2ANd6E_j937PiLrkg1axqttmJVBWqKd5j7LegA",
      "payload": {
        "sub": "6d6335e1-cbd5-4fa6-9a77-8a7913bbd6ec",
        "iss": "https://cognito-idp.ap-south-1.amazonaws.com/ap-south-1_zmfycIeeT",
        "client_id": "6r06ofd7sjm9kehdshk40bog08",
        "origin_jti": "ad1f3079-6e80-4c7c-82b8-f725e9f65ae8",
        "event_id": "c5bdbb5d-9b04-442a-a6c0-392e3e33793f",
        "token_use": "access",
        "scope": "aws.cognito.signin.user.admin",
        "auth_time": 1675778569,
        "exp": 1675782169,
        "iat": 1675778569,
        "jti": "32902b43-2940-4405-81b2-64aac047ac0e",
        "username": "6d6335e1-cbd5-4fa6-9a77-8a7913bbd6ec"
      }
    },
    "clockDrift": 0
  },
  "authenticationFlowType": "USER_SRP_AUTH",
  "storage": {
    "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.dev.livefire@gmail.com.refreshToken": "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAifQ.EGt0Kjy8JW076zvYCEy_qfvKjmGnyruZ6GsR4BGp4dusfAkGjNDCLP07rHk2OMfj5FamEsVxNHBSNFV4rLfu4XTwXN7nxODm_q9i9TSZ6NaSnv75S5eywECv_QE0oEqDzo4ZgomdcOkWfHSPRprP4eN7uhv0yX_SmIMm6byRPWNU9Fy07CJejWcw4NRg8ehcvh31hm2w8pBchVMsxHlk8fKe9Bswg1_RhdjPoqzW9U3CRPl6ITS9I60dDwmMgo86qszToWDKBvF63d9kz9PiyLCY9ByL8vVeGq7ko7Tk9KyPkqGmTwW2c5t8lWVu_EdD4ENBzZyXtMOW42Cdx8qNmQ.FtJSf5JnVSZ5omcm.9zmVEdFn8zAl2z_YEhnZb_Ycj7MLwC3xCWDDd1FmAExh2eGhPNGNDONA13n0ldAuQuY6_Z1_G50j-MoagPYh5U-Iepd6zE8UMK_Lo2E3QI6UTj4xzhSSmeYY1tda8q1hZ92W0JCLtV7Dv5kWW7DbuhA95OF76nKtL9iJei63OrTgWrPKYziINhvjJwAiiAyAUqrR2fgqm_7J0c06qTm_PQzzeB3DZOs-XiKjCP_W-CBZdrSbjsbviL9sHyR9dOExeiqWSDpqX8mi5QbXnDN1C1GRhx-oi4HDSYfhA5LSJ9V6y_MOX7kEKyMRAtyYqUGjsrUXJe9-s0vzUcShYY39gM1G3X8YSQAoR64D9W_Fgq7sXvb2U5HZR7ax_N5k_imZkSeBtBpxF8gh8ZVJTksxEzmXMQm39J8yRD9W7lp5lYAsWVYyLyIzQIZmMwxTRDt4r0fSpEhJtD1eb1R_tcI8rvHtb6BSh0asH33sTdl2mx4Xv6aNu4iWGBJDiAPcYRtcReXb547RM-8urtsYyO20vDKquRL7VjBUPdOwNBSrSP1m612un5mm9EVhA6maJxUdwJDBcTf-PVpivjnN2UTA9ENwPChsLM4OXqr68NWNxdkDiMZ6AwdHXLT7MrRxp_2bdlQmzZoH6MSEkZQO0MOi4jqNhr4KGEcn4L1nDlhxU3Kd80mfWYwcfzCPX9r0l8n3fNGiC3LDsJw1xkvJqaI2OkNb0JfrOZ0Q4q7JX1wHQXpGnJqRPC4T09GugGJCTiUbgdqozPHNOo555wZNIEqp9p6Mk5Z4usrJnTcnYP8yHSH426ApZQpjGJpZUzFOqbNipb9V9BnE03DswJVJRtPRhT2Sx8sTxsnjNG4eU7sq-MiGvWQUWYtFu7FHlrpWIWnYnyjymfmJA6XQ6G_RDbUcopb5P02vBa9h3dXJC0QnI0aTPOKTBVC5S9ZVjHuikWHK9yLkZ1w6wbHARxJlrMCr5_GF0t56I4CwOl8XEkhouhvaXPwZI8SD-rw86zy9fBJe8HAHJZ0gqB1fY4kZRE_spl05nOQUCBUVGdIta1iqmDTVm2OpOxjb800_nDvKaPJh2FDgAVxiYIOO63ATEaCLxDN6stG62u__tXyYMPqyljtPOMk58rMX8VVARFFXoVg5NLFEYUnrkD4Gi5hAowm12TO3y10qnpKYoZeNgoRBmaLlCa0z8R0XNUwE_5dthlHCQDW7qOA-PbF54j4x9ulvfRakF-foEA9m_055WaF5kqUwfpS_wGA9OFtAAmutIDgsiahXXGbNmDLzfo7muOWKacOMYdkP-pe3QlM3donbu-gqf_hcw24bSqv88I_5eg.31FI95_1Rr3Z0pWZ3isfog",
    "amplify-signin-with-hostedUI": "false",
    "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.dev.livefire@gmail.com.userData": "{\"UserAttributes\":[{\"Name\":\"sub\",\"Value\":\"6d6335e1-cbd5-4fa6-9a77-8a7913bbd6ec\"},{\"Name\":\"email_verified\",\"Value\":\"true\"},{\"Name\":\"email\",\"Value\":\"dev.livefire@gmail.com\"}],\"Username\":\"6d6335e1-cbd5-4fa6-9a77-8a7913bbd6ec\"}",
    "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.dev.livefire@gmail.com.idToken": "eyJraWQiOiJ2TFBCbndwaFJqdE1RTHRqUGJlZWFmblhuYWhreWh2U3hxRzJjSllUcklZPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI2ZDYzMzVlMS1jYmQ1LTRmYTYtOWE3Ny04YTc5MTNiYmQ2ZWMiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLmFwLXNvdXRoLTEuYW1hem9uYXdzLmNvbVwvYXAtc291dGgtMV96bWZ5Y0llZVQiLCJjb2duaXRvOnVzZXJuYW1lIjoiNmQ2MzM1ZTEtY2JkNS00ZmE2LTlhNzctOGE3OTEzYmJkNmVjIiwib3JpZ2luX2p0aSI6ImFkMWYzMDc5LTZlODAtNGM3Yy04MmI4LWY3MjVlOWY2NWFlOCIsImF1ZCI6IjZyMDZvZmQ3c2ptOWtlaGRzaGs0MGJvZzA4IiwiZXZlbnRfaWQiOiJjNWJkYmI1ZC05YjA0LTQ0MmEtYTZjMC0zOTJlM2UzMzc5M2YiLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTY3NTc3ODU2OSwiZXhwIjoxNjc1NzgyMTY5LCJpYXQiOjE2NzU3Nzg1NjksImp0aSI6IjRkZWQyYTQ4LTQwODMtNGJiOS1hYzI4LWExOGIwNGRkOThhYyIsImVtYWlsIjoiZGV2LmxpdmVmaXJlQGdtYWlsLmNvbSJ9.q_0_SXMdslkF-hY55JvU0WwgG3hk0Lh5UgKyOwK7GwgcTwCBv26UMAGk_nA9uuw30ygCdeijfqjPicskPyk9iTwZZz3DTxSVjrUpiMZRej-8h6DFEPlAaO0kZNyof-06lb4T7yo6VpgnHPqhuK1bsCw-ZXwAgpNU2kY6b5vRqp6uVUvz3-M5tUiZIJ9kd9FAZO0NuuleYvgLsS5-X5HU1UPdJvGM34Z8ivmN4ESHgEhDNggZ0_rFgQP5-27r4gnVfn2aIIShWANNvedKqTxU4bd5msu0PaMYErJENZ7cc-L4BfYiLAZzSccYztBaFvMTzoRiZ48jGFqKmSc8eYzlsw",
    "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.dev.livefire@gmail.com.clockDrift": "0",
    "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.dev.livefire@gmail.com.accessToken": "eyJraWQiOiJNYWMrUk9ZMFdFTXNaYmpGaHRYUEpJZ1hFV3ZOc1dteUNJZ240YUR3WUdNPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI2ZDYzMzVlMS1jYmQ1LTRmYTYtOWE3Ny04YTc5MTNiYmQ2ZWMiLCJpc3MiOiJodHRwczpcL1wvY29nbml0by1pZHAuYXAtc291dGgtMS5hbWF6b25hd3MuY29tXC9hcC1zb3V0aC0xX3ptZnljSWVlVCIsImNsaWVudF9pZCI6IjZyMDZvZmQ3c2ptOWtlaGRzaGs0MGJvZzA4Iiwib3JpZ2luX2p0aSI6ImFkMWYzMDc5LTZlODAtNGM3Yy04MmI4LWY3MjVlOWY2NWFlOCIsImV2ZW50X2lkIjoiYzViZGJiNWQtOWIwNC00NDJhLWE2YzAtMzkyZTNlMzM3OTNmIiwidG9rZW5fdXNlIjoiYWNjZXNzIiwic2NvcGUiOiJhd3MuY29nbml0by5zaWduaW4udXNlci5hZG1pbiIsImF1dGhfdGltZSI6MTY3NTc3ODU2OSwiZXhwIjoxNjc1NzgyMTY5LCJpYXQiOjE2NzU3Nzg1NjksImp0aSI6IjMyOTAyYjQzLTI5NDAtNDQwNS04MWIyLTY0YWFjMDQ3YWMwZSIsInVzZXJuYW1lIjoiNmQ2MzM1ZTEtY2JkNS00ZmE2LTlhNzctOGE3OTEzYmJkNmVjIn0.uh7o-3vdkxZi3Nc2PqahoKKA-mKrpdL0GbiiYWjlzQfzGerPtvi9dMjkNp4jDsNsTCHL9YlRRQ1LMIGHmO4H8SymabyAmtvwEkKKjYTItUshNXJtSuyBeziZhg6TZ9qlAQet13yIofmrFQd6_3_0d2QHNT8w4fAkjg89j3EALIhGE3rogOceQ7No0ssphGm7oO32wocxKjyPZe-d_vMe6gKATVNqGFOQvEubtJ7qdX8zq2yRzLxXanpPQMEQtyv-ukJDqvh-lAcbitJScL52Y93Gg7dAgHKKl1g_CfgU0LL9osaP2ANd6E_j937PiLrkg1axqttmJVBWqKd5j7LegA",
    "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.LastAuthUser": "dev.livefire@gmail.com"
  },
  "keyPrefix": "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08",
  "userDataKey": "CognitoIdentityServiceProvider.6r06ofd7sjm9kehdshk40bog08.dev.livefire@gmail.com.userData",
  "attributes": {
    "sub": "6d6335e1-cbd5-4fa6-9a77-8a7913bbd6ec",
    "email_verified": true,
    "email": "dev.livefire@gmail.com"
  },
  "preferredMFA": "NOMFA"
}

In the above object, you will get the user's email ID and username. So, when requesting the content for the user from the backend, you COULD send over the username when asking for the content. DO NOT DO THIS. This is not secure. A hacker can get access to the user name and then send it with their own request. A few mins with the person whose comp they are attacking will make it possible for them to do this.

The better way to do this is to use the JWT created by AWS. The JWT is part of the above object. Its located at:

signInUserSession.idToken.jwtToken

In case, you are not sure what JWTs are here is a good video:

You can send this JWT token with the request you make to your backend. From the above object, if we extract the JWT, we get the following in the payload field:

{
  "sub": "6d6335e1-cbd5-4fa6-9a77-8a7913bbd6ec",
  "email_verified": true,
  "iss": "https://cognito-idp.ap-south-1.amazonaws.com/ap-south-1_zmfycIeeT",
  "cognito:username": "6d6335e1-cbd5-4fa6-9a77-8a7913bbd6ec",
  "origin_jti": "ad1f3079-6e80-4c7c-82b8-f725e9f65ae8",
  "aud": "6r06ofd7sjm9kehdshk40bog08",
  "event_id": "c5bdbb5d-9b04-442a-a6c0-392e3e33793f",
  "token_use": "id",
  "auth_time": 1675778569,
  "exp": 1675782169,
  "iat": 1675778569,
  "jti": "4ded2a48-4083-4bb9-ac28-a18b04dd98ac",
  "email": "dev.livefire@gmail.com"
}

You can try to decode the JWT and see its contents using a tool like this.

Important: How To Make Sure That The JWT Is Actually From Cognito?

AWS has created a URL to get keys to verify the JWT. The keys are given in JSON form. To get the keys, you will need 2 pieces of information:

  1. Region of your user pool
  2. Your user Pool Id

Once you have the above, you can make a URL like so:

https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json

And when you hit the URL you should get something like this:

{
  "keys": [
    {
      "alg": "RS256",
      "e": "AQAB",
      "kid": "oGCxVDHMIK9xxxxxh3zxQUR2O6jxmk+OEr4=",
      "kty": "RSA",
      "n": "2LuO2DbxxxxxxdTHXv1GQgYlqQdRB5050mbg40cFPtV7dU70wWRhowuAWozXULbJ097gRe2R4_7d7sEBaIFLRlIY-fDyHugmxlwz95uctgEgIbOAKRvO2tQYdbterhJdCIF5n3MFaekLV00J1HybFhyxXZjU_OqYz7B_X-50VRKXDfQPDvlE3JoC_2wHHVdzHypyKokFaXXqvjel4GuwuDJByPQtbZZ01YNU8lkoiGS0_JP3QotvyWATIfd-Dn3gheIow3tIDx7NsipIZ9T5zwuRPUvSwNlAlvlJQurnHtfLZmeS6zMsUq2Sr9G9imNNmbw",
      "use": "sig"
    },
    {
      "alg": "RS256",
      "e": "AQAB",
      "kid": "1eTxczxDNhW9rexxxxIB+1sdxwOucU=",
      "kty": "RSA",
      "n": "vTRuV3i7oAgdxxxxxxxxxw6XgwBGvqhpRqNWMf_fWWJ9XT6zDfJaaIijvKPMv7h6b0W43kEMetgz-TwBoeDP1ETFZsgVNDz5F73RFUwdQFEZ8_ybbe0KnYYb8q3nW3wRybe29g0HDPkqQagkw520GH9wMQ5keTWT3PgZnv32yEtcFOKDx_jUQrfQhoZJUS26EUh6ez_uOmnZZYl73MIVTuAjGShx0xV7CyLFFZVhmXdE0_CSegxn-m6uvsyIY1yhzuhom_xfBL_eiFrYF49hcNwKKRq-uVyNZ0Was_grUG_bxxtFp45Wsw",
      "use": "sig"
    }
  ]
}

This is an array of "keys". Now when AWS creates the above JWT, it "signs it" with one of these keys. So, with a little Node script, you can actually verify that the JWT is created by AWS and get the decoded JWT with all the information in it.

// You will need this lib from NPM
let jwkToPem = require('jwk-to-pem');

// You will also need this lib from NPM
let jwt = require('jsonwebtoken');

const fs = require('fs');

// The keys are stored in a file
// so that things are faster
let jwkJson = fs.readFileSync('jwk.json', 'utf8');

// Pasring the contents of the file to
// get the keys
let keysArray = JSON.parse(jwkJson);

// The token we got from AWS
let token = "YOUR_TOKEN_HERE_XXXXXXXX";
let decodedJWT = undefined;

keysArray.keys.forEach((keyObj) => {

    let pem = jwkToPem(keyObj);

    try {
        let decoded = jwt.verify(token, pem);
        decodedJWT = decoded;
    } catch (err) {
        // console.log(`KID: ${keyObj.kid} did not work.`)
    }

});

console.log(decodedJWT);

To make the above node script run you will need to do a few things:

  1. Store the keys JSON into a file. It is referenced in line 11 above.
  2. Install the NPM package: jwk-to-pem into your node project.
  3. Install the NPM package: jsonwebtoken into your node project

Setting Up An Express Middleware To Vaifty The Cognito JWT

In order to quickly help you validate the JWT token on the server, I have created a very simple ExpressJS middleware function.

Now, this will not be helpful if you are not using ExpressJS on the backend. But, it will still give you an idea.

Below is a tiny Express Server including the middleware.

const express = require('express')
const app = express()
const port = 3000

const jwkToPem = require('jwk-to-pem')
const jwt = require('jsonwebtoken')
const fs = require('fs')

const cognitoJWTVerificationMiddleWare = (req, res, next) => {

    let decodedJWT = undefined;
    const authorizationHeader = req.headers.authorization;

    req.jwtData = {}

    if ( authorizationHeader != undefined ) {

        const token = authorizationHeader;
        let jwkJson = fs.readFileSync('jwk.json', 'utf8');
        let keysArray = JSON.parse(jwkJson);
    
        console.log(keysArray);

        keysArray.keys.forEach((keyObj) => {
            let pem = jwkToPem(keyObj);
    
            try {
                let decoded = jwt.verify(token, pem);
                decodedJWT = decoded;
            } catch (err) {
                console.log(err);
            }
        });
        req.jwtData = decodedJWT;
    
    }

    next();
}

app.use(cognitoJWTVerificationMiddleWare)

app.get('/', (req, res) => {
  res.json(req.jwtData);
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

Some things are being logged out to the console etc so that you can run this quickly and see what is going on.

As a setup step, you will have to download the JWK array I have mentioned above from:

https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/jwks.json

And save the JSON contents in a file called: jwk.json and update the path if needed. With this done, the middleware should attach a jwtData key to the request object.

With all this done, you can test things out in Postman. Below is how it looks for me in Postman.

Using an ExpressJS middleware to decode and verify the JWT sent from the client via the "authorization" header(Click To See A Larger Image)

As you can see in the above image, when sending the request, the JWT is sent in the "authorization" header. You can change this to suit your needs of course.

What About Social Login, FB, Google Etc?

If you need to do more things like federated login etc, do look at the official Amplify docs on the subject. All the info is provided in detail with screenshots etc. Because it was so detailed with screenshots etc, I really did not feel I needed to re-hash it here.

Conclusion

And with that, we have seen a complete AWS Cognito JS example. I hope you found it useful.