Authenticating a React App with Laravel Sanctum - Part 4
Dog Smile Factory
Posted on April 21, 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. Part two discussed the React app display, including the application state and the use of React context. Part three was a step by step look at how a user logs in and authenticates. This final article will focus on the user registration process.
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.
Registration Process Outline
Before someone can use our app, she first has to sign up or register, providing the app with some personal information that can be used to identify her in the future. Here are the steps that are required to accomplish this registration.
User - The user fills out the signup form.
User - The user clicks the Signup 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/register endpoint, along with the username, 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 register function in UserController.
Laravel - The register function in UserController validates the information provided by the user.
Laravel - If the validation was successful, Laravel creates a new User record and adds it to the database, returning a 200 message to the browser. If the validation was not successful, a 422 message is returned.
React - If registration 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 new user information, including userId.
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.
Registration Code
We'll now review the code that accomplishes each of these steps.
When the user clicks the signup button, the signup() function in AppContext is run. The first thing that's done is to set the axios withCredentials attribute to true.
// REACT APP src/contexts/AppContext.js - signup()
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, protecting the application from cross site forgery requests.
// REACT APP src/contexts/AppContext.js - signup()
// CSRF COOKIE
axios.get(hostName + "sanctum/csrf-cookie").then(
Next, axios sends a POST request to the api/register endpoint, along with the username, email and password that the user entered in the signup form.
// REACT APP src/contexts/AppContext.js - signup()
// SIGNUP / REGISTER
axios.post(hostName + "api/register", {
name: userNameInput,
email: userEmail,
password: userPassword,
})
Laravel matches the endpoint to the route that was created in the routes/api.php file, and calls the register function in UserController.
// LARAVEL APP routes/api.php
Route::post('/register', 'UserController@register');
The register function in UserController validates the information provided by the user. Laravel creates a new User record, adds it to the database, and responds with a 200 message if the validation was successful.
// LARAVEL APP app/Http/Controllers/UserController.php
public function register(Request $request)
{
$this->validator($request->all())->validate();
$user = $this->create($request->all());
$this->guard()->login($user);
return response()->json([
'user' => $user,
'message' => 'registration successful'
], 200);
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
//'password' => ['required', 'string', 'min:4', 'confirmed'],
// NO PASSWORD CONFIRMATION
'password' => ['required', 'string', 'min:4'],
]);
}
If the registration was successful, React sends a GET message to the API api/user endpoint.
// REACT APP src/contexts/AppContext.js - signup()
// 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.
// 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.
// REACT APP src/contexts/AppContext.js - signup()
(response) => {
//console.log(response);
setUserId(response.data.id);
setUserName(response.data.name);
setErrorMessage("");
setAuthStatus(LOGGED_IN);
},
The entire signup 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.
// REACT APP src/contexts/AppContext.js - signup()
const signup = () => {
axios.defaults.withCredentials = true
// CSRF COOKIE
axios.get(hostName + "sanctum/csrf-cookie").then(
(response) => {
//console.log(response);
// SIGNUP / REGISTER
axios
.post(hostName + "api/register", {
name: userNameInput,
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 sign up")
}
)
},
// SIGNUP ERROR
(error) => {
if (error.response.data.errors.name) {
setErrorMessage(error.response.data.errors.name[0])
} else if (error.response.data.errors.email) {
setErrorMessage(error.response.data.errors.email[0])
} else if (error.response.data.errors.password) {
setErrorMessage(error.response.data.errors.password[0])
} else if (error.response.data.message) {
setErrorMessage(error.response.data.message)
} else {
setErrorMessage("Could not complete the sign up")
}
}
)
},
// COOKIE ERROR
(error) => {
setErrorMessage("Could not complete the sign up")
}
)
}
Logout
All that's left now is for the user to logout. The process for this is pretty simple.
The user presses the logout button.
React runs the AppContext logout() function.
Axios calls the API api/logout endpoint.
// REACT APP src/contexts/AppContext.js - logout()
function logout() {
axios.defaults.withCredentials = true
axios.get(hostName + "api/logout")
- Laravel matches the endpoint to the route we created in the routes/api.php file, and calls the logout function in UserController.
// LARAVEL APP routes/api.php
Route::post('/logout', 'UserController@logout');
- The logout() function in UserController logs the user out and returns a 200 message to the browser.
// LARAVEL APP app/Http/Controllers/UserController.php
public function logout()
{
Auth::logout();
return response()->json(['message' => 'Logged Out'], 200);
}
- The logout() function in React then reinitializes the application state. authStatus is set to NOT_LOGGED_IN, which returns the user back to where he started, displaying the original splash screen. Here is the entire logout() function.
// REACT APP src/contexts/AppContext.js - logout()
function logout() {
axios.defaults.withCredentials = true
axios.get(hostName + "api/logout")
setUserId(0)
setUserName("")
setUserNameInput("")
setUserEmail("")
setUserPassword("")
setAuthStatus(NOT_LOGGED_IN)
}
Posted on April 21, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.