AWS Cognito

Amazon Cognito is a relatively simple and secure service offering sign-up, sign-in and access control to web, mobile apps in a simple and easy way. Supports sign-in using social identity providers such as Google, Facebook, Apple, Amazon.

User pool

Cognito user pool is a user directory that provides a sign-in/sign-up option.

Identity pool

Enables access grant to other AWS services.

Use case

Assume following requirements:

Diagram

Following setup consists of multiple users interacting with the sistem using different client types like web interface, mobile, or both. In all cases users can login and will access system features only while being logged in the system.

AWS Cognito use case

Notes

User flow starts at login page, which leads to user authentication via Cognito and depending on configuration can result in session token generation. This token will be used during client (web/mobile) and server interaction, API Gateway will use Cognito service for token verification, so that only valid active sessions can access the system.

SAM Template

Following configuration contains Cognito user pool and client, few API Gateway V1 rest endpoints representing the system features. API Gateway Authorizer is responsible for access control by verifying JWT token via Cognito service.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  AWS Cognito User pool and client configuration example  

Parameters:
  Env:
    Type: String
    Description: Logical sandbox prefix
    Default: val
    AllowedValues:
      - val
      - test
  App:
    Type: String
    Description: Application name
    Default: cognito-example

Globals:
  Function:
    Timeout: 3
    MemorySize: 128

Resources:
  UserPool:
    Type: AWS::Cognito::UserPool
    Properties:
      UserPoolName: !Sub ${Env}-user-pool
      Schema:
        - Name: email
          Required: true
          Mutable: true
      AutoVerifiedAttributes: ['email']
      AdminCreateUserConfig:
        AllowAdminCreateUserOnly: true
      Policies:
        PasswordPolicy:
          MinimumLength: 6
          RequireLowercase: true
          RequireUppercase: true
          RequireNumbers: true
          RequireSymbols: true
          TemporaryPasswordValidityDays: 15
      UsernameAttributes:
        - email
      UsernameConfiguration:
        CaseSensitive: false
      UserPoolTags:
        Key: App
        Value: !Ref App

  UserClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
      ClientName: !Sub ${Env}-user-pool-client
      UserPoolId: !Ref UserPool
      AccessTokenValidity: 1
      IdTokenValidity: 1
      ExplicitAuthFlows:
        - ALLOW_USER_SRP_AUTH
        - ALLOW_REFRESH_TOKEN_AUTH
      GenerateSecret: false

  RestApi:
    Type: AWS::Serverless::Api
    Properties:
      Description: Rest API for foo bar
      StageName: !Ref Env
      Auth:
        Authorizers:
          CognitoAuth:
            UserPoolArn: !GetAtt UserPool.Arn
            AuthType: COGNITO_USER_POOLS
        DefaultAuthorizer: CognitoAuth
      Tags:
        App: !Ref App
        Env: !Ref Env

  FooFunction:
    Type: AWS::Serverless::Function
    Properties:
      Description: Foo feature
      FunctionName: !Sub ${Env}-${App}-foo
      CodeUri: app/foo/
      Handler: handler
      Runtime: go1.x
      Tracing: Active
      Events:
        Foo:
          Type: Api
          Properties:
            Method: get
            Path: /foo
            RestApiId: !Ref RestApi
      Tags:
        App: !Ref App
        Env: !Ref Env

  BarFunction:
    Type: AWS::Serverless::Function
    Properties:
      Description: Bar feature
      FunctionName: !Sub ${Env}-${App}-bar
      CodeUri: app/foo/
      Handler: handler
      Runtime: go1.x
      Tracing: Active
      Tags:
        App: !Ref App
        Env: !Ref Env

Outputs:
  PoolId:
    Description: User pool identifier
    Value: !Ref UserPool
  ClientId:
    Description: User pool client identifier
    Value: !Ref UserClient

Notes

Auth flow SRP (Safe Remote Password) prevents password exposure for Man in the middle scenario. SRP

Authentication via Amplify

Nice thing about amplify is that it’s highly modular and it’s possible to take only Auth related packages like this one @aws-amplify/auth. Current pool configuration allows admin only user registration AllowAdminCreateUserOnly: true (either via web cli or programmatically via SDK).

User Confirmation & SignIn

Amplify setup requires user pool identifier and client indentifier.

import { Auth } from "@aws-amplify/auth";

Auth.configure({
  Auth: {
    userPoolId: 'XXX',
    userPoolWebClientId: 'XXX',
    region: 'eu-west-1',
  }
})

Next step is sign in attempt using temporary password (that was sent by cognito to specified user email) and changing it to new one, this will result in valid cofgirmed user.

const user = 'foo@bar.com'
const pass = '7uJ,q9'
const newPass = 'secret'
const cognitoUser = await Auth.signIn(user, pass)
  .then(user => {
    if (user.challengeName === 'NEW_PASSWORD_REQUIRED') {
      Auth.completeNewPassword(
        user,
        newPass,
      ).then(user => {
        return user
      })
    }
    return user
  })

Successful sign in & confirmation will result in cognito user details provided to the client, and cognito access token requird for lambda api gateway authorizers.

All related code sits in Git