Skip to main content
React Native Introduction
CHAPTER 21 Beginner

Authentication in React Native

Updated: May 16, 2026
7 min read

# CHAPTER 21

Authentication in React Native

1. Introduction

Identity is the foundation of almost every modern application. If a user likes a post, adds an item to a cart, or sends a message, the database needs to know *who* took that action. Building a secure authentication system requires orchestrating multiple systems: fetching a token from an API, saving it to the hard drive, and instantly redirecting the user to the Home screen without them pressing a "Back" button. In this chapter, we will master Authentication in React Native. We will combine Axios, Context API, AsyncStorage, and React Navigation to build a professional, un-hackable "Protected Routes" login flow.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Understand the mechanics of JSON Web Tokens (JWT).
  • Architect a global AuthContext.
  • Build a Login screen that fetches and saves tokens.
  • Conditionally render navigation stacks (Protected Routes).
  • Implement an automatic session-restore feature on app launch.

3. The JWT Mechanics

When a user types their email and password and hits "Login", an Axios POST request is sent to the server. The server verifies the password and replies with a JSON Web Token (JWT). This token is a cryptographically signed string that proves the user is who they say they are. The Goal: The mobile app must catch this token, save it to AsyncStorage, and attach it to the Headers of all future Axios requests.

4. Step 1: The Auth Context

We need a global cloud to hold the token so the entire app knows if the user is logged in.
javascript
12345678910111213141516171819202122232425262728293031323334353637
import React, { createContext, useState, useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';

export const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [userToken, setUserToken] = useState(null);
  const [isLoading, setIsLoading] = useState(true);

  // 1. On App Launch, check if a token is already on the hard drive!
  useEffect(() => {
    const bootstrapAsync = async () => {
      let token = await AsyncStorage.getItem('userToken');
      if (token) setUserToken(token); // Restore session!
      setIsLoading(false); // Done checking
    };
    bootstrapAsync();
  }, []);

  // 2. The Login Function (Called by the UI)
  const login = async (token) => {
    await AsyncStorage.setItem('userToken', token); // Save to disk
    setUserToken(token); // Save to RAM (Triggers navigation update!)
  };

  // 3. The Logout Function
  const logout = async () => {
    await AsyncStorage.removeItem('userToken');
    setUserToken(null);
  };

  return (
    <AuthContext.Provider value={{ userToken, isLoading, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

5. Step 2: Protected Routes (The Magic Switch)

In App.js, we wrap our app in the AuthProvider. Then, we use a standard if/else statement inside our <NavigationContainer>.
  • If userToken is null, we return the <AuthStack> (Login, Signup).
  • If userToken exists, we return the <MainStack> (Home, Profile).
javascript
12345678910111213141516171819202122232425262728
import React, { useContext } from &#039;react&#039;;
import { NavigationContainer } from &#039;@react-navigation/native&#039;;
import { AuthContext } from &#039;./AuthContext&#039;;

function AppNavigator() {
  const { userToken, isLoading } = useContext(AuthContext);

  if (isLoading) {
    return <SplashScreen />; // Show a logo while reading the hard drive
  }

  return (
    <NavigationContainer>
      {/* THE MAGIC SWITCH! */}
      {userToken == null ? (
        // No Token? Show Login Screen. (User CANNOT access the Home screen)
        <Stack.Navigator>
          <Stack.Screen name="Login" component={LoginScreen} />
        </Stack.Navigator>
      ) : (
        // Token exists! Show Main App. (Login screen is completely destroyed)
        <Stack.Navigator>
          <Stack.Screen name="Home" component={HomeScreen} />
        </Stack.Navigator>
      )}
    </NavigationContainer>
  );
}

6. Step 3: The Login Screen

The user types their credentials. We hit the API, get the token, and feed it to the Context.
javascript
1234567891011121314151617181920212223242526272829303132333435
import React, { useState, useContext } from &#039;react&#039;;
import { View, TextInput, Button } from &#039;react-native&#039;;
import { AuthContext } from &#039;./AuthContext&#039;;
import axios from &#039;axios&#039;;

export default function LoginScreen() {
  const [email, setEmail] = useState(&#039;&#039;);
  const [password, setPassword] = useState(&#039;&#039;);
  const { login } = useContext(AuthContext);

  const handleLogin = async () => {
    try {
      // 1. Ask the server to authenticate
      const response = await axios.post(&#039;https://api.example.com/login', { email, password });
      
      // 2. Grab the JWT token from the response
      const token = response.data.token;
      
      // 3. Pass it to our Global Context
      login(token); 
      // BOOM! The Context changes 'userToken', triggering the App.js magic switch.
      // The screen will instantly swap to the Home Screen!
    } catch (e) {
      alert("Invalid email or password");
    }
  };

  return (
    <View style={{ padding: 50 }}>
      <TextInput placeholder="Email" onChangeText={setEmail} autoCapitalize="none" />
      <TextInput placeholder="Password" onChangeText={setPassword} secureTextEntry />
      <Button title="Login" onPress={handleLogin} />
    </View>
  );
}

7. Visual Learning: The Authentication Flow

txt
12345678910111213
[ USER LAUNCHES APP ]
         |
[ useEffect reads AsyncStorage ]
      /           \
 (No Token)     (Token found!)
     |               |
 [ Login ]       [ Home ]
     |
(User logs in)
     |
(Context updates token)
     |
(App.js magically switches to [ Home ])

8. Common Mistakes

  • Navigating to Home Manually: Beginners often write navigation.navigate('Home') inside the handleLogin function. DO NOT DO THIS. If you navigate manually, the user can press the physical "Back" button on Android and accidentally go back to the Login screen while authenticated! By conditionally rendering the Stacks (Section 5), the Login stack is physically destroyed from memory when the token is received. It is impossible to "go back" to it.

9. Best Practices

  • Token Expiration: JWT tokens expire (usually after 24 hours). If a user opens the app on day 2, the useEffect will find the token and send them to the HomeScreen, but the first Axios API call they make will return a 401 Unauthorized error. You must use an Axios Response Interceptor (learned in Ch 19) to catch that 401, trigger the logout() context function automatically, and kick them back to the Login screen!

10. Practice Exercises

  1. 1. In a conditionally rendered navigation architecture, what variable dictates whether the <AuthStack> or the <MainStack> is actively drawn on the screen?
  1. 2. What asynchronous function is triggered inside the useEffect on app launch to seamlessly log a returning user straight into the dashboard?

11. MCQs with Answers

Question 1

Why is conditionally rendering navigation stacks (e.g., userToken ? <AppStack/> : <AuthStack/>) architecturally superior to simply using navigation.navigate('Home') after a successful login?

Question 2

When the app first launches, a developer displays a <SplashScreen /> for one second. What critical background operation is usually occurring during this specific delay?

12. Interview Questions

  • Q: Explain the paradigm of "Protected Routes" in React Navigation. How is the global AuthContext utilized to enforce this boundary?
  • Q: Walk through the complete lifecycle of a JWT authentication flow, starting from the user pressing "Submit" on the Login form, the token caching strategy, and ending with the execution of a secure Axios interceptor on subsequent API requests.
  • Q: A returning user launches your app, reads their valid JWT from AsyncStorage, and is successfully routed to the Dashboard. 5 minutes later, their token expires on the backend. Describe the exact error handling architecture required to gracefully eject the user back to the Login screen.

13. FAQs

Q: How do I implement "Sign in with Google" or "Sign in with Apple"? A: You install specific Expo packages (like expo-auth-session or @react-native-google-signin/google-signin). These packages launch the native OS popup. Upon success, Apple/Google hands your app a secure Token. You take that token, send it to YOUR backend server for validation, and proceed with the exact same Context flow outlined above!

14. Summary

In Chapter 21, we fortified our application architecture. We transitioned from isolated UI screens to a globally integrated, state-driven routing system. We established a global AuthContext to track the user's session, utilized AsyncStorage to securely rehydrate that session across app restarts, and executed secure Axios requests to procure JWTs. Crucially, we abandoned manual .navigate() logic for login flows, adopting the Protected Routes paradigm to conditionally mount and destroy entire navigation stacks based on cryptographic identity.

15. Next Chapter Recommendation

Writing a custom NodeJS backend to hash passwords, generate JWTs, and store users in SQL takes months. What if you could outsource all of that to Google? Proceed to Chapter 22: Firebase Integration.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·