AWS Mobile Blog

Streaming videos to millions of mobile app users via Amazon CloudFront CDN

by Dennis Hills | on | in AWS Mobile, CloudFront, Elastic Transcoder, HLS, Lambda, S3, Streaming Video | | Comments

About

Welcome to the second installment of our blog series (the first post is here) showing how to build some of the most popular end-to-end experiences used by mobile apps.

This post is about HTTP Live Streaming (HLS) video content via Amazon CloudFront to your mobile application. You’ll utilize AWS Mobile Hub to enable the App Content – Global CDN feature that provisions an Amazon S3 bucket to store our streaming content and creates an Amazon CloudFront Web Distribution that will cache your video files using your Amazon S3 bucket as the source (origin) in edge locations around the world. This provides fast, low latency access to your content from mobile devices around the world. In addition, you’ll enable the Cloud Logic feature and use AWS Lambda to automatically generate transcoding jobs for videos uploaded to your S3 bucket.

You’ll walk away with a fully functioning mobile application streaming your own encoded HTTP Live Streaming (HLS) video on demand (VOD) directly from your Amazon CloudFront Web Distribution. This a complete end-to-end solution demonstrating a very common mobile cloud video distribution architecture.
There are five steps in this tutorial.

Step 1. Configure the backend AWS resources via AWS Mobile Hub. This consists of creating a project, enabling features App Content Delivery – Global CDN and Cloud Logic for transcoding videos via Amazon Elastic Transcoder.

Step 2. Staging video and provisioning Amazon S3, AWS Lambda, and Amazon Elastic Transcoder pipeline to be used for transcoding a basic HD video into HLS.

Step 3. Upload video to S3 and encoding HLS content

Step 4. Modify the sample app project code generated by Mobile Hub to explore and stream HLS video content from Amazon CloudFront using the transcoded media files stored in S3.

Step 5. Streaming HLS video content from your own app by following the AWS Mobile Hub integration guide customized for your project.

First, we’ll explain briefly what HLS is, how it works, and how to use Amazon Elastic Transcoder to transcode media into the HLS format. We’ll then briefly explain AWS features (Amazon S3, AWS Lambda, and Amazon CloudFront) that you’ll enable within a Mobile Hub project.

What is HLS?
HTTP Live Streaming (HLS) is a protocol that segments media files for optimization during streaming. HLS enables media players to play segments with the highest quality resolution that is supported by their network connection during playback.
In Step 3 you’ll transcode an HD action movie (filmed on a GoPro) into HLS segments, copy those media files to S3, and stream this content from our modified sample app in Step 4.

What is AWS Lambda?
The Cloud Logic feature is powered by AWS Lambda functions. These functions allow you to simply write code without worrying about managing frameworks and scaling of backend infrastructure. You can write your functions in JavaScript, Java, or Python. You’ll be using Lambda to create encoding jobs via Amazon Elastic Transcoder whenever a video is uploaded to your S3 bucket. Learn more about AWS Lambda

Transcoding HLS using Amazon Elastic Transcoder
Transcoding is the process of converting a media file (audio or video) from one format, size, or quality to another. Elastic Transcoder takes input from an S3 bucket, transcodes it, and writes the resulting file to another bucket or folder. You start with one or more input files and create transcoding jobs in a transcoding pipeline for each file. Each job must reference a particular transcoding preset, and in our case will result in the generation of HLS segments and playlists. Our Lambda starter code will be the job creator and is configured with all the HLS transcoding presets available. Learn more about Elastic Transcoder.

What is Amazon S3?
Amazon Simple Storage Service (Amazon S3) provides secure, durable, highly scalable object storage in the cloud. Using the AWS Mobile SDK, you can directly access Amazon S3 from your mobile app. The AWS Mobile SDK allows you to consume the Amazon S3 service in your mobile application via the S3 Transfer Utility. The fully functional iOS sample app generated by Mobile Hub includes the AWS Mobile SDK for iOS and all the helper code for running the demo app. For this tutorial, you’ll create an S3 bucket and use it to store the video content. The S3 bucket acts as the origin (source of distributed content) for your CloudFront Web Distribution. Learn more about Amazon S3 cloud storage and pricing.

What is Amazon CloudFront?
Amazon CloudFront is a global content delivery network (CDN) service that accelerates delivery of your websites, APIs, video content, and other web assets. It integrates with other AWS services to give developers and businesses an easy way to accelerate content delivery to end users with no minimum usage commitments. For this tutorial, you’ll use CloudFront to distribute HLS streaming content to a mobile device. Learn more about Amazon CloudFront and pricing.

Let’s Begin

Step 1 – Configure your backend content delivery

You’ll use Mobile Hub to create a project. Next, you’ll enable the App Content Delivery feature to store our video content on Amazon S3 and Cloud Logic to launch encoding jobs to encode HLS content. We’ll walk you through the steps and follow up with a recap (explain the magic) of what just happened.
1. Launch AWS Mobile Hub console

2. Select Create new mobile project

step1-2

3. Provide a project name, (e.g. StreamingBlog) and then select Create project.

 

MH-Project-Name

Note: If you have never used Mobile Hub before, you’ll be prompted to select Get Started and Yes, grant permissions before you can create a new project.
4. Select App Content Delivery.

MH-Select-AppContentDelivery

5. Select Global CDN and then choose Save changes.

MH-Select-GlobalCDN-Save

6. Select Configure more features button so we can enable one additional feature.
7. Select Cloud Logic.

CloudLogic-Card

8. Select Enable logic and then choose Save changes.

enable-cloud-logic

9. Select Integrate from the left panel.

step1-6

From the integration tab, make sure iOS Swift is selected and that you are in the Getting Started section.
10. Select the iOS Swift tab and select Download a sample app to download your iOS Swift project source code. We’ll run this project in Xcode in a later step.

download-sample-app

Done. You now have all of your AWS resources configured and the custom-built iOS Swift source code project is downloaded and ready.

What did we just do?
First, you created a new project in AWS Mobile Hub. Note that, for each Mobile Hub project created, an Amazon Cognito identity pool is created on your behalf, regardless of which features are enabled. For this tutorial, we are only utilizing Amazon Cognito, giving permission to list the content of the S3 bucket that you are using as the source for streaming media.

After the project was created with the default Amazon Cognito identity pool, you then enabled the App Content Delivery and Cloud Logic features in your Mobile Hub project.

The App Content Delivery feature first creates an Amazon S3 bucket to store media content and creates a policy within the AWS Identity and Access Management (IAM) role that grants your users access to list all content in your Amazon S3 bucket when they launch the app. Within the S3 bucket, Mobile Hub has uploaded five sample images (e.g., example-image-1.png).

The Cloud Logic feature creates a “hello-world” Node.js function along with its own IAM role that allows the Lambda function to call other AWS services such as Amazon Elastic Transcoder. This Lambda function will become your HLS Amazon Elastic Transcoding encoding job creator when new videos are uploaded to your origin S3 bucket via an S3 Event.

Finally, you downloaded the generated project source code to run the Mobile Hub sample app demonstrating the features you just configured.

How does the sample app work?
You’ll open the iOS Swift project source project in Xcode to build and run the sample app. Upon first launch of the app, that user is given a unique unauthenticated Amazon Cognito Identity. The user then selects the App Content Delivery feature that lists the sample images from your S3 bucket you just configured. From this demo, the user can choose to Open Remote or Download the sample image. For Open Remote, the image is actually cached from S3, downloaded directly from CloudFront, and viewed in the provided WebViewController. For Download, the app downloads the cached content from CloudFront and caches it locally on the device. You can then open the image offline because it’s now stored on the device.

After running through a few more steps below, you’ll re-launch the app and instead of selecting the sample image, select the HLS master playlist (.m3u8 file) from the list. The app will begin streaming the cached HLS movie natively from your CloudFront Web Distribution using the iOS MoviePlayerController on the device.

Step 2 – Stage Video Content and Encoding Service

In this step, you’ll be doing some backend provisioning to set the stage for storing video content on S3, triggering encoding jobs via Lambda, and creating an Amazon Elastic Transcoder pipeline to transcode our HD video into HTTP Live Streaming (HLS) segments.

This step is broken into five important tasks and we’ll walk you through each one now.

A. Create an Elastic Transcoder Pipeline

Launch the Elastic Transcoder Management Console in the same region as your Mobile Hub project and select Create New Pipeline.

create-new-pipeline

For the bucket locations, choose the App Content Delivery S3 bucket that was created by Mobile Hub. The field should auto-populate a list of your S3 buckets, taking any guess work away. Leave the default setting for IAM Role and set Storage Class to Standard. Your new pipeline configuration should look something like below. The Notifications and Encryption section can be ignored for this tutorial.

create-pipeline
Once you have provided the details, select the Create Pipeline button on the lower right. You just created a new Elastic Transcoder Pipeline.
Save for later: Copy the Pipeline ID from the Pipelines Summary as we’ll reference this in your Lambda function in the next task.

pipeline-id

B. Modify the “hello-world” Lambda function.

You enabled Cloud Logic earlier, which created a Lambda function within your project. You are now going to overwrite that default function code with the provided Lambda code below.

First, go back to your Mobile Hub project and select Resources from the left navigation and then select the hello-world link from the AWS Lambda Functions card to launch the function in the AWS Lambda console.

hello-world-lambda

The code editor that appears for the hello-world function should look something like this.

// Copyright 2015 Amazon.com, Inc. or its affiliates (Amazon). All Rights Reserved.
// Code generated by AWS Mobile Hub. Amazon gives unlimited permission to copy, distribute and modify it.
console.log('Loading function');
exports.handler = function(event, context) {
    //console.log('Received event:', JSON.stringify(event, null, 2));
    console.log('value1 =', event.key1);
    console.log('value2 =', event.key2);
    console.log('value3 =', event.key3);
    context.succeed(event.key1);  // Echo back the first key value
    // context.fail('Something went wrong');
};

You are now going to overwrite the default “hello-world” code from above with the starter code below. Just copy this code below and paste it into the code editor for the default hello-world function.

//Author: Dennis Hills
//Date: October 18, 2016
//Purpose: HLS Streaming mobile blog
//Requirements: An Elastic Transcoder Pipeline

var aws = require('aws-sdk');
var elastictranscoder = new aws.ElasticTranscoder();

// return filename without extension
function baseName(path) {
   return path.split('/').reverse()[0].split('.')[0];
}

exports.handler = function(event, context) {
    console.log('Received event:', JSON.stringify(event, null, 2));
    var key = event.Records[0].s3.object.key;
    var outputPrefix = baseName(key) + '-' + Date.now().toString();

    var params = {
      Input: { 
        Key: key
      },
      PipelineId: '1476775662902-2dp0az', /*Your Elastic Transcoder Pipeline Id*/
      OutputKeyPrefix: 'HLS/' + outputPrefix,
      Outputs: [
        {
          Key: '/output/2M',
          PresetId: '1351620000001-200015', // HLS v3 and v4 (Apple HTTP Live Streaming), 2 megabits/second, Video-only
          SegmentDuration: '10',
        },
        {
          Key: '/output/15M',
          PresetId: '1351620000001-200025', // HLS v3 and v4 (Apple HTTP Live Streaming), 1.5 megabits/second, Video-only
          SegmentDuration: '10',
        },
        {
          Key: '/output/1M',
          PresetId: '1351620000001-200035', // HLS v3 and v4 (Apple HTTP Live Streaming), 1 megabit/second, Video-only
          SegmentDuration: '10',
        },
        {
          Key: '/output/600k',
          PresetId: '1351620000001-200045', // HLS v3 and v4 (Apple HTTP Live Streaming), 600 kilobits/second, Video-only
          SegmentDuration: '10',
        },
        {
          Key: '/output/400k',
          PresetId: '1351620000001-200055', // HLS v3 and v4 (Apple HTTP Live Streaming), 400 kilobits/second, Video-only
          SegmentDuration: '10',
        },
        {
          Key: '/output/aud',
          PresetId: '1351620000001-200060', // AUDIO ONLY: HLS v3 and v4 Audio, 160 k
          SegmentDuration: '10',
        }
      ],
      Playlists: [
        {
            Format: 'HLSv4',
            Name:  '/' + baseName(key) + '-master-playlist',
            OutputKeys: [ '/output/2M', '/output/15M', '/output/1M','/output/600k', '/output/400k', '/output/aud']
        }
      ]
    };

    elastictranscoder.createJob(params, function(err, data) {
      if (err) console.log(err, err.stack); // an error occurred
      else     console.log(data);           // successful response
    });
};

Critical Step: Before saving the Lambda code, change the PipelineId to your Elastic Transcoder Pipeline Id that you copied in the previous task. That is the only code change needed for this Lambda function. Select Save.

pipeline-id-code

One other small change you need to make is change the Runtime of this function from Node 0.10 to Node.js 4.3 by selecting the Configuration tab, then change the dropdown from Node.js to Node.js 4.3. Select Save.

C. Enable an S3 event on your S3 bucket to trigger the Lambda function

Go back to your Mobile Hub project. Select Resources from the left navigation pane and then select the Content Delivery S3 bucket to launch the S3 AWS Management Console.

bucket-resource

You should now be viewing your Content Delivery S3 bucket in the console. You are going to add an S3 event to this bucket that will invoke your hello-world Lambda function. The Lambda function will start transcoding any video files uploaded to this bucket automatically.

Configure the S3 event by following the steps shown below.

s3-event-configuration

Select Save.

That’s it for creating an S3 event to trigger the Lambda function.
How does this S3 event work? Whenever you PUT a video (with a suffix matching your filter) into this bucket, S3 triggers your hello-world Lambda function and passes the video filename to the function. Lambda then starts the transcoding job(s) to convert the video into HLS segments.

D. Allow Lambda to create Elastic Transcoder Jobs.

By default, your hello-world function was created with a limited IAM execution role as it was intended to only return a key/value pair for demonstration purposes. Now, we need to allow Lambda to call the Elastic Transcoder Service.

Go back to your Mobile Hub project and select Resources from the left navigation pane and then select the Lambda Execution Role to launch the role in the IAM console.

lambda-execution-role-resource

In the IAM console for the Lambda Execution role, select Edit Policy under the Inline Policy section as shown.

lambda-exec-role-edit-policy

Within this policy, we are adding permission to call the Amazon Elastic Transcoder CreateJob API. Copy the statement below and paste it into the policy as shown. The highlighted yellow area is the newly appended action after pasting into the policy document editor for the lambdaexecution policy.

{ "Effect": "Allow",
"Action": [
"ElasticTranscoder:CreateJob"
],
"Resource": "*"
}

lambda-execute-policy-details

Select Apply Policy. Now Lambda has access to create transcoding jobs within Elastic Transcoder when triggered by S3 after a new video is uploaded to your Content Delivery S3 bucket.

E. Grant CloudFront read permissions by adding a bucket policy just for CloudFront.

For this task, you need to edit the origin configuration for your CloudFront Web Distribution so that it has permission to read from your content delivery S3 bucket for caching the newly transcoded files. By default, Mobile Hub creates the Web Distribution so that all objects in the S3 bucket (origin) need to be Public for the distribution to cache those files. Making this change keeps the restricted access to the S3 bucket but allows CloudFront to read objects without you having to manually update the object permissions in the bucket after each PUT or after transcoding.

Start by going to your Mobile Hub project resources and select the CloudFront Web Distribution.

cdn-config-1

Within the CloudFront console, select the Origins tab, then select the S3 bucket origion, and select Edit.

cdn-config-2
Configure the Origin Settings as shown in the following image and select Yes, Edit button on lower right (not shown).

cdn-3

Now CloudFront can access all the contents of your S3 bucket and cache upon request, while keeping your S3 content protected. That was the final task for Step 2.

What did we just do in Step 2?
You just completed Step 2 with five important tasks. Congratulations. It was a big step. In the first task, (A), you created and configured an Amazon Elastic Transcoder pipeline. This pipeline sets the stage for encoding jobs that will convert your videos into HLS segments. In the second task, (B), you modified the “hello-world” Lambda function by pasting in the starter code and provided your Elastic Transcoder pipeline ID. In the third task, (C), you enabled an S3 event in your Content Delivery S3 bucket to trigger a Lambda function when you PUT new videos in the bucket. In the fourth task, (D), you modified the Lambda IAM execution policy, allowing it to call the Elastic Transcoder APIs. In the final task, (E), you modified the Origin Settings of the CloudFront Web Distribution, restricting access to your S3 bucket and allowing this distribution to read all contents within the S3 bucket to cache when requested.

Now that we have everything configured on the backend for storing, distributing, and encoding media content, you’ll focus on the client-side project code, upload a video, and start streaming!

Step 3 – Upload video to S3 and trigger HLS encoding

In this step, you’ll download a sample video and upload it to your Content Delivery S3 bucket to begin encoding it to HLS for streaming.

1. Download sample 1-minute HD video (approx. 388MB) here. You can skip this step if you want to upload your own video in the next step!
2. Upload the provided video (or your own .mov or .mp4) to your Content Delivery S3 bucket, using the S3 console here.
3. Select Upload.
4. Select Add Files.
5. Select the video you want to upload.
6. Select Start Upload.
Note: It may take several minutes to upload the video via the S3 console. If you have the AWS CLI or another S3 client, feel free to utilize any of those tools to speed up the process of uploading content to your S3 bucket.

What did we just do?
In this step you just uploaded a video to your Content Delivery S3 bucket. When the file upload completed, your S3 PUT event triggered the Lambda function to begin transcoding your video into HLS segments. For a 1-minute video, the transcoding of all the segments and playlists should be done in less than a minute.

What does the transcoding look like? Once transcoding is complete, you’ll see an HLS/ folder in your bucket and within that folder you’ll see a folder with the video filename, appended with a date string. Within that sub folder is the HLS 4 playlist (.m3u8) and an output/ folder containing all the encoded HLS segments.

encoded-folder-s3

Here’s what the output folder looks like, containing all the various bitrates, iframes, and playlists.

hls-output-folder

From the mobile app you are about to configure, you’ll select the master playlist (rafting-playlist.m3u8) from the S3 bucket list demo and the transcoded video will automatically begin streaming the cached video from CloudFront.

Step 4 – Modify sample app and stream HLS content

In this final step, you just need to make one small modification to the iOS Swift project source code so that it sees the .m3u8 file type as a video file. Remember downloading the source project in Step 1?
Start by opening the project code in Xcode on your Mac.
1. Unzip the iOS Swift project code you downloaded previously.

streamin-zip-folder

2. Open the sample app project in Xcode by double-clicking on the Xcode project, MySampleApp.xcodeproj, which is in the project folder.

step2-2

3. Expand the project folders and open the ContentDeliveryViewController.swift class. Add the highlighted line as shown to your isAudioVideo() function. This allows the app to identify your HLS 4 playlist as video content.

add-HLS

4. Select build/run within Xcode to build and run the sample app.
5. At the main screen of the sample app, select App Content Delivery and then Demo Content Delivery.

app-content-main    app-content-1

 

7. Select the HLS/ folder and the newly encoded video folder.
8. Tap on the HLS playlist (.m3u8 file) and choose Open Remote.

playlist

The app will automatically start streaming the video choosing the correct bitrate for your connection speed. The app is using the default iOS MPMoviePLayerViewController and it does all the heavy lifting to determine the best way to stream the video content.

video-playing

The magic is that you should experience the same playback quality and smoothness regardless of the network the device is connected to. Switching the app from Wi-Fi to cellular, the built-in player will auto-adjust and begin or continue playing the same video using one of the different bitrate variants you provided when transcoding the original source mp4 video into HLS.

Now, to really see the power of HLS encoded video, play around with the app connection (switching from Wi-Fi to cellular) and see how it performs.

More cool stuff to play with: To highlight the power of HLS streaming content from CloudFront, just select the original .mp4 video source and the app will automatically start playing the movie. If you have a modern device and great bandwidth, you may not notice much. However, try the same source video on another older device or over a slower connection. Doing this, you’ll see the device will have a hard time playing the video without lag, artifacts, or other unbearable viewing experiences. Repeat the same process by selecting the HLS master playlist and you should notice a significant difference in playability.

Planning to integrate this feature into your own app? No problem! In the next step, we’ll walk you through the Mobile Hub integration guide, helping you integrate all the code from the sample app into your own app.

Step 5 – Streaming HLS content to your own app

You just configured backend content delivery and ran our generated sample app to demonstrate the end-to-end functionality of this common mobile feature. To get the same functionality in your own app, use the Mobile Hub integration instructions specific to each mobile project you create.

Let’s get started.
Go back to your Mobile Hub project in the console and select Integrate. The integration instructions from Mobile Hub walk you through Getting Started. The instructions then describe each feature you enabled in your Mobile Hub project and how to integrate each of them into your own app.
Here’s a quick overview of Getting Started and additional instructions for integrating this project into your own application.
The Mobile Hub Getting Started integration instructions include:

  • Download the Mobile SDK for iOS and custom source code provided
    The download provides the latest SDK, Mobile Hub helper code, and example source code to get you started.
  • After downloading the SDK and helper code, drag and drop the Sdk folder contained in the download into your own project within Xcode. This assumes you already have an iOS Swift project of your own; if not, create a new one and follow along.
  • Add the AWS SDKs as Embed Frameworks.
  • Add Run Script phase to your project as directed.
  • Copy the AmazonAws folder from the helper code to your project and add a group for it. This source code contains a customized helper framework that simplifies the use of all the SDKs that are required to support your project.
    It also contains the AWSMobileClient.swift file, which bootstraps the app and creates an identity manager to establish the user identity with Amazon Cognito.
  • Copy the AWS dictionary from the integration Info.plist into your own project Info.plist. The AWS dictionary provides the CognitoIdentity poolId and region for your project.
  • Add AWS iOS SDK dependencies, libsqlite3.tbd and libz.tbd, under Linked Frameworks and Libraries as directed.
  • Set up your Application Delegate by returning the AWSMobileClient singleton as shown in the guide.

You are now done with the Getting Started section.
After you complete Getting Started, you can select App Content Delivery in the left panel of the integration instructions. Here, you just need to do the following:

  • Copy the ContentManager key from the AWS dictionary in the integration Info.plist into your own project’s Info.plist. The AWS dictionary already provides all the CognitoIdentity pool IDs for your project as well as your Content Delivery S3 bucket and region.
  • View or copy the example upload/download source code provided.

That’s it! You should now be able to run your own application and connect it to your AWS resources like we demonstrated using the sample app generated by AWS Mobile Hub.

Conclusion

You just configured a backend app content delivery feature via AWS Mobile Hub; generated a fully functional sample iOS app that demonstrates opening and streaming videos directly from your Amazon CloudFront CDN; and then walked through integrating the client code into your own app by connecting to the same backend resources demonstrated in the sample app.

Thanks and happy coding!

Customizing Amazon Cognito User Pool Authentication Flow

by Ionut Trestian | on | in AWS Mobile | | Comments

Introduction

Modern authentication flows incorporate new challenge types, in addition to a password, to verify the identity of users. For example, these challenge types include CAPTCHAs or dynamic challenge questions. With Amazon Cognito Your User Pools, we now have a flexible authentication flow that you can customize to incorporate additional authentication methods and support dynamic authentication flows that are server driven. First, we generalize authentication into two common steps, which are implemented through two APIs (InitiateAuth and RespondToAuthChallenge). In this flow, a user authenticates by answering successive challenges until authentication either fails or the user is issued tokens. With these two steps, which can be repeated to include different challenges, we support any custom authentication flow. Second, we provide the ability to customize your authentication flow with AWS Lambda triggers. These triggers issue and verify their own challenges as part of the authentication flow.

In this post, we provide detail on the two APIs and their inputs and outputs as we demonstrate how you can customize Your User Pools authentication flow to contain additional challenges such as CAPTCHAs.

New APIs

InitiateAuth

This API kicks off the authentication flow. It explicitly indicates to Amazon Cognito how you are trying to authenticate, along with initial authentication parameters that are passed to the pre-authentication Lambda trigger. With a successful call, the response provides either tokens (for an authenticated user) or a challenge.

The InitiateAuth API has the following inputs:

AuthFlow String

The name of the auth flow is determined by the service. The following are supported: USER_SRP_AUTH, REFRESH_TOKEN_AUTH, CUSTOM_AUTH, ADMIN_NO_SRP_AUTH. USER_SRP_AUTH and REFRESH_TOKEN_AUTH were previously available through other APIs but they are easier to use with the new APIs. For a custom authentication flow, the CUSTOM_AUTH value is provided. For more information on the flows, see Custom Authentication Flow in the Amazon Cognito Developer Guide.

AuthParameters Map of String, String

Key/value pairs containing all of the inputs necessary to initiate this authentication method (e.g., USERNAME=johndoe, SRP_A=AB009809). Based on authentication flow and a combination of some parameters missing, authentication can be failed.

[ClientMetadata] Map of String, String

Optional field. Key/value pairs containing inputs that aren’t authentication parameters but are inputs to the pre-authentication Lambda trigger. Using this Lambda trigger, you can implement custom validations to accept/deny the request, based on, for example, the user context.

ClientId String

The app clientId for the app attempting to authenticate.

RespondToAuthChallenge

Used to respond to challenges, which can include multiple rounds until the user successfully authenticates (and Amazon Cognito issues tokens) or fails. You have control over how many challenge rounds are presented to the user. This can be decided based on the challenges already answered. With each call, the response is either a successful authentication when tokens are issued, a new challenge, or failure of the authentication.

The RespondToAuthChallenge API has the following inputs:

ChallengeName String

Name of the challenge being responded to.

Session String

An encrypted session received by the client in the previous step that the client must pass back as-is. The session contains state information about the current authentication. It cannot be replayed and it expires after 3 minutes.

ChallengeResponses Map of String, String

Key/value pairs containing all of the parameters necessary to respond to the challenge (e.g., captchaAnswer=AZ235F).

ClientId String

ClientId trying to authenticate.

InitiateAuth and RespondToAuthChallenge outputs

A successful call to either of the APIs results in tokens that indicate that the authentication flow is complete, or a challenge with a session and parameters.

AuthenticationResult containing Tokens

If this is the last step in the authentication flow, the result contains ID, access and refresh tokens. For more information, see Using Tokens with User Pools in the Amazon Cognito Developer Guide.

ChallengeName String

Name of the next challenge. For example, possible values can be CUSTOM_CHALLENGE, if a custom challenge needs to be answered, or PASSWORD_VERIFIER, if password verification is required.

Session String

An encrypted session received by the client in the previous step that the client must pass back as-is. The session contains state information about the current authentication. It cannot be replayed and it expires after 3 minutes.

ChallengeParameters Map of String, String

Key/value pairs containing all of the parameters necessary to prompt the user for the returned challenge (e.g., captchaUrl=https://xyz.com/captcha/123415).

Diving into the custom authentication flow

To provide you control over the authentication flow, we introduced a flow of CUSTOM_AUTH type and provided you with Lambda triggers you can use to implement this custom authentication flow. The flow can be broken down into the following decisions that you can customize through Lambda triggers:

  • Analyze the challenges a user has answered so far (successfully or unsuccessfully) and then succeed the authentication (and generate tokens), fail authentication, or prompt the user with a new challenge. This is called the Define Auth Challenge Lambda trigger.
  • Generate a challenge that consists of parameters used to challenge the user and valid answers that can be used when the challenge is answered. This is called the Create Auth Challenge Lambda trigger.
  • Verify if the answer provided by the user is valid. This is called the Verify Auth Challenge Lambda trigger.

Lambda triggers can be entered as code in the AWS Lambda console as shown.

lambda_console

You can configure Lambda triggers in the Amazon Cognito console on the User Pools Triggers page as shown next. When a Lambda trigger is selected in the Amazon Cognito console, the necessary execution rights are created automatically by the console.

configure_lambda

When an app initiates a flow of CUSTOM_AUTH type, Amazon Cognito executes the Define Auth Challenge Lambda trigger to issue a challenge type. This can be a standard (built-in) challenge, such as SRP, that can be handled by the Amazon Cognito service. Or it can be a custom challenge handled by a Lambda trigger that you supply (CUSTOM_CHALLENGE type). In general, this process takes as input the past challenges answered by the user and their result. If the challenge issued is of custom type, Amazon Cognito calls a Lambda trigger to create and issue the challenge. The Lambda trigger passes back the challenge parameters and valid answers. This process is shown in the following two diagrams.

diagram_1

diagram_2

Amazon Cognito User Pools passes the challenge and an encrypted piece of session information back to the client SDK. The client SDK gathers the answers to the challenge and passes back the answers and the encrypted session data as it received it. Amazon Cognito User Pools decrypts the data and calls a Lambda trigger to check if the answers match. Amazon Cognito User Pools then calls the Lambda trigger with the challenges answered so far (adding the challenge just answered). The Lambda trigger can issue a new challenge, issue tokens, or fail authentication, and the process can repeat accordingly. We dive deep into the Lambda contract specifications next.

Lambda Trigger contract specifications

There are three Lambda triggers mentioned previously. We provide detail on the different parameters the Lambda triggers take as inputs and the valid outputs. All three of these Lambda triggers also receive all of the user’s attributes as inputs.

DefineAuthChallenge: The challenges (state machine) Lambda trigger

This Lambda trigger has as input the unencrypted session challenge answering information (it was decrypted by the service after it was passed to and from the client). The trigger then returns a challenge name or a Boolean value that indicates whether Amazon Cognito should issue tokens or not.

Inputs

session List of ChallengeResult objects

This is a list containing ChallengeResult objects that indicate whether the challenge was successfully answered. A ChallengeResult object contains a challenge name (challengeName), a Boolean value with the challenge answering result (challengeResult), and challenge metadata that you populate when you generate a challenge (challengeMetadata).

Outputs

challengeName String

Amazon Cognito returns a new challenge name that the client must answer. Alternatively, Amazon Cognito can issue tokens or fail authentication using the following parameters. If you want to generate a challenge with the Create Auth Challenge Lambda trigger, your trigger must output a challengeName of CUSTOM_CHALLENGE.

issueTokens Boolean

Indicates whether tokens should be issued to the user, which means the user was authenticated.

failAuthentication Boolean

Indicates whether the user’s authentication attempt should be terminated because of a failure to authenticate.

Example Lambda Trigger

This example Lambda trigger issues tokens if SRP_A, PASSWORD_VERIFIER, and CUSTOM_CHALLENGE challenges were passed; otherwise it fails the authentication.

exports.handler = function(event, context) {
    if (event.request.session.length == 1 && event.request.session[0].challengeName == 'SRP_A') {
        event.response.issueTokens = false;
        event.response.failAuthentication = false;
        event.response.challengeName = 'PASSWORD_VERIFIER';
    } else if (event.request.session.length == 2 && event.request.session[1].challengeName == 'PASSWORD_VERIFIER' && event.request.session[1].challengeResult == true) {
        event.response.issueTokens = false;
        event.response.failAuthentication = false;
        event.response.challengeName = 'CUSTOM_CHALLENGE';
    } else if (event.request.session.length == 3 && event.request.session[2].challengeName == 'CUSTOM_CHALLENGE' && event.request.session[2].challengeResult == true) {
        event.response.issueTokens = true;
        event.response.failAuthentication = false;
    } else {
        event.response.issueTokens = false;
        event.response.failAuthentication = true;
    }
    context.done(null, event);
}

CreateAuthChallenge: The challenge generator Lambda trigger

This Lambda trigger takes a challenge name as input and then generates challenge parameters based on that challenge.

Inputs

challengeName String

The name of the challenge the Lambda trigger has been requested to issue.

session List of ChallengeResult objects

This is a list containing ChallengeResult objects that indicate whether the challenge was successfully answered. A ChallengeResult object contains a challenge name (challengeName), a Boolean value with the challenge answering result (challengeResult), and challenge metadata that you populate when you generate a challenge (challengeMetadata).

Outputs

publicChallengeParameters Map of String, String

The Lambda trigger returns a map of public challenge parameters. This map is sent to the client unencrypted so that the client can present the challenge to the user; for example, a CAPTCHA URL.

privateChallengeParameters Map of String, String

The Lambda trigger returns a map of private challenge parameters that it generates. This output is labeled “private” and the client cannot see it. For example, it might be used to hold CAPTCHA answer or other valid answers for the challenge.

challengeMetadata String

Metadata about the challenge. This data is put in the session of challenge results when this challenge is answered.

Example Lambda trigger

The following Lambda trigger generates the publicChallengeParameters and privateChallengeParameters maps, with the URL to a CAPTCHA in the public map and the valid answer to the CAPTCHA in the private map. The two pieces usually come from an external service.

exports.handler = function(event, context) {
    if (event.request.session.length == 2 && event.request.challengeName == 'CUSTOM_CHALLENGE') {
        event.response.publicChallengeParameters = {};
        event.response.publicChallengeParameters.captchaUrl = 'url/123.jpg'
        event.response.privateChallengeParameters = {};
        event.response.privateChallengeParameters.answer = '5';
        event.response.challengeMetadata = 'CAPTCHA_CHALLENGE';
    }
    context.done(null, event);
}

Verify Auth Challenge: The response verifying Lambda Trigger

This Lambda trigger verifies the answers to the challenges. It gets acceptable answers to the challenge, provided by the previous Create Auth Challenge Lambda trigger, and compares them to the client provided answer.

Inputs

privateChallengeParameters – Map of String, String

This input is the same as described previously for the Create Auth Challenge Lambda trigger. Amazon Cognito forwards the map of private challenge parameters from the Create Auth Challenge Lambda trigger to the Verify Auth Challenge Lambda trigger. These parameters include valid answers for the challenge.

challengeAnswer String

The answer provided by the user to the challenge.

Outputs

answerCorrect Boolean

Indicates whether the answer provided to the challenge was correct.

Example Lambda trigger

The following Lambda trigger checks if the challengeAnswer value is equal to an answer entry in the privateChallengeParameters map to determine if the challenge was answered correctly.

exports.handler = function(event, context) {
    if (event.request.privateChallengeParameters.answer == event.request.challengeAnswer) {
        event.response.answerCorrect = true;
    } else {
        event.response.answerCorrect = false;
    }
    context.done(null, event);
}

Client SDK considerations

The client SDKs expose a new callback that requests input from users in the case of custom challenges. The challengeParameters parameter is used to present the challenge to the user and the user’s response is passed back in the challengeResponses parameter to the RespondToAuthChallenge API call. Here is an example in JavaScript to complement the example Lambda triggers described previously. As can be seen in the following, the JavaScript SDK provides a customChallenge callback while the Android and iOS SDKs provide similar callbacks.

var authenticationData = {
    Username : 'username',
    Password : 'password',
};
var authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
var poolData = { 
    UserPoolId : '...', // Your user pool id here
    ClientId : '...' // Your client id here
};
var userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
var userData = {
    Username : 'username',
    Pool : userPool
};
var cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.setAuthenticationFlowType(‘CUSTOM_AUTH’);
cognitoUser.authenticateUser(authenticationDetails, {
    onSuccess: function (result) {
      console.log('access token + ' + result.getAccessToken().getJwtToken());
    },

    customChallenge: function (challengeParameters) {
      //gather user responses in challengeResponses based on challengeParameters
      cognitoUser.sendCustomChallengeAnswer(challengeResponses, this);
    },

    onFailure: function(err) {
      alert(err);
    },
});
 
          

Security considerations

The custom user pool authentication flow works in parallel with all other user pool authentication flows, unless you specify otherwise. If you want to implement a custom flow that presents more challenges than, for example, USER_SRP_AUTH, you should choose Only allow Custom Authentication in the Amazon Cognito console, or include the CUSTOM_AUTH_FLOW_ONLY enum in the ExplicitAuthFlows user pool client property.

flag_custom_auth

Conclusions

As you can see, you can customize your authentication flow using the AWS Mobile SDKs. This enables you to create an experience that is designed to provide security and a user-friendly experience for your users. We’d like to hear how you plan to use this feature in your applications, so feel free to leave a comment to share other uses for this feature.

If you have comments or questions, please leave a comment, visit our forums, or post on Stack Overflow.

How to Store Your App Photos in the Cloud Using Amazon S3 [tutorial and code samples]

by Dennis Hills | on | in AWS Mobile, CloudFront, Elastic Transcoder, HLS, Lambda, S3, Streaming Video | | Comments

About

Welcome to the first installment of our blog series showing how to build some of the most popular end-to-end experiences used by mobile apps. In each installment, you’ll see how you can build these experiences – including the client (iOS and Android) and the backend (AWS services) – in just a few minutes. We’ll typically use AWS Mobile Hub to accomplish this; Mobile Hub simplifies many of the steps you would normally have to go through, including authenticating users, securely configuring backend services, running backend code, and generating reference iOS and Android apps that use these features. We’ll then dive deeper, showing you how to integrate the experiences into your own applications and explaining what is happening within Mobile Hub.

Through the series, we’ll touch on a number of AWS services, including Amazon Cognito, Amazon S3, Amazon DynamoDB, and AWS Lambda. We welcome your suggestions in the comments for additional experiences or AWS services we should highlight!

Let’s get started with our first installment. This first post is about uploading photos to and downloading photos from the cloud from a mobile application. You’ll walk away with a fully functioning mobile application connecting to your AWS backend resources.

There are three steps in this tutorial.

Step 1. Configure the backend AWS resources via AWS Mobile Hub. This consists of creating a project, enabling a single feature called User Data Storage, and building/downloading the iOS Swift sample app source code project.

Step 2. Explore uploading to and downloading photos from Amazon S3 using the sample app generated by Mobile Hub.

Step 3. Store and Retrieve Photos from your own app by following the AWS Mobile Hub integration guide customized for your project.

First, we’ll explain briefly what Mobile Hub is and the AWS features (Amazon S3 and Amazon Cognito Identity) that we’ll enable within a Mobile Hub project.

What is AWS Mobile Hub
AWS Mobile Hub simplifies the process of building, testing, and monitoring mobile applications that make use of one or more AWS services. It can help you skip the front- and back-end heavy lifting of manually creating cloud service configurations and integrating them into your client.  The mobile app features supported includes user authentication, data storage, backend logic, push notifications, content delivery, and analytics—all from a single, integrated console.

There is no charge for Mobile Hub; you only pay for the AWS services used. For example, after you complete this tutorial, you are only charged for the objects stored in your S3 bucket. Learn more about AWS Mobile Hub features.

What is Amazon Cognito Federated Identity
Amazon Cognito Federated Identities enable you to create unique identities for your users and authenticate them with identity providers. With an identity, you can obtain temporary, limited-privilege AWS credentials to synchronize data with Amazon Cognito Sync, or directly access other AWS services. Amazon Cognito Identity supports public identity providers—Amazon, Facebook, Google, and SAML identity providers—as well as unauthenticated identities, which is what we’ll be utilizing in this tutorial. Learn more about Amazon Cognito Federated Identities.

What is Amazon S3
Amazon Simple Storage Service (Amazon S3) provides secure, durable, highly scalable object storage in the cloud. Using the AWS Mobile SDK, you can directly access Amazon S3 from your mobile app. The AWS Mobile SDK allows you to consume the Amazon S3 service in your mobile application via the S3 Transfer Utility. The fully functional iOS sample app generated by Mobile Hub includes the AWS Mobile SDK for iOS, S3 Transfer Utility, and all the helper code for running the demo app used for this tutorial. Learn more about Amazon S3 cloud storage and pricing.

Let’s Begin

Step 1 – Configure the backend

We’ll use Mobile Hub to create a project and then enable the User Data Storage feature to store and retrieve photos from Amazon S3. We walk you through the steps and follow up with a recap (explain the magic) of what just happened.

1)  Launch AWS Mobile Hub console

2)  Select Create new mobile project.

3)  Provide a project name, S3Blog, and then select Create project.


Note: If you have never used Mobile Hub before, you’ll be prompted to select Get Started and Yes, grant permissions before you can create a new project.

4)  Select User Data Storage.


5)  Select Store user data and then choose Save changes.


6)  Select Integrate from the left panel


7)  From the integration tab, make sure iOS Swift is selected and that you are in the Getting Started section.
8)  Select Download a sample app to download your iOS Swift project source code (zipped project folder). We’ll run this project in Xcode in just a minute.

Done. You now have all your AWS resources configured and the custom-built iOS Swift source code project is downloaded and ready.

What did we just do?

First, we created a new project in AWS Mobile Hub. Note that, for each Mobile Hub project created, an Amazon Cognito identity pool is created on your behalf, regardless of what features are enabled. Amazon Cognito Identity is being used in our app to obtain temporary, unauthenticated, limited-privilege credentials to directly access Amazon S3 from a mobile app.

In a future blogpost, we’ll discuss the Mobile Hub User Sign-in feature where Amazon Cognito provides authenticated identities through Amazon Cognito User Pools and public identity providers—Amazon, Facebook, Google, and SAML. for this tutorial, just know that we’ll be using Amazon Cognito unauthenticated identities to demonstrate this topic.

After the project was created with the default Amazon Cognito identity pool, you then enabled the User Data Storage feature in your Mobile Hub project. This feature first creates an Amazon S3 bucket to store user photos and creates a policy within the AWS Identity and Access Management (IAM) role that grants your user’s access to Amazon S3 when they launch the app. Within the S3 bucket, a /Public folder was created with access permissions set to allow all users of your app to view and upload files stored there. There’s also a /Private folder created, which we’ll discuss in the next blog post. This folder is used to store private data for each user when authenticated through the User Sign-in feature of Mobile Hub.

Here’s how the sample app works:

The user launches the app and immediately that user is given a unique unauthenticated Amazon Cognito Identity. The user then selects the User Data Storage demo feature to upload a photo from his or her photo library to the /Public folder within the S3 bucket. The unique unauthenticated Amazon Cognito Identity given to that user provides limited-privilege credentials to directly access the /Public folder of your S3 bucket.

Now that we have created file storage that is designed for scalability, let’s go demo the app!

Step 2 – Running the Sample APP

Now for the fun part. In the previous step 1, you configured the AWS resources you needed, built the sample app project code, and downloaded it to your Mac. At the end of this walkthrough, we’ll go into how Mobile Hub guides you in integrating these features into your own app.

For now, we’ll now describe how to open the sample project code in Xcode and run the iOS sample app on a physical iOS device or simulator. It’s that simple.

The iOS sample app provides a demo for retrieving files from your S3 bucket and uploading photos from your iOS device photo library. Mobile Hub has placed a sample image to get started.

1) Unzip the iOS Swift project code you downloaded previously

step2-1

2) Open the sample app project in Xcode by double-clicking on the Xcode project, MySampleApp.xcodeproj, which is in the project folder.

3) Select build/run within Xcode to build and run the sample app.

When the sample app is first launched, a function called is called to retrieve an ID generated by the Amazon Cognito Identity service. The function invokes that manages access to Amazon Cognito. The following method illustrates how to retrieve an app user’s identity

4) At the main screen of the sample app, select User Data Storage and then Demo User File Storage feature.

step2-4a     step2-4b

You are now in the User File Storage demo and you’ll see example-image.png, which Mobile Hub provided to get started. You can now download the example image from Amazon S3 to your device.

5) To download an image from the list, select the file and then choose Download from the action item list as shown.

step2-5

Here’s the sample function that is used to download files from Amazon S3 to the mobile app.

You should now see a green check mark next to the example image, which indicates that it has been downloaded from Amazon S3 and cached locally in the app.

Now you can upload an image to Amazon S3 from your camera roll.

6) To upload a photo from your photo library to Amazon S3, select the action icon on the upper right of the screen and then choose Upload from within the User File Storage demo of the sample app.

user-storage-upload-1

7) The app will request access to your photo library. Select OK.

step2-7

8) Now the app has access to your camera roll and you can select a photo to upload to Amazon S3. Here, you can select a waterfall image.

step2-8

9) After you select an image, you are prompted to give your file a name. Give it a name and make sure you add .png to the end as shown so the app can recognize this as an image. Select Done and you’ll see the activity indicating the image is being uploaded to Amazon S3.

step2-9a   step2-9b

After the image upload completes, it’ll show in the list but without the green check mark, which indicates it’s now in the cloud (stored in your S3 bucket) but not cached locally in the app.

Here’s the sample iOS Swift function code used for uploading images from your app to Amazon S3.

What did we just do?
We just provisioned AWS backend resources consisting of an Amazon Cognito ID and Amazon S3 bucket using AWS Mobile Hub. We then built and launched the generated sample app. The app demonstrates the User Data Storage feature by uploading photos from your iOS device to Amazon S3 and downloading photos from Amazon S3 to your iOS device.

Are you done playing? We hope you enjoyed this tutorial. Stay tuned for the next installment where we’ll build on this idea and add user sign-in for more private storage and user data sync.

Are you interested in looking behind the curtain? To see that image you uploaded in the S3 bucket that was created for you, choose the Resources button on the left-hand navigation of the Mobile Hub console, choose the link in the tile labeled Amazon S3 buckets to open the Amazon S3 console, then choose the Public folder.

Planning to integrate this feature into your own app? No problem! In step 3, next, we’ll walk you through the Mobile Hub integration guide, helping you integrate all the code from the sample app into your own app.

Step 3 – Store and retrieve photos from your own app

You just configured a backend file storage and ran our generated sample app to demonstrate the end-to-end functionality of this common mobile feature. However, what you really need is the same functionality in your own app. Mobile Hub provides integration instructions specific to each mobile project you create. Let’s get started.

Go back to your Mobile Hub project in the console and select Integrate. The integration instructions from Mobile Hub will first walk you through Getting Started and then describe each feature you enabled in your Mobile Hub project and how to integrate each of them into your own app.

Here’s a quick overview of Getting Started and additional instructions for integrating this project into your own application.

The Mobile Hub Getting Started integration instructions include:

Download the Mobile SDK for iOS and custom source code provided

  • The download provides the latest SDK, Mobile Hub helper code, and example source code to get you started.

  • After downloading the SDK and helper code, drag and drop the Sdk folder contained in the download into your own project within Xcode. This assumes you already have an iOS Swift project of your own; if not, create a new one and follow along.
  • Add the AWS SDKs as Embed Frameworks.
  • Add Run Script phase to your project as directed.
  • Copy the AmazonAws folder from the helper code to your project and add a group for it. This source code contains a customized helper framework that simplifies the use of all the SDKs that are required to support your project.It also contains the AWSMobileClient.swift file, which bootstraps the app and creates an identity manager to establish the user identity with Amazon Cognito.
  • Copy the AWS dictionary from the integration Info.plist into your own project Info.plist. The AWS dictionary provides the CognitoIdentity poolId and region for your project.
  • Add AWS iOS SDK dependencies, libsqlite3.tbd and libz.tbd, under Linked Frameworks and Libraries as directed.
  • Set up your Application Delegate by returning the AWSMobileClient singleton as shown:
    You are now done with the Getting Started section.

After you complete Getting Started, you can then select User Data Storage in the left panel of the integration instructions. Here, you just need to do the following:

  • Copy the AWS dictionary from the integration Info.plist into your own project Info.plist. The AWS dictionary already provides all the CognitoIdentity pool IDs for your project as well as the S3 bucket and region.
  • View or copy the example upload/download source code provided. The idea is to use this code in your own UIViewController.

That’s it! You should now be able to run your own application and connect it to your AWS resources like we demonstrated using the sample app generated by AWS Mobile Hub.

Conclusion

We just configured a backend data storage feature via AWS Mobile Hub, generated a fully functional sample iOS app demonstrating uploading and downloading photos to and from S3, and then walked through integrating the client code into your own app, connecting to the same backend resources demonstrated in the sample app.

Thanks and happy coding!

Using webpack with the Amazon Cognito Identity SDK for JavaScript

by Marc Teichtahl | on | | Comments

This blog post is aimed at developers of all experience levels who develop and deploy JavaScript based applications (whether server-side with Node.js or client side) that incorporate the AWS SDK, the Amazon Cognito Identity SDK for JavaScript and who also use the popular webpack module bundler.

In July 2016, Amazon Web Services launched Amazon Cognito User Pools, a feature that makes it easy for developers to add sign-up and sign-in functionality to mobile and web applications. To help developers easily realize the power of user pools within their own applications, we also released the Amazon Cognito Identity SDK for JavaScript.

Amazon Cognito User Pools allows you to easily add user sign-up and sign-in to your mobile and web applications. The fully managed user pool can scale to hundreds of millions of users and you can have multiple directories per AWS account. Creating a user pool takes just a few minutes and allows you to decide exactly which attributes (including address, email, phone number as well as custom attributes) are mandatory and even optional when a new user signs up for your application or service. Your application can also specify the desired password strength, whether the use of Multi-Factor Authentication (MFA) is required, and verify new users via phone number or email address to further enhance the security of your application.

If you are new to the Amazon Cognito Identity SDK for JavaScript this AWS blog post is a great place to start.

Why Use Asset & Module Bundling with the Amazon Cognito Identity SDK for JavaScript

Today, modern web applications for both mobile and desktop have to provide the user with a secure, fast, responsive, and native-app-like experience. There is no doubt that modern browsers are extremely powerful and cater to a vast array of possible implementation approaches. Many of the more popular implementations rely heavily on the deployment of a JavaScript application through some form of asset packaging and/or module bundling. This allows a developer to take their JavaScript application and create one or more files that can be loaded by the client browser by using script tags.

There are many schools of thought on how you can achieve this packaging , including task runners such as Grunt and Gulp, and bundlers such as Browserify. However, there is a general consensus that asset packaging is not only about improving load times—it enables the modularization of your application while ensuring testability and robustness.

Using webpack with the Amazon Cognito Identity SDK for JavaScript

In the many requests we get to provide more detail on how to integrate the Amazon Cognito Identity SDK for JavaScript within a webpack environment, we’re specifically asked how to ensure that webpack correctly manages the following third-party dependencies:

Throughout these examples, the following bower libraries are used by bower.json

"aws-cognito-sdk": "https://raw.githubusercontent.com/aws/amazon-cognito-identity-js/master/dist/aws-cognito-sdk.js",
"amazon-cognito-identity": "https://raw.githubusercontent.com/aws/amazon-cognito-identity-js/master/dist/amazon-cognito-identity.min.js",
"sjcl": "https://raw.githubusercontent.com/bitwiseshiftleft/sjcl/master/sjcl.js",
"jsbn": "https://raw.githubusercontent.com/andyperlitch/jsbn/master/index.js",

For all the reasons we gave earlier for the importance of asset packaging to development processes, and unless your application is extremely small, the use of an asset packaging tool such as webpack is almost always recommended. Of course, one could simply pull in all of these dependencies using tags. However, this would pollute global namespace, and not provide the most optimal resource management and loading approach. Many developers start with a standard webpack.config.js file that has a standard babel loader, as shown here.

{
  /** test for file ending in js or jsx 
   * exclude node_module and bower_components - we dont want to babel these 
   * use the babel loader 
   * apply the react and es2015 (es6) transformations **/

  test: /.jsx?$/,
  exclude: /(node_modules|bower_components)/,
  loader: 'babel',
  query: {
    presets: ['react', 'es2015']
  }
}

It’s important to remember that this configuration doesn’t take into account that some of third-party dependencies used by the Amazon Cognito Identity SDK for JavaScript currently do not use the Universal Module Definition (UMD) pattern for JavaScript.

The UMD pattern attempts to offer Asynchronous Module Definition (AMD) based compatibility with the most popular script loaders of the day such as RequireJS and CommonJS.

This is a pattern that webpack relies on, and so we must make some changes to how webpack loads these modules. Without these changes, you may encounter errors such as the following.

amazon-cognito-identity.min.js:19 Uncaught ReferenceError: BigInteger is not defined

Such an error may be encountered when making a call to AWSCognito.CognitoIdentityServiceProvider.CognitoUser property authenticateUser This is an example of where we can make use of the webpack imports and exports loader capability to overcome this error.

Using webpack Loaders

According to the webpack documentation "loaders allow you to preprocess files as you require() or “load” them. Loaders are kind of like “tasks” are in other build tools, and provide a powerful way to handle front-end build steps. Loaders can transform files from a different language like, CoffeeScript to JavaScript, or inline images as data URLs"

In order to resolve the lack of UMD compatibility, you will rely to two specific loaders, import and export.

Using the Export Loader

In the case of the Amazon Cognito Identity SDK for JavaScript, we need to ensure we export theAWSCognito variables into the scope of the module that requires/imports (for ES6) them.

{
  test: /aws-cognito-sdk/index.js/,
  loader: 'exports?AWSCognito'
}

Using the exports loader has the effect of exporting a module method within bundle created by webpack. As a result, both AWSCognito and AWS are now accessible when required or import(ed) (for ES6).

var AWSCognito = require('aws-cognito-sdk')

/*** EXPORTS from export-loader ***/ 
module.exports = AWSCongito

More information about the exports loader can be found here

Using the Import Loader

The import loader is mostly used to inject (import) variables into the scope of another module. This is especially useful if third-party modules are relying on global variables like BitInteger or sjcl as is the case with Amazon Cognito Identity SDK for JavaScript.

If you don’t use the webpack loader, the following is generated within the bundle.

__webpack_require__(431);       // refers to jsbin
__webpack_require__(432);       // refers to sjcl

Beacuse neither jsbin or sjcl export anything, any calls that rely on these modules will result in an error.

To resolve this, we can use the following webpack loader configuration:

{
  test: /amazon-cognito-identity/index.js/,
  loader: 'imports?jsbn,BigInteger=>jsbn.BigInteger,sjcl'
},
{
  test: /sjcl/index.js/,
  loader: 'imports?sjcl'
}

This injects the following into the bundle (in this case bundle.js) created by webpack.


/*** IMPORTS FROM imports-loader ***/
var jsbn = __webpack_require__(431);
var BigInteger = jsbn.BigInteger;
var sjcl = __webpack_require__(432);

As a result, jsbn, BigInteger and sjcl are all imported from their respective modules into Amazon Cognito Identity SDK for JavaScript.

More information about the import loader can be found here

Next Steps

We encourage you to download the Amazon Cognito Identity SDK for JavaScript and start building your application. Coupled with webpack, and by following the guidance in this blog, you we hope you have a smooth development experience.

If you have any comments or questions, please free to comment below, reach out via email (teichtah@amazon.com) or raise an issue here.

References

This blog post makes reference to the following third party resources

Announcing the AWS SDK for React Native

by Rohan Deshpande | on | | Comments
We’re excited to announce the immediate availability of the developer preview of the AWS SDK for React Native. The source code is available on GitHub under the Apache 2.0 license.
 
The SDK includes support for the following services:
  • Amazon S3 to store user data, including photos and videos, in the cloud. It uses the TransferUtility, which simplifies file transfers between your app and the cloud.
  • Amazon DynamoDB to store data into a NoSQL database.
  • AWS Lambda to run serverless code in the cloud without the need for backend infrastructure.
  • Amazon SNS to send and receive push notifications.
The SDK core uses Amazon Cognito Identity as the authentication provider to comply with best practices for mobile app development.
 

Getting Started with the SDK

To include the SDK in your React Native application:
  1. Download the packages from Github.
  2. Add the services that you need into your package.json.
  3. Run npm install.
Here is a sample package.json that uses AWS Lambda. 
 
% cat package.json
{
  "name": "sample",
  "version": "0.0.1",
  "scripts": {
    "start": "node node_modules/react-native/local-cli/cli.js start"
  },
  "dependencies": {
    "aws-sdk-react-native-core": "../aws-sdk-react-native-core-0.0.1.tgz",
    "aws-sdk-react-native-lambda": "../aws-sdk-react-native-lambda-0.0.1.tgz",
    "react": "15.3.1",
    "react-native": "0.32.0"
  }
}
 	
See the GitHub repository for sample apps that demonstrate how to use the Cognito functionality to authenticate and the TransferUtility to transmit files to S3.
 

Contributing to the SDK

We welcome issue reports and contributions to the AWS SDK for React Native. Please report issues using the Github issue tracker. To contribute,  submit a pull request to the GitHub repository with a description of your issue or suggested change. If this is a bug fix, please reference the issue and include steps to reproduce it.

 

Tracking and Remembering Devices Using Amazon Cognito Your User Pools

by Jeff Bailey | on | | Comments

Introduction

With the general availability launch of Amazon Cognito Your User Pools, we introduced a new feature that enables device tracking and remembering. This feature provides insight into the usage of your app’s users and reduces the friction associated with multi-factor authentication (MFA). This blog post provides an overview of the feature, identifies the primary use cases, and describes how to set up the feature for your application.

 

Use cases

First, let’s take a look at some of the primary use cases for device remembering. The following examples are not exhaustive, but we use them in this blog post to illustrate the functionality.

This feature enables developers to remember the devices on which end users sign in to their application. You can see the remembered devices and associated metadata through the console and by using the ListDevices and GetDevice APIs. In addition, you can build custom functionality using the notion of remembered devices. For example, with a content distribution application (e.g., video streaming), you can limit the number of devices from which an end user can stream their content.

This feature works together with MFA to reduce some of the friction end users experience when using MFA. If SMS-based MFA is enabled for an Amazon Cognito user pool, end users must input a security code received via SMS during every sign-in in addition to entering their password. This increases security but comes at the expense of user experience, especially if users must get and enter a code for every sign-in. By using the new device remembering feature, a remembered device can serve in place of the security code delivered via SMS as a second factor of authentication. This suppresses the second authentication challenge from remembered devices and thus reduces the friction users experience with MFA.


Console setup

The following image shows how you can enable device remembering from the Amazon Cognito console.


 

The specifics of these configurations shown above can be made clearer by going over some terminology first.
 

Tracked

When devices are tracked, a set of device credentials consisting of a key and secret key pair is assigned to every device. You can view all tracked devices for a specific user from the Amazon Cognito console device browser, which you can view by choosing a user from the Users panel. In addition, you can see some metadata (whether it is remembered, time it began being tracked, last authenticated time, etc.) associated with the device and its usage.
 

Remembered

Remembered devices are also tracked. During user authentication, the key and secret pair assigned to a remembered device is used to authenticate the device to verify that it is the same device that the user previously used to sign in to the application. APIs to see remembered devices have been added to new releases of the Android, iOS, and JavaScript SDKs. You can also see remembered devices from the Amazon Cognito console.
 

Not Remembered

A not-remembered device is the flipside of being remembered, though the device is still tracked. The device is treated as if it was never used during the user authentication flow. This means that the device credentials are not used to authenticate the device. The new APIs in the AWS Mobile SDK do not expose these devices, but you can see them in the Amazon Cognito console.

 

Now, let’s go over the first configuration setting: Do you want to remember devices?
 

No (default) – By selecting this option, devices are neither remembered nor tracked.
 

Always – By selecting this option, every device used by your application’s users is remembered.
 

User Opt-In – By selecting this option, your user’s device is remembered only if that user opts to remember the device.  This configuration option enables your users to decide whether your application should remember the devices they use to sign in, though keep in mind that all devices are tracked regardless. This is a particularly useful option for a scenario where a higher security level is required but the user may sign in from a shared device; for example, if a user signs in to a banking application from a public computer at a library. In such a scenario, the user requires the option to decide whether their device is to be remembered.

The second configuration appears if you selected either Always or User Opt-In for the first configuration. It enables your application to use a remembered device as a second factor of authentication and thus suppresses the SMS-based challenge in the MFA flow. This feature works together with MFA and requires MFA to be enabled for the user pool. The device must first become remembered before it can be used to suppress the SMS-based challenge, so the first time a user signs in with a new device, the user must complete the SMS challenge; subsequently, the user does not need to complete the SMS challenge.

 

A deeper dive on device identification

As described previously, the device is identified and authenticated with a key and secret key credentials pair.

The path to getting these credentials is as follows:

1)      Every time the user signs in with a new device, the client is given the device key at the end of a successful authentication event.

2)      From this key, the client creates a secret, using the secure remote password (SRP) protocol, and generates a salt and password verifier.

3)      With that salt, verifier, and the key it was originally given, the client calls the ConfirmDevice API remotely. It is only then that Amazon Cognito begins tracking this device. During this entire flow, the secret remains on only the physical device.

The AWS Mobile SDKs for Android, JavaScript, and iOS support this flow implicitly – you do not need to do anything to make device confirmation work. It happens in the background of user authentication.

If you choose to have your users’ devices always remembered, then confirming the device marks it as remembered and begins tracking. If users must opt in to remember a device, confirming begins tracking the device as a not-remembered device. The response the client gets from that call indicates to the client that it must ask users if they want to remember the device. Each SDK can take a callback during the authentication call, which defines how users are asked if they want to remember the device. This authentication call consumes the UpdateDeviceStatus API. That API can be called any time to update the status as needed. The mobile SDKs use convenience wrappers around this method to make the calls more intuitive.
 

Android:

	// Create a callback handler to remember the device
	GenericHandler changeDeviceSettingsHandler = new GenericHandler() {
	    @Override
	    public void onSuccess() {
	        // Device status successfully changed
	    }

	    @Override
	    public void onFailure(Exception exception) {
	        // Probe exception for the cause of the failure
	    }
	};

	// To remember the device
	device.rememberThisDevice(changeDeviceSettingsHandler);
	// To not remember the device
	device.doNotRememberThisDevice(changeDeviceSettingsHandler);

 

iOS:


	// To remember a device
	[self.user updateDeviceStatus:YES] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserUpdateDeviceStatusResponse*> * _Nonnull task) {
	    //Do something with task result here
	    return nil;
	}];

	// To not remember a device.
	[self.user updateDeviceStatus:NO] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserUpdateDeviceStatusResponse*> * _Nonnull task) {
	    //Do something with task result here
	    return nil;
	}];

JavaScript:

	cognitoUser.setDeviceStatusRemembered({
	    onSuccess: function (result) {
	        console.log('call result: ' + result);
	    },

	    onFailure: function(err) {
	        alert(err);
	    }
	});

	cognitoUser.setDeviceStatusNotRemembered({
	    onSuccess: function (result) {
	        console.log('call result: ' + result);
	    },

	    onFailure: function(err) {
	        alert(err);
	    }
	});

The credentials provided to the device should be persistent and will be stored by the AWS Mobile SDKs, so any logic you build on top of the device key as an identifier can assume it will not change. The only way it could change is if the user wipes the device’s storage, if the user uninstalls the application, or if the ForgetDevice API is called for a device (this API removes all tracked devices for a user). If any of those occur, the next authentication treats the device as if it had never been used before.

If a device is remembered, the device credentials are authenticated as part of the user authentication flow, using the SRP protocol. This authentication verifies that the key the service was provided is one that it generated itself, from the user it was generated for, and from the device it was given to.

A successful authentication by a user generates a set of tokens – an ID token, a short-lived access token, and a longer-lived refresh token. The access token only works for one hour, but a new one can be retrieved with the refresh token, as long as the refresh token is valid. With device tracking, these tokens are linked to a single device. If a refresh token is used on any other device, the call fails. In a scenario where, for example, a device is stolen, the ForgetDevice API can be used to forget that specific device, and as a result, all future calls to revalidate that device’s refresh tokens will fail.
 

How can you use this device identifier?

There are a few APIs exposed on the client SDKs that enable you to see the remembered devices for the user that is currently signed in. Users must be signed in to view their devices because all APIs are authenticated with an access token.

First, if you want to get a single device’s metadata, you call GetDevice as shown in the following examples.
 

Android:

	DevicesHandler getDeviceHandler = new DevicesHandler() {
	    @Override
	    public void onSuccess(List<CognitoDevice> devices) {
	        // This list will have one device in it, and will update the
	        // current device (currDevice)
	    }

	    @Override
	    public void onFailure(Exception exception) {
	        // Check exception for the cause of failure.
	    }
	};
	currDevice.getDevice(getDeviceHandler);

	// If you want to get the current device’s metadata
	user.thisDevice();


iOS:

	[self.user getDevice] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserGetDeviceResponse *> * _Nonnull task) {
	    //Do something with task result here
	    return nil;
	}];

JavaScript:

	cognitoUser.getDevice({
	    onSuccess: function (result) {
	        console.log('call result: ' + result);
	    },

	    onFailure: function(err) {
	        alert(err);
	    }
	});

If you want to list all remembered devices for a user, you call the ListDevics API.

Android:

	DevicesHandler listDevicesHandler = new DevicesHandler() {
	    @Override
	    public void onSuccess(List<CognitoDevice> devices) {
	        // devices contains the list of all devices that are remembered
	    }

	    @Override
	    public void onFailure(Exception exception) {
	       // Check exception for the cause of failure.
	    }
	};

	user.listDevices(listDevicesHandler);

iOS:

	//The first parameter is page size, second is paginationToken from previous call
	[self.user listDevices:10 paginationToken:nil] continueWithSuccessBlock:^id _Nullable(AWSTask<AWSCognitoIdentityUserListDevicesResponse *> * _Nonnull task) {
	    //Do something with task result here
	    return nil;
	}];

JavaScript:

	cognitoUser.listDevices(limit, paginationToken, {
	    onSuccess: function (result) {
	        console.log('call result: ' + result);
	     },

	    onFailure: function(err) {
	        alert(err);
	    }
	});


If a user wants to stop tracking a device that is currently remembered, you call the ForgetDevice API.
 

Android:

	DevicesHandler forgetDeviceHandler = new DevicesHandler() {
	    @Override
	    public void onSuccess(List<CognitoDevice> devices) {
	        // this will forget whatever currDevice is. If it’s the
	        // physical device, it will also clear the local tokens.
	    }

	    @Override
	    public void onFailure(Exception exception) {
	        // Check exception for the cause of failure.
	    }
	};

	currDevice.forgetDevice(forgetDeviceHandler);

iOS:

	[self.user forgetDevice] continueWithSuccessBlock:^id _Nullable(AWSTask* _Nonnull task) {
	    //Do something with task result here
	    return nil;
	}];

JavaScript:

	cognitoUser.forgetDevice({
	    onSuccess: function (result) {
	         console.log('call result: ' + result);
	     },

	     onFailure: function(err) {
	         alert(err);
	     }
	});

From the console, if you search for a user or choose a user that is on the user list, you can see every device tracked for that user with its key, name, the IP used when last authenticated, whether or not the device is remembered, the AWS SDK it used, and the time it last authenticated.

 

 

Limiting devices per user

You can use this feature if, for example, you are developing a content distribution app and want to limit the number of devices that users can connect to their account.

To enable this use case, we have included an extra parameter in our inputs to our post-authentication AWS Lambda function: newDeviceUsed. It is a Boolean flag that is only true if you have device remembering turned on and if the device being used to authenticate is a new device.

With this small addition, you can set a maximum for the number of devices that can be linked to a user’s account. Within your post-authentication Lambda hook, you can call the AdminListDevices API to count the number of devices currently linked if the newDeviceUsed flag is set to true. If it is over your determined limit, you fail the call, which then fails the authentication.

 

Conclusion

As you can see, integrating device remembering into your mobile and web applications is straightforward using the AWS Mobile SDKs. Device remembering enables you to create an experience that is designed to provide security and a user-friendly experience for your users. We’d love to hear how you plan to use this feature in your applications, so feel free to leave a comment to share other uses for this feature.

If you encounter issues or have comments or questions, please leave a comment, visit our forums, or post on Stack Overflow

 

AWS Mobile Hub Helper Code for iOS is now Available on GitHub

by Karthik Saligrama | on | | Comments

In March 2016 we announced Swift support for AWS Mobile Hub. To make it easier for developers to integrate, we moved the code into a single framework. A side effect of that move was that the source code was no longer included in the project download. Customers could not update the source code of the framework if they wanted to extend the features AWS Mobile Hub already supports.

Last week, we open-sourced the AWS Mobile Hub helper code on GitHub. You can find the source here and the API reference documentation here. To download the binary (framework) please continue to use the AWS Mobile Hub Console. From your AWS Mobile Hub project, choose Build, choose iOS Obj-C or iOS Swift, and download the source package.

Building the Source

If you decide to make changes to the source code, you can build the framework using the scripts located here. Here are the steps:

1) Clone the repo https://github.com/aws/aws-mobilehub-helper-ios.git in any directory using:

	git clone https://github.com/aws/aws-mobilehub-helper-ios.git

2) Change the directory to cd aws-mobilehub-helper-ios/. The Source code uses CocoaPods for dependency management. You can install CocoaPods by using sudo gem install cocoapods. Run Pod Install from the root directory. This installs all of the dependencies for the project. After the installation is complete, you should get the following message:

 

	Analyzing dependencies Downloading dependencies
	Installing AWSCognito (2.4.7)
	Installing AWSCore (2.4.7)
	Installing AWSLambda (2.4.7)
	Installing AWSS3 (2.4.7)
	Installing AWSSNS (2.4.7)
	........
	Integrating client project Sending stats Pod installation complete!
	There are 7 dependencies from the Podfile and 14 total pods installed.

3)You can now run the following scripts from your project root directory using

	./Scripts/GenerateHelperFramework.sh

This generates a static framework in the <ProjectRoot>/builtframework/framework and you can include it in your project. If you decide to update the version of any dependency, make sure that the framework file for that dependency is also updated in your project.

Contributing

You can contribute to AWS Mobile Hub helper code by submitting pull requests on our GitHub repo. However, there are a few restrictions. If you would like to add an entirely new feature, submit feedback on the console for the new feature, using the feedback link on the bottom left corner of the console. Everything else, like minor patches and bug fixes, can be via pull requests.

Find, Try, and Purchase Mobile Software in AWS Marketplace

by Rob Lipschutz | on | | Comments

AWS Marketplace has worked with ISVs throughout our ecosystem to introduce new mobile software in AWS Marketplace throughout the past several months. This is software that helps enterprises, mid-market companies, and startups create and manage mobile apps for their customers or employees.

The Mobile Factory

With more than 30 new mobile products from over 20 different ISVs categorized by the three phases of the mobile application development process: Build & Launch, Secure & Integrate, and Launch & Manage, you’ll find software options for every phase of your project. Check out the AWS Marketplace Mobile Factory Web Page where products in each stage are highlighted with links to their product listing page in AWS Marketplace. You’ll find products in easy-to-deploy Amazon Machine Images (AMIs) and SaaS, paid and open source, and products suitable for startups or large enterprises. We call this the AWS Marketplace Mobile Factory because AWS Marketplace provides the components to help you construct and launch Mobile Apps more efficiently.

AWS Marketplace Mobile Factory: Mobile Software for the Entire Mobile App Lifecycle

Product Highlights

For example, you can find enterprise mobile frameworks from Kony to build mobile apps that connect to back-end systems and innovative stacks from Bitfusion for integrating video, deep learning, and imaging into mobile apps by off-loading these compute intensive tasks to AWS compute services. You can find open source software such as Phone Gap, Cordova, and Ionic from GlobalSolutions available as a complete stack that installs in 2 clicks into your AWS environment. For testing before and after deployment, you’ll find HPE’s suite of products including StormRunner Load and AppPulse Mobile. For securing Apps, Proofpoint makes its Mobile Defense product available in AWS Marketplace. You’ll also find listings for Apigee Edge, a sophisticated API management system, and a number of analytics and prediction tools like New Relic, Arimo, and AppDynamics.

AWS Marketplace Web site

Integration with AWS

AWS Marketplace software extends and complements AWS Mobile Services and AWS services. For example, Bitfusion’s products can leverage the specialized g2.2xlarge and g2.8xlarge Amazon EC2 instances for its media tasks and Apigee Edge works together with AWS Lambda. All products run on EC2 instances within a customer’s AWS infrastructure or as a SaaS offering leveraging AWS infrastructure.

Given your specific requirements and the myriad of products available, it’s helpful to have a place where you can quickly try software that runs on AWS. Flexible pricing for many listings includes pay as you go hourly pricing, and many listings have free trials (AWS usage fees may apply) to get started. These pricing options give AWS customers the opportunity to evaluate products more cost-effectively. This flexibility and reversibility makes AWS Marketplace a great place to experiment for Agile developers.

Please send your questions or suggestions for mobile software in AWS Marketplace to aws-marketplace-mobile@amazon.com. If you are an ISV with a mobile product, check out the informative blog post “How to List Your Product in AWS Marketplace” written by Suney Sharma on the AWS Partner Network (APN) blog.

Amazon Mobile Analytics Auto Export to Amazon S3 – Feature Update

by Georgie Mathews | on | | Comments

Amazon Mobile Analytics Auto Export to Amazon S3 accumulates and exports events sent to the Amazon Mobile Analytics service from your mobile and web applications into your own Amazon S3 bucket, within one hour from when we receive the event. This allows you to access the full data being recorded by your application to perform additional detailed analysis in addition to the analytics automatically produced in the Amazon Mobile Analytics console, such as Daily Active Users (DAU), Monthly Active Users (MAU), Average Revenue per Daily Active User and other out-of-the-box and custom metrics.

 

What’s New?

We’re making feature improvements to how data is processed after we receive it. Before these changes, files were written three times to ensure all data are published to your Amazon S3 bucket. Going forward, files will generally be written only a single time which will reduce the inbound bandwidth to your Amazon S3 bucket. This will also reduce the average amount of time it takes for Auto Export to Amazon S3 to deliver files into your Amazon S3 bucket. If you are an existing customer of this feature, make sure to read our forum announcement regarding a small change to how the files are named. Please note that the structure of the files is not changing and there are no changes in how the Auto Export to Amazon Redshift feature works.

 

How do I start using it?

If you’re already using Amazon Mobile Analytics but have not enabled Auto Export to Amazon S3, log in to AWS and navigate to the Amazon Mobile Analytics console to enable the feature for one or more of your applications.  You can also choose to have your data exported to Amazon Redshift while you’re there. You can find the steps to enable Auto Export here.

If you haven’t integrated Amazon Mobile Analytics into any of your applications yet, check out our User Guide and get your first Android, iOS, JavaScript, Unity, Xamarin, or custom application integrated today!

 

FAQs

1. Which development platforms do you support?

We have SDKs to integrate with the following platforms: iOS, Android, JavaScript, Unity, and Xamarin. We also support REST API access.

 

2. How do I export data collected from before I enabled Auto Export?

At this time, the Amazon Mobile Analytics Auto Export to Amazon S3 feature only supports data export on a forward basis.  Data collected prior to enabling this feature is not available for export to Amazon S3 or Amazon Redshift, however, it is used to calculate the analytic metrics that are accessible via the Amazon Mobile Analytics console.

 

3. How are the Amazon S3 file names generated by Auto Export to Amazon S3 changing?

The current file name pattern is as follows:

<bucket-name>/awsma/events/<appId>/<YYYY>/<MM>/<DD>/<hh>/<appId>-<mm>-part-<partNum>.gz

After the change, the file name pattern will be:

<bucket-name>/awsma/events/<appId>/<YYYY>/<MM>/<DD>/<hh>/<appId>-<mm>-part-<partNum>-<hexCode>.gz


 

Test User Interfaces in iOS Apps with XCTest UI and AWS Device Farm

by Asha Chakrabarty | on | | Comments

With AWS Device Farm, you can quickly start testing your Android, iOS, and FireOS apps on real devices in the AWS Cloud. Choose to start an interactive session with a device or run automated tests on many devices at once. AWS Device Farm will provide the results of your tests including pass/fail status, logs, performance metrics, screenshots, and videos.

Introduction to XCTest UI

As of Xcode 7, you can access UI testing capabilities (XCUI) integrated into the Xcode IDE. This functionality allows you to find and interact with UI elements in your app and confirm their properties and states. A few new features make it possible for you to programmatically test and exercise the UI of an iOS app with:

  • New Xcode target type for UI tests:

To set up a UI test in Xcode, you create an Xcode target with the iOS UI Testing Bundle template. The Xcode target type fulfills the special requirements required by the UI tests, including launching a proxy for the application in a separate process and providing accessibility permissions.

  • UI testing APIs include three key classes:

    • XCUIElementQuery: Every UI element is backed by the ability to query its properties. For this reason, each XCUIElement must be unique.
    • XCUIElement: A proxy object for a UI element that is represented as types (for example, a cell or a button).
    • XCUIApplication: An instantiation of your application object that forms the origin for finding UI elements.
  • UI recording:

This allows you to record interactions with your app’s user interface. Xcode will transform these interactions into source code that can be included in your existing tests or to create new tests. 

AWS Device Farm now allows you to run the UI Testing feature incorporated in Xcode 7 on real devices in the AWS Cloud. In this post, we will walk you through how to create an XCTest UI test, package it for testing on AWS Device Farm, schedule a run, and view test results from real devices in the cloud.

Prerequisites

  • You’ll find the sample iOS app used in this post on AWS Labs on GitHub.
  • UI Testing was introduced in Xcode 7 and iOS 9, so be sure to update accordingly.
  • iOS devices must be enabled for development and connected to a host running Xcode.
  • It is assumed that you have created the .ipa file for the sample iOS app before you schedule a run in AWS Device Farm.

Step 1: Create a UI Test for the AWS Sample iOS App

After you have downloaded and opened the sample app in Xcode, build the project. After the build is successful, you will create a new a target type for the UI tests.

Your project navigator should look like the following:

With UI testing, you can record interactions within your app and Xcode will write the code required to re-enact those interactions in your test. You will still need to use XCTAssert to add your test assertions. You can record interactions with your UI by pressing the record button (the small red dot at the bottom left corner of the editor pane).

Copy the following UI test code to your AWSDeviceFarmiOSReferenceAppUITests.m implementation file.

#import 

@interface AWSDeviceFarmiOSReferenceAppUITests : XCTestCase

@end

@implementation AWSDeviceFarmiOSReferenceAppUITests

- (void)setUp {
    
    [super setUp];
    self.continueAfterFailure = NO;
    [[[XCUIApplication alloc] init] launch];
    
}


- (void)tearDown {
    
    [super tearDown];
}


- (void)testNativeInput {
    
    XCUIApplication *app = [[XCUIApplication alloc] init];
    XCUIElementQuery *tabBarsQuery = app.tabBars;
    [tabBarsQuery.buttons[@"Native"] tap];
    
    XCUIElementQuery *collectionViewsQuery = app.collectionViews;
    [collectionViewsQuery.staticTexts[@"Table of elements"] tap];
    [app.navigationBars[@"ElementsTableView"].buttons[@"Menu"] tap];
    [collectionViewsQuery.staticTexts[@"Scrolling View"] tap];
    [app.navigationBars[@"Scrolling View"].buttons[@"Menu"] tap];
    [tabBarsQuery.buttons[@"Home"] tap];
    
}


- (void)testNestedView {
    
    
    XCUIApplication *app = [[XCUIApplication alloc] init];
    XCUIElementQuery *tabBarsQuery = app.tabBars;
    [tabBarsQuery.buttons[@"More"] tap];
    [app.staticTexts[@"Nested"] tap];
    
    XCUIElement *moreNavigationBar = app.navigationBars[@"More"];
    XCUIElement *nextButton = moreNavigationBar.buttons[@"Next"];
    [nextButton tap];
    [nextButton tap];
    [nextButton tap];
    
    XCUIElement *backButton = [[[moreNavigationBar childrenMatchingType:XCUIElementTypeButton] matchingIdentifier:@"Back"] elementBoundByIndex:0];
    [backButton tap];
    [backButton tap];
    [backButton tap];
    [moreNavigationBar.buttons[@"More"] tap];
    [tabBarsQuery.buttons[@"Home"] tap];
    
}


- (void)testAlertControl {
    
    
    XCUIApplication *app = [[XCUIApplication alloc] init];
    XCUIElementQuery *tabBarsQuery = app.tabBars;
    [tabBarsQuery.buttons[@"More"] tap];
    [app.staticTexts[@"Alerts"] tap];
    [app.buttons[@"Modal"] tap];
    [app.buttons[@"OK"] tap];
    [app.buttons[@"Alert"] tap];
    [app.alerts[@"Alert"].collectionViews.buttons[@"OK"] tap];
    [app.navigationBars[@"More"].buttons[@"More"] tap];
    [tabBarsQuery.buttons[@"Native"] tap];
    [app.collectionViews.staticTexts[@"Image Gallery"] tap];
        
}

@end

Before packaging your test for AWS Device Farm, be sure to build the project with Xcode. Use the Product/Build for Running option select an iOS device. Keep in mind that the Build Active Architecture setting for your app and your UI test targets should be the same.

Step 2: Package Your Test for AWS Device Farm

When packaging your test for upload to AWS Device Farm, make sure your iOS XCTest UI Runner test runner bundle is contained in a correctly formatted .ipa file. For more information, see the AWS Device Farm documentation. You can also view the creation of an .ipa file on AWS Labs on GitHub.

Make sure that you package your test in the Payload folder under debug-iphoneos as shown here. In the following screenshot, we renamed the resulting zip file of the Payload folder to UITest.ipa for easier file management.

Step 3: Schedule a Run in AWS Device Farm

Sign in to the AWS Device Farm console and create a run under a new or existing project. Upload the .ipa file of the sample app.

In the next step, you will choose XCTest UI as the test type and upload the .ipa file you created in step 2.

Select the devices on which you’d like to test. If you like, you can create a new device pool for your test run to reuse in subsequent runs.

Finally, review and start the test run.

Step 4: View XCTest UI Test Results

When your test run is complete, you will see the test results summary.

Choose a device to examine its test suites. Here we are reviewing the test suite results for an Apple iPhone 5s and Apple iPhone 6 device.

The results for each device will contain screenshots, video recordings, performance data, and log files that can be downloaded for further review.

Conclusion

We are always happy to hear your feedback. Feel free to leave your feedback, including questions, in the comments or on our developer forum.

Happy testing!