Front-End Web & Mobile

Amplify Framework adds support for AWS Lambda Triggers in Auth and Storage categories

The Amplify Framework is an open source project for building cloud-enabled mobile and web applications. Today, we’re happy to announce that you can set up AWS Lambda triggers directly from the Amplify CLI.

Using Lambda triggers, you can call event-based Lambda functions for authentication, database actions, and storage operations from other AWS services like Amazon Simple Storage Service (Amazon S3), Amazon Cognito, and Amazon DynamoDB. Now, the Amplify CLI allows you to enable and configure these triggers. The CLI further simplifies the process by providing you with trigger templates that you can customize to suit your use case.

The Lambda trigger capabilities for Auth category include:

  1. Add Google reCaptcha Challenge: This enables you to add Google’s Captcha implementation to your mobile or web app.
  2. Email verification link with redirect: This trigger enables you to define an email message that can be used for an account verification flow.
  3. Add user to a Amazon Cognito User Pools group: This enables you to add a user to an Amazon Cognito User Pools group upon account registration.
  4. Email domain filtering: This enables you to define email domains that would like to allow or block during sign up.
  5. Custom Auth Challenge Flow: This enables you add custom auth flow to your mobile and web application by providing a basic skeleton which you can edit to achieve custom authentication in your application.

The Lambda trigger for Storage category can be added when creating or updating the storage resource using the Amplify CLI.

Auth Triggers for Authentication with Amazon Cognito

The Lambda triggers for Auth enable you to build custom authentication flows in your mobile and web application.
These triggers can be associated with Cognito User Pool operations such as sign-up, account confirmation, and sign-in. The Amplify CLI provides the template triggers for capabilities listed above which can be customized to suit your use case.

A custom authentication flow using Amazon Cognito User Pools typically comprises of 3 steps:

  1. Define Auth Challenge: Determines the next challenge in the custom auth flow.
  2. Create Auth Challenge: Creates a challenge in the custom auth flow.
  3. Verify Auth Challenge: : Determines if a response is correct in a custom auth flow.

When you add auth to your Amplify project, the CLI asks you if you want to add capabilities for custom authentication. It generates the trigger templates for each step in your custom auth flow depending on the capability chosen. The generated templates can be edited as per your requirements. Once complete, you push your project using ‘amplify push’ command. For more information on these capabilities, refer to our documentation.

Here is an example of how you add one of these custom auth capabilities in your application.

Adding a new user to group in Amazon Cognito

Using Amazon Cognito User Pools, you can create and manage groups, add users to groups, and remove users from groups. With groups, you can create collections of users to manage their permissions or to represent different user types.

You can now use the Amplify CLI to add a Lambda trigger to add a user to a group after they have successfully signed up. Here’s how it works.

Creating the authentication service and configuring the Lambda Trigger

From the CLI, create a new Amplify project with the following command:

amplify init

Next, add authentication with the following command:

amplify add auth

The command line interface then walks you through the following steps for adding authentication:

? Do you want to use the default authentication and security configuration? Default configuration
? How do you want users to be able to sign in? Username
? Do you want to configure advanced settings? Yes, I want to make some additional changes.
? What attributes are required for signing up? Email
? Do you want to enable any of the following capabilities?
 ◯ Add Google reCaptcha Challenge
 ◯ Email Verification Link with Redirect
❯◉ Add User to Group
 ◯ Email Domain Filtering (blacklist)
 ◯ Email Domain Filtering (whitelist)
 ◯ Custom Auth Challenge Flow (basic scaffolding - not for production)
 ? Enter the name of the group to which users will be added. STUDENTS
 ? Do you want to edit the local PostConfirmation lambda function now? No
 ? Do you want to edit your add-to-group function now? Yes

The interface should then open the appropriate Lambda function template, which you can edit in your text editor. The code for the function will be located at amplify/backend/function/<functionname>/src/add-to-group.js.

The Lambda function that you write for this example adds new users to a group called STUDENTS when they have an .edu email address. This function triggers after the signup successfully completes.

Update the Lambda function add-to-group.js with the following code:

const aws = require('aws-sdk');

exports.handler = (event, context, callback) => {
  const cognitoidentityserviceprovider = new aws.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });

  const email = event.request.userAttributes.email.split('.')
  const domain = email[email.length - 1]

  if (domain === 'edu') {
    const params = {
      GroupName: 'STUDENTS',
      UserPoolId: event.userPoolId,
      Username: event.userName,
    }
  
    cognitoidentityserviceprovider.adminAddUserToGroup(params, (err) => {
      if (err) { callback(err) }
      callback(null, event);
    })
  } else {
    callback(null, event)
  }
}

To deploy the authentication service and the Lambda function, run the following command:

amplify push

Now, when a user signs up with an .edu email address, they are automatically placed in the STUDENTS group.

Integrating with a client application

Now that you have the authentication service up and running, let’s integrate with a React application that signs the user in and recognizes that the user is part of the STUDENTS group.

First, install the Amplify and Amplify React dependencies:

npm install aws-amplify aws-amplify-react

Next, open src/index.js and add the following code to configure the app to recognize the Amplify project configuration:

import Amplify from 'aws-amplify'
import config from './aws-exports'
Amplify.configure(config)

Next, update src/App.js. The code recognizes the user groups of a user after they have signed in and displays a welcome message if the user is in the STUDENTS group.

// src/App.js
import React, { useEffect, useState } from 'react'
import logo from './logo.svg'
import './App.css'
import { withAuthenticator } from 'aws-amplify-react'
import { Auth } from 'aws-amplify'

function App() {
  const [isStudent, updateStudentInfo] = useState(false)
  useEffect(() => {
    /* Get the AWS credentials for the current user from Identity Pools.  */
    Auth.currentSession()
      .then(cognitoUser => {
        const { idToken: { payload }} = cognitoUser
        /* Loop through the groups that the user is a member of */
        /* Set isStudent to true if the user is part of the STUDENTS group */
        payload['cognito:groups'] && payload['cognito:groups'].forEach(group => {
          if (group === 'STUDENTS') updateStudentInfo(true)
        })
      })
      .catch(err => console.log(err));
  }, [])
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        { isStudent && <h1>Welcome, Student!</h1> }
      </header>
    </div>
  );
}

export default withAuthenticator(App, { includeGreetings: true })

Now, if the user is part of the STUDENTS group, they will get a specialized greeting.

Storage Triggers for Amazon S3 and Amazon DynamoDB

With this release, we’ve also enabled the ability to setup Lambda triggers for Amazon S3 and Amazon DynamoDB. This means you can execute a Lambda function on events such as create, update, read, and write. When adding or configuring storage from the Amplify CLI, you now have the option to add and configure a storage trigger.

Resizing an image with AWS Lambda and Amazon S3

Let’s take a look at how to use one of the new triggers to resize an image into a thumbnail after it has been uploaded to an S3 bucket.

From the CLI, create a new Amplify project with the following command:

amplify init

Next, add storage with the following command:

amplify add storage

The interface then walks you through the add storage setup.

? Please select from one of the below mentioned services: Content (Images, audio, video, etc.)
? You need to add auth (Amazon Cognito) to your project in order to add storage for user files. Do you want to add auth now? Yes
? Do you want to use the default authentication and security configuration? Default configuration
? How do you want users to be able to sign in? Username
? Do you want to configure advanced settings? No, I am done.
? Please provide a friendly name for your resource that will be used to label this category in the project: MyS3Example
? Please provide bucket name: <YOUR_UNIQUE_BUCKET_NAME>
? Who should have access: Auth and guest users
? What kind of access do you want for Authenticated users? create/update, read, delete
? What kind of access do you want for Guest users? read
? Do you want to add a Lambda Trigger for your S3 Bucket? Y
? Select from the following options: Create a new function

The CLI then generates a code template for the new Lambda function, which you can modify as needed. It will be located at amplify/backend/function/<functionname>/src/index.js.

Replace the code in index.js with the following code:

const gm = require('gm').subClass({ imageMagick: true })
const aws = require('aws-sdk')
const s3 = new aws.S3()

const WIDTH = 100
const HEIGHT = 100

exports.handler = (event, context, callback) => {
  const BUCKET = event.Records[0].s3.bucket.name

  /* Get the image data we will use from the first record in the event object */
  const KEY = event.Records[0].s3.object.key
  const PARTS = KEY.split('/')

  /* Check to see if the base folder is already set to thumbnails, if it is we return so we do not have a recursive call. */
  const BASE_FOLDER = PARTS[0]
  if (BASE_FOLDER === 'thumbnails') return

  /* Stores the main file name in a variable */
  let FILE = PARTS[PARTS.length - 1]

  s3.getObject({ Bucket: BUCKET, Key: KEY }).promise()
    .then(image => {
      gm(image.Body)
        .resize(WIDTH, HEIGHT)
        .setFormat('jpeg')
        .toBuffer(function (err, buffer) {
          if (err) {
            console.log('error storing and resizing image: ', err)
            callback(err)
          }
          else {
            s3.putObject({ Bucket: BUCKET, Body: buffer, Key: `thumbnails/thumbnail-${FILE}` }).promise()
            .then(() => { callback(null) })
            .catch(err => { callback(err) })
          }
        })
    })
    .catch(err => {
      console.log('error resizing image: ', err)
      callback(err)
    })
}

You can trace the execution of the code above in Amazon CloudWatch Logs on an event such as upload to the S3 bucket.

Next, install the GraphicsMagick library in the Lambda function directory. This ensures that you have the needed dependencies to execute the Lambda function.

cd amplify/backend/function/<functionname>/src

npm install gm

cd ../../../../../

To deploy the services, run the following command:

amplify push

Next, visit the S3 console, open your bucket and upload an image. Once the upload has completed, a folder named thumbnails will be created and the resized image will be stored there.

To learn more about creating storage triggers, check out the documentation.

Feedback

We hope you like these new features! As always, let us know how we’re doing, and submit any requests in the Amplify Framework GitHub Repository. You can read more about AWS Amplify on the AWS Amplify website.