Lesson 132 of 158 – React Native Auth Context
84%

React Native Auth Context

In a React Native application, many screens need authentication information such as the current user, JWT token, and login status. React Context allows us to keep authentication state in one central place and make it available to different components.

Note: Auth Context manages authentication state in the React Native application. The PHP REST API must still verify the JWT token on every protected request.

1. What is Auth Context?

Auth Context is a React Context used to share authentication information throughout the application.

It can contain:

  • Current user
  • JWT token
  • Authentication status
  • Login function
  • Logout function
  • Loading state

2. Why Use Auth Context?

Without Context, authentication data may need to be passed through many components using props.

App
 ↓
Home
 ↓
Profile
 ↓
StudentProfile

Auth Context allows these components to access authentication state without passing the same props through every level.

3. Authentication State

interface User {
    id: number;
    name: string;
    email: string;
    role: "admin" | "teacher" | "student";
}

interface AuthState {
    user: User | null;
    token: string | null;
    isAuthenticated: boolean;
    loading: boolean;
}

4. Creating an Auth Context

import {
    createContext
} from "react";

interface AuthContextType {
    user: User | null;
    token: string | null;
    isAuthenticated: boolean;
}

export const AuthContext =
    createContext<AuthContextType | undefined>(
        undefined
    );

5. Creating Auth Context Type

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

This interface describes everything that the authentication context will provide to components.

6. Creating the Auth Provider

import React, {
    useState
} from "react";

export const AuthProvider = ({
    children
}: {
    children: React.ReactNode;
}) => {

    return (
        <>
            {children}
        </>
    );
};

7. Storing the User in State

const [user, setUser] =
    useState<User | null>(null);

Initially there may be no authenticated user, so the state starts with null.

8. Storing the JWT Token

const [token, setToken] =
    useState<string | null>(null);

The token can be stored in memory while the application is running. Persistent storage can also be used to restore authentication after the application starts again.

9. Authentication Status

const isAuthenticated =
    !!token;

If a valid token is available in application state, the application can consider the user authenticated. The server must still verify the token for protected API requests.

10. Loading State

const [loading, setLoading] =
    useState(true);

Loading is useful while the application checks AsyncStorage and restores a previous authentication session.

11. Login Function

const login = async (
    newToken: string,
    newUser: User
) => {

    await AsyncStorage.setItem(
        "authToken",
        newToken
    );

    setToken(newToken);
    setUser(newUser);
};

The login function updates both persistent storage and React state.

12. Logout Function

const logout = async () => {

    await AsyncStorage.removeItem(
        "authToken"
    );

    setToken(null);
    setUser(null);
};

Removing the local token prevents the client from using that stored token for future requests.

13. Restoring Authentication

useEffect(() => {

    const restoreAuth =
        async () => {

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

        if (savedToken) {
            setToken(savedToken);
        }

        setLoading(false);
    };

    restoreAuth();

}, []);

14. Restoring the User Profile

A stored token alone may not contain all the user information required by the application. After restoring the token, the app can call a protected profile endpoint.

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

setUser(
    response.data.user
);

15. Auth Provider Value

const value: AuthContextType = {
    user,
    token,
    isAuthenticated: !!token,
    loading,
    login,
    logout
};

This object contains the authentication data and functions that will be available to child components.

16. Providing the Context

return (
    <AuthContext.Provider value={value}>

        {children}

    </AuthContext.Provider>
);

Every component inside the provider can access the authentication context.

17. Wrapping the Application

const App = () => {

    return (
        <AuthProvider>

            <AppNavigator />

        </AuthProvider>
    );
};

18. Using useContext

import {
    useContext
} from "react";

const auth =
    useContext(AuthContext);

The component can now access authentication information from the context.

19. Creating a Custom useAuth Hook

export const useAuth = () => {

    const context =
        useContext(AuthContext);

    if (!context) {

        throw new Error(
            "useAuth must be used inside AuthProvider"
        );
    }

    return context;
};

A custom hook makes authentication access easier throughout the application.

20. Using useAuth in a Screen

const ProfileScreen = () => {

    const {
        user,
        logout
    } = useAuth();

    return (
        <View>

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

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

        </View>
    );
};

21. Login Screen with Auth Context

const LoginScreen = () => {

    const {
        login
    } = useAuth();

    const handleLogin = async () => {

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

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

    return (
        <Button
            title="Login"
            onPress={handleLogin}
        />
    );
};

22. Auth Context and Axios

Auth Context manages the authentication state while Axios handles communication with the REST API.

Auth Context
     ↓
Token
     ↓
AsyncStorage
     ↓
Axios Interceptor
     ↓
Authorization Header
     ↓
PHP API

23. Handling an Expired Token

try {

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

} catch (error) {

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

        await logout();
    }
}

A 401 response can indicate that the token is no longer accepted by the server.

24. Protected Navigation Concept

Auth Context can be used to decide which navigation flow should be displayed.

if (loading) {

    return <LoadingScreen />;

}

if (isAuthenticated) {

    return <MainNavigator />;

}

return <AuthNavigator />;

This controls the user experience. The backend must still protect its APIs.

25. Complete Auth Context Example

import React, {
    createContext,
    useContext,
    useEffect,
    useState
} from "react";

import AsyncStorage
    from "@react-native-async-storage/async-storage";

interface User {
    id: number;
    name: string;
    email: string;
    role: "admin" | "teacher" | "student";
}

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

const AuthContext =
    createContext<
        AuthContextType | undefined
    >(undefined);

export const AuthProvider = ({
    children
}: {
    children: React.ReactNode;
}) => {

    const [user, setUser] =
        useState<User | null>(null);

    const [token, setToken] =
        useState<string | null>(null);

    const [loading, setLoading] =
        useState(true);

    const login = async (
        newToken: string,
        newUser: User
    ) => {

        await AsyncStorage.setItem(
            "authToken",
            newToken
        );

        setToken(newToken);
        setUser(newUser);
    };

    const logout = async () => {

        await AsyncStorage.removeItem(
            "authToken"
        );

        setToken(null);
        setUser(null);
    };

    useEffect(() => {

        const restoreAuth =
            async () => {

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

            if (savedToken) {
                setToken(savedToken);
            }

            setLoading(false);
        };

        restoreAuth();

    }, []);

    const value: AuthContextType = {
        user,
        token,
        isAuthenticated: !!token,
        loading,
        login,
        logout
    };

    return (
        <AuthContext.Provider value={value}>
            {children}
        </AuthContext.Provider>
    );
};

26. Complete useAuth Hook

export const useAuth = () => {

    const context =
        useContext(AuthContext);

    if (!context) {

        throw new Error(
            "useAuth must be used inside AuthProvider"
        );
    }

    return context;
};

Screens can now simply call useAuth() instead of directly accessing the Context object.

27. Auth Context with User Profile

const restoreAuth =
    async () => {

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

    if (!savedToken) {

        setLoading(false);
        return;
    }

    try {

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

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

    } catch (error) {

        await AsyncStorage.removeItem(
            "authToken"
        );

        setToken(null);
        setUser(null);

    } finally {

        setLoading(false);
    }
};

This approach verifies the stored session through the protected API before treating the user as fully authenticated.

28. Auth Context Project Structure

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

Keeping authentication logic in a dedicated context makes a project easier to maintain.

29. Auth Context Security Considerations

  • Never store the JWT signing secret in React Native.
  • Never send passwords in URLs.
  • Use HTTPS for production API communication.
  • Do not trust authentication state only because a token exists locally.
  • Let the PHP server verify the JWT.
  • Handle expired or invalid tokens properly.
  • Do not expose sensitive information in logs.
  • Use an appropriate secure storage solution when the application's threat model requires it.

Auth Context improves application architecture, but it does not replace server-side security.

30. Complete Authentication Architecture

Login Screen
     ↓
Login API
     ↓
JWT Token
     ↓
Auth Context
     ↓
AsyncStorage
     ↓
Axios Interceptor
     ↓
Protected API
     ↓
PHP JWT Verification
     ↓
User Identification
     ↓
Authorization
     ↓
MySQL Database
     ↓
JSON Response
     ↓
React Native Screen

Auth Context becomes the central authentication layer of the React Native application. It keeps login, logout, user state, token state, and loading state organized while Axios communicates with the protected PHP REST API.

📌 Key Points

  • React Context can centralize authentication state.
  • Auth Context can store the current user and JWT state.
  • AsyncStorage can persist the authentication token.
  • A login function can save the token and user information.
  • A logout function can remove the local authentication token.
  • A custom useAuth() hook makes authentication easier to access.
  • Auth Context can work together with Axios interceptors.
  • A protected API should verify the JWT on the server.
  • A 401 response can be used to trigger authentication cleanup.
  • Auth Context controls application state and navigation experience, not backend security.

🧠 Quick Quiz

Question: What is the main purpose of an Auth Context in React Native?