Implementing JWT Authentication in Laravel 11: A Complete Guide
Introduction
JSON Web Tokens (JWT) have become the gold standard for stateless authentication in modern web applications. Unlike traditional session-based authentication, JWT allows for scalable, distributed authentication that works seamlessly across microservices and single-page applications. In this comprehensive guide, we'll implement JWT authentication in Laravel 11 from scratch, covering installation, configuration, and security best practices.
Understanding JWT Structure
Before diving into implementation, let's understand what makes JWT special. A JWT consists of three parts separated by dots:
- Header: Contains the token type and signing algorithm
- Payload: Contains the claims (user data and metadata)
- Signature: Ensures the token hasn't been tampered with
The format looks like: xxxxx.yyyyy.zzzzz
Setting Up JWT in Laravel 11
First, let's install the popular tymon/jwt-auth package:
composer require tymon/jwt-authPublish the configuration file:
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"Generate a secret key for JWT:
php artisan jwt:secretThis adds a JWT_SECRET to your .env file. Never share this key publicly!
Configuring the User Model
Update your User model to implement the JWT contract:
getKey();
}
public function getJWTCustomClaims()
{
return [
'role' => $this->role,
'email_verified' => !is_null($this->email_verified_at)
];
}
}Authentication Controller
Create a dedicated authentication controller:
middleware('auth:api', ['except' => ['login', 'register']]);
}
public function register(Request $request)
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:8|confirmed',
]);
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
$token = auth()->login($user);
return $this->respondWithToken($token);
}
public function login(Request $request)
{
$credentials = $request->validate([
'email' => 'required|email',
'password' => 'required',
]);
if (!$token = auth()->attempt($credentials)) {
return response()->json(['error' => 'Invalid credentials'], 401);
}
return $this->respondWithToken($token);
}
public function me()
{
return response()->json(auth()->user());
}
public function logout()
{
auth()->logout();
return response()->json(['message' => 'Successfully logged out']);
}
public function refresh()
{
return $this->respondWithToken(auth()->refresh());
}
protected function respondWithToken($token)
{
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => auth()->factory()->getTTL() * 60,
'user' => auth()->user()
]);
}
}API Routes Configuration
Set up your API routes in routes/api.php:
'auth'], function () {
Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);
Route::post('logout', [AuthController::class, 'logout']);
Route::post('refresh', [AuthController::class, 'refresh']);
Route::get('me', [AuthController::class, 'me']);
});
// Protected routes
Route::middleware('auth:api')->group(function () {
Route::get('/protected', function () {
return response()->json(['message' => 'This is a protected route']);
});
});Security Best Practices
To enhance security, consider these important practices:
1. Token Expiration
Configure appropriate token lifetimes in config/jwt.php:
'ttl' => env('JWT_TTL', 60), // 1 hour
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160), // 2 weeks2. Token Blacklisting
Enable token blacklisting to invalidate tokens:
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),3. Custom Middleware for Role-Based Access
check() || auth()->user()->role !== $role) {
return response()->json(['error' => 'Unauthorized'], 403);
}
return $next($request);
}
}Frontend Integration Example
Here's how to consume the JWT API from a JavaScript frontend:
class AuthService {
constructor() {
this.baseURL = 'http://localhost:8000/api';
this.token = localStorage.getItem('token');
}
async login(email, password) {
const response = await fetch(`${this.baseURL}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await response.json();
if (data.access_token) {
localStorage.setItem('token', data.access_token);
this.token = data.access_token;
}
return data;
}
async makeAuthenticatedRequest(url, options = {}) {
return fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${this.token}`
}
});
}
}Conclusion
JWT authentication in Laravel 11 provides a robust, scalable solution for modern applications. By following the implementation steps and security practices outlined in this guide, you'll have a production-ready authentication system. Remember to regularly rotate your JWT secrets, implement proper token expiration, and always validate tokens on the server side. The stateless nature of JWT makes it perfect for APIs, microservices, and mobile applications where traditional session-based authentication falls short.
Related Posts
Implementing GraphQL with NestJS: A Complete Guide for Modern API Development
Learn how to build scalable GraphQL APIs with NestJS using decorators, resolvers, and type-safe schemas for modern backend development.
Building Scalable Node.js APIs with NestJS: A Production-Ready Guide
Learn how to build enterprise-grade APIs with NestJS, featuring modular architecture, dependency injection, and advanced patterns.
Building Production-Ready APIs with NestJS: Advanced Patterns and Best Practices
Master advanced NestJS patterns including custom decorators, interceptors, and guards to build scalable, maintainable APIs.