Let's Add Google Auth To Our React Native App With AWS Amplify!

2020-03-02 - 11 min read

In this post, we'll create a React Native project, using AWS Amplify to allow our users to log in with their Google accounts.

Amplify helps you add things like authentication, APIs, databases, hosting, and much more to your project by doing the heavy lifting for you.

Table of Contents

Intro

This tutorial will explain how to use Amplify's Auth module with a Typescript React Native app. It'll cover both React Native CLI and Expo. If you're not familiar with TypeScript yet, you can read this post: From zero to hero with React and TypeScript: Let's build another to-do app!

I'm writing this blog post because when I tried to implement Amplify Auth in my React Native app, I ran into some problems. Through this tutorial, I hope I can make it easier for both you and future me to get started!

For this tutorial, we'll be using React Native CLI. I'll tell you when there's something specific to Expo or to React Native CLI.

Creating a new React Native project

First, we'll scaffold a new React Native app. To do so, we'll use the official tutorial for React Native CLI Quickstart. I'm using a Mac and Android phone, but you can select whichever platforms apply to you.

  1. Scaffold a new project with Expo or the React Native CLI: https://reactnative.dev/docs/getting-started, either Expo or React Native CLI is fine. Expo is easier to get started with whilst React Native CLI offers more freedom (and support for native modules).
    • When running expo init, you can pick a TypeScript template. If you're using React Native CLI, you'll want to use the following command: npx react-native init MyTSProject --template react-native-template-typescript
  2. You should now have a folder with the same name as you entered while scaffolding your app. From here on out we'll call it "myapp".
  3. Start an emulator, Expo has tutorials for IOS and Android.
  4. Now, depending on the platform, either run npm run android or npm run ios.

You should now see something like this:

Android react-native default screen

Let's add Amplify!

Adding AWS Amplify

Before following these steps, install Amplify first. To do that, you can follow these steps: https://aws-amplify.github.io/docs/cli-toolchain/quickstart?sdk=js

  1. At the root of your project, run amplify init.
  2. Specify the name of your project, the default is usually fine.
  3. Specify a name for your environment, since this is your local development environment, enter "develop".
  4. Pick your default editor, if it's not listed, pick "none".
  5. Choose "javascript"
  6. Choose "react-native"
  7. For source, distribution paths, build command, and start command, you can use the defaults.
  8. When asked if you want to use an AWS profile, choose "Y"
  9. Either create a new one or choose any option on the list.

You should now be greeted by this message:

1⠧ Initializing project in the cloud...
2
3CREATE_IN_PROGRESS DeploymentBucket AWS::S3::Bucket Tue Mar 03 2020 11:45:13 GMT+0100 (Central European Standard Time) Resource creation Initiated
4CREATE_IN_PROGRESS UnauthRole AWS::IAM::Role Tue Mar 03 2020 11:45:13 GMT+0100 (Central European Standard Time) Resource creation Initiated
5CREATE_IN_PROGRESS AuthRole AWS::IAM::Role Tue Mar 03 2020 11:45:13 GMT+0100 (Central European Standard Time) Resource creation Initiated
6CREATE_IN_PROGRESS AuthRole AWS::IAM::Role Tue Mar 03 2020 11:45:12 GMT+0100 (Central European Standard Time)
7CREATE_IN_PROGRESS UnauthRole AWS::IAM::Role Tue Mar 03 2020 11:45:12 GMT+0100 (Central European Standard Time)
8CREATE_IN_PROGRESS DeploymentBucket AWS::S3::Bucket Tue Mar 03 2020 11:45:12 GMT+0100 (Central European Standard Time)
9CREATE_IN_PROGRESS amplify-myapp-develop-114507 AWS::CloudFormation::Stack Tue Mar 03 2020 11:45:09 GMT+0100 (Central European Standard Time) User Initiated
10⠏ Initializing project in the cloud...
11
12CREATE_COMPLETE UnauthRole AWS::IAM::Role Tue Mar 03 2020 11:45:27 GMT+0100 (Central European Standard Time)
13CREATE_COMPLETE AuthRole AWS::IAM::Role Tue Mar 03 2020 11:45:27 GMT+0100 (Central European Standard Time)
14⠦ Initializing project in the cloud...
15
16CREATE_COMPLETE DeploymentBucket AWS::S3::Bucket Tue Mar 03 2020 11:45:34 GMT+0100 (Central European Standard Time)
17⠇ Initializing project in the cloud...
18
19CREATE_COMPLETE amplify-myapp-develop-114507 AWS::CloudFormation::Stack Tue Mar 03 2020 11:45:36 GMT+0100 (Central European Standard Time)
20✔ Successfully created initial AWS cloud resources for deployments.
21✔ Initialized provider successfully.
22Initialized your environment successfully.
23
24Your project has been successfully initialized and connected to the cloud!
25
26Some next steps:
27"amplify status" will show you what you've added already and if it's locally configured or deployed
28"amplify add <category>" will allow you to add features like user login or a backend API
29"amplify push" will build all your local backend resources and provision it in the cloud
30“amplify console” to open the Amplify Console and view your project status
31"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
32
33Pro tip:
34Try "amplify add api" to create a backend API and then "amplify publish" to deploy everything

You've successfully added AWS Amplify! 🚀️

Adding Amplify Auth

  1. In the root of your project, run amplify auth.
  2. Choose "Default configuration with Social Provider (Federation)".
  3. Choose "Username".
  4. Choose "No, I am done.".
  5. Pick any prefix, the default is fine.
  6. For the redirect signin URI, enter myapp:// where myapp is the name of your app.
  7. One redirect signin URI is fine, enter N.
  8. For redirect signout URI, also enter myapp://. Again, replace myapp the name of your app.
  9. One redirect signout URI is fine, enter N.
  10. Select the auth providers you'd like to support, you can move up/down with arrow keys and select options with your spacebar. When you're done choosing, hit "enter". For this tutorial we're going to pick "Google".
  11. You'll notice AWS asking you for your Google OAuth credentials.

Getting Google OAuth credentials

First, go to the Google API Developer Console: https://console.developers.google.com/

Select a project:

Select project

Select an existing one or create a new one:

Create new project or select an existing one

If you're creating a new one, enter your project information and click "create".

Before Google allows you to create your OAuth credentials, you have to set up a consent screen first.

Head to the OAuth consent screen:

Head to the OAuth consent screen
  1. Choose the user type. For this tutorial, we'll use "External". Click "create".
  2. Enter an application name, which is most likely the same as the other few times they asked 😉️.
  3. Optionally, you can upload an application logo.
  4. Leave the rest as-is and click Save.

Getting the credentials

Head to the credentials screen:

Head to the credentials screen

Create new OAuth credentials:

Create credentials
  1. For application type, choose "Web application".
  2. Enter a name e.g: "myapp, Amplify client".
  3. Click "create".
  4. Copy the client id and paste it into your terminal
  5. Copy the client secret and paste it into your terminal
  6. In your terminal, run amplify push
  7. This might take a while. AWS is creating everything it needs to get your authentication running!
  8. Take note of your hosted UI Endpoint, it'll look something like this: https://myappbcfc15bf-bcfc15bf-develop.auth.eu-west-1.amazoncognito.com, this is your Cognito endpoint URI, take note as you'll need it for the next steps!

The next step is letting Google know you trust your Amplify Cognito pool:

Configuring your Google OAuth client to work with Amplify

The last step of configuring things is here!

Go to the edit screen of your OAuth client:

Edit OAuth client
  1. Add an authorized JavaScript origin with your Cognito endpoint URI from earlier, without the / at the end.
  2. Add an authorized redirect URI, it's the same as your authorized JavaScript origin but with /OAuth2/idpresponse at the end.
  3. Hit save.

That's it! Cognito is now configured and the only thing left to do is let users open the cognito login form.

Creating a login screen

First, we'll need to install Amplify as NPM dependencies. To do so, run npm i -S aws-amplify aws-amplify-react-native. AWS has added types already.

In App.tsx, remove all the boilerplate of react-native and create a new App component. We'll render "Hello, please login." if the user has not been authenticated yet, if they are, we'll render "Hello, {name}".

1import React, {FunctionComponent, useState} from 'react';
2import {SafeAreaView, Text} from 'react-native';
3
4interface User {
5 name: string;
6}
7
8const App: FunctionComponent = () => {
9 const [user, setUser] = useState<User|null >(null);
10
11 return <SafeAreaView>
12 <Text>Hello, {user?.name ?? "please login."}</Text>
13 </SafeAreaView>;
14};
15
16export default App;

To configure Amplify:

Add

1import Amplify from "aws-amplify"
2import config from "./aws-exports"

to the top and right beneath your imports add

1Amplify.configure(config);

That's it. Now let's create a login button:

1import React, {FunctionComponent, useCallback, useState} from 'react';
2import {Button, SafeAreaView, Text} from 'react-native';
3import {Auth} from "aws-amplify"
4
5interface User {
6 name: string;
7}
8
9const App: FunctionComponent = () => {
10 const [user, setUser] = useState<User | null>(null);
11
12 const signin = useCallback(() => {
13 Auth.federatedSignIn({provider: "google"});
14 }, []);
15
16 return <SafeAreaView>
17 <>
18 <Text>Hello, {user?.name ?? "please login."}</Text>
19
20 {user === null && <Button title="Login with Google" onPress={signin} />}
21 </>
22 </SafeAreaView>;
23};
24
25export default App;
Tip

Using TypeScript? Then this might give you a type error:

1Auth.federatedSignIn({provider: "Google"});

You can either choose to add @ts-ignore, or you can use

1Auth.federatedSignIn({provider: CognitoHostedUIIdentityProvider.Google});
Tip

If you get this error:

Unable to resolve module @react-native-community/netinfo

  1. Run npm i -S @react-native-community/netinfo.
  2. Inside the IOS folder, run pod install
  3. Rebuild & re-run the app

Your app should now look like this:

Login screen with login button

When you click on "LOGIN WITH GOOGLE" you'll see your web browser opening you'll be able to log in with your Google account. When you log in you'll notice a blank screen. Time for the next part!

Adding an URI handler to your app

What happens is that after logging in, AWS redirects you to your redirect signin URI but your app isn't catching this URI yet! Let's fix that. After following any of these steps restart your app.

Expo apps

Inside app.json add

1{
2 "expo": {
3 "scheme": "myapp"
4 }
5}

React Native CLI apps

Android

First open android/app/src/main/AndroidManifest.xml, then add a new intent:

1<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2 package="com.myapp">
3
4 <uses-permission android:name="android.permission.INTERNET"/>
5
6 <application
7 android:name=".MainApplication"
8 android:label="@string/app_name"
9 android:icon="@mipmap/ic_launcher"
10 android:roundIcon="@mipmap/ic_launcher_round"
11 android:allowBackup="false"
12 android:theme="@style/AppTheme">
13 <activity
14 android:name=".MainActivity"
15 android:label="@string/app_name"
16 android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
17 android:windowSoftInputMode="adjustResize">
18 <intent-filter>
19 <action android:name="android.intent.action.MAIN"/>
20 <category android:name="android.intent.category.LAUNCHER"/>
21 </intent-filter>
22 <intent-filter android:label="filter_react_native"> <!-- THE NEW INTENT -->
23 <action android:name="android.intent.action.VIEW"/>
24 <category android:name="android.intent.category.DEFAULT"/>
25 <category android:name="android.intent.category.BROWSABLE"/>
26 <data android:scheme="myapp"/>
27 </intent-filter>
28 </activity>
29 <activity android:name="com.facebook.react.devsupport.DevSettingsActivity"/>
30 </application>
31
32</manifest>

IOS

First open ios/Inventory/Info.plist, then add

1<key>CFBundleURLTypes</key>
2<array>
3 <dict>
4 <key>CFBundleURLSchemes</key>
5 <array>
6 <string>myApp</string>
7 </array>
8 </dict>
9</array>

Storing the authenticated user in your state

Now your app catches the redirect URI. After logging in you should now see the homescreen of your app again. Amplify allows you to retrieve the current user, let's do that and store the logged in user in our state so we can show either a greeting or a log in button.

In App.tsx add

1useEffect(() => {
2 Auth.currentAuthenticatedUser()
3 .then(user => {
4 user.getUserData((err?: Error, userData?: UserData) => {
5 setUser({
6 email: userData?.UserAttributes.find(ud => ud.Name === "email")?.Value
7 })
8 });
9 })
10 .catch(error => {
11 setUser(null);
12 });
13}, []);

Let's step through it

1Auth.currentAuthenticatedUser()

Returns a promise with the currently signed in user. This user can already be logged in when opening the app. If there is no user, it rejects. Amplify persists the user automatically so they remain logged in.

1user.getUserData((err?: Error, userData?: UserData) => {}

Retrieves the user's data. This data can be configured in your user pool. We then loop through all user data until we find an item containing the user's e-mail, which we then add to our state.

You should now see something like this:

Logged in screen

That's it!

Closing words

By now you've installed AWS Amplify in your react-native app and your users are able to log in. Now go ahead and create something awesome!

See a typo or something incorrect? Please contact me on Twitter or create a Pull Request! =)