Contents
Introduction:
Hi! Today we will learn how to create an authentication on our Laravel 12 API. But before that let’s have a discussion about API and what is JSON Web Token(JWT).
API stands for Application Program Interface, API is an interface that allows applications to exchange data. To make it more clear, APIs are a set of functions that can be used by programmers to build software and applications.
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.
Now that we have a glimpse of the idea on the topic, We will now proceed on building the app.
Prerequisite:
Before we proceed we must update the composer, laravel installer, node, and npm on our local environment:
- PHP >= 8.2
- Node >= 18.18.2
- NPM >= 9.8.1
- Composer >= 2.8.6
- Laravel Installer >= 5.12.2
If you are having trouble updating the Composer follow these steps:
composer self-update
If you are having trouble updating the Laravel Installer follow these steps:
composer global remove laravel/installer
composer global update
composer global require laravel/installer
Step1: Install Laravel 12
Run this command on Terminal or CMD to install Laravel:
laravel new laravel-12-jwt
Follow these choices:


Step 2: Set Database Configuration
We set up the database configuration during our installation. We can change it by going inside the project root folder opening the file .env and put the configuration for the database.
.env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=your database name(laravel_11_jwt)
DB_USERNAME=your database username(root)
DB_PASSWORD=your database password(root)
Step 3: Enable API and Update Authentication Exception
By default, laravel 12 API route is not enabled in laravel 12. We will enable the API:
php artisan install:api
After enabling the API, we will now update the authentication exception of our API middleware so that it will not redirect to login but will throw an exception:
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 4: Install and Set Up JWT Auth package
Run the following command to pull in the latest version:
composer require php-open-source-saver/jwt-auth
publish the package config file:
php artisan vendor:publish --provider="PHPOpenSourceSaver\JWTAuth\Providers\LaravelServiceProvider"
generate a secret key. This will add JWT config values on .env
file:
php artisan jwt:secret
update auth guard config.
config/auth.php
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
Step 5: Update User Model
Implement first 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\Contracts\Auth\MustVerifyEmail;
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<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
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 6: Create the AuthController
Create a controller using this command:
php artisan make:controller AuthController
Then add these codes:
app/Http/Controllers/AuthController.php
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\User;
use Validator;
class AuthController extends Controller
{
/**
* Register a User.
*
* @return \Illuminate\Http\JsonResponse
*/
public function register() {
$validator = Validator::make(request()->all(), [
'name' => 'required',
'email' => 'required|email|unique:users',
'password' => 'required|confirmed|min:8',
]);
if($validator->fails()){
return response()->json($validator->errors()->toJson(), 400);
}
$user = new User;
$user->name = request()->name;
$user->email = request()->email;
$user->password = bcrypt(request()->password);
$user->save();
return response()->json($user, 201);
}
/**
* Get a JWT via given credentials.
*
* @return \Illuminate\Http\JsonResponse
*/
public function login()
{
$credentials = request(['email', 'password']);
if (! $token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $this->respondWithToken($token);
}
/**
* Get the authenticated User.
*
* @return \Illuminate\Http\JsonResponse
*/
public function me()
{
return response()->json(auth()->user());
}
/**
* Log the user out (Invalidate the token).
*
* @return \Illuminate\Http\JsonResponse
*/
public function logout()
{
auth()->logout();
return response()->json(['message' => 'Successfully logged out']);
}
/**
* Refresh a token.
*
* @return \Illuminate\Http\JsonResponse
*/
public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}
/**
* Get the token array structure.
*
* @param string $token
*
* @return \Illuminate\Http\JsonResponse
*/
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60
]);
}
}
Step 7: Register Routes
then register the routes on routes/api.php, routes with auth:api middleware checks if the user is authenticated before a request can proceed.
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\AuthController;
Route::group([
'middleware' => 'api',
'prefix' => 'auth'
], function ($router) {
Route::post('/register', [AuthController::class, 'register'])->name('register');
Route::post('/login', [AuthController::class, 'login'])->name('login');
Route::post('/logout', [AuthController::class, 'logout'])->middleware('auth:api')->name('logout');
Route::post('/refresh', [AuthController::class, 'refresh'])->middleware('auth:api')->name('refresh');
Route::post('/me', [AuthController::class, 'me'])->middleware('auth:api')->name('me');
});
Step 8: Run the App
Run the laravel app:
php artisan serve
Screenshots:
/api/auth/register (This route will be used for registering new users)

/api/auth/login (This route will be used for login and for getting the bearer token)

/api/auth/refresh (This route will be used on refreshing the bearer token)

/api/auth/me (this route is for getting the authenticated user’s information)

/api/auth/logout (this route is used for logging out the authenticated user)
