Full Stack Laravel 10 & React JS | SPA Authentication | Part 1

Author

Sahil Kumar

Publihsed On

Aug 18, 2024

Updated On

Aug 18, 2024

Category

Laravel

Full Stack Laravel 10 & React JS | SPA Authentication | Part 1


Share this post:

Introduction

Full Stack Laravel 10 & React JS is a robust technology stack used for building modern web applications. When combined, they provide a seamless and intuitive user experience that is both responsive and secure. One critical aspect of building such applications is implementing authentication, which ensures that only authorized users can access sensitive data and perform specific actions.

To implement SPA authentication in Full Stack Laravel 10 & React JS, Laravel Sanctum is a lightweight and token-based authentication package that can be used to secure API endpoints. Laravel Sanctum allows developers to generate and manage API tokens, which can be used to authenticate users' requests and provide access to protected resources.

When using Laravel Sanctum for SPA authentication, developers can choose to implement either stateless or stateful requests. Stateful requests involve persisting a user's authentication state on the server-side, while stateless requests do not. In stateful requests, the user's authentication state is stored on the server, and the user is identified using a session cookie. In contrast, stateless requests involve sending the API token with each request, and the server verifies the token's validity before responding.

Implementing stateful requests in Laravel Sanctum involves configuring session driver and stateful domain, while implementing stateless requests involves setting up middleware that checks for the token in the request headers.

Overall, Full Stack Laravel 10 & React JS with Laravel Sanctum provides developers with a robust and secure way to implement SPA authentication. Whether using stateful or stateless requests, Laravel Sanctum makes it easy to manage user authentication and authorization, providing developers with a secure and flexible way to build modern web applications.

Click here to read Part 2.

Creating APIs For SPA (React.js) Authentication Using Laravel 10 & Laravel Sanctum

Laravel is a PHP web application framework that provides a simple and elegant syntax to create web applications. Laravel 10 is the latest version of the Laravel framework. Laravel Sanctum is a package that allows you to create secure APIs for your web applications.

Here are the steps to create APIs using Laravel 10 and Laravel Sanctum:

Step 1: Install Laravel

Create a new Laravel project by running the following command in your terminal:

composer create-project laravel/laravel backend

This will create a new Laravel project in a folder named backend.

Step 2: Configure ENV File

FRONTEND_URL=http://localhost:3000
SESSION_DOMAIN=localhost
SANCTUM_STATEFUL_DOMAINS=localhost:3000

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=full_stack_laravel_react_js
DB_USERNAME=root
DB_PASSWORD=

BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=cookie
SESSION_LIFETIME=120

Add SESSION_DOMAIN and SANCTUM_STATEFUL_DOMAINS in the env file in Laravel, it is recommended for security purposes.

SESSION_DOMAIN sets the domain for session cookie, which helps to prevent cookie-based attacks such as session hijacking or CSRF attacks. If you do not specify a domain, Laravel will use the current domain of the application.

SANCTUM_STATEFUL_DOMAINS specifies a list of domains that are allowed to make stateful requests to your application's endpoints protected by Laravel Sanctum. This helps to prevent cross-site request forgery (CSRF) attacks.

If you do not set SESSION_DOMAIN and SANCTUM_STATEFUL_DOMAINS, Laravel will use the current domain of the application by default. However, if your application is accessed by multiple domains or subdomains, it is recommended to set these variables in order to enhance security.

Enter your database credentials and set SESSION_DRIVER=cookie

SESSION_DRIVER=cookie means that Laravel is using cookies to store session data. When a user logs in to your Laravel application, a cookie is set in their browser to store their session data. This cookie is then sent back to the server with each subsequent request, allowing Laravel to retrieve the user's session data and maintain their state across requests.

Using cookies for session storage is a common practice in web applications, as it allows for a simple and efficient way to maintain user state. However, it's important to note that cookies can be vulnerable to certain types of attacks, such as cross-site scripting (XSS) and cross-site request forgery (CSRF). Laravel includes built-in protection against these types of attacks, but it's still important to be aware of the risks and take appropriate security measures.

Step 3: Update Some Files for Laravel Sanctum

Open app/Http/Kernal.php file and uncomment this \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class for stateful requests under $middlewareGroups and api section.

protected $middlewareGroups = [
  'web' => [
      \App\Http\Middleware\EncryptCookies::class,
      \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
      \Illuminate\Session\Middleware\StartSession::class,
      \Illuminate\View\Middleware\ShareErrorsFromSession::class,
      \App\Http\Middleware\VerifyCsrfToken::class,
      \Illuminate\Routing\Middleware\SubstituteBindings::class,
  ],

  'api' => [
      \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
      \Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
      \Illuminate\Routing\Middleware\SubstituteBindings::class,
  ],
];

\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class is a middleware class provided by Laravel Sanctum. This middleware is used to ensure that requests from the frontend of your application are treated as stateful requests and can access the protected routes that require authentication.

This middleware adds a cookie to the response that contains an encrypted CSRF token, and it expects the frontend to send this token back with each subsequent request. This way, the middleware can check the CSRF token and identify the user making the request, allowing access to the protected routes.

Open config/cors/php file and make

'supports_credentials' => true

'supports_credentials' => true is a configuration option in Laravel Sanctum that allows cross-site requests to include cookies and other credentials.

When making requests across different domains or subdomains, the browser may block the requests due to security reasons. This is known as the Same-Origin Policy, which restricts how a web page from one domain can interact with resources from another domain.

To allow cross-site requests, you can use Cross-Origin Resource Sharing (CORS) headers, which allow a web page to access resources from a different domain. Laravel Sanctum provides a middleware called EnsureFrontendRequestsAreStateful, which sets the required CORS headers for Sanctum to work correctly with cookies.

The 'supports_credentials' => true configuration option is used to indicate that cookies and other credentials should be included in cross-site requests. If this option is set to false, then the CORS headers will not include any credentials, and the browser will not send cookies or other authentication information with the request.

Step 4: Migrate Tables

In order to use Laravel Sanctum there is no need to create any additional migration, Laravel 10 comes with all migration which will be used for Laravel Sanctum. We only need to run the migration so, to run the migration just use below command:

php artisan migrate

Once migration successfully run then you can check in your database that following tables will be created like as below screenshot:

Database & Tables

Step 5: Creating Controller

To create a new controller just use below command in your terminal:

php artisan make:controller AuthController

The make:controller command is a shortcut for generating a new controller class. When you run php artisan make:controller AuthController, Laravel will create a new file called AuthController.php in the app/Http/Controllers directory. The file will contain a skeleton controller class with some basic methods that you can use as a starting point for your authentication logic.

Step 6: Creating Routes

Once you've generated the AuthController class, you can use it to define routes in your application's routes/api.php file. For example, you might define a login route that maps to the login method of the AuthController like in the below codes:

<?php

use App\Http\Controllers\AuthController;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "api" middleware group. Make something great!
|
*/

Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);

Route::middleware('auth:sanctum')->group(function () {
    Route::post('/logout', [AuthController::class, 'logout']);
    Route::get('/user', [AuthController::class, 'user']);
});

The auth:sanctum middleware is provided by Laravel Sanctum and is used to authenticate requests using an API token generated by Sanctum. When a request is made to a route that uses the auth:sanctum middleware, Sanctum will check if the request contains a valid API token. If the token is valid, the request is allowed to proceed. If the token is invalid or missing, the request will be rejected with a 401 Unauthorized response.

Step 7: Creating Requests for Login & Register

To create a new request in Laravel 10 is very easy, you just have to run this command in your terminal:

php artisan make:request RegisterRequest

php artisan make:request RegisterRequest is a command in Laravel that generates a new form request class called RegisterRequest in your application's app/Http/Requests directory.

Form requests in Laravel are used to validate incoming HTTP requests. They define the rules for validating the data submitted with a request and can be used to ensure that the data meets certain requirements before it is processed by your application.
Now Open app/Http/Requests/RegisterRequest.php file and just use below codes:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Password;

class RegisterRequest extends FormRequest {
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
     */
    public function rules(): array {
        return [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users,email|max:255',
            'password' => [
                'required',
                'string',
                Password::min(8)->mixedCase()->numbers()->symbols()->uncompromised(),
                'confirmed',
            ]
        ];
    }
}

Similarly, we will also create a request class for Login:

php artisan make:request LoginRequest

Now Open app/Http/Requests/LoginRequest.php file and just use below codes:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class LoginRequest extends FormRequest {
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
     */
    public function rules(): array {
        return [
            'email' => 'required|email|exists:users,email',
            'password' => 'required|string',
        ];
    }
}

Step 8: Creating Resource for User Model

To create a new resource class in Laravel 10 just use below command in your terminal:

php artisan make:resource UserResource

php artisan make:resource UserResource is a command in Laravel that generates a new resource class called UserResource in your application's app/Http/Resources directory.

Resource classes in Laravel are used to transform data from your application's models into a format that can be returned in a JSON response. They provide a way to customize the data that is returned in a response and can be used to hide sensitive information, include related data, and perform other data transformations.

The make:resource command is a shortcut for generating a new resource class. When you run php artisan make:resource UserResource, Laravel will create a new file called UserResource.php in the app/Http/Resources directory. The file will contain a skeleton resource class with a toArray method that you can use to define the data that should be returned in a JSON response.

Now open app/Http/Resources/UserResource.php file and just use below codes:

<?php

namespace App\Http\Resources;

use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;

class UserResource extends JsonResource {
    /**
     * Transform the resource into an array.
     *
     * @return array<string, mixed>
     */
    public function toArray(Request $request): array {
        return [
            'id' => $this->id,
            'name' => $this->name,
            'email' => $this->email,
            'created_at' => $this->created_at,
            'updated_at' => $this->updated_at,
        ];
    }
}

Step 9: Defining All Method in AuthController

Now all files are ready, it's time to define all methods in AuthController.php file. Below are the defined methods for user register, user login, get user information and user logout.

<?php

namespace App\Http\Controllers;

use App\Http\Requests\LoginRequest;
use App\Http\Requests\RegisterRequest;
use App\Http\Resources\UserResource;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;

class AuthController extends Controller {
    // register a new user method
    public function register(RegisterRequest $request) {

        $data = $request->validated();

        $user = User::create([
            'name' => $data['name'],
            'email' => $data['email'],
            'password' => Hash::make($data['password']),
        ]);

        $token = $user->createToken('auth_token')->plainTextToken;

        $cookie = cookie('token', $token, 60 * 24); // 1 day

        return response()->json([
            'user' => new UserResource($user),
        ])->withCookie($cookie);
    }

    // login a user method
    public function login(LoginRequest $request) {
        $data = $request->validated();

        $user = User::where('email', $data['email'])->first();

        if (!$user || !Hash::check($data['password'], $user->password)) {
            return response()->json([
                'message' => 'Email or password is incorrect!'
            ], 401);
        }

        $token = $user->createToken('auth_token')->plainTextToken;

        $cookie = cookie('token', $token, 60 * 24); // 1 day

        return response()->json([
            'user' => new UserResource($user),
        ])->withCookie($cookie);
    }

    // logout a user method
    public function logout(Request $request) {
        $request->user()->currentAccessToken()->delete();

        $cookie = cookie()->forget('token');

        return response()->json([
            'message' => 'Logged out successfully!'
        ])->withCookie($cookie);
    }

    // get the authenticated user method
    public function user(Request $request) {
        return new UserResource($request->user());
    }
}

Register Method Explanation:

This code is the register method of an AuthController class in a Laravel application, which is responsible for handling the registration of new users.

The method first validates the request data using a RegisterRequest class, which contains the validation rules for the request.

After validation, the method creates a new user with the validated data and stores the user's password using Laravel's Hash facade to ensure it is securely encrypted. The createToken method on the user object creates a new personal access token for the user that will be used to authenticate future API requests.

The token is then used to create an HTTP cookie that is returned in the response, along with the token and user data, as a JSON object. The UserResource class is used to transform the user data into the appropriate format for the response.

The cookie is set to expire after 1 day, or 60 * 24 minutes.

This approach allows the user to maintain their authentication status across multiple requests without having to manually include the access token in each request. Instead, the access token is stored in an HTTP cookie, which is automatically sent with each subsequent request.

Login Method Explanation:

This code is the login method of an AuthController class in a Laravel application, which is responsible for handling user authentication.

The method first validates the request data using a LoginRequest class, which contains the validation rules for the request.

After validation, the method attempts to find a user with the provided email address using the where method on the User model. If no user is found, or the provided password does not match the user's hashed password, a 401 Unauthorized response is returned with a message indicating that the email or password is incorrect.

If the user is successfully authenticated, the method creates a new personal access token for the user using the createToken method on the user object. The token is then used to create an HTTP cookie that is returned in the response, along with the token and user data, as a JSON object. The UserResource class is used to transform the user data into the appropriate format for the response.

The cookie is set to expire after 1 day, or 60 * 24 minutes.

This approach allows the user to maintain their authentication status across multiple requests without having to manually include the access token in each request. Instead, the access token is stored in an HTTP cookie, which is automatically sent with each subsequent request.

Logout Method Explanation:

This code is the logout method of an AuthController class in a Laravel application, which is responsible for handling user logout and revoking the user's access token.

The method expects a Request object to be passed to it, which should contain a valid user session and an access token associated with that session. The currentAccessToken method on the user object is used to retrieve the user's current access token, which is then deleted using the delete method.

Once the access token has been deleted, the method creates a new HTTP cookie that instructs the user's browser to delete the existing access token cookie. This is done using the forget method on the cookie helper function, which creates a new cookie with an empty value and an expiration time in the past.

The method then returns a JSON response indicating that the user has been successfully logged out.

This approach ensures that the user's access token is immediately revoked when they log out, and that any existing access token cookies are deleted from the user's browser. This helps to prevent unauthorized access to the user's account in the event that the user's device or browser is compromised.

User Method Explanation:

This code is the user method of an AuthController class in a Laravel application, which is responsible for returning the authenticated user's data.

The method expects a Request object to be passed to it, which should contain a valid user session and an access token associated with that session. The user method on the request object is used to retrieve the authenticated user from the session.

The method then creates a new instance of the UserResource class, passing the authenticated user object to its constructor. The UserResource class is used to transform the user data into the appropriate format for the response, typically removing sensitive or unnecessary data and formatting the remaining data in a more user-friendly way.

Finally, the method returns the transformed user data as a JSON object. This approach ensures that the user's data is only returned to authenticated users, and that any sensitive data is filtered out before being returned to the client.

Step 10: Setting Cookie Token in Every Requests

Now this is the important step, we'll edit app/Http/Middleware/Authenticate.php file, see the below codes:
We'll only add handle method.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;

class Authenticate extends Middleware {
    /**
     * Get the path the user should be redirected to when they are not authenticated.
     */
    protected function redirectTo(Request $request): ?string {
        return $request->expectsJson() ? null : route('login');
    }

    public function handle($request, Closure $next, ...$guards) {

        if ($token = $request->cookie('token')) {
            $request->headers->set('Authorization', 'Bearer ' . $token);
        }

        $this->authenticate($request, $guards);

        return $next($request);
    }
}

This code is the handle method of the Authenticate middleware in a Laravel application, which is responsible for authenticating user requests.

The method expects a $request object and a $next closure to be passed to it, as well as an optional $guards parameter. The $guards parameter is used to specify the authentication guards that should be used to authenticate the request.

The method first checks if the user's access token is present in the request cookie by using the cookie method on the $request object. If a token is found, it is added to the request headers using the set method on the $request->headers object.

The method then calls the authenticate method to perform the actual authentication of the request. The authenticate method is responsible for verifying that the user is authenticated and that their access token is valid.

If the authentication is successful, the method calls the $next closure with the authenticated $request object. This allows the request to proceed to the next middleware in the stack, or to the controller method if no further middleware is present.

This approach ensures that the user's access token is automatically attached to their requests when present in the request cookie, and that the user is properly authenticated before any sensitive actions are performed on their behalf.

Some Testing Screenshots of APIs

Use these headers in every request:

"Accept: application/json",
"Content-Type: application/json"

For User Registration:

API Testing for User Registration
API Testing for User Registration
API Testing for User Registration

For Login:

API Testing for User Login
API Testing for User Login

For Getting Authenticated User Details:

API Testing Fo Getting Authenticated User Details
API Testing Fo Getting Authenticated User Details

For User Logout:

API Testing for User Logout

For Getting Authenticated User details and Logout we don't need to send Bearer token from the Frontend explicitly. Because We are using secure way, we are storing http only cookie in client browser's cookie, and also storing session details in the server. So, whenever a request is sent then cookie will be sent from the client side automatically to server and rest will be processed in server side.
You can also check the GitGub repository of this backend Laravel project. 
In next part I'll show you how to create a react app and consume this API.


Share this post:

Sahil Kumar
Sahil Kumar
Full Stack Web Developer

Hello! I'm a part-time blogger & YouTuber living in India. This is my personal blog where I write Web Design & Development tutorial posts!

Know more about me

Discussion (0)

Log in to comment!

No comments yet!