As a Laravel enthusiast and web developer, I understand the paramount importance of securing REST APIs in today's interconnected digital landscape. Laravel, known for its elegant syntax and powerful features, is an excellent choice for building robust APIs.
And when it comes to authentication, Laravel Sanctum is the go-to package for simplifying the process while maintaining top-notch security. In this step-by-step guide, I will walk you through the process of setting up REST API authentication using Laravel 10 and Sanctum.
Laravel Sanctum provides a featherweight authentication system for SPAs (single page applications), mobile applications, and simple, token based APIs. Sanctum allows each user of your application to generate multiple API tokens for their account.
API authentication is a crucial aspect of modern web development, ensuring that only authorized users can access protected resources. With Sanctum, Laravel provides a seamless solution to handle token-based authentication, making your API authentication journey both straightforward and secure.
So, let's dive into the world of Laravel 10 and Sanctum and learn how to protect your APIs with ease. Whether you're building a single-page application, a mobile app, or any other service that communicates over HTTP, this guide will equip you with the knowledge and skills needed to secure your Laravel API effectively.
So, let's see Laravel 10 REST API Authentication using Sanctum, how to create REST API Authentication using Sanctum in Laravel 9/10, Laravel 9/10 Sanctum, and Laravel Sanctum API Authentication.
Laravel 10 REST API Authentication using Sanctum - A Step-by-Step Guide
Open your terminal and run the following command to create a new Laravel project.
composer create-project --prefer-dist laravel/laravel laravel-sanctum-api
Install Sanctum using the Composer command.
composer require laravel/sanctum
Next, publish the Sanctum configuration file.
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
Configure your .env
file with database credentials and run migrations.
php artisan migrate
Now, we configure the sanctum to add middleware into the kernel.php file.
app/Http/Kernel.php
....
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
....
In app/Models/User.php
, update the use HasApiTokens
trait to enable API token generation for users.
app/Models/User.php
<?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 HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
Create migration and model for the product using the command.
php artisan make:migration create_products_table
Migration:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('detail');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists('products');
}
};
Now, run the migration table using the migration command.
php artisan migrate
app/Models/Product.php
<?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'
];
}
Then, we will create API routes for login, register, and product rest API.
routes/api.php
<?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::controller(RegisterController::class)->group(function(){
Route::post('register', 'register');
Route::post('login', 'login');
});
Route::middleware('auth:sanctum')->group( function () {
Route::resource('products', ProductController::class);
});
Now, create a controller in the API folder.
app/Http/Controllers/API/BaseController.php
<?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);
}
}
app/Http/Controllers/API/RegisterController.php
<?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;
use Illuminate\Http\JsonResponse;
class RegisterController extends BaseController
{
/**
* Register api
*
* @return \Illuminate\Http\Response
*/
public function register(Request $request): JsonResponse
{
$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): JsonResponse
{
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']);
}
}
}
app/Http/Controllers/API/ProductController.php
<?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\ProductResource;
use Illuminate\Http\JsonResponse;
class ProductController extends BaseController
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(): JsonResponse
{
$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): JsonResponse
{
$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): JsonResponse
{
$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): JsonResponse
{
$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): JsonResponse
{
$product->delete();
return $this->sendResponse([], 'Product deleted successfully.');
}
}
Now, we will create API resources using the following command.
php artisan make:resource ProductResource
app/Http/Resources/ProductResource.php
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ProductResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array
*/
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'name' => $this->name,
'detail' => $this->detail,
'created_at' => $this->created_at->format('d/m/Y'),
'updated_at' => $this->updated_at->format('d/m/Y'),
];
}
}
Now, run the laravel application using the artisan command.
php artisan serve
Now, Go to your postman and check the following APIs.
Add header details into the POSTMAN.
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
]
Here is Routes URL with Verb:
- Register API: Verb: GET, URL:http://localhost:8000/api/register
- Login API: Verb: GET, URL:http://localhost:8000/api/login
- Product List API: Verb: GET, URL:http://localhost:8000/api/products
- Product Create API: Verb: POST, URL:http://localhost:8000/api/products
- Product Show API: Verb: GET, URL:http://localhost:8000/api/products/{id}
- Product Update API: Verb: PUT, URL:http://localhost:8000/api/products/{id}
- Product Delete API: Verb: DELETE, URL:http://localhost:8000/api/products/{id}
You might also like:
- Read Also: Laravel Livewire: 10 Tips & Tricks
- Read Also: Laravel 10 REST API CRUD Operation
- Read Also: Building Complete CRUD Application in Laravel 10
- Read Also: AJAX CRUD Operations In Laravel 10: Step-by-Step Guide