React Native Authentication in Depth

Implementing real world production tested multi factor authentication in a React Native application.

This tutorial uses AWS Mobile Hub + AWS Amplify + Amazon Cognito
To view the video tutorials for this post, click here
To view the repo for the final project we will be building, click here.
To view Part 2, click here.

Over the past year or so, I have been working with many companies around the world as a consultant with React Native Training. By far the number one question was how to properly implement real world user authentication.

There are many reasons for this, but it really comes down to two major ones in my opinion:

  1. Many developers & companies are coming into React Native from the web where routing and navigation differ substantially.
  2. State management matters a lot when reasoning about authentication in a React Native Application, and many developers & companies coming into React Native are learning both state management as well as navigation at the same time that they are learning the framework.

Even for seasoned React Native developers, implementing authentication with React Native has not been easy or straight forward up until now.

In this post, I hope to greatly simplify what has been up until this point a somewhat complicated task using some state of the art tooling along with clear and concise instructions.

This is a two part series.

In Part 1, we will wire up full user sign up and sign in using a real service, Amazon Cognito with AWS Amplify, allowing users to sign in with username + Password with two factor phone authentication.

This will get you most of the way as far as authentication functionality, while part two will focus on Navigation and state flow.
Amplify supports many category scenarios such as Auth, Analytics, APIs and Storage as outlined in this Developer Guide, but we are really only worried about Auth for purposes of this post.

In Part 2, we will continue using this project and will add a real world navigation flow that I have used many times in production as well as some nice styling. This will include Redux + React-Navigation.

To see the code for the final version of what we will be building in part 1, check out this repo

Getting Started

The first thing we need to do to get started is create a new React Native app and install any dependencies we will be needing:

react-native init RNAuth
You can also use create-react-native-app with Expo, as the expo team has integrated Amplify into the Expo SDK. If you choose this route, please ignore the step that calls for linking the react native project.

Next, we will change into the newly created project and install our dependencies using yarn or npm:

yarn add aws-amplify react-navigation redux react-redux redux-thunk

Next, we need to link the Cognito SDK, which is a dependency of aws-amplify:

react-native link amazon-cognito-identity-js

Now that the dependencies are installed and linked, we will need to create a src directory in the root of the project, src to hold the future project files:

mkdir src

Next we need to configure our AWS credentials.

We can either do this manually in the Cognito console, or we can automate this using the AWSMobile CLI. I recommend using the CLI unless you already know how Cognito works.

Automated Configuration

First, we need to install AWS Mobile CLI:

npm install -g awsmobile-cli

Next, we need to configure the CLI to use your correct credentials.

Run the following command:

awsmobile configure

This will configure the IAM role of the user you will be working with. This will be the accessKeyId, & secretAccessKey of the user, as well as the region you would like to use.

If you already have the AWS cli installed and credentials configured, the cli will automatically have populated this configuration into your AWS Mobile configuration, which you can always override with the same command (awsmobile configure).

To see how to create a new IAM user and configure the CLI, check out the following video.

Next, we will run the awsmobile init command.

awsmobile init

You can choose defaults for all question answers

Running this command enables your Mobile Hub project with default features (Analytics, Hosting & Streaming), adds aws-amplify to your package.json, and adds a config file in your src directory src/aws-config.js.

  1. Analytics, for receiving the analytics sent from your app.
  2. Hosting and Streaming for easily hosting your app in the cloud.
  3. If you do not already have the aws-amplify library installed, it will install it

Finally, we will enable user sign in by running the following command:

awsmobile user-signin enable
awsmobile push

Here is a full videowalk through of the automated configuration:

Manual Configuration

If you already set up your project using the automated configuration above, you can skip to the next section: Implementation of Sign Up with MFA.

In Cognito, we need to create a new user pool and federated identity.

First, create a Cognito User Pool with the following rules:

  • Allow sign in with verified email address
  • Allow sign in with verified phone number
  • Attributes should include email and phone number
  • Multi-Factor Authentication required, with SMS being the second factor we want to enable
  • Verification should be set to both email and phone number
  • We also need to create an App Client for us to use as our federated identity. Make sure to leave the Generate client secret box unchecked.

Second, create an Identity Pool using Cognito as the Identity Provider. Pass in the User Pool ID and App client id we just created in Cognito.

In the src directory of the project, create a new file called aws-exports.js with the following information:

const config = {
identityPoolId: '<Cognito Identity Pool ID>',
region: '<Region>',
userPoolId: '<Cognito User Pool ID>',
userPoolWebClientId: '<Cognito App Client ID>'
}

To see this manual configuration step by step, check out the following video:

Implementation of Sign Up with MFA

Now that the project is set up, we can go ahead and write some code.

The first thing we will do is import the aws-exports.js configuration object in to our App.js file and configure Amplify to work with the configuration:

// App.js, below React Native Imports
import Amplify, { Auth } from 'aws-amplify'
import config from './src/aws-exports'
Amplify.configure(config)

Next, we need to create two class methods to handle the sign up process.

The user needs to sign up (with username, password, email, and phone number), then we need to authenticate the user using their phone number.

Once the user has successfully signed up, Cognito will send an sms to the user that we will need to send back to the API to confirm the user is authenticated.

We need to have a form input with some state that will keep up with the input code once we have it.

To handle all of this, we’ll have three methods: signIn, confirmUser, and onChangeText. We’ll create a state variable called authCode and keep up with it in the onChangeText method for input of the auth code:

export default class App extends Component<{}> {
state = { // 1
authCode: ''
}
onChangeText(authCode) { // 2
this.setState({ authCode })
}
signUp() {
Auth.signUp({ // 3
username: 'myCoolUsername',
password: 'MyCoolP@ssword2!',
attributes: {
phone_number: '+15555555555',
email: 'yourcoolemail@gmail.com'
}
})
.then(res => {
console.log('successful signup: ', res)
})
.catch(err => {
console.log('error signing up: ', err)
})
}
confirmUser() { // 4
const { authCode } = this.state
Auth.confirmSignUp('myCoolUsername', authCode)
.then(res => {
console.log('successful confirmation: ', res)
})
.catch(err => {
console.log('error confirming user: ', err)
})
}

// rest of code
}
  1. Create a state object with an authCode property
  2. onChangeText will keep up with the TextInput value that will take in the authentication code once we get it.
  3. signUp calls the amplify Auth.signUp function, passing in some hardcoded values (for now). Later once we verify this is all working, we will change these hardcoded values to be dynamic from other text inputs. If this signs up the user correctly, the phone number provided should receive an sms with the confirmation code, which we’ll use in the next step. This returns a promise, which we handle by logging out the returned values or handling the error.
  4. confirmUser will take the sms confirmation that we type into the TextInput, and call the Auth.confirmSignUp function, passing in the username as the first argument, and the authCode as the second argument. This also returns a promise which we handle.

Now that the functionality is set up, we need to wire up our UI to work with these methods.
First, let’s go ahead and import Button and TextInput from 'react-native':

import {
// ..existing imports commented out
Button,
TextInput
} from 'react-native'

Now, let’s update the UI to have a SignUp Button, a TextInput, and a Confirm User button.

Let’s change the render method to return the following:

render() {
return (
<View style={styles.container}>
<Button title='Sign Up' onPress={this.signUp.bind(this)} />
<TextInput
placeholder='Input Code'
onChangeText={value => this.onChangeText(value)}
style={styles.input}
/>
<Button
title='Confirm User'
onPress={this.confirmUser.bind(this)}
/>
</View>
)
}

For the style, we will update the container style to remove the alignItems property and create a new input style for the text input:

const styles = StyleSheet.create({
input: {
height: 50,
backgroundColor: '#ededed',
marginVertical: 10
},
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#F5FCFF'
}
})

Congratulations, you have successfully integrated some basic authentication into your React Native app, you should now be able to test this out!

Sign In with MFA

Now, let’s take a look at how some basic login functionality could easily be added:

signIn() { // 1
Auth.signIn(username, password)
.then(user => {
// save user in state somewhere
})
.catch(err => {
console.log('error signing in: ', err)
})
}
confirmSignIn() { // 2
Auth.confirmSignIn(user, authCode)
.then(user => {
console.log('user: ', user)
}).catch(err => {
console.log('error confirming sign in: ', err)
})
}

If you have a successful sign up and confirmation, you should be able to go into the console at https://console.aws.amazon.com/cognito/ , find the User Pool that was created (it should start with the name of your React Native project unless you configured this manually or changed it in some way), and view the user that was just created.

If you want to view this entire setup process end to end, check out one of the above videos.

To see the a final version of the above app, check out this repo

In part two, we will continue this authentication process to add better UI as well as integrating Navigation.

My Name is Nader Dabit . I am a Developer Advocate at AWS Mobile working with projects like AppSync and Amplify, and the founder of React Native Training.
If you like React and React Native, checkout out our podcast — React Native Radio on Devchat.tv.
Also, check out my book, React Native in Action now available from Manning Publications.
If you enjoyed this article, please recommend and share it! Thanks for your time.