Using Open Source AWS Amplify JS with Cognito to Secure Angular Apps

Written by gilad-david-maayan | Published 2022/05/06
Tech Story Tags: devsecops | aws | angular | amazon-web-services | security | cybersecurity | aws-services | npm

TLDRThis article shows how to set up the Cognito UserPools JWT authentication flow and how it will integrate with an Angular Web Application. The steps are similar with Implicit flow also, except the session won’t get refreshed after id_token expires. NPM security audit is capable of finding and fixing known vulnerabilities in dependencies which could potentially cause data loss, service outages, unauthorized access to sensitive information. You need to install an additional package aws-amplify-angular in your Angular app.via the TL;DR App

If you are planning to use the open-source AWS Amplify JS library in your Web Application to manage authentication with AWS Cognito UserPools Hosted UI, it is not fully clear how Amplify handles the authentication.

This article shows you how to set up the Cognito UserPools JWT authentication flow and how it will integrate with an Angular Web Application. I will be considering AWS Cognito UserPools Code Grant flow. However, the steps are similar with Implicit flow also, except the session won’t get refreshed after id_token expires.

Background: DevSecOps in Angular Applications

Most development organizations are waking up to realize that security cannot be an afterthought in their development lifecycle. As part of the shift towards DevSecOps, dev teams are building security as an automated step in their delivery pipeline.

When using AWS Amplify JS and similar open-source libraries for security testing, it is extremely important to maintain these libraries up to date, keeping track of the ongoing release changes since these open source components lay the foundation for the security of the Web Application.

NPM now has a security auditing feature to perform an assessment of package dependencies for known security vulnerabilities. Since most of these libraries contain multiple third-party NPM dependencies, it's important to carry out audits for all of these. NPM security audit is capable of finding and fixing known vulnerabilities in dependencies which could potentially cause data loss, service outages, unauthorized access to sensitive information. For the ones NPM is not able to fix automatically, the audit report indicates them for manual developer intervention.

Step 1: Install AWS Amplify in your Angular App

If you are developing an Angular app, you can install an additional package aws-amplify-angular. This package contains an Angular module with a provider and components:

$ npm install aws-amplify --save

$ npm install aws-amplify-angular --save

Step 2: Initialize Amplify Configurations

You can initialize the Amplify Cognito UserPools and Cognito Federated Identities configuration in main.ts file.

import Amplify from 'aws-amplify';

Amplify.configure({

 Auth: {

   // other configurations...

   identityPoolId: 'us-east-1:xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',

   // REQUIRED - Amazon Cognito Identity Pool ID

   region: 'us-east-1', // REQUIRED - Amazon Cognito Region

   userPoolId: 'us-east-1_XYZPQRS',

   // OPTIONAL - Amazon Cognito User Pool ID

   userPoolWebClientId: 'xxxxxxxxxxxxxxxxxxxxxxxxx',

   // OPTIONAL - Amazon Cognito Web Client ID

   oauth: {

     domain: 'my-app-development.auth.us-east-1.amazoncognito.com',

     // Authorized scopes

     scope: ['email', 'openid'],

     // Callback URL

     redirectSignIn: 'http://localhost:4200/authenticated',

     // Sign out URL

     redirectSignOut: 'http://localhost:4200/logout',

     // 'code' for Authorization code grant,

     // 'token' for Implicit grant

     responseType: 'code',

     // optional, for Cognito hosted ui specified options

     options: {

     // Indicates if the data collection is enabled to support Cognito advanced security features.

     // By default, this flag is set to true.

       AdvancedSecurityDataCollectionFlag: true

     }

   },

 },

});

Step 3: Initialize the Amplify Angular Resources

You need to initialize the Angular Module and Service inside your app.module.ts file.

import { AmplifyAngularModule, AmplifyService } from 'aws-amplify-angular';

@NgModule({

declarations: [

   ...

 ],

 imports: [

   BrowserModule,

   FormsModule,

   HttpModule,

   AmplifyAngularModule,

   ...

 ],

 providers: [

   AmplifyService,

   ...

 ]

})

Step 4: Create Required Routes

You need to create multiple routes to implement the Cognito UserPools Hosted UI authentication process.

  1. Authenticated Route (e.g /authenticated) — This route where the HostedUI will redirect towards. Note: Make sure this route is not having any AuthGuard (More in next section) configured
  2. Home Route (e.g /home or /) — This route is where the user is redirected once the Cognito Token Handshake Completes.
import { AuthGuardService as AuthGuard } from '../service/auth-guard.service';

const routes: Routes = [

 { path: 'authenticated', component: AuthenticatedComponent},

 { path: '', component: HomeComponent, canActivate: [AuthGuard]},

];

Once Cognito Hosted UI redirects to the application, if you set up the above steps properly, Amplify internally communicates with Cognito UserPool token endpoint and retrieves the Tokens depending on the Grant Type and Scopes.


Step 5: Create an AuthGuard to Protect Authenticated Routes

Create an Angular Service implementing CanActivate method in Angular Router.

import { Injectable } from '@angular/core';

import { Router, CanActivate, CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

import { AmplifyService }  from 'aws-amplify-angular';

@Injectable()

export class AuthGuardService implements CanActivate {

 constructor() {}

 redirectToLogin() {

   const config = Amplify.Auth.configure();

   const {

     domain,

     redirectSignIn,

     redirectSignOut,

     responseType

   } = config.oauth;

   const clientId = config.userPoolWebClientId;

   const url = 'https://' + domain + '/login?redirect_uri=' + redirectSignIn + '&response_type=' + responseType + '&client_id=' + clientId;

   // Launch hosted UI

   window.location.assign(url);

 }

 /* canActivate(): boolean {

 canActivate(

   next: ActivatedRouteSnapshot,

   state: RouterStateSnapshot): Observable<boolean> {

   return this.amplifyService.auth().currentSession().catch((error)   =>  {

      this.redirectToLogin();

   });

 }

}

Step 6: Wait for Cognito Token Handshake at Authenticated Route

Once the user is redirected to the authenticated route from Cognito UserPools Hosted UI, it is required to wait till Amplify handles the Cognito Token Handshake.

Unfortunately for this, the Amplify Service State Change Observable is not properly working. Therefore it required using the Hub Middleware to Listen to the Event.

import { Component, OnInit, TemplateRef } from '@angular/core';

import { Router, RouterModule, Routes } from '@angular/router';

import { Hub } from 'aws-amplify'

@Component({

 selector: 'app-authenticated',

 templateUrl: './authenticated.component.html',

 styleUrls: ['./authenticated.component.scss']

})

export class AuthenticatedComponent implements OnInit {

 constructor(  private router: Router) {

   Hub.listen('auth', this, 'AuthCodeListner');

 }

 onHubCapsule(capsule) {

   const { channel, payload } = capsule;

   if (channel === 'auth' && payload.event === 'signIn' ) {

     this.router.navigate([''])

   }

 }

 ngOnInit() {}

}

Step 7: Use an HTTP Interceptor to send the Cognito ID Token for API Requests

After authenticating properly, Amplify will store the Tokens in LocalStorage and you can access it using the amplifyService.auth().currentSession() method.

Accessing the Tokens

this.amplifyService.auth().currentSession().catch((error) =>  {

 this.redirectToLogin();

 });

}

Writing the Auth Interceptor

For more details on writing, Auth Interceptor refer Angular Authentication: Using the Http Client and Http Interceptors.

Step 8: Runn NPM Audit

First of all upgrade NPM to the latest and then run the audit command,

$ npm install npm@latest -g

$ npm audit

Npm audit automatically runs while installing packages with npm install. However, its useful to audit locally installed packages once in a while to identify recently found vulnerabilities.

After running the audit command or if you see vulnerabilities after installing a new package in the npm audit report, run the npm audit fix subcommand to automatically install the compatible updates to fix identified vulnerable dependencies.

$ npm audit fix

At the time of this writing, when running npm audit for the AWS Amplify Angular Package, dependencies, npm audit found 30 vulnerabilities (7 low, 17 moderate, 6 high) in 4358 scanned packages where running `npm audit fix` is able to fix 29 of them.



Written by gilad-david-maayan | Technology Writer and Startup Advisor
Published by HackerNoon on 2022/05/06