📒 

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',
],
],

This tells Laravel to use the jwt driver for API requests and authenticate against the users provider.

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();
}

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:

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
{
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);
}

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]);
}

public function logout()
{
Auth::logout();

return response()->json(['message' => 'Successfully logged out']);
}

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;

Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);
Route::post('logout', [AuthController::class, 'logout'])->middleware('auth:api');
Route::get('me', [AuthController::class, 'me'])->middleware('auth:api');

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
    Content-Type: application/json

    {
    "name": "John Doe",
    "email": "john@example.com",
    "password": "password",
    "password_confirmation": "password"
    }

  • Log in:
    POST /api/login
    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.