Using Digits with Amazon Web Services

Wednesday, 15 July 2015

I recently went through the exercise of building 30 apps in 30 days. It was a fantastic way to be more disciplined about writing efficient mobile apps, and learning to find and apply the best tools and services for the job at hand.

As with many apps these days, a few of my own needed a combination of back-end services, including authentication, sharing, data storage and analytics. I ended up trying a number of different platforms, and quickly found the AWS platform to be easy to use, highly scalable and simple to manage.

Moreover, AWS is very complementary with Twitter’s own Digits platform for SMS authentication. My colleague Ryan Choi and I carved out the relevant integration pieces for an Android app into it’s own GitHub repo and reference this code throughout the following walkthrough.

Login with Digits

One of my go-to features to add to my apps is sign-in via phone number. It’s an elegant solution to onboard new users to your service. As part of my job, I meet with mobile startups and Fortune 500 companies alike, and see phone identity becoming the primary identity for apps across the world.

In fact, Twitter originally solved phone-based login for their own mobile app, and scaled it to work across 216 countries. This involved tackling all the challenges and complexities of SMS onboarding and carrier negotiations that go along with Twitter-scale.

With the launch of Digits by Twitter, we’ve made it free and available to developers on iOS, Android and web. Adding Digits is one of the first things I do for every new app I create. For Android, adding the library involves updating your gradle build configuration to include the Fabric and the Digits Kit itself:

buildscript {
  repositories {
    jcenter()
    maven { url 'https://maven.fabric.io/public' }
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:1.0.+'
    // The Fabric Gradle plugin uses an open ended version to react
    // quickly to Android tooling updates
    classpath 'io.fabric.tools:gradle:1.+'
  }
}

apply plugin: 'com.android.application'
//Put Fabric plugin after Android plugin
apply plugin: 'io.fabric'

repositories {
    jcenter()
    maven { url 'https://maven.fabric.io/public' }
}


dependencies {
    compile('com.digits.sdk.android:digits:1.6.0@aar') {
        transitive = true;
    }
}

From here, you add the Digits button to the layout:

<com.digits.sdk.android.DigitsAuthButton
     android:id="@+id/auth_button"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content" />

And then the actual Java code is simple, specifying a callback on success:

// Create a digits button and callback
DigitsAuthButton digitsButton = (DigitsAuthButton) findViewById(R.id.auth_button);
digitsButton.setCallback(new AuthCallback() {
	@Override
	public void success(DigitsSession session, String phoneNumber) {
   	println(phoneNumber)
	}

	@Override
	public void failure(DigitsException exception) {
    		// Do something on failure
	}
});

Authentication to AWS stack via Cognito

With user sign-in solved, the next challenge is to store the credentials and other data somewhere. The Amazon Web Service (AWS) platform has a full suite of server and database storage solutions, but the first step is authenticating to their services.

This is where Amazon Cognito comes into play. By passing the returned Digits auth tokens to Cognito, you can start making calls to other back-end AWS services. And it’s as simple as this:

// Setup Cognito
final CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(
    	this, // get the context for the current activity
    	"YOUR_COGNITO_IDENTITY_POOL_ID", // your identity pool id
    	Regions.US_EAST_1 //Region
);

// Create a digits button and callback
DigitsAuthButton digitsButton = (DigitsAuthButton) findViewById(R.id.auth_button);
digitsButton.setCallback(new AuthCallback() {

	@Override
	public void success(DigitsSession session,
                    	String phoneNumber) {

    	TwitterAuthToken authToken = (TwitterAuthToken)session.getAuthToken();
    	String value = authToken.token + ";" + authToken.secret;
    		Map logins = new HashMap();
    	logins.put("www.digits.com", value);

    		// Store the data in Amazon Cognito
    	credentialsProvider.setLogins(logins);
	}

});

With those simple additions, I’m ready to make server calls directly from my app.

Simple back-end services with Lambda

When I learned about Amazon Lambda, I was amazed at the simplicity of configuration and ease of scale. They’ve really taken to heart the design patterns of Node.js to heart. The AWS console lets you drop in JavaScript directly, and even test it in the same page:

Using Digits with Amazon Web Services

The code I include in the Github repo expects a simple JSON object of phone number, id and access tokens.

console.log('Loading event');
var AWS = require('aws-sdk');

exports.handler = function(event, context) {
	
	console.log('Received event:' + JSON.stringify(event));
	
	var phoneNumber = event.phoneNumber;
	var id = event.id;
	var accessToken = event.accessToken;
	var accessTokenSecret = event.accessTokenSecret;
	
	...
};

Clicking on “Save” deploys it to servers automatically, and it auto-scales to 10K instances effortlessly.

Making calls to Lambda from the Android app are almost equally easy, although not as terse. It includes defining a PhoneInfo message object, an Interface, and invoking it with the LambdaInvokerFactory, included in the AWS libraries. I’ll leave the mechanics to the Github repo.

Highly scalable data storage with DynamoDB

Once the information gets to Lambda, storing and retrieving it is practically trivial. Amazon has a host of storage solutions, including S3 and MySQL, but I’ve been playing around with schemaless stores lately. DynamoDB is the one that just works, with a 1-minute setup and near seamless integration with Node.js. In the interface, I create an instance with the name “digits-with-lambda”, pre-define a primary key index and hit “Save”.

Using Digits with Amazon Web Services

Taking the previous Node.js code, here’s the additions for including my DynamoDB library and inserting directly into it:

console.log('Loading event');
var AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB({params: {TableName: "digits-with-lambda"}});

exports.handler = function(event, context) {
	
	console.log('Received event:' + JSON.stringify(event));
	
	var phoneNumber = event.phoneNumber;
	var id = event.id;
	var accessToken = event.accessToken;
	var accessTokenSecret = event.accessTokenSecret;
	
	var item = {Item: {
  phoneNumber: {S: phoneNumber},
  id: {N: id + ""},
  accessToken : {S: accessToken},
  accessTokenSecret : {S: accessToken}, 
}};
	
	dynamodb.putItem(item, function(err, data) {
    	if (data) {
	console.log('db success: ' + JSON.stringify(data));
            	context.succeed('Hello AWS NYC');  // Echo back the response
    	} else {
        	console.log('db error: ' + err);
            	context.succeed('There was an error with your request: '
   + JSON.stringify(event));  // Echo back the response
    	}
	});
	
};

I want to point out a few things with the above code:

  • Connecting to DynamoDB was one line. Amazing.
  • The putItem() function takes a JSON object with the field types included in the values (Those “S” and “N” bits)
  • getItem() works as you’d expect, and it’s just as easy.
  • context.succeed() returns directly to the Android client, in this case a simple String.

Closing thoughts

The design pattern described above with Digits for sign in and AWS for back-end and storage can be of great value to mobile developers. Moreover, this architecture could be a great starting point for many other use cases, including:

  • Protect against bot/spam abuse through phone verification
  • Connect with existing CRM solution to pre-populate user experience (Frequent Flyer status, Rewards member, etc.)
  • Track in-app activity for personalization or A/B testing

Having services like Lambda and DynamoDB make it easy for mobile developers to build and iterate on great apps with little back-end configuration and maintenance. If you want to give it a go, check out the Github repo and send me a note on Twitter if you have any questions.

Gareth Paul Jones is a Developer Advocate at Twitter. Read more updates of his work on his Medium blog.