NEXT JS Authentication using Laravel Sanctum and Taking care of CORS

The React Next JS Framework provides all the fundamental tools and utilities for building SEO-friendly Single Page websites. For this reason, it is liked and used by many developers.
So in this post, I’m going to use Next JS as the frontend and Laravel as a backend for the purpose of login and authentication. When you are working on such projects it’s essential to learn about CORS ( Cross-Site Resource Sharing ) which I’ll teach you in this post.

The Laravel application here acts as a backend application that is used to save or retrieved the users. And for authentication, I’ll be using Laravel Sanctum Package which will provide all the authenticating utility classes and methods. This Package is also recommended by Laravel to be used for Single Page Applications and Mobile Apps.

Table of Contents

Create Next JS App

First I’ll start by creating a Next Js application and I’ll keep the frontend and backend separately.
I’ll name my next js app as nextjs_app.

npx create-next-app nextjs_app
Installation of React Next Js
Installation of React Next Js

Create a Login Form

Within nextjs_app open pages\index.js file and create a Login Component and export it as a default component.

import styles from '../styles/Home.module.css'
import React, { useState } from 'react'
import axios from 'axios'

const API_URL = "http://localhost:8000/test_laravel/api/";

export default function Login() {

    let [ username, setUsername ] = useState('');
    let [ password, setPassword ] = useState('');

    let login_token = null;

    const handleSubmit = async ( e ) => {
        e.preventDefault();

        const headers = {
            "Content-Type" : `multipart/form-data`
        };

        let data = new FormData();
        data.append( 'username', username );
        data.append( 'password', password );

        let result = await axios( {
            method:'post',
            url: 'login-verify',
            baseURL: API_URL,
            data: data,
            headers: headers,
        } );

        let response = result.data;

        if( response['success'] ){
            console.log("Login Successful");
            login_token = response['token'];
        } else {
            console.log("Failed to Login");
        }

    }

    const get_user = async ( ) => {

        if( login_token ){
            const headers = {
                "Authorization" : `Bearer ${ login_token }`
            };

            let result = await axios( {
                method:'get',
                url: 'auth-user',
                baseURL: API_URL,
                data: JSON.stringify({}),
                headers: headers,
            } );

            let response = result.data;

            console.log( "get_user", response );

        } else {
            console.log("Login Token is empty");
        }

    }

    return (
        <div className={styles.container}>
            <div className={styles['m-inner']}>
                <form onSubmit={ ( e ) => handleSubmit( e ) } action="" method="post">
                    <div>
                        <label htmlFor="">Username</label><br />
                        <input onInput={ ( e ) => setUsername( e.target.value ) } type="text" id="username" value={ username } />
                    </div>
                    <div>
                        <label htmlFor="">Password</label><br />
                        <input onInput={ ( e ) => setPassword( e.target.value ) } type="password" id="password" value={ password } />
                    </div>
                    <div>
                        <input type="submit" value="Login" />
                        <button onClick={get_user} type="button" >Get User</button>
                    </div>
                </form>
            </div>
        </div>
    )
}

The Login Component has two states username and password and also a login_token variable to which auth token will be assigned when method handleSubmit is called.
The login form submission is handled by method handleSubmit while onSubmit Event.

Within handleSubmit method post request is sent to the backend laravel server for this the endpoint is the API_URL which contains the server URL. We’ll be using Axios the send the request to a backend server.
Also, make sure in the options of Axios object you specify baseURL as the backend server URL. If you don’t provide this then it will throw an error. For route path use the option URL in this case our route path is login-verify which I’m using as url: ‘login-verify’.

The URL will be appended to baseURL and the request URL will be http://localhost:8000/test_laravel/api/login-verify.

The backend server sends a response with the values success and token. The success specifies if the user exists and the token contains alphanumeric characters through which we can get authenticated user. The token gets saved to login_token variable which can be used to get the auth user.

The method get_user sends a GET request to the server route auth-user this route in the Laravel app is protected via middleware auth:sanctum which only allows if the request has Authorization token in the header or else will respond with a message unauthenticated.
The token received during login verification is used in the headers object which is pass to the requested server. If everything goes right then a response will be received from the backend which contains all the user information.

Also, take a look at our other posts on Laravel Gates and Laravel Multi-user Authentication.

Setup Laravel Application

Now for the backend create a laravel app

    composer create-project laravel/laravel test_laravel

Configure Laravel Sanctum for Authentication

To use laravel sanctum install the package via composer.

    composer require laravel/sanctum

After installation, publish the sanctum configuration files using the below commands.

    php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"

To manage authentication and token generation sanctum needs certain tables to be present in your database. So use the migrate command for that.

    php artisan migrate

For api authentication middleware include EnsureFrontendRequestsAreStateful class in C:\laragon\www\test_laravel\app\Http\Kernel.php.

    protected $middlewareGroups = [
        'web' => [
            ...
            ...
            ...
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // include this
            'throttle:60,1',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

Next is to specify the model which is responsible to issue API Tokens and for this, you have to use app\Models\User.php ie User model.

In User, model includes use Laravel\Sanctum\HasApiTokens; to the top of the class. And all HasApiTokens traits to it like the below.

use Laravel\Sanctum\HasApiTokens;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    protected $table = 'users';
    protected $primaryKey = 'id';

    protected $fillable = [
        'name', 'username', 'email', 'phone',
    ];

    protected $guarded = [ 'password' ];
}

Enable CROS in Laravel App

All the requests from nextjs_app will be received in routes\api.php. So when you send a post request to route login-verify. You receive an error that says CROS Error. To fix such issues you must include headers in the api.php.

The two headers are:

In routes\api.php. Make sure you add these headers at the top of the file.

    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: Authorization');
By default laravel also provides a package fruitcake/laravel-cors which is a CORS middleware. But in this post, I’ll only be using core PHP functions as I found they were more reliable than this package.

Post Login form to Laravel

In routes\api.php.

    header('Access-Control-Allow-Origin: *');
    header('Access-Control-Allow-Headers: Authorization');

    use Illuminate\Support\Facades\Hash;
    use App\User;


    Route::post('login-verify', function (Request $request) {

        $info = [
            'success' => false,
            'token' => null,
        ];

        $user = User::where('username', $request->username )->first();

        if ( !empty( $user ) && Hash::check($request->password, $user->password) ) {
            $info['success'] = true;
            $token = $user->createToken( $user->id )->plainTextToken;
            return [
                'success' => true,
                'token' => $token,
            ];
        } else {
            return [
                'success' => false,
            ];
        }

    });

    Route::middleware('auth:sanctum')->get('/auth-user', function (Request $request) {
        return $request->user();
    });

The authentication is done using the username sent by the client and fetching the user with that particular username. After this, the password check is done using the built-in laravel method Hash::check( client_sent_password, client_db_password) which returns a boolean value if the password matches.
Next to create a token call $user->createToken( $user->id )->plainTextToken on the user object and send the token as a response to the requested client.

Now that the token is received by the requested client. The client can receive user data from the token by sending a GET request to route /auth-user and passing the token within the headers with Bearer <token>. Make sure you first add the keyword Bearer.
Here is how your headers should look after adding an authorization token.
In pages\index.js within method get_user.

const headers = {
    "Authorization" : `Bearer ${ login_token }`
};

Retrieve Authenticated User information from Server using Token

After receiving the token you save it to variable login_token and within the method, get_user uses the token and sends it to backend laravel via headers.

const get_user = async ( ) => {

    if( login_token ){
        const headers = {
            "Authorization" : `Bearer ${ login_token }`
        };

        let result = await axios( {
            method:'get',
            url: 'auth-user',
            baseURL: API_URL,
            data: JSON.stringify({}),
            headers: headers,
        } );

        let response = result.data;

        console.log( "get_user", response );

    } else {
        console.log("Login Token is empty");
    }

}

The response received contains user information displayed in the console.

Video Output

Conclusion

That’s all for this post on NEXT JS Authentication using Laravel Sanctum and Taking care of CORS. If you have any issues then feel free to reach us through our contact us page.