In today’s world of modern web and mobile apps, securing API endpoints is more important than ever. One popular and secure way to handle API authentication is using JSON Web Tokens (JWT). In this blog, we’ll explore how to implement JWT authentication in a Laravel application for secure and stateless API access.

What is JSON Web Token (JWT)?
JWT stands for JSON Web Token. It is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. JWT is commonly used for Authorization, Information Exchange, etc.

Steps for API Authentication Using JSON Web Tokens (JWT):
Step 1:Install Laravel
composer create-project laravel/laravel example-app
Step 2: Enable API and Update Authentication Exception
php artisan install:api
Now, if the user is not authenticated, then the exception will be called, and we will return a JSON response. So, let’s update the app.php file.
bootstrap/app.php
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
//
})
->withExceptions(function (Exceptions $exceptions) {
$exceptions->render(function (AuthenticationException $e, Request $request) {
if ($request->is('api/*')) {
return response()->json([
'message' => $e->getMessage(),
], 401);
}
});
})->create();
Step 3: Install and Setup JWT Auth package
In this step, we will install the php-open-source-saver/jwt-auth composer package.
composer require php-open-source-saver/jwt-auth
Now, publish the package config file.
php artisan vendor:publish –provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"
Next, generate a secret key. This will add JWT config values to the .env file.
php artisan jwt:secret
Now, we will update the auth guard config file.
config/auth.php
<?php
return [
/*
|--------------------------------------------------------------------------
| Authentication Defaults
|--------------------------------------------------------------------------
|
| This option defines the default authentication "guard" and password
| reset "broker" for your application. You may change these values
| as required, but they're a perfect start for most applications.
|
*/
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
/*
|--------------------------------------------------------------------------
| Authentication Guards
|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| which utilizes session storage plus the Eloquent user provider.
|
| All authentication guards have a user provider, which defines how the
| users are actually retrieved out of your database or other storage
| system used by the application. Typically, Eloquent is utilized.
|
| Supported: "session"
|
*/
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
...
Step 4: Update User Model
In the model, we first implement the Tymon\JWTAuth\Contracts\JWTSubject contract on the User Model and implement the getJWTIdentifier() and getJWTCustomClaims() methods.
app/Models/User.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use PHPOpenSourceSaver\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* @return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}
}
Step 5: Create API Routes
routes/api.php
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\API\AuthController;
Route::group([
'middleware' => 'api',
'prefix' => 'auth'
], function ($router) {
Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:api');
Route::post('/refresh', [AuthController::class, 'refresh'])->middleware('auth:api');
Route::post('/profile', [AuthController::class, 'profile'])->middleware('auth:api');
});
Step 6: Create Controller Files
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/AuthController.php
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\API\BaseController as BaseController;
use App\Models\User;
use Validator;
use Illuminate\Http\Request;
class AuthController extends BaseController
{
/**
* Register a User.
*
* @return \Illuminate\Http\JsonResponse
*/
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['user'] = $user;
return $this->sendResponse($success, 'User register successfully.');
}
/**
* Get a JWT via given credentials.
*
* @return \Illuminate\Http\JsonResponse
*/
public function login()
{
$credentials = request(['email', 'password']);
if (! $token = auth()->attempt($credentials)) {
return $this->sendError('Unauthorised.', ['error'=>'Unauthorised']);
}
$success = $this->respondWithToken($token);
return $this->sendResponse($success, 'User login successfully.');
}
/**
* Get the authenticated User.
*
* @return \Illuminate\Http\JsonResponse
*/
public function profile()
{
$success = auth()->user();
return $this->sendResponse($success, 'Refresh token return successfully.');
}
/**
* Log the user out (Invalidate the token).
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth()->logout();
return $this->sendResponse([], 'Successfully logged out.');
}
/**
* Refresh a token.
*
* @return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
$success = $this->respondWithToken(auth()->refresh());
return $this->sendResponse($success, 'Refresh token return successfully.');
}
/**
* Get the token array structure.
*
* @param string $token
*
* @return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken($token)
{
return [
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
];
}
}
Then run the project
php artisan serve
Final Thoughts
JWT is a powerful and scalable way to handle API authentication in Laravel. It eliminates the need for session management, supports secure communication, and works seamlessly with web and mobile apps.

FAQ
- What is Laravel?
Laravel is a web application framework that has an expressive, elegant syntax to provide a foundation and starting point for development of web applications in PHP.
- Why Use Laravel?
Laravel has a rich set of features, including an expressive database abstraction layer (Eloquent), powerful templating engine (Blade), built-in authentication and authorization, routing system, queues, scheduled jobs, and a command-line interface (Artisan CLI). It is focused on developer experience and provides many tools to facilitate development.
- What is API authentication in Laravel?
API authentication helps to ensure that only users or applications that are authorized in accordance with the defined authentication flow can access your Laravel API. It essentially verifies that the user or system making a request is valid.
- What is JSON Web Token (JWT)?
A JWT is a compact, URL-safe, token that contains a claims (data) set encoded as a JSON object. It is used more broadly to securely transmit information between a set of parties. It’s most common use is often seen in a stateless authentication system like REST APIs.