devmahmoudmustafa/laravel-nafath

Laravel package for Saudi Nafath (National Authentication) integration - حزمة Laravel للتكامل مع خدمة نفاذ الوطنية السعودية

Installs: 1

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/devmahmoudmustafa/laravel-nafath

1.0.0 2026-01-26 16:14 UTC

This package is auto-updated.

Last update: 2026-01-26 17:00:00 UTC


README

📦 حزمة Laravel للتكامل مع خدمة نفاذ الوطنية السعودية

حزمة Laravel احترافية وشاملة للتكامل مع نفاذ (Nafath) - نظام المصادقة الوطني السعودي. مبنية بأفضل الممارسات البرمجية (SOLID Principles) مع دعم كامل لـ TypeHints و DTOs.

✨ Features

  • 🏗️ Professional Architecture - SOLID principles with Contracts/Interfaces
  • 📦 DTOs - Type-safe Data Transfer Objects for all operations
  • 🔐 Full Nafath API Support - createRequest, getStatus, requestInfo, getJwk
  • 🎯 Events System - React to all request states (Completed, Rejected, Expired)
  • 🛡️ JWT Verification - Complete JWT token verification from Nafath
  • 🔄 Auto Retry - Automatic retry with configurable attempts
  • 📊 Request Tracking - Full database tracking of all requests
  • 🎨 Artisan Commands - Cleanup, statistics, and connection testing
  • 🌐 i18n - Arabic and English language support
  • Rate Limiting & IP Whitelisting - Advanced callback protection
  • 🧪 Testable - Easy to mock with interface-based design

📋 Requirements

  • PHP >= 8.1
  • Laravel >= 10.0
  • Extensions: ext-json, ext-openssl

📥 Installation

1. Install via Composer

composer require devmahmoudmustafa/laravel-nafath

2. Publish Configuration & Migrations

# Publish config file
php artisan vendor:publish --tag=nafath-config

# Publish migrations
php artisan vendor:publish --tag=nafath-migrations

# Run migrations
php artisan migrate

3. Publish Translations (Optional)

php artisan vendor:publish --tag=nafath-lang

4. Environment Configuration

Add to your .env file:

NAFATH_APP_ID=your_app_id
NAFATH_APP_KEY=your_app_key
NAFATH_ENVIRONMENT=staging  # or 'production'

# Optional settings
NAFATH_HTTP_TIMEOUT=30
NAFATH_RETRY_ATTEMPTS=3
NAFATH_EXPIRY_SECONDS=200

🚀 Quick Start

Using the Facade with DTOs

use Nafath\LaravelNafath\Facades\Nafath;
use Nafath\LaravelNafath\DTO\CreateRequestDTO;
use Nafath\LaravelNafath\DTO\GetStatusDTO;

// Create a new Nafath request
$dto = CreateRequestDTO::fromArray([
    'national_id' => '1234567890',
    'service' => 'DigitalServiceEnrollmentWithoutBio',
    'locale' => 'ar',
]);

$result = Nafath::createRequest($dto);

if ($result->isSuccess()) {
    $transId = $result->data['trans_id'];
    $random = $result->data['random'];
    
    // Show verification code to user
    echo "رمز التحقق: {$random}";
}

// Check request status
$statusDto = GetStatusDTO::fromArray([
    'national_id' => '1234567890',
    'trans_id' => $transId,
    'random' => $random,
]);

$status = Nafath::getStatus($statusDto);

if ($status->isSuccess()) {
    // WAITING, COMPLETED, REJECTED, EXPIRED
    echo $status->data['status'];
}

Using Dependency Injection (Recommended)

use Nafath\LaravelNafath\Contracts\NafathServiceInterface;
use Nafath\LaravelNafath\DTO\CreateRequestDTO;

class AuthController extends Controller
{
    public function __construct(
        private readonly NafathServiceInterface $nafathService
    ) {}

    public function initiateNafath(Request $request)
    {
        $dto = new CreateRequestDTO(
            nationalId: $request->national_id,
            service: 'DigitalServiceEnrollmentWithoutBio',
            locale: 'ar',
        );

        $result = $this->nafathService->createRequest($dto);

        if ($result->isError()) {
            return response()->json($result->toArray(), $result->statusCode);
        }

        return response()->json($result->toArray());
    }
}

📐 Architecture

Package Structure

src/
├── Contracts/           # Interfaces for dependency inversion
│   ├── NafathServiceInterface.php
│   ├── NafathApiClientInterface.php
│   ├── NafathPersistenceInterface.php
│   ├── JwtVerificationInterface.php
│   └── JwtIssuerInterface.php
├── DTO/                 # Data Transfer Objects
│   ├── CreateRequestDTO.php
│   ├── GetStatusDTO.php
│   ├── RequestInfoDTO.php
│   ├── NafathResponseDTO.php
│   ├── ApiResponseDTO.php
│   └── ...
├── Services/            # Business logic implementations
├── Http/
│   ├── Controllers/
│   ├── Middleware/
│   └── Requests/
├── Events/              # Event classes
├── Enums/               # Status enums
├── Exceptions/          # Custom exceptions
├── Models/              # Eloquent models
├── Support/             # Helper classes
└── ...

Available Contracts

Contract Description
NafathServiceInterface Main service for Nafath operations
NafathApiClientInterface HTTP client for Nafath API
NafathPersistenceInterface Database operations
JwtVerificationInterface JWT token verification
JwtIssuerInterface JWT token generation

Available DTOs

DTO Description
CreateRequestDTO Data for creating new MFA request
GetStatusDTO Data for checking request status
RequestInfoDTO Data for fetching detailed info
NafathResponseDTO Standardized service response
ApiResponseDTO Raw API response wrapper
CallbackDataDTO Callback data from Nafath

🎯 Events

Listen to Nafath events in your EventServiceProvider:

use Nafath\LaravelNafath\Events\NafathRequestCompleted;
use Nafath\LaravelNafath\Events\NafathRequestRejected;
use Nafath\LaravelNafath\Events\NafathRequestExpired;
use Nafath\LaravelNafath\Events\NafathCallbackReceived;

protected $listen = [
    NafathRequestCompleted::class => [
        \App\Listeners\HandleNafathSuccess::class,
    ],
    NafathRequestRejected::class => [
        \App\Listeners\HandleNafathRejection::class,
    ],
];

Example Listener

namespace App\Listeners;

use Nafath\LaravelNafath\Events\NafathRequestCompleted;

class HandleNafathSuccess
{
    public function handle(NafathRequestCompleted $event): void
    {
        $nafathRequest = $event->nafathRequest;
        $jwtPayload = $event->jwtPayload;

        // User data from Nafath
        $nationalId = $jwtPayload['nafathId'] ?? null;
        $fullName = $jwtPayload['nafathData']['fullName'] ?? null;

        // Create or authenticate user
        $user = User::firstOrCreate(
            ['national_id' => $nationalId],
            ['name' => $fullName]
        );

        // Generate session/token for user
        // ...
    }
}

🛡️ Enums

NafathStatus

use Nafath\LaravelNafath\Enums\NafathStatus;

$status = NafathStatus::COMPLETED;

$status->label();       // "Completed" or "مكتمل" based on locale
$status->isSuccess();   // true
$status->isFailed();    // false
$status->isPending();   // false
$status->colorClass();  // "success"

Available statuses: WAITING, COMPLETED, REJECTED, EXPIRED

🔧 Artisan Commands

# Test Nafath API connection
php artisan nafath:test

# View request statistics
php artisan nafath:stats --period=30

# Cleanup old requests
php artisan nafath:cleanup --days=30

⚙️ Configuration

See config/nafath.php for all available options:

  • API Credentials - app_id, app_key
  • Environment - staging/production
  • Timeouts - Per-endpoint timeout configuration
  • Retry Logic - attempts, sleep_ms
  • Rate Limiting - max_attempts, decay_minutes
  • IP Whitelisting - callback_allowed_ips
  • JWT Settings - private_key_path, algorithm, ttl
  • Routes - enabled, prefix, middleware

🧪 Testing

The package is designed for easy testing with interface-based architecture:

use Nafath\LaravelNafath\Contracts\NafathServiceInterface;
use Nafath\LaravelNafath\DTO\NafathResponseDTO;

// Mock the service
$mock = Mockery::mock(NafathServiceInterface::class);
$mock->shouldReceive('createRequest')
    ->andReturn(NafathResponseDTO::success([
        'trans_id' => 'test-uuid',
        'random' => '42',
    ]));

$this->app->instance(NafathServiceInterface::class, $mock);

Run package tests:

composer test

📚 Documentation

🤝 Contributing

Contributions are welcome! Please see CONTRIBUTING.md for details.

📝 License

MIT License - see LICENSE for details.

🔗 Links

💬 Support

For support and questions, please open an issue on GitHub.