The introduction

I originally planned to write the content of React Navigation-Guides in this article, but it was too long and difficult to read, so the content of the Guides chapter was recorded in sections here. Next, let’s look at Authentication flows!

Authentication Flows (permission control for pages)

Most applications require users to authenticate in some way in order to access user-related data or other private content. Typically, the process looks like this:

  • The user opens the application.
  • The application loads some authentication state from a persistent store (for example, AsyncStorage).
  • Once the state is loaded, the user is presented with either the authentication screen or the main application, depending on whether a valid authentication state is loaded.
  • When the user logs out, we clear the authentication status and send it back to the authentication page.

Note: We say “authentication page” because there is usually more than one. You might have a home page with a username and password field, another that says “forgot password,” and another set to register.

1 What do we need

We want the behavior of the authentication flow to be: when the user logs in, we want to discard the state of the authentication flow and unload all pages related to authentication; When we press the hardware back button, we want to be unable to return to the authentication flow.

2 how to do

We can define different pages based on certain criteria. For example, if the user is logged in, we can define home pages, profiles, Settings, and so on. If the user is not logged in, you can define login and registration pages.

Such as:

isSignedIn ? (
  <>
    <Stack.Screen name="Home" component={HomeScreen} />
    <Stack.Screen name="Profile" component={ProfileScreen} />
    <Stack.Screen name="Settings" component={SettingsScreen} />
  </>
) : (
  <>
    <Stack.Screen name="SignIn" component={SignInScreen} />
    <Stack.Screen name="SignUp" component={SignUpScreen} />
  </>
)
Copy the code

When we define such a page, React Navigation will only see the Home, Profile, and Settings pages when isSignedIn is true, while React Navigation will see the SignIn and SignUp pages when it is false. This makes it impossible to navigate to the Home, Profile, and Settings pages when the user is not logged in, or to the login and registration pages when the user is logged in.

This pattern has been used by other routing libraries (such as the React Router) for a long time and is often referred to as “protected routing.” Here, we want the page the user is logged into to be “protected,” and if the user is not logged in, there is no other way to navigate.

This magic happens when the value of the isSignedIn variable changes. For example, the initial isSignedIn is false. This means that a login or registration page will be displayed. After the user logs in, isSignedIn will change to true. React Navigation will see that the login and registration pages are no longer defined, so it will delete them. It then automatically displays the main page, since this is the first page defined when isSignedIn is true.

Note that this setting does not require manual navigation to the main page with a call to navigation.navigate(‘Home’). React Navigation will automatically navigate to the main page when isSignedIn is true.

This takes advantage of a new feature in React navigation: the ability to dynamically define and change the page definition of the navigator based on properties or state. The example shows a stack navigator, but you can use the same method for any navigator.

By conditionally defining different pages based on variables, we can implement validation flow in a simple way that does not require additional logic to ensure that the correct page is displayed.

Define our page

In the Navigator, we can conditionally define the appropriate page. For our example, suppose we have three pages:

  • SplashScreen- This will display a startup or load page when we restore the token.
  • SignInScreen- This is the page that is displayed when the user is not logged in (we could not find the token).
  • HomeScreen- If the user is already logged in, this is the page we display.

So our navigation can look like this:

if (state.isLoading) { // We haven't finished checking for the token yet return <SplashScreen />; } return ( <Stack.Navigator> {state.userToken == null ? ( // No token found, user isn't signed in <Stack.Screen name="SignIn" component={SignInScreen} options={{ title: 'Sign in', // When logging out, a pop animation feels intuitive // You can remove this if you want the default 'push' animation animationTypeForReplace:  state.isSignout ? 'pop' : 'push', }} /> ) : ( // User is signed in <Stack.Screen name="Home" component={HomeScreen} /> )} </Stack.Navigator> );Copy the code

In the code snippet above, isload means we are still checking for tokens. This can usually be done by checking AsyncStorage for tokens and validating tokens. After we get the token, we need to set the userToken if it is valid. We also have another state called isSignout, which has a different animation on logout.

Note that we conditionally define the page based on these state variables:

  • The login page is defined only if the userToken is null (the user is not logged in)
  • The home page is defined only if the userToken is non-NULL (the user is logged in)

Here, we conditionally define a page for each case. But you can also define multiple pages. For example, you might also need to define a password reset, registration, and so on page when the user is not logged in. Again, you may have multiple pages that are accessible after login. We can use react. Fragment to define multiple screens:

state.userToken == null ? (
  <>
    <Stack.Screen name="SignIn" component={SignInScreen} />
    <Stack.Screen name="SignUp" component={SignUpScreen} />
    <Stack.Screen name="ResetPassword" component={ResetPassword} />
  </>
) : (
  <>
    <Stack.Screen name="Home" component={HomeScreen} />
    <Stack.Screen name="Profile" component={ProfileScreen} />
  </>
);
Copy the code

4 Restore the token logic

Note: The following is just an example of how authentication logic can be implemented in an application. You don’t have to do it exactly as it is.

As you can see from the previous section, we need three state variables:

  • IsLoading – When we try to check if the token has been saved in AsyncStorage, we set it to true
  • IsSignout – We set it to true when the user logs out and false otherwise
  • UserToken – Indicates the user’s token. If not empty, the user is assumed to be logged in, otherwise not.

So we need to:

  • Add some logic for recovering tokens, logins, and logouts
  • Expose login and logout methods to other components

In this tutorial we will use React. UseReducer and React. UseContext. But if you are using a state management library, such as Redux or Mobx, you can use them for this functionality. In fact, in larger applications, the global state Management library is better suited for storing authentication tokens. You can apply the same approach to your state management library.

First, we need to create a context for Auth, where we can expose the necessary methods:

import * as React from 'react';

const AuthContext = React.createContext();
Copy the code

Our component could look like this:

import * as React from 'react'; import AsyncStorage from '@react-native-community/async-storage'; export default function App({ navigation }) { const [state, dispatch] = React.useReducer( (prevState, action) => { switch (action.type) { case 'RESTORE_TOKEN': return { ... prevState, userToken: action.token, isLoading: false, }; case 'SIGN_IN': return { ... prevState, isSignout: false, userToken: action.token, }; case 'SIGN_OUT': return { ... prevState, isSignout: true, userToken: null, }; } }, { isLoading: true, isSignout: false, userToken: null, } ); React.useEffect(() => { // Fetch the token from storage then navigate to our appropriate place const bootstrapAsync = async () => { let userToken; try { userToken = await AsyncStorage.getItem('userToken'); } catch (e) { // Restoring token failed } // After restoring token, we may need to validate it in production apps // This will switch to the App screen or Auth screen and this loading // screen will be unmounted and thrown away. dispatch({ type: 'RESTORE_TOKEN', token: userToken }); }; bootstrapAsync(); } []); const authContext = React.useMemo( () => ({ signIn: async data => { // In a production app, we need to send some data (usually username, password) to server and get a token // We will also need to handle errors if sign in failed // After getting token, we need to persist the token using `AsyncStorage` // In the example, we'll use a dummy token dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' }); }, signOut: () => dispatch({ type: 'SIGN_OUT' }), signUp: async data => { // In a production app, we need to send user data to server and get a token // We will also need to handle errors if sign up failed // After getting token, we need to persist the token using `AsyncStorage` // In the example, we'll use a dummy token dispatch({ type: 'SIGN_IN', token: 'dummy-auth-token' }); }}), []); return ( <AuthContext.Provider value={authContext}> <Stack.Navigator> {state.userToken == null ? ( <Stack.Screen name="SignIn" component={SignInScreen} /> ) : ( <Stack.Screen name="Home" component={HomeScreen} /> )} </Stack.Navigator> </AuthContext.Provider> ); }Copy the code

5 Fill in other components

We will not discuss how to implement text entry and buttons for authentication pages, which go beyond navigation. We just need to fill in some placeholders.

function SignInScreen() {
  const [username, setUsername] = React.useState('');
  const [password, setPassword] = React.useState('');

  const { signIn } = React.useContext(AuthContext);

  return (
    <View>
      <TextInput
        placeholder="Username"
        value={username}
        onChangeText={setUsername}
      />
      <TextInput
        placeholder="Password"
        value={password}
        onChangeText={setPassword}
        secureTextEntry
      />
      <Button title="Sign in" onPress={() => signIn({ username, password })} />
    </View>
  );
}
Copy the code

RN routing -React Navigation–Drawer Navigation

React Navigation – Authentication flows