blueids/mak-jwt-middleware

JWT stateless authentication middleware for MAK ERP Hospitalier

Maintainers

Package info

github.com/AureDulvresse/mak-jwt-middleware

pkg:composer/blueids/mak-jwt-middleware

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.1.0 2026-03-25 15:14 UTC

This package is auto-updated.

Last update: 2026-03-25 15:17:56 UTC


README

Package Composer partagé pour l'authentification JWT stateless dans MAK ERP Hospitalier.

Architecture

JWT Stateless Authentication
├── Validation locale (pas d'appel HTTP vers Core)
├── Payload : { user_id, hospital_id, roles[], permissions[], jti, exp }
├── Blacklist Redis avec TTL
├── Clé partagée APP_JWT_SECRET (HS256)
└── Isolation automatique par hospital_id

Installation

Via Composer (recommandé)

# Dans chaque service Laravel (mak-core, mak-patient, mak-finance)
composer require mak/jwt-middleware

Configuration

  1. Publier la configuration :
php artisan vendor:publish --provider="MAK\JwtMiddleware\JwtMiddlewareServiceProvider" --tag="jwt-middleware-config"
  1. Variables d'environnement (dans .env) :
APP_JWT_SECRET=votre-clé-256-bit-minimum-64-caractères-pour-HS256
APP_JWT_TTL=480          # 8 heures (journée de travail)
APP_JWT_REFRESH_TTL=43200 # 30 jours

Utilisation

1. Protection des routes

// routes/api.php
use Illuminate\Support\Facades\Route;

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

    // Avec vérification de permission
    Route::middleware(['jwt.permission:patient.create'])->group(function () {
        Route::post('/patients', [PatientController::class, 'store']);
        Route::put('/patients/{id}', [PatientController::class, 'update']);
    });

    Route::middleware(['jwt.permission:finance.facture.annuler'])->group(function () {
        Route::delete('/factures/{id}', [FactureController::class, 'annuler']);
    });
});

2. Accès aux données utilisateur

// Dans un Controller
public function profile(Request $request)
{
    $user = $request->user();

    return response()->json([
        'user_id' => $user->id,
        'hospital_id' => $user->hospital_id,
        'roles' => $user->roles,
        'permissions' => $user->permissions,
    ]);
}

// Vérification de permission dans le code
public function createPatient(Request $request)
{
    if (!$request->user()->can('patient.create')) {
        abort(403, 'Permission insuffisante');
    }

    // Logique métier...
}

3. Login/Logout (dans mak-core)

use MAK\JwtMiddleware\Services\JwtService;

class AuthController extends Controller
{
    public function login(LoginRequest $request, JwtService $jwtService)
    {
        // Validation des credentials...

        $user = User::findByEmail($request->email);

        $tokenData = [
            'user_id' => $user->id,
            'hospital_id' => $user->hospital_id,
            'roles' => $user->roles->pluck('name')->toArray(),
            'permissions' => $user->getAllPermissions()->pluck('name')->toArray(),
        ];

        $accessToken = $jwtService->generateToken($tokenData);
        $refreshToken = $jwtService->generateRefreshToken($tokenData);

        return response()->json([
            'access_token' => $accessToken,
            'refresh_token' => $refreshToken,
            'token_type' => 'Bearer',
            'expires_in' => config('jwt-middleware.ttl') * 60,
        ]);
    }

    public function logout(Request $request, JwtService $jwtService)
    {
        $token = $request->bearerToken();
        $jti = $jwtService->extractJti($token);

        if ($jti) {
            $jwtService->blacklistToken($jti);
        }

        return response()->json(['message' => 'Déconnexion réussie']);
    }

    public function refresh(Request $request, JwtService $jwtService)
    {
        $refreshToken = $request->input('refresh_token');

        // Recharger les données utilisateur depuis la DB
        $user = User::find($request->user()->id);

        $userData = [
            'user_id' => $user->id,
            'hospital_id' => $user->hospital_id,
            'roles' => $user->roles->pluck('name')->toArray(),
            'permissions' => $user->getAllPermissions()->pluck('name')->toArray(),
        ];

        $tokens = $jwtService->refreshToken($refreshToken, $userData);

        return response()->json($tokens);
    }
}

Isolation par hôpital

Le middleware applique automatiquement l'isolation par hospital_id :

// Dans un Model (ex: Patient)
protected static function booted(): void
{
    static::addGlobalScope(new HospitalScope());
}

// HospitalScope utilise automatiquement hospital_id du JWT
class HospitalScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        $hospitalId = app(HospitalContext::class)->getId();
        $builder->where('hospital_id', $hospitalId);
    }
}

Gestion des erreurs

Le middleware retourne des réponses JSON structurées :

// Token manquant
{
  "message": "Token manquant",
  "code": "TOKEN_MISSING",
  "status": 401
}

// Token expiré
{
  "message": "Token expiré",
  "code": "TOKEN_EXPIRED",
  "status": 401
}

// Permission insuffisante
{
  "message": "Permission insuffisante",
  "code": "INSUFFICIENT_PERMISSIONS",
  "status": 403,
  "required_permission": "patient.create"
}

Tests

# Exécuter les tests du package
vendor/bin/phpunit

# Tests spécifiques
vendor/bin/phpunit tests/JwtServiceTest.php

Sécurité

Clé secrète

  • Minimum 64 caractères pour HS256
  • Identique dans tous les services
  • Stockée dans les variables d'environnement
  • Changée régulièrement en production

Blacklist Redis

  • TTL automatique basé sur la durée restante du token
  • Fail-safe : en cas d'erreur Redis, les tokens sont considérés valides
  • Nettoyage automatique à l'expiration

Payload JWT

  • JTI unique (JWT ID) pour chaque token
  • Expiration stricte (pas de grâce période)
  • Claims obligatoires : user_id, hospital_id, roles, permissions

Développement

Structure du package

mak-jwt-middleware/
├── src/
│   ├── JwtMiddlewareServiceProvider.php
│   ├── Services/
│   │   └── JwtService.php
│   ├── Middleware/
│   │   ├── JwtAuthenticate.php
│   │   └── CheckPermission.php
│   ├── Exceptions/
│   │   └── JwtExceptions.php
│   └── Context/
│       └── HospitalContext.php
├── config/
│   └── jwt-middleware.php
└── tests/
    └── JwtServiceTest.php

Commandes de développement

# Installation des dépendances
composer install

# Tests
composer test

# Analyse statique (si configuré)
composer analyse

# Formatage du code
composer format

Dépannage

Token rejeté avec "Signature invalide"

Cause : Clé secrète différente entre services Solution : Vérifier APP_JWT_SECRET identique partout

Token blacklisté après logout

Cause : Token révoqué dans Redis Solution : Se reconnecter pour obtenir un nouveau token

Erreur "Hospital ID non défini"

Cause : Contexte CLI sans hospital_id configuré Solution : Définir HOSPITAL_ID=1 dans .env

Performance Redis

Cause : Latence réseau élevée vers Redis Solution : Vérifier connexion Redis et monitoring

MAK ERP Hospitalier © 2026# mak-jwt-middleware