Danh mụcThẻBài viết

admin

I'm a Full-stack developer

Thẻ

Linked List
Data Structure
Chat GPT
Design Pattern
Microservices
API
AWS CDK
ReactJS
AWS Lightsail
Flutter Mobile
Create Cognito User Pool with AWS CDK
Ngày đăng: 09/06/2023

In the previous post, I showed you how to create a simple S3 bucket. Next, in this article, I will guide you to create a Cognito User Pool.


What is Amazon Web Service?


Amazon Cognito is an identity platform for web and mobile apps. It’s a user directory, an authentication server, and an authorization service for OAuth 2.0 access tokens and AWS credentials. With Amazon Cognito, you can authenticate and authorize users from the built-in user directory, from your enterprise directory, and from consumer identity providers like Google and Facebook.

Amazon Cognito has two main components:




  • User Pool: user directories that provide registration and login options for your web and mobile app users.
  • Identity Pools (Federated Identities): provide AWS credentials to give your users access to other AWS services.


How to set it up?


As step 11 of the previous post. We add a new stack below the Api Stack with the name auth stack like the code below:

new AuthStack(this, `${id}-auth-stack`, {
  stackName: `${id}-auth-stack`,
});


Step 12: Enter the below code to lib/stacks/auth-stack.ts

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

import { CognitoResource } from '../resources';

export class AuthStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    this.createCognito(this, id);
  }

  private createCognito(stack: Stack, id: string) {
    new CognitoResource(stack, `${id}-cognito`, {})
      .setupUserPool()
      .setupAppClient()
      .setupDomain()
      .build();
  }
}


Step 12: Enter the below code to lib/resources/cognito/index.ts

import { Duration, Stack, StackProps, aws_cognito as cognito, aws_iam as iam } from 'aws-cdk-lib';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import path from 'path';

import { BaseResource } from '../base';

export class CognitoResource extends BaseResource {
  private _cognitoUserPool: cognito.UserPool;

  constructor(scope: Stack, id: string, props: StackProps) {
    super(scope, id, props);
  }

  setupUserPool(name?: string) {
    this._cognitoUserPool = new cognito.UserPool(
      this._scope,
      `${this._scopeId}-${name ?? 'user-pool'}`,
      {
        userPoolName: `${this._scopeId}-${name ?? 'user-pool'}`,
        passwordPolicy: {
          minLength: 8,
          tempPasswordValidity: Duration.days(7),
          requireDigits: false,
          requireLowercase: false,
          requireSymbols: false,
          requireUppercase: false,
        },
        mfa: cognito.Mfa.REQUIRED,
        mfaSecondFactor: {
          otp: true,
          sms: true,
        },
        accountRecovery: cognito.AccountRecovery.PHONE_AND_EMAIL,
        autoVerify: {
          phone: false,
          email: true,
        },
        selfSignUpEnabled: true,
        standardAttributes: {
          phoneNumber: {
            required: true,
          },
        },
        signInAliases: {
          preferredUsername: true,
          email: true,
          phone: true,
          username: true,
        },
        signInCaseSensitive: false,
        snsRegion: process.env.AWS_REGION,
        lambdaTriggers: {
          createAuthChallenge: this.createNodeJsFn(
            'createAuthChallengeFn',
            'create-auth-challenge',
            new iam.Policy(this._scope, `${this._scopeId}-create-auth-challenge-sns-policy`, {
              statements: [
                new iam.PolicyStatement({
                  effect: iam.Effect.ALLOW,
                  actions: ['SNS:Publish'],
                  resources: ['*'],
                }),
              ],
            }),
          ),
          defineAuthChallenge: this.createNodeJsFn(
            'defineAuthChallengeFn',
            'define-auth-challenge',
          ),
          preSignUp: this.createNodeJsFn('preSignUpFn', 'pre-sign-up'),
          verifyAuthChallengeResponse: this.createNodeJsFn(
            'verifyAuthChallengeResponseFn',
            'verify-auth-challenge-response',
          ),
        },
      },
    );

    return this;
  }

  setupAppClient() {
    this._cognitoUserPool.addClient(`${this._scopeId}-user-pool-app-client`, {
      userPoolClientName: `${this._scopeId}-user-pool-app-client`,
      authFlows: {
        custom: true,
        userPassword: true,
        userSrp: true,
        adminUserPassword: false,
      },
      refreshTokenValidity: Duration.days(
        parseInt(process.env.REFRESH_TOKEN_DURATION_DAYS || '365', 10),
      ),
      idTokenValidity: Duration.days(parseInt(process.env.ID_TOKEN_DURATION_DAYS || '1', 10)),
      accessTokenValidity: Duration.days(
        parseInt(process.env.ACCESS_TOKEN_DURATION_DAYS || '1', 10),
      ),
      enableTokenRevocation: true,
      preventUserExistenceErrors: true,
    });

    return this;
  }

  setupDomain() {
    if (!process.env.COGNITO_DOMAIN_PREFIX) {
      return this;
    }

    this._cognitoUserPool.addDomain(`${this._scope}-user-pool-domain`, {
      cognitoDomain: {
        domainPrefix: process.env.COGNITO_DOMAIN_PREFIX,
      },
    });

    return this;
  }

  build() {
    return this._cognitoUserPool;
  }

  private createNodeJsFn(name: string, id: string, role?: iam.Policy) {
    const fn = new NodejsFunction(this._scope, name, {
      functionName: `${this._scopeId}-${id}`,
      runtime: Runtime.NODEJS_14_X,
      entry: path.join(__dirname, `lambda-function/${id}/index.ts`),
    });

    if (role) fn.role?.attachInlinePolicy(role);

    return fn;
  }
}




Step 14: Create lambda functions


  • create-auth-challenge/index.ts
import { CreateAuthChallengeTriggerEvent } from 'aws-lambda';
import AWS from 'aws-sdk';

function sendSMS(phone: string, message: string) {
  const params: AWS.SNS.PublishInput = {
    Message: message,
    PhoneNumber: phone,
  };

  return new AWS.SNS({ apiVersion: '2010-03-31' }).publish(params).promise();
}

export const handler = async (event: CreateAuthChallengeTriggerEvent) => {
  try {
    const evtReq = event.request;
    const evtReqSession = evtReq.session;
    const phoneNumber = event.request.userAttributes.phone_number;
    const otp = this.generateOtp();

    if (!evtReqSession || evtReqSession.length === 0) {
      const message = `OTP to login to WebsiteX is ${otp}`;

      await sendSMS(phoneNumber, message);

      event.response.privateChallengeParameters = {
        answer: otp,
      };
      event.response.challengeMetadata = 'CUSTOM_CHALLENGE';
    }

    return event;
  } catch (error) {
    Promise.reject(error);
  }
};
  • define-auth-challenge/index.ts
import { DefineAuthChallengeTriggerEvent } from 'aws-lambda';

export const handler = async (event: DefineAuthChallengeTriggerEvent) => {
  const evtReq = event.request;
  const evtReqSession = evtReq.session;

  // User is not registered
  if (evtReq.userNotFound) {
    event.response.issueTokens = false;
    event.response.failAuthentication = true;

    throw new Error('User does not exist', {
      cause: evtReq,
    });
  }

  // wrong OTP even After 3 sessions
  if (evtReqSession.length >= 3 && evtReqSession.slice(-1)[0].challengeResult === false) {
    event.response.issueTokens = false;
    event.response.failAuthentication = true;

    throw new Error('Invalid OTP');
  }
  // Correct OTP!
  else if (evtReqSession.length > 0 && evtReqSession.slice(-1)[0].challengeResult === true) {
    event.response.issueTokens = true;
    event.response.failAuthentication = false;
  }
  // not yet received correct OTP
  else {
    event.response.issueTokens = false;
    event.response.failAuthentication = false;
    event.response.challengeName = 'CUSTOM_CHALLENGE';
  }

  return event;
};
  • pre-sign-up/index.ts
export const handler = (event, _, callback) => {
  // Confirm the user
  event.response.autoConfirmUser = true;

  // Set the email as verified if it is in the request
  if (event.request.userAttributes.hasOwnProperty('email')) {
    event.response.autoVerifyEmail = true;
  }

  // Set the phone number as verified if it is in the request
  if (event.request.userAttributes.hasOwnProperty('phone_number')) {
    event.response.autoVerifyPhone = true;
  }

  // Return to Amazon Cognito
  callback(null, event);
};
  • verify-auth-challenge-response/index.ts
import { VerifyAuthChallengeResponseTriggerEvent } from 'aws-lambda';

export const handler = async (event: VerifyAuthChallengeResponseTriggerEvent) => {
  if (event.request.privateChallengeParameters.answer === event.request.challengeAnswer) {
    event.response.answerCorrect = true;
  } else {
    event.response.answerCorrect = false;
  }

  return event;
};


Step 15: Build an AWS CDK application

yarn build


Step 16: Deploy the stack

cdk deploy --profile agapifa


Step 17: Verify on the AWS console


  • CloudFormation

  • Lambda functions

  • Cognito

Summary

In this tutorial, you learned how to install the AWS CDK, set up and initialize an AWS CDK project, assemble it into a CloudFormation template, and deploy to AWS Cloud. If you want to remove the newly created stack from your AWS account, run the following command.

cdk destroy --profile agapifa


Good luck with your installation!!!

--------------------------------

Reference documents:


  1. https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html
Đề xuất

TypeScript Design Pattern - Prototype
admin07/08/2023

TypeScript Design Pattern - Prototype
The prototype pattern is one of the Creational pattern groups. The responsibility is to create a new object through clone the existing object instead of using the new key. The new object is the same as the original object, and we can change its property does not impact the original object.
TypeScript Design Pattern - Builder
admin07/08/2023

TypeScript Design Pattern - Builder
TypeScript Design Pattern - Builder
Data structure: Singly Linked List
admin07/04/2024

Data structure: Singly Linked List
In this article, I will show you how to set up a singly linked list algorithm in Python.
Mới nhất

How to integrate ChatGPT-3.5 Turbo into Node.js
admin10/01/2024

How to integrate ChatGPT-3.5 Turbo into Node.js
Step-by-Step Guide to Incorporating ChatGPT-3.5 Turbo into Node.js for Basic ReactJS Applications
TypeScript Design Pattern - Bridge
admin08/08/2023

TypeScript Design Pattern - Bridge
Decouple an abstraction from its implementation so that the two can vary independently.
Part 2: The hooks are used popularly in React
admin18/06/2023

Part 2: The hooks are used popularly in React
As a newbie React developer, does not understand when is use stateless (functional) components or stateful components. React hook is a new feature from v16.8, the developer does not worry about react lifecycle, and it is difficult to learn for newbies.
Đinh Thành Công Blog

My website, where I write blogs on a variety of topics and where I have some experiments with new technologies.

hotlinelinkedinskypezalofacebook
DMCA.com Protection Status
Góp ý
Họ & Tên
Số điện thoại
Email
Nội dung
Tải ứng dụng
hotline

copyright © 2023 - AGAPIFA

Privacy
Term
About