Building a Secure Laravel API with JWT Authentication
When building APIs with Laravel, ensuring secure authentication is crucial, especially when dealing with user data. JSON Web Tokens (JWT) offer a robust method for handling authentication in stateless applications, allowing secure transmission of user information between parties. This guide will walk you through the process of building a secure Laravel API using JWT authentication.
Prerequisites
- Basic knowledge of PHP and Laravel.
- Laravel installed on your machine (preferably Laravel 9 or newer).
- Composer installed.
- MySQL or another database set up for your application.
Step 1: Create a New Laravel Project
First, create a new Laravel project using Composer:
composer create-project laravel/laravel laravel-jwt-api
 Navigate to your project directory:
cd laravel-jwt-api
 Step 2: Install JWT Authentication Package
The most commonly used package for JWT authentication in Laravel is tymon/jwt-auth. Install it using Composer:
composer require tymon/jwt-auth
 After the package installation, publish the JWT configuration:
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"
 This will create a config/jwt.php file in your project.
Step 3: Generate JWT Secret Key
Generate a secret key that JWT will use for signing tokens:
php artisan jwt:secret
 This will add a JWT_SECRET entry to your .env file, which is used for encoding and decoding the tokens.
Step 4: Set Up Authentication
To set up JWT authentication, you need to configure the default guard for your API. Update the config/auth.php file as follows:
'defaults' => [
 'guard' => 'api',
 'passwords' => 'users',
 ],
 'guards' => [
 'api' => [
 'driver' => 'jwt',
 'provider' => 'users',
 ],
 ],Step 5: Create the User Model and Migration
If you haven’t created a User model yet, you can create it along with its migration file:
php artisan make:model User -m
 Open the generated migration file in database/migrations/ and add the necessary fields, such as name, email, and password:
public function up()
 {
 Schema::create('users', function (Blueprint $table) {
 $table->id();
 $table->string('name');
 $table->string('email')->unique();
 $table->string('password');
 $table->timestamps();
 });
 }
 Run the migration to create the users table:
php artisan migrate
 Step 6: Implement JWT Methods in the User Model
In the User model (app/Models/User.php), implement the JWTSubject interface and the required methods:
use Tymon\JWTAuth\Contracts\JWTSubject;
 use Illuminate\Foundation\Auth\User as Authenticatable;
 class User extends Authenticatable implements JWTSubject
 {
 public function getJWTIdentifier()
 {
 return $this->getKey(); // Usually the primary key (id)
 }
 public function getJWTCustomClaims()
 {
 return [];
 }
 }Step 7: Create Authentication Controllers
Create a controller for handling user authentication:
php artisan make:controller AuthController
 In AuthController.php, add methods for registering a user, logging in, and logging out:
<?php
 namespace App\Http\Controllers;
 use App\Models\User;
 use Illuminate\Http\Request;
 use Illuminate\Support\Facades\Auth;
 use Illuminate\Support\Facades\Hash;
 use Tymon\JWTAuth\Facades\JWTAuth;
 class AuthController extends Controller
 {
 // User Registration
 public function register(Request $request)
 {
 $request->validate([
 'name' => 'required|string|max:255',
 'email' => 'required|string|email|max:255|unique:users',
 'password' => 'required|string|min:6|confirmed',
 ]);
 $user = User::create([
 'name' => $request->name,
 'email' => $request->email,
 'password' => Hash::make($request->password),
 ]);
 $token = JWTAuth::fromUser($user);
 return response()->json(['token' => $token], 201);
 }
 // User Login
 public function login(Request $request)
 {
 $credentials = $request->only('email', 'password');
 if (!$token = Auth::attempt($credentials)) {
 return response()->json(['error' => 'Invalid credentials'], 401);
 }
 return response()->json(['token' => $token]);
 }
 // Logout User (Invalidate Token)
 public function logout()
 {
 Auth::logout();
 return response()->json(['message' => 'Successfully logged out']);
 }
 // Get Authenticated User
 public function me()
 {
 return response()->json(Auth::user());
 }
 }Step 8: Define API Routes
Add routes for authentication in routes/api.php:
use App\Http\Controllers\AuthController;
 use Illuminate\Support\Facades\Route;
 Route::post('register', [AuthController::class, 'register']);
 Route::post('login', [AuthController::class, 'login']);
 // Protected routes (require JWT token)
 Route::middleware('auth:api')->group(function () {
 Route::post('logout', [AuthController::class, 'logout']);
 Route::get('me', [AuthController::class, 'me']);
 });These routes handle user registration, login, logout, and fetching the authenticated user.
Step 9: Protecting API Routes
To protect other API routes, you can use the auth:api middleware. For example, if you have a resource route for posts:
Route::middleware('auth:api')->group(function () {
 Route::resource('posts', PostController::class);
 });
 This ensures that only authenticated users can access these routes.
Step 10: Testing the API
You can test your API using tools like Postman or cURL.
- Register a user:POST /api/register
 Host: your-domain.com
 Content-Type: application/json
 {
 "name": "John Doe",
 "email": "john@example.com",
 "password": "password",
 "password_confirmation": "password"
 }
- Log in:POST /api/login
 Host: your-domain.com
 Content-Type: application/json
 {
 "email": "john@example.com",
 "password": "password"
 }This will return a token that you can use to authenticate other requests.
- Access protected route:To access a protected route like GET /api/me, include the token in the Authorization header:Authorization: Bearer <token>
 
Step 11: Refreshing JWT Tokens (Optional)
To refresh a token, you can add a method in AuthController:
public function refresh()
 {
 $token = Auth::refresh();
 return response()->json(['token' => $token]);
 }Add the route for refreshing:
Route::post('refresh', [AuthController::class, 'refresh'])->middleware('auth:api');
 Conclusion
By following this guide, you’ve built a secure Laravel API with JWT authentication, enabling user registration, login, and access to protected routes. JWT allows you to maintain a stateless authentication mechanism, which is especially useful for scaling APIs. With this setup, you can further customize your application’s authentication logic and integrate additional features, such as role-based access control and refresh tokens for long-lived sessions.


