Lesson 133 of 158 – React Native Auth Navigation
84%

React Native Auth Navigation

Authentication navigation controls which screens a user can access based on their authentication state. In a React Native application, we can use Auth Context to decide whether to show login screens or protected application screens.

Note: Navigation protects the user interface, but it does not protect the API. The PHP REST API must independently verify the JWT token for every protected request.

1. What is Authentication Navigation?

Authentication navigation means showing different navigation flows depending on whether the user is authenticated.

Not Logged In
      ↓
Login / Registration

Logged In
      ↓
Home / Profile / Dashboard

2. Why Use Auth-Based Navigation?

A mobile application may have public screens and protected screens. Authentication navigation provides a clean way to separate them.

  • Login screen
  • Registration screen
  • Home screen
  • Profile screen
  • Student dashboard
  • Settings screen

3. Public and Protected Screens

Public Screens Protected Screens
Login Home
Registration Profile
Forgot Password Student Dashboard

4. Authentication Navigation Flow

App Starts
    ↓
Check Authentication
    ↓
Loading?
    ↓
Yes → Loading Screen
    ↓
No
    ↓
Authenticated?
  ↙       ↘
Yes        No
 ↓          ↓
App        Auth
Navigator  Navigator

5. Auth Context Values

interface AuthContextType {
    user: User | null;
    token: string | null;
    isAuthenticated: boolean;
    loading: boolean;
    login: (
        token: string,
        user: User
    ) => Promise<void>;
    logout: () => Promise<void>;
}

Navigation can use isAuthenticated and loading to decide which navigator to display.

6. Creating an Auth Navigator

const AuthNavigator = () => {

    return (
        <Stack.Navigator>

            <Stack.Screen
                name="Login"
                component={LoginScreen}
            />

            <Stack.Screen
                name="Register"
                component={RegisterScreen}
            />

        </Stack.Navigator>
    );
};

7. Creating the App Navigator

const AppNavigator = () => {

    return (
        <Stack.Navigator>

            <Stack.Screen
                name="Home"
                component={HomeScreen}
            />

            <Stack.Screen
                name="Profile"
                component={ProfileScreen}
            />

        </Stack.Navigator>
    );
};

8. Conditional Navigation

const RootNavigator = () => {

    const {
        isAuthenticated,
        loading
    } = useAuth();

    if (loading) {

        return <LoadingScreen />;
    }

    if (isAuthenticated) {

        return <AppNavigator />;
    }

    return <AuthNavigator />;
};

9. Loading Screen

const LoadingScreen = () => {

    return (
        <View>

            <ActivityIndicator
                size="large"
            />

            <Text>
                Checking authentication...
            </Text>

        </View>
    );
};

The loading screen prevents the application from immediately showing the wrong navigation flow while the stored authentication session is being checked.

10. Why Loading State is Important

When the application starts, Auth Context may need to read the stored token from AsyncStorage and verify the current session.

App Starts
   ↓
loading = true
   ↓
Read Token
   ↓
Restore User
   ↓
loading = false
   ↓
Show Correct Navigator

11. Login and Navigation

const handleLogin = async () => {

    const response =
        await api.post(
            "/login.php",
            {
                email,
                password
            }
        );

    await login(
        response.data.token,
        response.data.user
    );
};

Updating the Auth Context can cause the root navigation to switch to the authenticated application flow.

12. Logout and Navigation

const handleLogout = async () => {

    await logout();
};

After the authentication state becomes unauthenticated, the root navigation can display the Auth Navigator again.

13. Logout Button

const ProfileScreen = () => {

    const {
        user,
        logout
    } = useAuth();

    return (
        <View>

            <Text>
                {user?.name}
            </Text>

            <Button
                title="Logout"
                onPress={logout}
            />

        </View>
    );
};

14. Protected Navigation Does Not Protect the API

A user can modify a mobile application or send requests directly to the API. Therefore, hiding a screen is not a security mechanism for the backend.

React Native Navigation
        ↓
User Experience

PHP API
        ↓
Actual Security

The PHP server must verify the JWT independently.

15. Protected API with Auth Navigation

Authenticated User
       ↓
App Navigator
       ↓
Profile Screen
       ↓
Axios Request
       ↓
Bearer JWT
       ↓
PHP Protected API
       ↓
JWT Verification

16. React Navigation Stack

import {
    createNativeStackNavigator
} from "@react-navigation/native-stack";

const Stack =
    createNativeStackNavigator();

The native stack navigator can be used for both authenticated and unauthenticated navigation flows.

17. Auth Navigator Example

const AuthNavigator = () => {

    return (
        <Stack.Navigator>

            <Stack.Screen
                name="Login"
                component={LoginScreen}
            />

            <Stack.Screen
                name="Register"
                component={RegisterScreen}
            />

            <Stack.Screen
                name="ForgotPassword"
                component={ForgotPasswordScreen}
            />

        </Stack.Navigator>
    );
};

18. Main App Navigator Example

const AppNavigator = () => {

    return (
        <Stack.Navigator>

            <Stack.Screen
                name="Home"
                component={HomeScreen}
            />

            <Stack.Screen
                name="Profile"
                component={ProfileScreen}
            />

            <Stack.Screen
                name="Students"
                component={StudentListScreen}
            />

        </Stack.Navigator>
    );
};

19. Root Navigator

const RootNavigator = () => {

    const {
        loading,
        isAuthenticated
    } = useAuth();

    if (loading) {

        return <LoadingScreen />;
    }

    return isAuthenticated
        ? <AppNavigator />
        : <AuthNavigator />;
};

20. App.tsx Structure

const App = () => {

    return (
        <NavigationContainer>

            <AuthProvider>

                <RootNavigator />

            </AuthProvider>

        </NavigationContainer>
    );
};

AuthProvider must wrap the components that need access to authentication state.

21. Navigation After Login

Login Screen
     ↓
POST /login.php
     ↓
JWT + User
     ↓
login()
     ↓
isAuthenticated = true
     ↓
RootNavigator
     ↓
AppNavigator
     ↓
Home Screen

The navigation flow can change automatically when the context state changes.

22. Navigation After Logout

Profile Screen
     ↓
logout()
     ↓
Remove Token
     ↓
Clear User
     ↓
isAuthenticated = false
     ↓
RootNavigator
     ↓
AuthNavigator
     ↓
Login Screen

23. Handling Expired JWT

If a protected API returns HTTP 401 because the token is no longer accepted, the authentication state can be cleared.

if (
    axios.isAxiosError(error) &&
    error.response?.status === 401
) {

    await logout();
}

The root navigator can then show the authentication flow.

24. Role-Based Navigation

The user role can influence which screens are displayed.

if (user?.role === "admin") {

    return <AdminDashboard />;
}

if (user?.role === "teacher") {

    return <TeacherDashboard />;
}

return <StudentDashboard />;

This controls the UI. The server must still enforce role-based permissions.

25. Admin Navigation Example

const AdminScreen = () => {

    const { user } = useAuth();

    if (user?.role !== "admin") {

        return (
            <Text>
                Access denied
            </Text>
        );
    }

    return (
        <View>
            <Text>
                Admin Dashboard
            </Text>
        </View>
    );
};

This is a UI-level check. The corresponding admin API must also check the user's role on the server.

26. Complete Root Navigation Example

const RootNavigator = () => {

    const {
        loading,
        isAuthenticated,
        user
    } = useAuth();

    if (loading) {

        return <LoadingScreen />;
    }

    if (!isAuthenticated) {

        return <AuthNavigator />;
    }

    if (user?.role === "admin") {

        return <AdminNavigator />;
    }

    if (user?.role === "teacher") {

        return <TeacherNavigator />;
    }

    return <AppNavigator />;
};

27. Auth Navigation with API Verification

const restoreAuth =
    async () => {

    const token =
        await AsyncStorage.getItem(
            "authToken"
        );

    if (!token) {

        setLoading(false);
        return;
    }

    try {

        const response =
            await api.get<UserResponse>(
                "/profile.php"
            );

        setToken(token);
        setUser(
            response.data.user
        );

    } catch {

        await AsyncStorage.removeItem(
            "authToken"
        );

        setToken(null);
        setUser(null);

    } finally {

        setLoading(false);
    }
};

The stored token can be checked against a protected API before the application considers the session restored.

28. Recommended Project Structure

src/
│
├── context/
│   └── AuthContext.tsx
│
├── navigation/
│   ├── RootNavigator.tsx
│   ├── AuthNavigator.tsx
│   └── AppNavigator.tsx
│
├── screens/
│   ├── LoginScreen.tsx
│   ├── RegisterScreen.tsx
│   ├── HomeScreen.tsx
│   └── ProfileScreen.tsx
│
├── services/
│   └── api.ts
│
└── types/
    └── auth.ts

Separating context, navigation, screens, services, and types makes the project easier to maintain.

29. Common Auth Navigation Mistakes

  • Checking only navigation state and not protecting the API.
  • Showing the wrong navigator before authentication restoration finishes.
  • Forgetting to remove the local token during logout.
  • Not handling HTTP 401 responses.
  • Trusting a locally stored token without server verification.
  • Putting authentication logic in every individual screen.
  • Using role checks only in the UI while leaving admin APIs unprotected.

30. Complete Auth Navigation Architecture

App.tsx
   ↓
NavigationContainer
   ↓
AuthProvider
   ↓
Restore Token
   ↓
Loading Screen
   ↓
Check Authentication
   ↓
┌──────────────────────┐
│                      │
Authenticated      Not Authenticated
│                      │
↓                      ↓
App Navigator      Auth Navigator
│                      │
↓                      ↓
Home/Profile       Login/Register
│
↓
Axios
│
↓
Bearer JWT
│
↓
PHP Protected API
│
↓
JWT Verification
│
↓
MySQL

Auth Context, authentication navigation, Axios, JWT, and the PHP protected API work together to create a complete authentication architecture for a React Native application.

📌 Key Points

  • Authentication navigation separates public and protected screens.
  • Auth Context provides authentication state to navigation components.
  • A loading screen can be shown while the stored session is restored.
  • Authenticated users can see the main application navigator.
  • Unauthenticated users can see the login and registration navigator.
  • Logout can switch the application back to the authentication flow.
  • A 401 API response can trigger authentication cleanup.
  • Role-based navigation can customize the mobile UI.
  • Navigation does not replace server-side authorization.
  • The PHP API must independently verify JWT tokens and permissions.

🧠 Quick Quiz

Question: What should a React Native app normally show when the user is not authenticated?