Building a Secure Laravel API with JWT Authentication

Posted on April 8th, 2024

JSON Web Token, also known as JWT, is a Laravel authentication system like Sanctum and Passport that enables you to develop a secure API for your application.

It’s an industry-standard method for securely transmitting user information between applications. It’s a self-contained token containing encoded information.

It consists of three parts separated by dots which are Header, Payload, and Signature.

Advantages of Using JWT Authentication

The first advantage of using JWT is it is Stateless. Meaning the server doesn’t need to store session data, making it scalable and easier to manage.

JWT prevents unauthorized modifications, and it is self-contained, so The token carries user information, eliminating the need for additional database lookups.

Let’s have a look at a practical example.

Setting Up Laravel Project and JWT Package

Prerequisites:

 

1. Create a Laravel Project

Open your terminal, navigate to the desired directory, and run the following command to create a new Laravel project.

composer create-project laravel/laravel Laravel-jwt

Here, our project name is Laravel-jwt. You can give the project name on your own.

Once you create a project, navigate to your project directory using the terminal.

cd Laravel-jwt

2. Install JWT

Now copy and paste the following composer command to install the JWT Package.

composer require tymon/jwt-auth

3. Generate the Secret Key and publish the configuration file.

Generate a JWT secret key and publish the configuration file for customizing JWT behavior.

php artisan jwt:secret --force
php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider" --tag="config"

Running the above commands will add the JWT_SECRET in your .env file as follows.

4. Connect Database

Before further processing, update your database information in the .env file.

5. Update Auth FIle

Now, it’s time to update and set up the API authentication guard. So update the config/auth.php file like below.

<?php

return [

/*

|--------------------------------------------------------------------------
| Authentication Defaults

|--------------------------------------------------------------------------
|
| This option controls the default authentication "guard" and password
| reset options for your application. You may change these defaults
| as required, but they're a perfect start for most applications.
|
*/

'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],

/*

|--------------------------------------------------------------------------
| Authentication Guards

|--------------------------------------------------------------------------
|
| Next, you may define every authentication guard for your application.
| Of course, a great default configuration has been defined for you
| here which uses session storage and the Eloquent user provider.
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session"
|
*/

'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],

'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],

/*

|--------------------------------------------------------------------------
| User Providers

|--------------------------------------------------------------------------
|
| All authentication drivers have a user provider. This defines how the
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| If you have multiple user tables or models you may configure multiple
| sources which represent each model / table. These sources may then
| be assigned to any extra authentication guards you have defined.
|
| Supported: "database", "eloquent"
|
*/

'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],

// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],

/*

|--------------------------------------------------------------------------
| Resetting Passwords

|--------------------------------------------------------------------------
|
| You may specify multiple password reset configurations if you have more
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expiry time is the number of minutes that each reset token will be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
| The throttle setting is the number of seconds a user must wait before
| generating more password reset tokens. This prevents the user from
| quickly generating a very large amount of password reset tokens.
|
*/

'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_reset_tokens',
'expire' => 60,
'throttle' => 60,
],
],

/*

|--------------------------------------------------------------------------
| Password Confirmation Timeout

|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/

'password_timeout' => 10800,

];

6. Update User model

We’ll leverage Laravel’s built-in user functionality or create a custom model if needed. Here update the User Model like the below.

<?php

namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Tymon\JWTAuth\Contracts\JWTSubject;

 

class User extends Authenticatable implements JWTSubject
{
use Notifiable;

/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];

/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];

/**
* Get the JWT identifier.
*
* @return int
*/
public function getJWTIdentifier()
{
return $this->id;
}

/**
* Get the JWT custom claims.
*
* @return array
*/
public function getJWTCustomClaims()
{
return []; // You can add custom claims here if needed
}
}

The model implements the JWTSubject interface required by the tymon/jwt-auth package. The getJWTIdentifier() method returns the user’s ID, which is a standard claim in JWT tokens.

7. Create a Login Controller.

Create a login controller with the below command.

php artisan make:controller LoginController

The Login Controller handles user login attempts and generates JWT tokens upon successful authentication. Update the file code like below.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Tymon\JWTAuth\Facades\JWTAuth;

class LoginController extends Controller
{
public function login(Request $request)
{
$credentials = $request->only('email', 'password');

if (Auth::attempt($credentials)) {
$user = Auth::user();
$token = JWTAuth::fromUser($user);

return response()->json(compact('token'));
}

return response()->json(['error' => 'Invalid credentials'], 401);
}

public function protectedEndpoint(Request $request)
{
try {
$user = JWTAuth::parseToken()->authenticate();
} catch (\Tymon\JWTAuth\Exceptions\TokenExpiredException $e) {
return response()->json(['error' => 'Token expired'], 401);
} catch (\Tymon\JWTAuth\Exceptions\TokenInvalidException $e) {
return response()->json(['error' => 'Invalid token'], 401);
} catch (\Tymon\JWTAuth\Exceptions\JWTException $e) {
return response()->json(['error' => 'Token is missing'], 401);
}

// Access user data or perform actions requiring authentication
return response()->json(['message' => 'Success! You are authorized.']);
}
}

In the above code, we use JWTAuth::parseToken()->authenticate() to verify the presence and validity of the JWT token included in the request header.

8. Create Route

Now go to routes/api.php and add the code that handles JWT verification.

<?php

use Illuminate\Support\Facades\Route;
use App\Http\Controllers

Route::post('/login', 'LoginController@login'); // Login endpoint

Route::get('/protected-endpoint', 'LoginController@protectedEndpoint');
// Requires JWT token in request header for access

After adding the code like above, remember to migrate the database schema using the php artisan migrate command and the end start development server by executing the php artisan serve command.

Now, you can test all the APIs in Postman for further testing.

That’s it for now. We hope this article may help you create API endpoints by using the JWT authentication system. For more details, check Laravel’s official documentation or follow the step-by-step guide to learning JWT Authentication with Laravel.

Conclusion

So, to sum it all up, using JSON Web Token (JWT) authentication in Laravel is a smart move for making your APIs secure. With JWT, you don’t need to store session data on the server, which makes things easier to manage and scale up if needed. Plus, JWTs pack all the user info they need, so there’s no need for extra database lookups, saving time and resources.

Setting up JWT in Laravel involves a few steps: creating a project, installing the JWT package, configuring authentication guards, updating the user model, setting up a login controller, defining routes, and testing everything out with Postman.

JWTs keep user info safe during transmission and make sure nobody messes with it along the way. By following these steps, you can build solid API endpoints for your app, all while sticking to industry standards for security and authentication.

So, armed with this know-how, you’re ready to create APIs that are both secure and efficient for your Laravel apps. If you want to dive deeper, check out Laravel’s official docs or other resources on JWT authentication with Laravel. Happy coding!

Leave a Reply