Secure Authentication and Authorization Between Laravel Microservices Using JWT Tokens

Moez Missaoui
5 min readJul 22, 2024

--

In the world of microservices architecture, managing user authentication and authorization is crucial. JSON Web Tokens (JWT) provide a secure and efficient way to handle these requirements. This guide will walk you through using JWT tokens for authentication and authorization between Laravel microservices, with detailed explanations and code examples.

1. Introduction to JWT

What is JWT?

JSON Web Token (JWT) is a compact, URL-safe token format used to securely transmit information between parties. It consists of three parts:

  • Header: Contains metadata about the token, including the type of token and the signing algorithm.
  • Payload: Contains the claims or data to be transmitted, such as user information or permissions.
  • Signature: Used to verify that the sender of the JWT is who it says it is and to ensure that the message wasn’t changed along the way.

Key Benefits of JWT

  • Stateless: JWTs are self-contained and do not require server-side session storage. This statelessness is ideal for distributed systems like microservices.
  • Secure: JWTs can be signed to ensure data integrity and optionally encrypted to ensure confidentiality.
  • Scalable: JWTs facilitate horizontal scaling by allowing different services to verify tokens independently without relying on centralized session storage.

2. Setting Up Laravel Microservices

In this setup, we have two Laravel microservices:

  1. AuthService: Manages user authentication and issues JWT tokens.
  2. CRMService: Manages customer relationship data and uses JWT tokens for authorization.

Prerequisites

Ensure that Laravel is installed and set up for both microservices. Basic knowledge of Laravel and REST APIs is required.

3. Installing and Configuring Laravel Passport

Laravel Passport provides OAuth2 server functionality, including JWT support. Here’s how to set it up:

Step 1: Install Laravel Passport

Install Passport via Composer in both AuthService and CRMService:

Step 2: Publish Passport Configurations

Run the following command to create Passport’s encryption keys and migration files:

php artisan passport:install

This command generates the keys necessary for signing tokens and the initial database migrations for Passport.

Step 3: Set Up Passport in AuthService

Add HasApiTokens Trait

In the User model, add the HasApiTokens trait provided by Passport. This trait enables the model to issue and manage API tokens.

use Laravel\Passport\HasApiTokens;

class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}

Register Passport Routes

In the AuthServiceProvider, add Passport routes to handle token issuance and validation:

use Laravel\Passport\Passport;

public function boot()
{
$this->registerPolicies();
Passport::routes();
}

Configure API Guard

In config/auth.php, configure the API guard to use Passport. This tells Laravel to use Passport’s token driver for API authentication.

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

4. Generating JWT Tokens with Permissions

JWT tokens will include permissions to control access. Here’s how to generate them:

Step 1: Define Permissions

Assume you have a Permission model and a many-to-many relationship with the User model:

class User extends Authenticatable
{
use HasApiTokens, Notifiable;

public function permissions()
{
return $this->belongsToMany(Permission::class);
}
}

Step 2: Create a Method to Generate JWT Tokens

Add a method in the User model to create a JWT token with permissions:

class User extends Authenticatable
{
use HasApiTokens, Notifiable;

public function createTokenWithPermissions()
{
// Retrieve user permissions
$permissions = $this->permissions->pluck('name')->toArray();

// Create a token with permissions
$tokenResult = $this->createToken('Personal Access Token', $permissions);

// Return the access token
return $tokenResult->accessToken;
}
}

Explanation:

  • pluck('name'): Retrieves the names of the permissions associated with the user.
  • createToken(): Creates a new personal access token with the given name and scopes (permissions).
  • $tokenResult->accessToken: Returns the generated token.

Step 3: Authenticate Users and Generate Tokens

Modify the LoginController to issue a token upon successful login:

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

class LoginController extends Controller
{
public function login(Request $request)
{
// Validate login credentials
$credentials = $request->only('email', 'password');
if (!Auth::attempt($credentials)) {
return response()->json(['message' => 'Unauthorized'], 401);
}

// Generate token with permissions
$user = Auth::user();
$token = $user->createTokenWithPermissions();

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

Explanation:

  • Auth::attempt($credentials): Attempts to authenticate the user with provided credentials.
  • createTokenWithPermissions(): Generates a JWT token with the user’s permissions.

5. Middleware for Authorization

Middleware ensures that only users with the correct permissions can access certain routes.

Step 1: Create Authorization Middleware

Create a middleware class to check for required permissions:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

class CheckPermissions
{
public function handle($request, Closure $next, ...$permissions)
{
// Retrieve authenticated user
$user = Auth::user();
if (!$user) {
return response()->json(['message' => 'Unauthenticated'], 401);
}

// Get the token's permissions
$token = $request->user()->token();
$tokenPermissions = $token->scopes;

// Check if user has all required permissions
foreach ($permissions as $permission) {
if (!in_array($permission, $tokenPermissions)) {
return response()->json(['message' => 'Forbidden'], 403);
}
}

return $next($request);
}
}

Explanation:

  • Auth::user(): Retrieves the authenticated user.
  • $request->user()->token(): Gets the token associated with the user.
  • $token->scopes: Retrieves the permissions (scopes) associated with the token.

Step 2: Register Middleware in Kernel.php

Add the middleware to app/Http/Kernel.php:

protected $routeMiddleware = [
// Other middleware
'permissions' => \App\Http\Middleware\CheckPermissions::class,
];

Step 3: Protect Routes with Middleware

Apply the permissions middleware to routes that require specific permissions:

Route::middleware(['auth:api', 'permissions:read-crm'])->group(function () {
Route::get('/customers', 'CustomerController@index');
});

Explanation:

  • auth:api: Ensures the request is authenticated.
  • permissions:read-crm: Checks if the user has the read-crm permission.

6. Secure Communication Between Microservices

Communication Flow

Microservices often need to communicate with each other. For instance, CRMService might need to fetch user information from AuthService. This communication should be secured with JWT tokens.

Example: CRMService Request to AuthService

In CRMService, use the JWT token to make authenticated requests to AuthService:

use GuzzleHttp\Client;

$client = new Client();
$response = $client->request('GET', 'http://authservice.local/api/user', [
'headers' => [
'Authorization' => 'Bearer ' . $jwtToken,
],
]);

$userData = json_decode($response->getBody(), true);

Explanation:

  • GuzzleHttp\Client: A PHP HTTP client used to make requests.
  • 'Authorization' => 'Bearer ' . $jwtToken: Sets the JWT token in the request header to authenticate with AuthService.

Example: AuthService Verifying Token

In AuthService, verify the token and respond with user details:

use Illuminate\Http\Request;

Route::middleware(['auth:api'])->get('/user', function (Request $request) {
return response()->json($request->user());
});

Explanation:

  • auth:api: Ensures the request has a valid token.
  • $request->user(): Retrieves and returns the authenticated user.

Handling Token Expiry and Refresh

JWTs have expiration times set during issuance. When a token expires, users must re-authenticate to obtain a new token. Implement token refresh logic if your application requires it.

7. Conclusion

JWTs provide a robust method for managing authentication and authorization in microservices. Laravel Passport simplifies JWT handling, allowing you to focus on your application’s core functionality. By following this guide, you can implement secure and scalable authentication and authorization mechanisms across your Laravel microservices.

This detailed guide should help you understand how to use JWTs effectively in a Laravel microservices environment, ensuring secure and seamless user authentication and authorization.

--

--

Moez Missaoui
Moez Missaoui

Written by Moez Missaoui

Software Developer | Tech Enthusiast | Problem Solver

Responses (1)