abdelaziz-hassan/authentica

Secure Laravel client for Authentica API (OTP, Nafath, Face, Voice).

Installs: 6

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/abdelaziz-hassan/authentica

v1.0.0 2025-10-05 18:29 UTC

This package is auto-updated.

Last update: 2025-10-05 18:33:11 UTC


README

A secure, DX‑friendly Laravel SDK for Authentica (Format: 1A, Host: https://api.authentica.sa/api/v2).
Features: OTP (SMS/WhatsApp/Email), Nafath (with package‑handled webhook → single event), Face & Voice verification, and an optional circuit breaker.

Table of Contents

Highlights

  • Clean API returning a predictable shape: { ok, status, body, message, circuit? }
  • Zero app boilerplate for Nafath webhooks — the package authenticates, validates, and dispatches a single Laravel event for you to handle.
  • Secure by default: JSON‑only responses, strict validation, Basic/Password webhook auth.
  • Production‑ready optional circuit breaker that you can enable/disable and choose the backing store for (Cache or in‑memory).

Requirements

  • PHP 8.1+
  • Laravel 9.x / 10.x / 11.x
  • Composer

Installation

composer require abdelaziz-hassan/authentica

The service provider and facades are auto‑discovered.

(Optional) Publish the configuration file:

php artisan vendor:publish --tag=authentica-config

Configuration

Set the basics in .env:

AUTHENTICA_API_KEY=your-authentica-api-key
AUTHENTICA_HOST=https://api.authentica.sa/api/v2

Nafath Webhook

Enable and choose one auth mode:

AUTHENTICA_NAFATH_WEBHOOK_ENABLED=true
AUTHENTICA_NAFATH_WEBHOOK_PATH=/webhooks/authentica/nafath

# ONE of: body_password | basic
AUTHENTICA_NAFATH_WEBHOOK_AUTH=body_password

# Body password mode
AUTHENTICA_NAFATH_PASSWORD_LABEL=Password
AUTHENTICA_NAFATH_PASSWORD_VALUE=super-secret

# OR Basic mode (header OR body labels accepted)
# AUTHENTICA_NAFATH_WEBHOOK_AUTH=basic
# AUTHENTICA_NAFATH_BASIC_USER_LABEL=username
# AUTHENTICA_NAFATH_BASIC_USER_VALUE=authentica
# AUTHENTICA_NAFATH_BASIC_PASS_LABEL=password
# AUTHENTICA_NAFATH_BASIC_PASS_VALUE=strongpass

Circuit Breaker (Optional)

Disabled by default. Enable if you want the SDK to fast‑fail during upstream outages:

AUTHENTICA_CIRCUIT_ENABLED=true
AUTHENTICA_CIRCUIT_DRIVER=cache   # or: array   (in‑memory)
# AUTHENTICA_CIRCUIT_FAILURES=5
# AUTHENTICA_CIRCUIT_OPEN=60
# AUTHENTICA_CIRCUIT_SUCC=2

Usage

Import the facade:

use AbdelAzizHassan\Authentica\Facades\Authentica;

All client calls return:

[
  'ok'      => bool,                  // Http::successful()
  'status'  => int,                   // HTTP status (0 => network error)
  'body'    => array|null,            // decoded JSON
  'message' => string|null,           // body['message'] if present
  'circuit' => ['state' => 'closed|open|half_open'] // when breaker enabled
]

OTP

Send OTP

$res = Authentica::sendOtp([
  'method' => 'sms',                  // sms|whatsapp|email
  'phone'  => '+9665XXXXXXXXX',       // required for sms/whatsapp
  // 'email'     => 'user@test.test', // required for email
  // 'template_id' => 31,
  // 'fallback_email' => 'fallback@test.test',
  // 'otp' => '123456',               // optional custom OTP
]);

Verify OTP

$res = Authentica::verifyOtp([
  'phone' => '+9665XXXXXXXXX',  // or 'email'
  'email' => 'user@test.test',
  'otp'   => '123456',
]);

Nafath

Start verification

$res = Authentica::verifyByNafath([
  'national_id' => '123XXXXXXX',
]);
// $res['body']['data'] -> ['TransactionId' => '...', 'Code' => '...']

Nafath with data

// 1) request OTP to device
$r1 = Authentica::nafathRequest([
  'national_id' => '123XXXXXXX',
  'phone_number'=> '966XXXXXXX',
]);

// 2) verify to get Code
$r2 = Authentica::nafathVerify([
  'otp'        => 'xxxxxx',
  'request_id' => 'xxxxx-xxxx-xxxx-xxxx-xxxxxxxxx',
]);

Webhook (package‑handled)

The package registers a POST route (default /webhooks/authentica/nafath).
It authenticates, validates, and dispatches one event:

\AbdelAzizHassan\Authentica\Webhooks\Events\NafathWebhookReceived

Listen to it in your app:

// app/Providers/EventServiceProvider.php
protected $listen = [
    \AbdelAzizHassan\Authentica\Webhooks\Events\NafathWebhookReceived::class => [
        \App\Listeners\HandleNafathWebhook::class,
    ],
];
// app/Listeners/HandleNafathWebhook.php
use AbdelAzizHassan\Authentica\Webhooks\Events\NafathWebhookReceived;

class HandleNafathWebhook
{
    public function handle(NafathWebhookReceived $e): void
    {
        // $e->transactionId  string
        // $e->nationalId     string
        // $e->status         "COMPLETED" | "REJECTED"
        // $e->raw            full original payload
        // ... your logic here ...
    }
}

Example payload from Authentica

{
  "TransactionId": "6419747b-b0d7-4564-8755-a7f554d16b10",
  "NationalId": "1234567891",
  "Status": "COMPLETED",
  "Password": "your-secure-password"
}

Face Verification

// Verify by face (base64 images as JSON)
$res = Authentica::verifyByFace([
  'user_id'              => 'u-123',
  // 'registered_face_image' => 'base64-...',
  'query_face_image'     => 'base64-...',
]);

// Store/Get/Delete reference face image
Authentica::storeFaceImage('u-123', ['face_image' => 'base64-...']);
Authentica::getFaceImage('u-123');
Authentica::deleteFaceImage('u-123');

Voice Verification

// Verify by voice (base64 audio as JSON)
$res = Authentica::verifyByVoice([
  'user_id'         => 'u-123',
  // 'registered_audio' => 'base64-...',
  'query_audio'     => 'base64-...',
]);

// Store/Get/Delete reference audio
Authentica::storeVoice('u-123', ['audio' => 'base64-...']);
Authentica::getVoice('u-123');
Authentica::deleteVoice('u-123');

Circuit Breaker (Optional)

When enabled, the SDK protects your app from upstream instability.

  • Opens after consecutive network errors or configured HTTP statuses (429,500,502,503,504 by default).
  • While open, calls short‑circuit with:
    { "ok": false, "status": 503, "message": "Upstream temporarily unavailable (circuit open)" }
  • After a cool‑down, it half‑opens and closes on a few successes.

Enable in .env:

AUTHENTICA_CIRCUIT_ENABLED=true
AUTHENTICA_CIRCUIT_DRIVER=cache   # or array
# AUTHENTICA_CIRCUIT_FAILURES=5
# AUTHENTICA_CIRCUIT_OPEN=60
# AUTHENTICA_CIRCUIT_SUCC=2

You can check the state in every response:

$res['circuit']['state'] ?? null; // 'closed' | 'open' | 'half_open'

Security Notes

  • Webhook auth: choose one mode — body_password or basic.
    • Basic accepts standard Authorization: Basic … header or labeled fields in JSON (username_label/password_label).
  • All webhook responses are JSON (no Blade pages).
  • Inputs are validated; failures return 422 with details.

Troubleshooting

  • Got Blade/HTML in webhook? Ensure the route is under the api stack and the package’s JSON middleware is active (it is by default).
  • Auth fails on webhook: Double‑check your .env values and whether you’re sending header or labeled fields.
  • Circuit always open: Start with AUTHENTICA_CIRCUIT_FAILURES=1 in dev and watch logs; ensure your cache driver works if using cache mode.

Versioning

Follows SemVer.
Targets Authentica Format: 1A, base host https://api.authentica.sa/api/v2.

License

MIT © AbdelAziz Hassan