Lesson 135 of 158 – React Native Persistent Login
85%

React Native Persistent Login

Persistent login allows a user to remain signed in after closing and reopening the React Native application. Instead of asking the user to enter their email and password every time, the application can restore the authentication state using a stored authentication credential.

Note: Persistent login should not mean trusting a stored token forever. The application should restore the credential, validate the session with the server when appropriate, and handle expired or invalid tokens.

1. What is Persistent Login?

Persistent login means restoring a user's authenticated state when the application starts again.

First Login
    ↓
JWT Token
    ↓
Store Token
    ↓
Close App
    ↓
Open App
    ↓
Read Token
    ↓
Validate Session
    ↓
User Remains Logged In

2. Normal Login vs Persistent Login

Normal Login Persistent Login
User logs in User logs in
Token stored Token stored
App closes App closes
User may need login again App restores authentication

3. Why Persistent Login is Useful

  • Better user experience.
  • User does not need to enter credentials repeatedly.
  • Useful for mobile applications.
  • Works with token-based authentication.
  • Can restore the user's profile after application restart.

4. Storing the Authentication Token

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

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

The token can later be read when the application starts.

5. Reading the Stored Token

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

If the key does not exist, the result is null.

6. TypeScript Token Type

const token: string | null =
    await AsyncStorage.getItem(
        "authToken"
    );

TypeScript correctly represents the possibility that no stored token exists.

7. Checking for a Stored Token

if (token) {

    console.log(
        "Stored authentication token found"
    );

} else {

    console.log(
        "No stored token"
    );
}

8. Authentication Loading State

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

The application should remain in a loading state while it checks whether a previous authentication session can be restored.

9. Restoring Authentication on App Start

useEffect(() => {

    const restoreAuth =
        async () => {

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

        if (token) {
            setToken(token);
        }

        setLoading(false);
    };

    restoreAuth();

}, []);

10. Why useEffect is Used

The authentication restoration process should run when the application or authentication provider starts.

useEffect(() => {

    restoreAuth();

}, []);

The empty dependency array means the effect runs after the component is initially mounted.

11. Restoring the User Profile

A token may identify the user, but the application often needs current profile information as well.

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

setUser(
    response.data.user
);

12. Restoring Token and User Together

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

if (token) {

    setToken(token);

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

    setUser(
        response.data.user
    );
}

13. Validating the Stored Token

Finding a token in local storage does not prove that the token is still valid. The protected API should verify it.

Stored Token
     ↓
Protected API Request
     ↓
PHP JWT Verification
     ↓
Valid?
  ↙      ↘
Yes      No
 ↓        ↓
User     Logout

14. Handling an Expired Token

try {

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

} catch (error) {

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

        await AsyncStorage.removeItem(
            "authToken"
        );
    }
}

15. Clearing Invalid Authentication

await AsyncStorage.removeItem(
    "authToken"
);

setToken(null);
setUser(null);

This returns the application to an unauthenticated state.

16. Persistent Login with Auth Context

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

Auth Context is a convenient place to manage persistent authentication.

17. Persistent Login and Root Navigation

const RootNavigator = () => {

    const {
        loading,
        isAuthenticated
    } = useAuth();

    if (loading) {

        return <LoadingScreen />;
    }

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

The loading state prevents the wrong navigator from appearing while authentication is being restored.

18. Axios Interceptor with Persistent Token

api.interceptors.request.use(
    async config => {

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

        if (token) {

            config.headers.Authorization =
                `Bearer ${token}`;
        }

        return config;
    }
);

The interceptor can automatically use the restored token for protected API requests.

19. Persistent Login Flow

App Opens
    ↓
AuthProvider Starts
    ↓
Read AsyncStorage
    ↓
Token Found?
  ↙       ↘
Yes       No
 ↓         ↓
Call      Login
Profile   Screen
API
 ↓
Verify JWT
 ↓
User Found
 ↓
App Navigator

20. Login and Persistence

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

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

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

Saving the token during login allows it to be restored later.

21. Logout Cancels Persistent Login

const logout = async () => {

    await AsyncStorage.removeItem(
        "authToken"
    );

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

Once the token is removed, the next application start will not restore the previous login from that token.

22. Complete restoreAuth Function

const restoreAuth =
    async () => {

    try {

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

        if (!savedToken) {
            return;
        }

        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);
    }
};

23. Calling restoreAuth

useEffect(() => {

    restoreAuth();

}, []);

This can be placed inside the AuthProvider so authentication restoration happens when the provider is initialized.

24. Complete AuthProvider Restoration

useEffect(() => {

    const restoreAuth =
        async () => {

        try {

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

            if (!token) {
                return;
            }

            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);
        }
    };

    restoreAuth();

}, []);

25. Persistent Login with Axios

App Starts
     ↓
AsyncStorage
     ↓
JWT Found
     ↓
Axios
     ↓
Request Interceptor
     ↓
Authorization: Bearer JWT
     ↓
PHP Profile API
     ↓
JWT Verification
     ↓
User Response
     ↓
Auth Context
     ↓
App Navigator

26. Handling Network Errors

try {

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

} catch (error) {

    if (
        axios.isAxiosError(error) &&
        !error.response
    ) {

        console.log(
            "Network error"
        );
    }
}

A network failure is different from a server response such as HTTP 401. The application should handle both cases appropriately.

27. Persistent Login Security

  • Use HTTPS for production API communication.
  • Do not store passwords for automatic login.
  • Do not put JWTs in URLs.
  • Do not log access tokens.
  • Handle expired tokens.
  • Let the server verify the JWT.
  • Use suitable secure storage for sensitive credentials according to the application's security requirements.
  • Consider short-lived access tokens and refresh-token strategies for higher-security applications.

28. Persistent Login Project Structure

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

29. Common Persistent Login Mistakes

  • Showing the login screen before checking AsyncStorage.
  • Trusting any stored token without server verification.
  • Never handling HTTP 401 responses.
  • Storing the user's password for automatic login.
  • Forgetting to clear the token during logout.
  • Leaving old user data in React state after logout.
  • Assuming that local token deletion revokes a JWT on the server.
  • Logging JWT tokens during debugging.

30. Complete Persistent Login Architecture

First Login
    ↓
PHP Login API
    ↓
JWT Token
    ↓
AsyncStorage
    ↓
Close Application
    ↓
Open Application
    ↓
AuthProvider
    ↓
Read Stored Token
    ↓
Axios Protected Request
    ↓
PHP JWT Verification
    ↓
Profile API
    ↓
User Data
    ↓
Auth Context
    ↓
isAuthenticated = true
    ↓
App Navigator

Persistent login provides a smooth mobile experience while still allowing the server to validate the authentication credential whenever the application restores the session.

📌 Key Points

  • Persistent login restores authentication after the application restarts.
  • AsyncStorage can persist an authentication token between app launches.
  • A stored token should not automatically be considered valid.
  • The application can call a protected profile API to validate the session.
  • Axios interceptors can automatically attach the restored JWT.
  • Auth Context can manage the restored user and authentication state.
  • A loading state prevents incorrect navigation during restoration.
  • HTTP 401 should be handled by clearing invalid authentication state.
  • Logout should remove the stored token.
  • Persistent login should be designed together with appropriate token security and expiration strategies.

🧠 Quick Quiz

Question: What is the main purpose of persistent login in React Native?