Authenticate a React App with Laravel Sanctum (Part 2)

April 4, 2020

This series of articles discusses how to use Laravel Sanctum to provide authentication for a front end React application. In part one, I outlined the steps required to build and configure the Laravel back end. In this article, we turn our attention to the React front end. I will be focussing on the areas that are unique to this application, namely the communication between React on the client and Laravel on the server. I am assuming that you already have the basic skills needed to build a React app.

For additional reference, there is a live version of the finished app as well as complete code listings for the React client application and the Laravel server application.

Creating a React Project

To get started, create a new project named react-auth with create-react-app.

npx create-react-app react-auth

Then add axios, which will be used to make XMLHttpRequests to the Laravel API from the browser.

cd c:/react-auth
npm install axios

Styling with TailwindCSS

There are different approaches for styling React components. I'm a big fan of TailwindCSS, which can be installed like this.

npm install tailwindcss

Next, create a configuration file named tailwind.config.js by using the following command.

npx tailwind init

In the root directory of the application, manually create a file named style.css. Along with tailwind.config.js, this file can be used to customize Tailwind. Initially, the file should look like this.

@tailwind base;

@tailwind components;

@tailwind utilities;

If you'd like, you can make any changes you want to tailwind.config.js and style.css. Whether you make any customizations or not, you have to run a build step that creates the actual css file used that will be used by the application.

npx tailwind build style.css -o src/css/tailwind.css

Finally, make the styles available to our components by importing tailwind.css into App.js.

import "./css/tailwind.css"

Icons

The forms in my application make use of a number of different icons. It's easy to include them by using React Icons.

npm install react-icons --save

And with this, all of the dependencies have now been installed.

App Overview

Let's take a look at how the app is going to work. It begins by displaying the following splash screen.

Splash page

There's a menu on the right side for choosing between

  • Signup - allowing first time users to register, and
  • Login - allowing restistered users to access the application.

To signup, the user enters a user name, email address, and password.

Signup page

Previously registered users can login with email and password.

Login page

A logged in user can then logout.

Logout page

After the user has logged out, the app will once again display the opening splash screen. There's nothing unusual or surprising here. This is the standard authentication sequence that everyone is more than familiar with.

State Management

Let's take a look at the state that's required to implement this process. This application is going to use React Context for managing the state. The context is defined in the contexts/AppContext.js file.

The main components of the state are:

authStatus - Used to control which component is visible at any particular time. As the user proceeds through each step of authentication, this value is changed so that the next component will be displayed.

User information (userId and userName). userId is the key that can be used by the Laravel backend to retrieve user information from the database. There's really no use for it in this simple authentication application, but userId would be an important piece of information in almost any project that's going to work with a real backend API.

Form information (userNameInput, userEmail, and userPassword) These are the items that will be obtained from the forms and passed to Laravel to perform the authentication functions.

Code Organization

The App.js file for the application looks like this.

import React from "react"
import "./css/tailwind.css"
import { AppProvider } from "./contexts/AppContext"
import AuthContainer from "./components/AuthContainer"

function App() {
return (
<div className="flex w-full justify-center bg-blue-200 pt-16 pb-32">
<div className="lg:flex w-11/12 lg:w-3/4 xl:w-3/5">
<AppProvider>
<AuthContainer />
</AppProvider>
</div>
</div>
)
}

export default App

App.js is basically just an AuthContainer component wrapped in an AppProvider component. AuthContainer is used to hold all the form components (splash screen, signup, login, logout) along with the logic needed to display the correct one at the appropriate time. AppProvider is needed so that the rest of the components can have access to the state by way of the context.

Here is the components/AuthContainer.js file.

import React, { useContext } from "react"
import {
NOT_LOGGED_IN,
LOG_IN_FORM,
SIGN_UP_FORM,
LOGGED_IN,
} from "../constants/AuthStatus"
import AuthNotLoggedIn from "./AuthNotLoggedIn"
import AuthSignup from "./AuthSignup"
import AuthLogin from "./AuthLogin"
import AuthLogout from "./AuthLogout"
import { AppContext } from "../contexts/AppContext"

const AuthContainer = () => {
const appContext = useContext(AppContext)
const { authStatus } = appContext
const showNotLoggedIn = authStatus === NOT_LOGGED_IN ? "" : "hidden"
const showLoginForm = authStatus === LOG_IN_FORM ? "" : "hidden"
const showSignupForm = authStatus === SIGN_UP_FORM ? "" : "hidden"
const showLoggedIn = authStatus === LOGGED_IN ? "" : "hidden"

return (
<div className="w-full">
<div className={showNotLoggedIn + " justify-end py-4"}>
<AuthNotLoggedIn />
</div>
<div className={showLoginForm + " justify-end py-4"}>
<AuthLogin option="login" />
</div>
<div className={showSignupForm + " justify-end py-4"}>
<AuthSignup option="signup" />
</div>
<div className={showLoggedIn + " justify-end py-4"}>
<AuthLogout />
</div>
</div>
)
}

export default AuthContainer

The following lines of code give the component access to the state in the context.

import React, { useContext } from "react";
import { AppContext } from "../contexts/AppContext";

const AuthContainer = () => {
const appContext = useContext(AppContext);
const { authStatus } = appContext;

AuthContainer just has one job. It reads the current status from the state authStatus variable, and then based on that value it shows the appropriate component to the user. All of the components are in the src/components folder.

Splash Screen

When the app begins, authStatus is initialized to NOT_LOGGED_IN, causing the splash screen from the component AuthNotLoggedIn to be made visible. AuthNotLoggedIn contains two components. The first is an illustration created by Katerina Limpitsouni and made freely available at her website unDraw. The second is the AuthMenu component, which is also used in the signup and login displays. AuthMenu has two buttons.

When the Signup button is clicked, the changeAuthStatusSignup() function in AppContext.js is run, and the value of authStatus is changed to SIGN_UP_FORM. This causes the current display to be hidden and the AuthSignup component to be made visible.

When the Login button is clicked, the changeAuthStatusLogin() function in AppContext.js is run, and the value of authStatus is changed to LOG_IN_FORM. This causes the current display to be hidden and the AuthLogin component to be made visible.

Part 3

This article has outlined how the user interface part of our React application works. In Part 3, we'll take a look at what happens when the user fills in the login form and presses the submit button, initiating communications between the React front end and the Laravel back end.