Day 5: Implementing Authentication with JWT in React Native


In this tutorial, you’ll learn how to integrate JWT-based authentication in a React Native app. By the end of today’s lesson, users will be able to log in and access protected routes using a JWT token that is stored securely.


What You Will Learn Today:

  1. Setting up a login form
  2. Authenticating a user with JWT and an API
  3. Storing the JWT securely using AsyncStorage
  4. Accessing protected routes with the stored JWT
  5. Logging out and clearing the JWT

Step 1: Setting Up a Login Form

Let’s create a simple login form where users can enter their credentials. We’ll use mock credentials in this example, but you can connect it to an actual API.

  1. Create a new file called LoginScreen.js:
touch LoginScreen.js
  1. Open LoginScreen.js and add the login form:
import React, { useState } from 'react';
import { View, Text, TextInput, Button, Alert } from 'react-native';
import { useDispatch } from 'react-redux';
import { loginUser } from './store/actions/authActions'; // Action we will create next

export default function LoginScreen() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const dispatch = useDispatch();

  const handleLogin = () => {
    if (!email || !password) {
      Alert.alert('Error', 'Please enter both email and password');
      return;
    }

    dispatch(loginUser({ email, password }));
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
      <Text>Email:</Text>
      <TextInput
        style={{ borderWidth: 1, marginBottom: 10, padding: 8 }}
        onChangeText={setEmail}
        value={email}
        keyboardType="email-address"
        autoCapitalize="none"
      />
      <Text>Password:</Text>
      <TextInput
        style={{ borderWidth: 1, marginBottom: 20, padding: 8 }}
        onChangeText={setPassword}
        value={password}
        secureTextEntry
      />
      <Button title="Login" onPress={handleLogin} />
    </View>
  );
}

Explanation:

  • handleLogin: Validates the email and password inputs. If valid, it dispatches the loginUser action, which we’ll create in the next step.
  • TextInput: Captures user inputs for the email and password fields.
See also  Day 4: Handling Side Effects with Redux-Saga

Step 2: Creating the Authentication Action

Now, let’s create an action to handle the login process. This action will send the login credentials to the API, receive a JWT token if successful, and store it in Redux.

  1. Inside the store/actions folder, create a new file called authActions.js:
touch store/actions/authActions.js
  1. Open authActions.js and add the login action:
import AsyncStorage from '@react-native-async-storage/async-storage';

export const loginUser = (credentials) => {
  return async (dispatch) => {
    dispatch({ type: 'LOGIN_REQUEST' });

    try {
      const response = await fetch('https://yourapi.com/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(credentials),
      });
      const data = await response.json();

      if (data.token) {
        await AsyncStorage.setItem('token', data.token); // Store token securely
        dispatch({ type: 'LOGIN_SUCCESS', payload: data.token });
      } else {
        dispatch({ type: 'LOGIN_FAILURE', error: data.message || 'Login failed' });
      }
    } catch (error) {
      dispatch({ type: 'LOGIN_FAILURE', error: error.message });
    }
  };
};

Explanation:

  • loginUser: This function sends the login credentials to the API. If the login is successful, it stores the JWT token in AsyncStorage and dispatches a success action.
  • AsyncStorage.setItem(): Stores the token securely on the device so it persists across app sessions.
  • LOGIN_REQUEST, LOGIN_SUCCESS, LOGIN_FAILURE: These action types handle the various states of the login process (loading, success, and failure).

Step 3: Creating the Authentication Reducer

Now, let’s create a reducer to manage the authentication state. This reducer will store the JWT token in the Redux state upon a successful login.

  1. Inside the reducers folder, create a file called authReducer.js:
touch store/reducers/authReducer.js
  1. Open authReducer.js and define the reducer:
const initialState = {
  token: null,
  loading: false,
  error: null,
};

const authReducer = (state = initialState, action) => {
  switch (action.type) {
    case 'LOGIN_REQUEST':
      return { ...state, loading: true, error: null };
    case 'LOGIN_SUCCESS':
      return { ...state, token: action.payload, loading: false };
    case 'LOGIN_FAILURE':
      return { ...state, loading: false, error: action.error };
    case 'LOGOUT':
      return { ...state, token: null, loading: false, error: null };
    default:
      return state;
  }
};

export default authReducer;

Explanation:

  • token: Stores the JWT token upon successful login.
  • loading: Tracks the loading state during the login process.
  • error: Stores any error message if the login fails.
  • LOGOUT: Clears the token from the state when the user logs out.
See also  How to Bypass Facebook CORS Restrictions and Access External Resources Securely

Combining the Reducer

  1. Open reducers/index.js and add the authReducer:
import { combineReducers } from 'redux';
import counterReducer from './counterReducer';
import postReducer from './postReducer';
import authReducer from './authReducer'; // Add the auth reducer

const rootReducer = combineReducers({
  counter: counterReducer,
  posts: postReducer,
  auth: authReducer, // Combine it with other reducers
});

export default rootReducer;

Step 4: Accessing Protected Routes with JWT

Now, let’s create a HomeScreen component that is accessible only when a user is authenticated.

  1. Create a new file called HomeScreen.js:
touch HomeScreen.js
  1. Open HomeScreen.js and create a basic component:
import React from 'react';
import { View, Text, Button } from 'react-native';
import { useDispatch } from 'react-redux';
import AsyncStorage from '@react-native-async-storage/async-storage';

export default function HomeScreen() {
  const dispatch = useDispatch();

  const handleLogout = async () => {
    await AsyncStorage.removeItem('token'); // Remove the token from AsyncStorage
    dispatch({ type: 'LOGOUT' }); // Dispatch logout action
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Welcome to the protected Home Screen!</Text>
      <Button title="Logout" onPress={handleLogout} />
    </View>
  );
}

Explanation:

  • handleLogout: Removes the JWT token from AsyncStorage and dispatches the LOGOUT action to clear the token from the Redux store.
  • This screen can now be accessed only when the token is present in the Redux state.

Step 5: Navigating Based on Authentication State

We’ll now conditionally render either the LoginScreen or the HomeScreen based on whether the user is authenticated (i.e., if the token exists in the Redux state).

  1. Modify App.js as follows:
import React from 'react';
import { Provider, useSelector } from 'react-redux';
import store from './store/store';
import LoginScreen from './LoginScreen';
import HomeScreen from './HomeScreen';

function AppContent() {
  const token = useSelector((state) => state.auth.token);

  return token ? <HomeScreen /> : <LoginScreen />;
}

export default function App() {
  return (
    <Provider store={store}>
      <AppContent />
    </Provider>
  );
}

Explanation:

  • AppContent: Conditionally renders either the HomeScreen if the token exists or the LoginScreen if it doesn’t.
  • This ensures that users are redirected to the login screen if they aren’t authenticated.
See also  Deep Dive: Orchestrating a Development Galaxy with Jenkins, Jira, Selenium, and Docker

Step 6: Testing Authentication

Let’s run the app and test the JWT-based authentication flow.

  1. Run your app:
npm start
  1. Enter your login credentials in the LoginScreen. If successful, you should be redirected to the HomeScreen.
  2. Test the logout button, which should log you out and redirect you back to the LoginScreen.

Step 7: Recap and Summary

In today’s tutorial, you learned how to implement JWT-based authentication in a React Native app. Here’s a quick summary of what you’ve done:

  • Created a LoginScreen with a form to capture user credentials.
  • Implemented a loginUser action to authenticate the user with an API.
  • Stored the JWT token securely using AsyncStorage.
  • Created an authReducer to manage authentication state in Redux.
  • Built a HomeScreen that is accessible only when the user is authenticated.
  • Conditionally rendered the login and home screens based on the authentication state.

With JWT-based authentication, your app can now securely log in users and control access to protected routes.


Next Up: Day 6 – Offline Data Storage with Redux Persist and AsyncStorage

In Day 6, we’ll explore offline data storage in React Native using Redux Persist and AsyncStorage, so users can continue using the app without internet connectivity.

Stay tuned for more advanced features tomorrow!


Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.