Authenticating a React App with Laravel Sanctum - Part 3
Tags: laravel, react, javascript, security
Published: 2020-04-16 11:50:34

This series of articles discusses how to use [Laravel Sanctum](https://laravel.com/docs/7.x/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. Part two discussed the React app display, including the application state and the use of React context. In this article, we will walk through the various things that need to occur when the user attempts to login or register with the application.

For additional reference, there is a [live version of the finished app](https://auth.bob-humphrey.com) as well as complete code listings for the [React client application](https://gitlab.com/Bob_Humphrey/laravel-auth) and the [Laravel server application](https://gitlab.com/Bob_Humphrey/react-auth).

### Login Process Outline

Let's start with an outline of each of the steps that needs to occur when the user logs in to our application.

- **User** - The user fills out the login form.

- **User** - The user clicks the Login button.

- **React** - React sends a message to the API **sanctum/csrf-cookie** endpoint.

- **Laravel** - Laravel responds with a CSRF token.

- **React** - React sends a POST message to the API **api/login** endpoint, along with the email and password information provided by the user.

- **Laravel** - Laravel matches the endpoint to the route we created in the routes/api.php file, and calls the login function in UserController.

- **Laravel** - The login function in UserController calls a function in the Auth class to authenticate the user with the credentials provided.

- **Laravel** - Laravel returns a 200 message to the browser if authentication was successful, or a 401 message if it was not.

- **React** - If authentication was successful, React sends a GET message to the API **api/user** endpoint.

- **Laravel** - Laravel matches the endpoint to the route in the routes/api.php file, and returns the currently logged in user.

- **React** - React receives the user information and updates userId and userName in state.

- **React** - authStatus is set to LOGGED_IN and the logout component is displayed to the user.

### Login Code

Now we'll take a look at the code that accomplishes each of these steps.

When the user clicks the login button, the login function in AppContext is run. The first thing that's done is to set the axios **withCredentials** attribute to true. This is an important step that cannot be skipped.

```js
// REACT APP src/contexts/AppContext.js - login()
const login = () => {
    axios.defaults.withCredentials = true;
```

Then axios is used to send a GET message to the **sanctum/csrf-cookie** endpoint. Laravel responds with a CSRF token that will then be attached to all subsequent calls to the API. The token is used by Laravel to verify that the authenticated user is the one who is actually making the requests to the application. This protects the application from [cross site forgery requests](https://en.wikipedia.org/wiki/Cross-site_request_forgery).

```js
// REACT APP src/contexts/AppContext.js - login()
// CSRF COOKIE
    axios.get(hostName + "sanctum/csrf-cookie").then(
```

Next, axios sends a POST request to the **api/login** endpoint, along with the email and password that the user entered in the login form.

```js
// REACT APP src/contexts/AppContext.js - login()
// LOGIN
axios.post(hostName + "api/login", {
  email: userEmail,
  password: userPassword,
})
```

Laravel matches the endpoint to the route we created in the routes/api.php file, and calls the login function in the UserController.

```php
// LARAVEL APP routes/api.php
Route::post('/login', 'UserController@login');
```

The login function in UserController calls a function in the Auth class to authenticate the user with the credentials provided. Laravel responds with a 200 message if authentication was successful, or a 401 message if it was not.

```php
// LARAVEL APP app/Http/Controllers/UserController.php
    public function login(Request $request)
    {
        $credentials = $request->only('email', 'password');

        if (Auth::attempt($credentials)) {
            // Authentication passed...
            $authuser = auth()->user();
            return response()->json(['message' => 'Login successful'], 200);
        } else {
            return response()->json(['message' => 'Invalid email or password'], 401);
        }
    }
```

If authentication was successful, React sends a GET message to the API **api/user** endpoint.

```js
// REACT APP src/contexts/AppContext.js - login()
// GET USER
axios.get(hostName + "api/user").then(
```

Laravel matches the endpoint to the route in the routes/api.php file, and returns the currently logged in user.

```php
// LARAVEL APP routes/api.php
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});
```

React receives the user information and updates userId and userName in state. authStatus is set to LOGGED_IN, which results in the logout component being displayed to the user.

```js
// REACT APP src/contexts/AppContext.js - login()
    (response) => {
        setUserId(response.data.id);
        setUserName(response.data.name);
        setErrorMessage("");
        setAuthStatus(LOGGED_IN);
},
```

The entire login function looks like this. If any of the calls to the API results in an error response being returned, an error message is created and displayed to the user by way of the errorMessage state attribute.

```js
// REACT APP src/contexts/AppContext.js - login()
const login = () => {
  axios.defaults.withCredentials = true
  // CSRF COOKIE
  axios.get(hostName + "sanctum/csrf-cookie").then(
    (response) => {
      //console.log(response);
      // LOGIN
      axios
        .post(hostName + "api/login", {
          email: userEmail,
          password: userPassword,
        })
        .then(
          (response) => {
            //console.log(response);
            // GET USER
            axios.get(hostName + "api/user").then(
              (response) => {
                //console.log(response);
                setUserId(response.data.id)
                setUserName(response.data.name)
                setErrorMessage("")
                setAuthStatus(LOGGED_IN)
              },
              // GET USER ERROR
              (error) => {
                setErrorMessage("Could not complete the login")
              }
            )
          },
          // LOGIN ERROR
          (error) => {
            if (error.response) {
              setErrorMessage(error.response.data.message)
            } else {
              setErrorMessage("Could not complete the login")
            }
          }
        )
    },
    // COOKIE ERROR
    (error) => {
      setErrorMessage("Could not complete the login")
    }
  )
}
```

### Part 4

This article has detailed how the login function works in the React portion of our authentication application. Part 4 will provide a similar breakdown for the signup function.