Most apps require that a user authenticate in some way to have access to data associated with a user or other private content. Typically the flow will look like this:
- The user opens the app.
- The app loads some authentication state from persistent storage (for example,
- When the state has loaded, the user is presented with either authentication screens or the main app, depending on whether valid authentication state was loaded.
- When the user signs out, we clear the authentication state and send them back to authentication screens.
Note: We say "authentication screens" because usually there is more than one. You may have a main screen with a username and password field, another for "forgot password", and another set for sign up.
What we need
This is the behavior that we want from the authentication flow: when users sign in, we want to throw away the state of the authentication flow and unmount all of the screens related to authentication, and when we press the hardware back button we expect to not be able to go back to the authentication flow.
How it will work
We can define different screens based on some condition. For example, if the user is signed in, we can define
Settings etc. If the user is not signed in, we can define
When we define screens like this, when
true, React Navigation will only see the
Settings screens, and when it's
false, React Navigation will see the
SignUp screens. This makes it impossible to navigate to the
Settings screens when the user is not signed in, and to
SignUp screens when the user is signed in.
This pattern has been in use by other routing libraries such as React Router for a long time, and is commonly known as "Protected routes". Here, our screens which need the user to be signed in are "protected" and cannot be navigated to by other means if the user is not signed in.
The magic happens when the value of the
isSignedIn variable changes. Let's say, initially
false. This means, either
SignUp screens are shown. After the user signs in, the value of
isSignedIn will change to
true. React Navigation will see that the
SignUp screens are no longer defined and so it will remove them. Then it'll show the
Home screen automatically because that's the first screen defined when
It's important to note that when using such a setup, you don't need to manually navigate to the
Home screen by calling
navigation.navigate('Home') or any other method. React Navigation will automatically navigate to the correct screen when
isSigned in changes -
Home screen when
true, and to
SignIn screen when
false. You'll get an error if you attempt to navigate manually.
The example shows stack navigator, but you can use the same approach with any navigator.
By conditionally defining different screens based on a variable, we can implement auth flow in a simple way that doesn't require additional logic to make sure that the correct screen is shown.
Define our screens
In our navigator, we can conditionally define appropriate screens. For our case, let's say we have 3 screens:
SplashScreen- This will show a splash or loading screen when we're restoring the token.
SignInScreen- This is the screen we show if the user isn't signed in already (we couldn't find a token).
HomeScreen- This is the screen we show if the user is already signed in.
So our navigator will look like:
In the above snippet,
isLoading means that we're still checking if we have a token. This can usually be done by checking if we have a token in
AsyncStorage and validating the token. After we get the token and if it's valid, we need to set the
userToken. We also have another state called
isSignout to have a different animation on sign out.
The main thing to notice is that we're conditionally defining screens based on these state variables:
SignInscreen is only defined if
null(user is not signed in)
Homescreen is only defined if
userTokenis non-null (user is signed in)
Here, we're conditionally defining one screen for each case. But you could also define multiple screens. For example, you probably want to define password reset, signup, etc screens as well when the user isn't signed in. Similarly for the screens accessible after sign in, you probably have more than one screen. We can use
React.Fragment to define multiple screens:
Implement the logic for restoring the token
Note: The following is just an example of how you might implement the logic for authentication in your app. You don't need to follow it as is.
From the previous snippet, we can see that we need 3 state variables:
isLoading- We set this to
truewhen we're trying to check if we already have a token saved in
isSignout- We set this to
truewhen user is signing out, otherwise set it to
userToken- The token for the user. If it's non-null, we assume the user is logged in, otherwise not.
So we need to:
- Add some logic for restoring token, sign in and sign out
- Expose methods for sign in and sign out to other components
React.useContext in this guide. But if you're using a state management library such as Redux or Mobx, you can use them for this functionality instead. In fact, in bigger apps, a global state management library is more suitable for storing authentication tokens. You can adapt the same approach to your state management library.
First we'll need to create a context for auth where we can expose necessary methods:
So our component will look like this:
Fill in other components
We won't talk about how to implement the text inputs and buttons for the authentication screen, that is outside of the scope of navigation. We'll just fill in some placeholder content.