JWT Authentication in Laravel & PHP

If you’re building a website or app that requires user authentication, you need a secure way to verify users and control what they can access. This is where JWT (JSON Web Token) comes in.

What is JWT?

JWT stands for JSON Web Token. It is a secure, compact, and URL-safe way to transmit data between a client (browser/app) and a server.

🔹 Think of JWT like a digital passport ️. When you travel, immigration stamps your passport to confirm your identity. Similarly, when you log in, the server gives you a JWT, which acts as proof of who you are.

🔹 This token contains encoded information about the user, like their ID, role (admin/user), and permissions.

🔹 The best part? JWT is stateless—the server doesn’t need to store any session data! Instead, the client (browser or mobile app) keeps the token and sends it with every request.

Why Do We Need JWT?

* Traditional Authentication (Session-Based Login)

  • When a user logs in, the server stores their session in a database.
  • Every time the user makes a request, the server checks the session.
  • This works but becomes slow and hard to scale (e.g., in microservices or APIs).

* JWT Authentication (Token-Based Login)

  • The server gives the user a JWT after login.
  • The client stores this token and sends it with every request.
  • The server verifies the token and grants or denies access.
  • No session storage is needed—so it’s faster and more scalable!

# JWT is commonly used in:
*️ REST APIs & Microservices
*️ Mobile & Web Apps
*️ Single Sign-On (SSO)

# How JWT Works (Step-by-Step)

1. User Logs In

  • The user enters their email & password.
  • The server checks the credentials.

2. Server Creates a JWT

  • If login is successful, the server generates a JWT.
  • The token is signed with a secret key so it cannot be tampered with.
  • The JWT contains information like:
{
  "user_id": 123,
  "role": "admin",
  "exp": 1700000000  // Expiration time
}

3. Client Stores the Token

  • The JWT is sent to the client (browser or app).
  • It can be stored in localStorage, sessionStorage, or HTTP-only cookies.

4. Client Requests a Protected Resource

  • When the client wants to access secure data, it sends the JWT in the Authorization header:
GET /profile  
Authorization: Bearer <your_jwt_here>

5. Server Verifies the JWT

  • The server decodes the JWT and checks:
    ✅ Is the signature valid?
    ✅ Is the token expired?
    ✅ Does the user have permission?

6. Access Granted or Denied

  • If the token is valid, the server returns the requested data.
  • If the token is invalid or expired, the user must log in again.

JWT Structure (3 Parts)

A JWT has three parts, separated by dots (.):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsInJvbGUiOiJhZG1pbiJ9.s0m3s3cr3tk3y

1. Header (contains metadata)

{
  "alg": "HS256",  // Algorithm used to sign the token
  "typ": "JWT"     // Token type
}

2. Payload (contains user data)

{
  "user_id": 123,
  "role": "admin",
  "exp": 1700000000
}

3. Signature (used to verify the token’s integrity)

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret_key
)

Implementing JWT in Laravel

Laravel has built-in authentication, but if you want to use JWT for API authentication, you can use the tymon/jwt-auth package.

Step 1: Install JWT Package in Laravel

First, install the JWT authentication package using Composer:

composer require tymon/jwt-auth

Then, publish the package configuration:

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

This will create a config/jwt.php file.

Now, generate a secret key:

php artisan jwt:secret

This will update your .env file with a JWT_SECRET key.

Step 2: Configure Auth in Laravel

Modify config/auth.php to use JWT as the default authentication guard for APIs.

Find this section:

'guards' => [
    'api' => [
        'driver' => 'token',
        'provider' => 'users',
    ],
],

Change 'token' to 'jwt' like this:

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

Step 3: Create Authentication Controller

Run this command to generate an AuthController:

php artisan make:controller AuthController

Now, add the following methods to AuthController.php:

Register User and Generate JWT

use App\Models\User;
use Illuminate\Http\Request;
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'
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password),
        ]);

        $token = JWTAuth::fromUser($user);

        return response()->json([
            'user' => $user,
            'token' => $token
        ]);
    }

Login User and Generate Token

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

    if (!$token = JWTAuth::attempt($credentials)) {
        return response()->json(['error' => 'Invalid credentials'], 401);
    }

    return response()->json([
        'token' => $token
    ]);
}

Get Authenticated User

public function userProfile()
{
    return response()->json(auth()->user());
}

Logout User

public function logout()
{
    auth()->logout();
    return response()->json(['message' => 'Successfully logged out']);
}

Step 4: Define Routes for Authentication

Open routes/api.php and add the following routes:

use App\Http\Controllers\AuthController;

Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);

Route::middleware('auth:api')->group(function () {
    Route::get('/profile', [AuthController::class, 'userProfile']);
    Route::post('/logout', [AuthController::class, 'logout']);
});

Step 5: Protect Routes with Middleware

Open app/Http/Kernel.php and register the JWT authentication middleware:

protected $routeMiddleware = [
    'jwt.auth' => \Tymon\JWTAuth\Http\Middleware\Authenticate::class,
];

Now, protect your API routes by adding middleware('jwt.auth'):

Route::middleware(['jwt.auth'])->group(function () {
    Route::get('/profile', [AuthController::class, 'userProfile']);
});

Step 6: Test JWT Authentication

You can test JWT authentication using Postman or CURL.

🔹 Register User

POST http://yourdomain.com/api/register
Content-Type: application/json

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

Response:

{
    "user": {
        "id": 1,
        "name": "John Doe",
        "email": "john@example.com"
    },
    "token": "your_jwt_token_here"
}

Login User

POST http://yourdomain.com/api/login
Content-Type: application/json

{
    "email": "john@example.com",
    "password": "password123"
}

Response:

{
    "token": "your_jwt_token_here"
}

Access Protected Route (Profile)

GET http://yourdomain.com/api/profile
Authorization: Bearer your_jwt_token_here

Response:

{
    "id": 1,
    "name": "John Doe",
    "email": "john@example.com"
}

Implementing JWT in Pure PHP (Without Laravel)

If you’re using plain PHP, you can still implement JWT authentication using the firebase/php-jwt package.

✅ Step 1: Install JWT Package

composer require firebase/php-jwt

Step 2: Generate JWT Token

use Firebase\JWT\JWT;

$secret_key = "your_secret_key";
$payload = [
    "user_id" => 123,
    "exp" => time() + (60 * 60) // Token expires in 1 hour
];

$token = JWT::encode($payload, $secret_key, 'HS256');

echo json_encode(["token" => $token]);

Step 3: Decode and Verify JWT Token

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

$token = $_GET['token']; // Get token from request

try {
    $decoded = JWT::decode($token, new Key($secret_key, 'HS256'));
    echo json_encode($decoded);
} catch (Exception $e) {
    echo json_encode(["error" => "Invalid token"]);
}

JWT vs. API Keys: What’s the Difference?

FeatureAPI KeysJWT Authorization
PurposeIdentifies the client, limits API usage.Authenticates & authorizes the user.
FormatLong string of characters.Encoded JSON object.
SecurityLess secure, can be stolen easily.More secure, digitally signed.
Authentication❌ No authentication.Used for authentication.
Authorization❌ No authorization.Used for authorization.
UsageSent as a query parameter or header.Sent as a Bearer token in the Authorization header.
FlexibilityLimitedMore flexible with access control.
StandardizationNo universal standard.Follows the JWT standard.

Best Practices for JWT Authentication

✅ Use HTTP-only cookies instead of local storage for security.
✅ Set expiration times (exp) to prevent token misuse.
✅ Store JWT secret keys securely (in .env file).
✅ Use refresh tokens for long-lived sessions.
Limit token scope—store only necessary user data in the payload.

JWT is an efficient, scalable, and secure way to handle authentication and authorization in modern applications. It eliminates the need for server-side session storage and works perfectly in microservices, APIs, and mobile apps.

If you’re building a secure login system, an API, or a scalable web app, JWT is the way to go!


Discover more from MountAviary

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *