Laravel Sanctum Authentication Example With Product Api

Hello all artisan in this brand new tutorial i am going to exaplain how Laravel sanctum works and why do we need it. Also i will create a complete authentication system using Laravel sanctum. In this example, we are going to learn laravel 8 sanctum api tutorial. you can understand a concept of laravel 8 sanctum spa example from this tutorial. 

If you see the laravel sanctum docs you will see that Laravel Sanctum provides a featherweight authentication system for SPAs (single page applications), mobile applications, and simple, token based APIs. Sanctum allows each user of our application to generate multiple API tokens for their account. 

How Laravel Sanctum Works

Laravel Sanctum exists solving two separate problems such as API tokens and SPA Authentication. Let's discuss each before digging deeper into this sanctum library.

API Tokens

First, Sanctum is a simple package that may use to issue API tokens to our users without the complication of OAuth. This laravel sanctum feature is inspired by GitHub and other applications which issue "personal access tokens". 

Laravel Sanctum offers this above feature by storing user API tokens in a single database table and authenticating incoming HTTP requests via the Authorization header which must contain a valid API token.

SPA Authentication

Second, Sanctum exists to offer a simple way to authenticate single page applications (SPAs) that need to communicate with a Laravel powered API. Those SPAs might exist in the same repository as your Laravel application or might be an entirely separate repository, such as a SPA created using Vue CLI or a Next.js application.

So let's start laravel sanctum spa authentication tutorial step by step.

 

Step 1: Download Laravel 8

In this laravel sanctum tutorial, i am going to start it step by step. So download a fresh Laravel application to complete this Laravel sanctum authentication tutorial.

composer create-project --prefer-dist laravel/laravel sanctum

 

Step 2: Install Sanctum

You can install Laravel Sanctum via the Composer package manager:

composer require laravel/sanctum

 

After successfully install package, you should publish the Sanctum configuration and migration files using the vendor:publish Artisan command. The sanctum configuration file will be placed in your application's config directory:

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

 

Finally, setup database name for this project and now you should run your database migrations. Laravel sanctum will create one database table in which to store API tokens:

php artisan migrate

 

Next, if you plan to utilize Sanctum to authenticate an SPA, you should add Sanctum's middleware to your api middleware group within your application's app/Http/Kernel.php file. So we have to add middleware for sanctum api, So let's add as like bellow:

app/Http/Kernel.php

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

 

Recommended : Laravel 8.x Passport API Authentication Tutorial Example

 

Step 3: Configure Sanctum

 

In model we added HasApiTokens class of Sanctum and In auth.php, we added api auth configuration.

app/Models/User.php

namespace App\Models;
  
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
  
class User extends Authenticatable
{
    use HasFactory, Notifiable, HasApiTokens;
  
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];
  
    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];
  
    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

 

Step 4: Create Product Table and Model

 

In this step, we need to create migration for posts table using Laravel 8 php artisan command, so first fire bellow command:

php artisan make:model Product -m

 

Add those field to newly created products table

  public function up()
    {
        Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->text('detail');
            $table->timestamps();
        });
    }

 

app/Models/Product.php

namespace App\Models;
  
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
  
class Product extends Model
{
    use HasFactory;
  
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'detail'
    ];
}

 

Step 5: Create API Routes

In this step, we need to create api routes. Laravel provide api.php file for writeing web services route. So, let's create those below new route on that file.

routes/api.php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
  
use App\Http\Controllers\API\RegisterController;
use App\Http\Controllers\API\ProductController;
  
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
  
Route::post('register', [RegisterController::class, 'register']);
Route::post('login', [RegisterController::class, 'login']);
     
Route::middleware('auth:sanctum')->group( function () {
    Route::resource('products', ProductController::class);
});

 

Recommended : Building a REST API with Laravel Microservices Lumen

 

Step 6: Create Controller and Files

Time to start our sanctum code to create api authentication. So in this step, we need to create new controller as BaseController, ProductController and RegisterController, i have created new folder "API" in Controllers folder because we will make alone APIs controller, So let's create both controller:

app/Http/Controllers/API/BaseController.php

namespace App\Http\Controllers\API;


use Illuminate\Http\Request;
use App\Http\Controllers\Controller as Controller;


class BaseController extends Controller
{
    /**
     * success response method.
     *
     * @return \Illuminate\Http\Response
     */
    public function sendResponse($result, $message)
    {
    	$response = [
            'success' => true,
            'data'    => $result,
            'message' => $message,
        ];


        return response()->json($response, 200);
    }


    /**
     * return error response.
     *
     * @return \Illuminate\Http\Response
     */
    public function sendError($error, $errorMessages = [], $code = 404)
    {
    	$response = [
            'success' => false,
            'message' => $error,
        ];


        if(!empty($errorMessages)){
            $response['data'] = $errorMessages;
        }


        return response()->json($response, $code);
    }
}

 

Now in register controller

app/Http/Controllers/API/RegisterController.php

namespace App\Http\Controllers\API;
   
use Illuminate\Http\Request;
use App\Http\Controllers\API\BaseController as BaseController;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Validator;
   
class RegisterController extends BaseController
{
    /**
     * Register api
     *
     * @return \Illuminate\Http\Response
     */
    public function register(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'name' => 'required',
            'email' => 'required|email',
            'password' => 'required',
            'c_password' => 'required|same:password',
        ]);
   
        if($validator->fails()){
            return $this->sendError('Validation Error.', $validator->errors());       
        }
   
        $input = $request->all();
        $input['password'] = bcrypt($input['password']);
        $user = User::create($input);
        $success['token'] =  $user->createToken('MyApp')->plainTextToken;
        $success['name'] =  $user->name;
   
        return $this->sendResponse($success, 'User register successfully.');
    }
   
    /**
     * Login api
     *
     * @return \Illuminate\Http\Response
     */
    public function login(Request $request)
    {
        if(Auth::attempt(['email' => $request->email, 'password' => $request->password])){ 
            $user = Auth::user(); 
            $success['token'] =  $user->createToken('MyApp')->plainTextToken; 
            $success['name'] =  $user->name;
   
            return $this->sendResponse($success, 'User login successfully.');
        } 
        else{ 
            return $this->sendError('Unauthorised.', ['error'=>'Unauthorised']);
        } 
    }
}

 

Paste this code in Product controller

app/Http/Controllers/API/ProductController.php

namespace App\Http\Controllers\API;
   
use Illuminate\Http\Request;
use App\Http\Controllers\API\BaseController as BaseController;
use App\Models\Product;
use Validator;
use App\Http\Resources\Product as ProductResource;
   
class ProductController extends BaseController
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $products = Product::all();
    
        return $this->sendResponse(ProductResource::collection($products), 'Products retrieved successfully.');
    }
    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $input = $request->all();
   
        $validator = Validator::make($input, [
            'name' => 'required',
            'detail' => 'required'
        ]);
   
        if($validator->fails()){
            return $this->sendError('Validation Error.', $validator->errors());       
        }
   
        $product = Product::create($input);
   
        return $this->sendResponse(new ProductResource($product), 'Product created successfully.');
    } 
   
    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        $product = Product::find($id);
  
        if (is_null($product)) {
            return $this->sendError('Product not found.');
        }
   
        return $this->sendResponse(new ProductResource($product), 'Product retrieved successfully.');
    }
    
    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, Product $product)
    {
        $input = $request->all();
   
        $validator = Validator::make($input, [
            'name' => 'required',
            'detail' => 'required'
        ]);
   
        if($validator->fails()){
            return $this->sendError('Validation Error.', $validator->errors());       
        }
   
        $product->name = $input['name'];
        $product->detail = $input['detail'];
        $product->save();
   
        return $this->sendResponse(new ProductResource($product), 'Product updated successfully.');
    }
   
    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy(Product $product)
    {
        $product->delete();
   
        return $this->sendResponse([], 'Product deleted successfully.');
    }
}

 

Step 7: Create API Resources

In this step we need to create api resources to create our product api collection. So create it by the following command.

php artisan make:resource Product

 

Now you will get a new folder inside Http/Resources there created new file with new folder on following path:

app/Http/Resources/Product.php

namespace App\Http\Resources;
   
use Illuminate\Http\Resources\Json\JsonResource;
   
class Product extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'data' => $this->collection->map(function($data) {
                return [
                    'id' => $data->id,
                    'name' => $data->name,
                    'detail' => $data->detail,
                    'created_at' => $data->created_at->format('d/m/Y'),
                    'updated_at' => $data->updated_at->format('d/m/Y'),
                ];
            })
        ];
    }


    public function with($request)
    {
        return [
            'success' => true,
            'status' => 200
        ];
    }


}

 

Recommended : Laravel 8.x Rest API CRUD Example with JWT Auth

 

Make sure in details api we will use following headers as listed bellow:

'headers' => [

    'Accept' => 'application/json',

    'Authorization' => 'Bearer '.$accessToken,

]

 

Now test those all api in your postman. 

Register API

Request: GET

API: http://localhost:8000/api/register


Login API

Request: GET

API: http://localhost:8000/api/login

 

Product List API

Request: GET

API: http://localhost:8000/api/products

Header: Bearer Token

 

Product Create API

Request: Post

API: http://localhost:8000/api/products

Header: Bearer Token

 

Product Show API

Request: Get

API: http://localhost:8000/api/products/{id}

Header: Bearer Token

 

Product Update API

Request: Put

API: http://localhost:8000/api/products/{id}

Header: Bearer Token


Product Delete API

Request: Delete

API: http://localhost:8000/api/products/{id}

Header: Bearer Token

 

Hope this Laravel sanctum tutorial will help you.

 

#laravel #laravel-8x #laravel-sanctum #api #authentication #spa-auth