Saturday, July 25, 2020

Authentication using Amazon Cognito and Node js


Cognito + API Gateway + Node.js


 




What is Amazon Cognito?

Amazon Cognito offers authentication, authorization, and user management for web and mobile apps. You can login with your username and password or use third-party services like Facebook, Google, Amazon, etc.

Cognito provides two major components:

Cognito user pool

User pool provides sign-up and sign-in options for users. The user can be created either by themselves or by the Administrator.

Cognito identity pool

By using Federated Identities, you can sign in through social identity providers such as Amazon, Facebook, Google, etc. But it will have limited permissions to access other AWS services. By using Identity pool we can grant permission for the user to access AWS services. 

This tutorial consists of three sections. The first section will focus on how to set up the Cognito user pool, 
In the second section, we can focus on how the user can sign-up, sign-in and confirm the verification code provided by Cognito by using node js application. 

In the last section, we can focus on how to authenticate your API by using the Cognito user pool with AWS API gateway.


How to create Cognito User Pool?

Select Cognito service from `Security, Identity & compliance` menu. You will be able to see the following screen:

                       


Click on ‘Manage User Pools’ and click on ‘Create a user pool’, this will take you to the below page:

                           


Either you can go with the default settings or you can customize the settings. We will take you through the ‘Step through Settings’. 

Enter the ‘Pool name’ and click on the ‘Step through settings’.

                          
    

Above screen, you can select what information is required for the user when they sign-up/sign-in. Default ‘username’ option will be selected. You can select ‘Email address or phone number' as well.

If you select username you can also select the optional attributes below the username section. In the next section you can select what attributes are required for the signup process.



In this section you can customize your password rules. Also you can configure who can create the user (either administrator can create users or user can create themselves).


Here you can configure the MFA authentication if needed. This will enable one more layer of security rather than just using the user name and password. You can configure the recovery process in this section.


You can customize how your user will get the email message by changing the template.

Here you can set the option to save the user’s device



Here you need to create an app client. This is the client our Node.js application will be interacting with. Also, you need to click on the `Create app Client` option to create the client. 


Here you can customize your workflow with triggers.

 

Here you can review your configuration and make the necessary changes. Once you hit the ‘Create pool’ button it will create the user pool.

 

You need Pool Id and Client id to interact with Cognito pool from your Node.js server. You can see the pool id highlighted above. To get the app client id, click on the app clients, see the image below:




You can read more at https://aws.amazon.com/cognito/


Create Node server

This section will focus on creating the Node.js server and how Node application will interact with Cognito pool. You can customize the code based on your folder structure.

1. Create a folder for your project and install the following packages:

npm install express --save
npm install nodemon --save -dev
npm install body-parser --save
npm install amazon-cognito-identity-js --save
npm isntall node-fetch --save

2. Create app.js 

const express = require("express")
const bodyParser = require("body-parser")
const app = express()

app.use(bodyParser.urlencoded({extended:true}))
app.use(bodyParser.json({limit:"10mb"}))
app.use('/api'require("./route"));

module.exports = app





3. Create index.js 

const app = require('./app')
const config = require("./config")
app.listen(config.http.port)
console.info(`Service listens on port ${config.http.port}`)


4. Create a config file and set the user pool id and client id
    


module.exports = {
    http: {
        port: 3000
    },
    cognito: {
        userPoolId: "ap-south-1_X8p5YNzu5",
        clientId: "2kvvjb3fvtbp2pv9lfr7p1umvn"
    }
}

4. Now we need to implement  three end points (Sign-up, confirm verification code, sign-in). Create AuthController.js

const AuthService  = require("./AuthService")
const objAuthService = new AuthService()
class AuthController {
    async signUp(reqres) {
        const userData = req.body
        try {
            const result = await objAuthService.signUp(userData)
            res.send(result)
        }
        catch (err) {
            res.send(err)
        }
    }

    async confirmUser(reqres) {
        const userName = req.body.username
        const verificationCode = req.body.verificationcode
        try {
            const result = await objAuthService.confirmUser(userNameverificationCode)
            res.send(result)
        }
        catch (err) {
            console.log(err)
            res.status(500).send(err)
        }
    }
    async signIn(reqres) {
        const userName = req.body.username
        const password = req.body.password
        try {
            const result = await objAuthService.signIn(userNamepassword)
            res.send(result)
        }
        catch (err) {
            res.status(500).send(err)
        }
    }
}

module.exports = AuthController

module.exports = AuthController;

5. Create AuthService.js 

const config = require('./config')
const AmazonCognitoIdentity = require('amazon-cognito-identity-js')
global.fetch = require('node-fetch')

class AuthService {
    constructor() {
        const poolData = {
            UserPoolId: config.cognito.userPoolId,
            ClientId: config.cognito.clientId
        }
        //const poolRegion = config.cognito.region
        this.userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData)
    }

    async signUp(userData) {
        return await new Promise((resolvereject=> {
            const name = userData.name
            const email = userData.email
            const password = userData.password
            const username = userData.username

            const userAttributes = []
            userAttributes.push(new AmazonCognitoIdentity.CognitoUserAttribute(
                {
                    Name: "email",
                    Value: email
                }
            ))
            userAttributes.push(new AmazonCognitoIdentity.CognitoUserAttribute(
                {
                    Name: "name",
                    Value: name
                }
            ))
            this.userPool.signUp(usernamepassworduserAttributesnull
                                        (errresult=> {
                if (err) {
                    reject(err)
                } else {
                    var cognitoUser = result.user
                    resolve(cognitoUser)
                }
            })
        })
    }

    async confirmUser(usernameverificationCode) {
        return await new Promise((resolvereject=> {
            const userData = {
                Username: username,
                Pool: this.userPool
            }
            const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData)
            cognitoUser.confirmRegistration(verificationCodetrue
                                            (errresult=> {
                if (err) {
                    reject(err)
                }
                else {
                    resolve(result)
                }
            })
        })
    }

    async signIn(usernamepassword) {
        return await new Promise((resolvereject=> {
            const authenticationDetails = new AmazonCognitoIdentity
                                            .AuthenticationDetails({ UserName: username
                                            Password: password })
            const userData = {
                Username: username,
                Pool: this.userPool
            }
            const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData)
            cognitoUser.authenticateUser(authenticationDetails, {
                onSuccess: (result=> {
                    const accessToken = result.getIdToken().getJwtToken()
                    resolve(accessToken)
                },
                onFailure: (err=> {
                    reject(err)
                }
            })
        })
    }
}

module.exports = AuthService



6. Create route.js

const express = require("express")
const AuthController  = require("./AuthController")
const router = express.Router()
const objAuthController = new AuthController()
router.post("/signup"objAuthController.signUp)
router.post("/confirm"objAuthController.confirmUser)
router.post("/signin"objAuthController.signIn)
module.exports = router;


7. Once the above steps are done, go to the command prompt and run the node server.

8.  Hit the following APIs

1. Sign up (It will create the user in Cognito pool)

POST /api/signup

{
    "name":"",
    "email":"",
    "password":",
    "username":""
}
This request will create the user in Cognito pool. Cognito will send an email with verification code. We need to verify the verification code with the following API.

2. Confirm user (Verify the code)

POST /api/confirm

{
    "username":"",
    "verificationcode":"" //you must have received the verification code in your email id
}

3. Sign in

POST /api/signin

{
    "username":"",
    "password":""
}

Cognito will return the id token which will be used to authenticate your APIs.


Configure API Gateway

1. Click on the `Authorizers` menu from your API and `Create New Authorizer`


2. Input the name, select type as 'cognito', select the user pool from the drop down and set the token source. See the image below:



3. Set up the Authorizer in your API resource - Go to Resources - > Select your API -> Method Request -> select Cognito Authorizer from the drop down (need to refresh the page, sometimes it wont be available in the dropdown box)



4. Deploy the API

5. Call the API from post man




Hope this helps. Thank you !!

Wednesday, July 20, 2016

PHP - google analytics ecommerce tracking from server side

Create a new Analytics account

Open google.com/analytics, click the Sign in to Analytics button [top right (Google Analytics) ], and follow the instructions.

Enable eCommerce in your reports

Sign in to your Analytics account.
Navigate to the desired account, property and view.
In the VIEW column, select Ecommerce Settings.
Click the Enable Ecommerce toggle ON.
Optional: Click the Enable Related Products toggle ON.
Click Next step.
Click Submit.

Google analytics integration with PHP (server side)

Download the Server Side Google Analytics class from github. Click here to download.Extract the files in your project folder. In case of laravel, copy the files in the project public folder

 Usage - Ecommerce tracking

<?php

 include(public_path().'/ss-ga.class.php'); [public path - Laravel public directory ]

     

$ssg_1 = new ssga( '<Your GA tracking id>','<Site name>' );

//$transaction_id, $affiliation, $total, $tax, $shipping, $city, $region, $country
$ssg_1 ->send_transaction("11111111", "Insurance", 500, 12, 0,"", "", "");

$ssg_2 = new ssga( '<Your GA tracking id>','<Site name>' );
//$transaction_id, $sku, $product_name, $variation, $unit_price, $quantity
$ssg_2->send_item("11111111", "222222", "TEST-INSURANCE", "", 50, 4);

?>

Monday, July 18, 2016

Laravel UUID generation

We can use the following package to generate UUID from laravel. 

ramsey/uuid

You can download it from UUID . 

Installation

Run the following command from the command prompt

> composer require ramsey/uuid

Usage

<?php
    
   use Ramsey\Uuid\Uuid;
  
   class Uuid{
    
            public function generateUuid(){
                    $uuid4 = Uuid::uuid4();
return $uuid4->toString();
            }
   }
?>

Friday, July 15, 2016

Laravel - Swagger documentation

Swagger is a powerful tool to generate interactive documentation for your RESTful API using phpdoc annotations

Installation

  • Add the following dependency in the composer.json file
"require": {
        "zircote/swagger-php": "*",
}
  • Run the following command to install the package
composer update
  • Add annotations to your php file
PushController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;

use App\Jobs\SendReminderEmail;
use App\Jobs\SendWelcomeEmail;
use App\Utilities\AuditTrail;


class PushController extends Controller
{

public function __construct(AuditTrail $AuditTrail){
$this->objAuditTrail = $AuditTrail;
}
/**
* @SWG\Info(title="Halo", version="1.0")
* @SWG\Get(
* path="/push",
* tags={"Queue"},
* @SWG\Response(response="200", description="Push job to the queue")
* )
*/
public function push(){

$customerName = 'Jefferey Turner';
$job = (new SendWelcomeEmail($customerName));

$this->dispatch($job);
$job->delete();

$customerName = 'Fay Padberg';
$job = (new SendReminderEmail($customerName));

$this->dispatch($job);
$job->delete();

$this->objAuditTrail->logData('Job push controller','Job pushed onto queue');
return 'Email pushed onto queue';

}
}

  • Generate the swagger documentation
    • From php 
$swagger = \Swagger\scan(<Project Path>);
header('Content-Type: application/json');
echo $swagger;
  • From the command line.Go to vendor/bin folder and issue the following command

    swagger <Project directory> --output <Output directory>
Eg: swagger ..\..\\app --output ..\..\public\api\
Swagger will create the json file (swagger.json) and store it in public/api folder. 
swagger.json
{
"swagger": "2.0",
"info": {

"title": "Halo",
"version": "1.0"

},
"paths": {

"/push": {
"get": {
"tags": [
"Queue"
],
"responses": {

"200": {
"description": "Push job to the queue"
}
}
}
}
},
"definitions": {}

}

View the API resources withe the help of swagger UI

Swagger UI is a dependency-free collection of HTML, Javascript, and CSS assets that dynamically generate documentation from a Swagger.json file.You can use the swagger-ui code AS-IS.This documentation we are describing the integration with Laravel 5.2
  • Download the Swagger UI from https://github.com/swagger-api/swagger-ui and extract the files.
  • Create the folder (swagger) folder in resources/view folder
  • Copy the files from dist folder and put it in swagger folder.
  • Copy the js,css,images and put it in public folder
  • Change the index.html file to index.blade.php
  • Make the following changes in index.blade.php.(Use blade template directives)

<link href="{{asset('css/typography.css')}}" media='screen' rel='stylesheet' type='text/css'/>
<link href="{{asset('css/reset.css')}}" media='screen' rel='stylesheet' type='text/css'/>
<link href="{{asset('css/screen.css')}}" media='screen' rel='stylesheet' type='text/css'/>
<link href="{{asset('css/reset.css')}}" media='print' rel='stylesheet' type='text/css'/>
<link href="{{asset('css/print.css')}}" media='print' rel='stylesheet' type='text/css'/>
<script src="{{asset('lib/jquery-1.8.0.min.js')}}" type='text/javascript'></script>

<script src="{{asset('lib/jquery.slideto.min.js')}}" type='text/javascript'></script>
<script src="{{asset('lib/jquery.wiggle.min.js')}}" type='text/javascript'></script>
<script src="{{asset('lib/jquery.ba-bbq.min.js')}}" type='text/javascript'></script>
<script src="{{asset('lib/handlebars-2.0.0.js')}}" type='text/javascript'></script>
<script src="{{asset('lib/js-yaml.min.js')}}" type='text/javascript'></script>
<script src="{{asset('lib/lodash.min.js')}}" type='text/javascript'></script>
<script src="{{asset('lib/backbone-min.js')}}" type='text/javascript'></script>

<script src="{{asset('js/swagger-ui.js')}}" type='text/javascript'></script>
<script src="{{asset('lib/highlight.7.3.pack.js')}}" type='text/javascript'></script>
<script src="{{asset('lib/jsoneditor.min.js')}}" type='text/javascript'></script>
<script src="{{asset('lib/marked.js')}}" type='text/javascript'></script>
<script src="{{asset('lib/swagger-oauth.js')}}" type='text/javascript'></script>
  • Change the url link in the index.blade.php
  • Create the controller to handle the request.
SwaggerController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;

class SwaggerController extends Controller
{

public function index(){
return view('swagger.index');
}
}
  • Update the routes.php
Route::get('swagger', 'SwaggerController@index');
  • Link to view the API documentation
http://<server>/swagger [http://api.int.halo.devthing.link/swagger]