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
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
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
.env
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
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],
This tells Laravel to use the
jwt
users
Step 5: Create the User Model and Migration
If you haven’t created a
User
php artisan make:model User -m
Open the generated migration file in
database/migrations/
name
email
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
php artisan migrate
Step 6: Implement JWT Methods in the User Model
In the
User
app/Models/User.php
JWTSubject
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
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
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
, include the token in theGET /api/me
header:Authorization
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.