
Laravel REST API Part 3: Authentication Using Sanctum
Published on Nov 21, 2024
Authentication is a fundamental part of any secure web application, when a user provide his credentials in a login form in we application using the browser, the server will have a session for that user and a cookie data containting session ID for that user will be stored in the user browser, when a new request sent to the server the cookie data will be sent also with the request, and the server will be able to know which user is sending that specific request.
Laravel provides multiple ways and starter kits for doing that kind of authentication, all ready made and could be done with ease. But what about API's?
To authenticate an API you need to send a token with each request in order to be able to identify the sending user, To do that, Laravel makes it also a breeze and provided us with many ways and packages to do that.
We are going to use Sanctum for our API authentication in it's very basic way, and it will serve our purpose with excellence. Without further a do, let's start our sanctum integration and creating our auth api's.
Let's install Sanctum
To install Sanctum you need to run the following artisan command:
php artisan install:api
After running this command, you will notice that we have some files updated and some files have been added.
routes/api.php file created, there is where we will add our api's endpoints
A migration file created for storing user's created tokens (it runs automatically), and a config file also created.
Let's have a look at routs/api.php file:
<?php use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; Route::get('/user', function (Request $request) { return $request->user(); })->middleware('auth:sanctum');
As you can see we have one route '/user' which could be accessed via http://localhost/api/user or http://127.0.0.1:800/api/user if you don't run docker
If you tried to use postman to run this url or in the browser you will have 500 error and a web page returned
mmm, wait a minute
We are developing an api we are not supposed to see a web page, instead we should see a json response, right?
Yes, that's absolutely correct, but the default Exceptions in Laravel expects that you'r developing a web page. To change that you may go to headers tab in postman and add a header: 'Accept': 'application/json'.
This way laravel exceptions will understand that you want the response to be in a json format.
Or maybe a better approach: you may open bootstrap/app.php and update ->withExceptions closure to be look like this:
use Illuminate\Foundation\Configuration\Exceptions; ->withExceptions(function (Exceptions $exceptions) { $exceptions->shouldRenderJsonWhen(function (Request $request) { if ($request->is('api/*')) { return true; } return $request->expectsJson(); }); })->create();
This way we are telling laravel to return all exceptions as json if the route is prefixed with api
If you run the api again, you will see 401 Unauthorized response with Json message without adding anything to request headers from postman.
And we see this message because the route is protected with auth:sanctum middleware, to be able to access protected routes, we need to create a token first and then provide it to the request header. We will do that after the next step.
A final step in the setup
We need to inform our model that we want to authenticate it by Sanctum, and it could be any model, for example: you may have Admin, Seller, Merchant, ...etc.
But in our case we need to authenticate only User model. So, we are going to add HasApiTokens Trait to User.php model
use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { /** @use HasFactory<\Database\Factories\UserFactory> */ use HasFactory, Notifiable, HasApiTokens;
Awesome!
We are now done with the setup.
Let's create Login Api
To be able to authenticate users and create access token.
First:
We will create a new Controller
php artisan make:controller AuthController
In AuthController we will add a login function
<?php namespace App\Http\Controllers; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use Illuminate\Validation\ValidationException; class AuthController extends Controller { public function login(Request $request) { $request->validate([ 'email' => 'required|email', 'password' => 'required', ]); $user = User::where('email', $request->email)->first(); if (!$user || !Hash::check($request->password, $user->password)) { throw ValidationException::withMessages([ 'email' => ['The provided credentials are incorrect.'], ]); } return $user->createToken($user->name)->plainTextToken; } }
Basicly, we are validating the input email and password and searching in 'users' table if we have a user with this email, if not we will return validation exception.
If it's exist and have the correct password we will create a token and will return that token in the response.
To use that code we need to make a route, our routs/api.php file would look like this:
<?php use App\Http\Controllers\AuthController; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; Route::post('login', [AuthController::class, 'login'])->name('login'); Route::get('/user', function (Request $request) { return $request->user(); })->middleware('auth:sanctum');
You may test in postman, and try any data send it as form data or in json format and test the validation.
For the happy scenario, we already have a seeded user data that comes with default laravel installation, you may want to check the previous article as well for factories and migrations.
The user will have the following credentials:
Email: test@example.com
password: password
When you test the api with the correct credentials, you will have the token in the response, you may copy this token and add it in the authorization tab, choose type Bearer Token and add `Bearer past_token_here` in the textbox.
and try to test /api/user to check if our authentication works fine, you should see a use details json object in the response.
Add Logout Api
In AuthController we will add logout function
public function logout(Request $request) { $request->user()->tokens()->delete(); return response()->json([ 'message' => 'user logged out', ]); }
This function is doing a very simple job, it removes the authenticated user tokens so he will not be able to access protected routes unless he logged in again and provided a correct token.
Don't forget to add the route:
We will update the route file to be like the following:
<?php use App\Http\Controllers\AuthController; use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; Route::post('login', [AuthController::class, 'login'])->name('login'); Route::middleware('auth:sanctum')->group(function () { Route::post('logout', [AuthController::class, 'logout'])->name('logout'); });
We don't need `user` route api anymore.
That's all for our authentication for now, you many want to add forget password, reset password and maybe registration. But I'll stop here for the authentication part, maybe I'll add to it later, but I believe we are good to go at this point.
If you are a visual learner you may want to check this series on youtube.
And if you enjoy reading you may want to click Next button at the bottom of this article.
Happy coding!