Social authentication with AWS Amplify and React Native
Tags: serverless, awsamplify, reactnative, expo
Published: 2019-10-16 20:59:57

If you are struggling with social providers and you are trying to implement an OAuth flow, with AWS Amplify you can easily integrate one inside your app.

## 🔌 Getting started
First of all let's install the [AWS Amplify](https://aws-amplify.github.io/) CLI: 
*(inside the link you can also sign up for a free AWS account)*

__Requirements:__ *Node.js version 8.x or greater and npm version 5.x*

```console
npm install -g @aws-amplify/cli
```

Now you can configure the CLI and setup your developing environment.
Follow the steps given by issuing this command:

```console
amplify configure
```

## 💾 Install Expo and create our app
If you still don't know it [Expo](https://expo.io) is a complete workflow that helps you build cross-platform native apps (React Native).
If you are planning to develop for iOS you need to work with a MacOS computer and install the latest version of Xcode.
If, on the other hand, your target is Android you need to install Android Studio.
Moreover, in case you need to develop a complete cross-platform app, you can use both tools. Once installed, these tools provide emulators that simulate iOS or Android systems.
Let's install the CLI:

```console
npm install expo-cli --global
```

Then we can create our project, install some packages and finally start coding:

```console
expo init social-authentication

cd social-authentication

expo install react-navigation react-navigation-stack react-native-gesture-handler react-native-reanimated react-native-screens expo-web-browser

npm install aws-amplify

```

We also need to initialize a new AWS Amplify project inside our app (accept the default values, you can customize them later).

```console
amplify init
```

## 🛠 Create the navigation
Now we can start Expo and emulate our app with this command:

```console
expo start
```

Once it has finished the initial loading, by pressing "i" key it will start up the Xcode emulator and it will also install the Expo app inside the emulated device.

Now you can open the project inside your favorite text editor.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/zj7y160sl4wd79mdtlfu.png)

In order to protect our app from unauthenticated use we are going to create the navigators using the previously installed [react-navigation](https://reactnavigation.org) package, but before doing it we should create some screens:

__``/screens/HomeScreen.js``__
```react
import React from 'react';
import {
    Button,
    View,
    Text
} from 'react-native';
import { Auth } from 'aws-amplify';

class HomeScreen extends React.Component {
    render(){
        return (
            <View>
                <Text> Home Screen </Text>
                <Button onPress={() => Auth.signOut()} title='Sign out'/>
            </View>
        )
    }
}

export default HomeScreen;
```

__``/screens/AuthScreen.js``__
```react
import React from "react";
import {
  Button,
  View,
  StyleSheet
} from "react-native";
import { Auth } from 'aws-amplify';

class AuthScreen extends React.Component {

  render() {
    return (
        <View style={styles.authScreen} >
          <Button
            onPress={() => Auth.federatedSignIn({provider: 'Facebook'})}
            title="Authenticate with Facebook"
          />
        </View>
    );
  }

}

export default AuthScreen;

const styles = StyleSheet.create({
  authScreen: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  }
});
```

__``/screens/AuthLoadingScreen.js``__
```react
import React from 'react';
import {
  ActivityIndicator,
  View,
} from 'react-native';
import { Auth, Hub } from 'aws-amplify';

class AuthLoadingScreen extends React.Component {

  async componentDidMount() {
    this._checkIfAuthenticated();
    Hub.listen('auth', async (data) => {
      switch (data.payload.event) {
        case 'signIn':
          this.props.navigation.navigate('Main');
          break;
        case 'signOut':
          this.props.navigation.navigate('Auth');
          break;
        default:
          break;
      }
    });
  }

  async _checkIfAuthenticated() {
    let currentSession = null;
    try {
      currentSession = await Auth.currentSession();
    } catch(err) {
      console.log(err);
    }
    this.props.navigation.navigate(currentSession ? 'Main' : 'Auth');
  };

  render() {
    return (
      <View>
        <ActivityIndicator size="large"/>
      </View>
    );
  }
  
}

export default AuthLoadingScreen;
```
After our component has been mounted we can use the Hub class from Amplify to create a "listener" that captures our auth events, so we can trigger an action when a User signs in or signs out.
In addition to this we can also check if there is a "currentSession" (through _checkIfAuthenticated function) in order to understand if the User has previously signed in in our app.

Now we can create the navigators:
- __AppNavigator:__ it is going to wrap all of our app navigation.

- __MainNavigator:__ it will be accessible only when we are correctly authenticated.

- __AuthNavigator:__ if we are unauthenticated we will be able to navigate only inside this navigator (AuthScreen).

__``/navigation/AppNavigator.js``__
```javascript
import { createAppContainer, createSwitchNavigator } from 'react-navigation';

import AuthLoadingScreen from '../screens/AuthLoadingScreen';
import MainNavigator from './MainNavigator';
import AuthNavigator from './AuthNavigator';

export default createAppContainer(createSwitchNavigator(
    {
      AuthLoading: AuthLoadingScreen,
      Main: MainNavigator,
      Auth: AuthNavigator
    },
    {
      initialRouteName: 'AuthLoading',
    }
  )
);
```

__``/navigation/MainNavigator.js``__
```javascript
import { createStackNavigator } from 'react-navigation-stack';

import HomeScreen from '../screens/HomeScreen';

export default createStackNavigator(
  {
    Home: { screen: HomeScreen }
  },
  { initialRouteName: 'Home' }
);
```
__``/navigation/AuthNavigator.js``__
```javascript
import { createStackNavigator } from 'react-navigation-stack';

import AuthScreen from '../screens/AuthScreen';

export default createStackNavigator(
  {
    Auth: { screen: AuthScreen }
  }
);
```

Now we need to place the AppNavigator into our app entry point and this will be the component which wraps all the others. 
In order to do this ``App.js`` has to look like that:

```react
import React from 'react';
import AppNavigator from './navigation/AppNavigator';

class App extends React.Component {
  render() {
    return (
      <AppNavigator/>
    );
  }
}

export default App;
```

Now if you look at the emulator you will find yourself inside the AuthScreen (so inside AuthNavigator). Do not press the button yet, because we are now going to configure AWS Amplify resources to handle our authentication stack.

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/qqktey9vyzgvacww0req.png)

## 🛠 Create Auth resources with AWS Amplify
Open up the console inside the root of your App and enter this command:

```console
amplify add auth
```

Enter the configuration used in the screenshot below:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/119tk2guq39vzlwpm088.png)

When you need to insert Facebook AppID and AppSecret follow [these instructions](https://aws-amplify.github.io/docs/js/cognito-hosted-ui-federated-identity#facebook-instructions) from AWS Amplify Docs.

Once you have successfully configured the Auth (that it's an AWS Cognito under the hood), you can:

```console
amplify push
```

Confirm and wait until cloud resources creation is completed.

Congratulations 👏👏👏 You have successfully generated the Cognito Pool for your app, where users can sign up and be identified when they sign back in 🚀🔥

## 🔩 Configure AWS Amplify resources inside your app
Everything should be at his own place now and you will find a file called __``aws-exports.js``__ inside your app root folder: open it and copy the value of the *domain* key and [follow these steps](https://aws-amplify.github.io/docs/js/cognito-hosted-ui-federated-identity#facebook-instructions-1).

We need to configure Amplify inside our __``App.js``__
```react
import React from 'react';
import AppNavigator from './navigation/AppNavigator';

import Amplify from 'aws-amplify';
import awsconfig from './aws-exports';

Amplify.configure(awsconfig);

class App extends React.Component {
  render() {
    return (
      <AppNavigator/>
    );
  }
}

export default App;
```

The time to press the auth button in your app has come, if you complete the flow that appears inside the emulator screen you should be able to see the final result:

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/knfb4aktjgfraeuko8d4.png)

And that's all 🎉🎉🎉

This can be a starting point to begin the development of your application, if you want to extend your app with modern and serverless API be sure to give a read to [AWS Amplify documentation](https://aws-amplify.github.io/docs/js/start?platform=react-native).

You can also use my [repo](https://github.com/bastianbb/social-authentication) as a template to create your AWS Amplify project:

```console
amplify init --app https://github.com/bastianbb/social-authentication
```