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
Requires
- php: ^8.1
- firebase/php-jwt: ^7.0
- laravel/framework: ^10.0|^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.0
- mockery/mockery: ^1.6
- orchestra/testbench: ^8.0|^9.0
- phpunit/phpunit: ^10.0|^11.0
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.