PHPackages                             samireltabal/secureapi - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. samireltabal/secureapi

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

samireltabal/secureapi
======================

Provides various types of secure api authentication for laravel applications

v1.0.0(1mo ago)13MITPHPPHP ^8.3CI passing

Since Jun 12Pushed 4w agoCompare

[ Source](https://github.com/Samireltabal/SecureApi)[ Packagist](https://packagist.org/packages/samireltabal/secureapi)[ Docs](https://github.com/samireltabal/secureapi)[ GitHub Sponsors]()[ RSS](/packages/samireltabal-secureapi/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (13)Versions (2)Used By (0)

SecureApi — Laravel API Authentication
======================================

[](#secureapi--laravel-api-authentication)

[![Latest Version on Packagist](https://camo.githubusercontent.com/e62081aac7aacd840b3d53f2a5c6886379b17fa0c10b0458a5203f62fdc50998/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f73616d6972656c746162616c2f7365637572656170692e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/samireltabal/secureapi)[![GitHub Tests Action Status](https://github.com/samireltabal/secureapi/actions/workflows/run-tests.yml/badge.svg)](https://github.com/samireltabal/secureapi/actions?query=workflow%3Arun-tests+branch%3Amain)[![PHPStan](https://github.com/samireltabal/secureapi/actions/workflows/phpstan.yml/badge.svg)](https://github.com/samireltabal/secureapi/actions/workflows/phpstan.yml)[![Total Downloads](https://camo.githubusercontent.com/59929793948242d9b2bdb53d3816ede4135b46045d84fd1a266c0b11117c2857/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f73616d6972656c746162616c2f7365637572656170692e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/samireltabal/secureapi)

A plug-in authentication package for Laravel 12+ that provides five API-security mechanisms through a single standalone middleware. Mix and match mechanisms per route group; each mechanism is independently testable and configurable.

**Mechanisms**

MechanismToken / TransportUse case`api_key``Bearer sk_…`Server-to-server, mobile clients`hmac`HMAC-SHA256 signed requestWebhook producers, high-integrity integrations`jwt`RS256 / HS256 Bearer JWTExternal IdPs, delegated auth`oauth_client`Built-in client-credentials flowMachine-to-machine OAuth2`mtls`Proxy-forwarded client certificateZero-trust service mesh (opt-in, fail-loud)All mechanisms share: scoped credentials, per-application IP allow-listing, per-credential rate limiting, tamper-evident audit logs, and replay protection for HMAC.

---

Requirements
------------

[](#requirements)

- PHP 8.3+
- Laravel 12 or 13

Installation
------------

[](#installation)

```
composer require samireltabal/secureapi
```

Publish and run the migrations:

```
php artisan vendor:publish --tag="secureapi-migrations"
php artisan migrate
```

Publish the config (optional — sensible defaults ship out of the box):

```
php artisan vendor:publish --tag="secureapi-config"
```

---

Quick Start
-----------

[](#quick-start)

### 1. Create an application and credential

[](#1-create-an-application-and-credential)

```
use SamirEltabal\SecureApi\Facades\SecureApi;

$app        = SecureApi::createApplication('My Service');
$credential = SecureApi::createApiKeyCredential($app->id);

// $credential->plaintextKey — the full sk_…_… token; shown once, never stored in plaintext
```

### 2. Protect routes

[](#2-protect-routes)

Add the `secureapi` middleware and pass the mechanism(s) you want to accept. Mechanisms are tried in order — the first one whose token format matches wins:

```
// Single mechanism
Route::middleware('secureapi:api_key')->group(function () {
    Route::get('/protected', fn () => response()->json(['ok' => true]));
});

// Multiple accepted mechanisms (tried in order)
Route::middleware('secureapi:jwt,api_key')->group(function () {
    Route::get('/flexible', fn () => response()->json(['ok' => true]));
});
```

### 3. Call from a client

[](#3-call-from-a-client)

```
GET /protected HTTP/1.1
Authorization: Bearer sk__

```

### 4. (Optional) Check scopes inside a controller

[](#4-optional-check-scopes-inside-a-controller)

```
use SamirEltabal\SecureApi\Facades\SecureApi;

Route::middleware(['secureapi:api_key', 'secureapi.scopes:read'])->group(function () {
    Route::get('/data', fn () => response()->json(['data' => []]));
});
```

---

Mechanisms
----------

[](#mechanisms)

### API Key

[](#api-key)

Bearer token in the format `sk__`. The secret is stored as a SHA-256 hash; the plaintext is shown once on creation.

```
$credential = SecureApi::createApiKeyCredential($appId, [
    'name'    => 'mobile-client',
    'scopes'  => ['read', 'write'],
    'expires' => now()->addYear(),
]);
// $credential->plaintextKey  — Bearer token to send in Authorization header
// $credential->credential    — Eloquent Credential model
```

**Rotate** when a key may have been compromised:

```
$new = SecureApi::rotateApiKeyCredential($credentialId);
// $new->plaintextKey — new token; old key stops working immediately
```

Full details: [docs/api-key.md](https://github.com/samireltabal/secureapi/blob/main/docs/api-key.md)

---

### HMAC Request Signing

[](#hmac-request-signing)

Every request is signed with `HMAC-SHA256` over `METHOD\nPATH\nTIMESTAMP\nNONCE\nBODY_HASH`. Replayed requests (duplicate nonce within the timestamp window) are rejected.

Required headers: `X-SecureApi-Signature`, `X-SecureApi-Timestamp`, `X-SecureApi-Nonce`.

The shared secret is stored **encrypted at rest** (AES-256-CBC via Laravel's `encrypted` cast) and can be retrieved at any time for signing.

```
$credential = SecureApi::createHmacCredential($appId, ['name' => 'webhook-producer']);
// $credential->plaintextKey  — the shared secret for HMAC signing

// Rotate when the secret needs to change
$new = SecureApi::rotateHmacCredential($credentialId);
// $new->plaintextKey — new shared secret
```

Full details: [docs/hmac.md](https://github.com/samireltabal/secureapi/blob/main/docs/hmac.md)

---

### JWT (RS256 / HS256)

[](#jwt-rs256--hs256)

Validates Bearer JWTs. Supports externally-issued tokens (e.g. from an IdP) and internally-issued tokens via `SecureApi::issueToken()`. `alg:none` is unconditionally rejected.

```
SECUREAPI_JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\n..."
SECUREAPI_JWT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n..."
SECUREAPI_JWT_ISSUER=https://auth.example.com
```

Generate a fresh RS256 key pair with:

```
php artisan secureapi:jwt:keys
```

**Create a JWT credential and issue a token:**

```
$credential = SecureApi::createJwtCredential($appId, ['name' => 'internal-service']);

// Issue a signed JWT (RS256 by default, uses the app's private key from config)
$token = SecureApi::issueToken($appId, ['credential_id' => $credential->id]);
// $token — signed JWT string; send as  Authorization: Bearer
```

Full details: [docs/jwt.md](https://github.com/samireltabal/secureapi/blob/main/docs/jwt.md)

---

### OAuth2 Client Credentials

[](#oauth2-client-credentials)

Built-in `POST /secureapi/oauth/token` endpoint; standard `client_credentials` grant. The client secret is stored as a SHA-256 hash; plaintext is shown once on creation.

```
$credential = SecureApi::createOauthClientCredential($appId, ['name' => 'partner-service']);
// $credential->credential->id  — client_id
// $credential->plaintextKey    — client_secret (shown once)
```

```
POST /secureapi/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=id>
&client_secret=

```

The endpoint returns a `Bearer` JWT. Protect downstream routes with:

```
Route::middleware('secureapi:jwt')->group(fn () => ...);
```

Full details: [docs/oauth2.md](https://github.com/samireltabal/secureapi/blob/main/docs/oauth2.md)

---

### mTLS (mutual TLS)

[](#mtls-mutual-tls)

Opt-in, fail-loud. Your TLS-terminating proxy (nginx, Envoy) verifies the client certificate and forwards the result in headers. SecureApi validates the forwarded certificate fingerprint against a stored SHA-256 hash.

```
SECUREAPI_MTLS_ENABLED=true
SECUREAPI_MTLS_TRUSTED_PROXIES=10.0.0.1,172.16.0.0/12
```

Register a client certificate:

```
php artisan secureapi:mtls:register my-app-name /path/to/client.crt
```

Full details: [docs/mtls.md](https://github.com/samireltabal/secureapi/blob/main/docs/mtls.md)

---

Middleware Reference
--------------------

[](#middleware-reference)

All aliases are registered automatically by the service provider — no manual registration needed.

AliasParametersDescription`secureapi``mechanism,...`Authenticate against one or more mechanisms`secureapi.scopes``scope,...`Require all listed scopes on the resolved credential`secureapi.scope``scope,...`Alias for `secureapi.scopes``secureapi.allow_ips`—Enforce the application's IP allowlist`secureapi.throttle`—Enforce per-application / per-credential rate limits`secureapi.audit`—Write an audit log entry for every requestExample — full middleware stack for a high-security endpoint:

```
Route::middleware([
    'secureapi:api_key',
    'secureapi.allow_ips',
    'secureapi.throttle',
    'secureapi.scopes:payments:write',
    'secureapi.audit',
])->post('/payments', PaymentController::class);
```

---

Artisan Commands
----------------

[](#artisan-commands)

CommandDescription`secureapi:app:create {name}`Create an application`secureapi:app:list`List all applications`secureapi:app:revoke {id}`Revoke an application and all its credentials`secureapi:app:settings {id}`View or set application settings`secureapi:credential:create {application}`Issue a new credential`secureapi:credential:revoke {id}`Revoke a credential`secureapi:credential:rotate {id}`Rotate an API key or HMAC credential`secureapi:jwt:keys`Generate an RS256 key pair`secureapi:mtls:register {app} {cert}`Register a client certificate for mTLS---

Credential Rotation
-------------------

[](#credential-rotation)

API key and HMAC credentials can be rotated without downtime. The new credential is issued first; the old one is revoked atomically inside a database transaction.

```
// API key
$new = SecureApi::rotateApiKeyCredential($credentialId);

// HMAC
$new = SecureApi::rotateHmacCredential($credentialId);

// Both return an IssuedCredential DTO:
// $new->plaintextKey   — new secret (shown once)
// $new->credential     — new Credential model
```

Via Artisan:

```
php artisan secureapi:credential:rotate
```

---

Configuration Reference
-----------------------

[](#configuration-reference)

```
// config/secureapi.php (published via vendor:publish --tag="secureapi-config")

'table_prefix' => env('SECUREAPI_TABLE_PREFIX', 'secure_api_'),

'oauth' => [
    'enabled'               => true,
    'path_prefix'           => 'secureapi/oauth',
    'token_ttl'             => 3600,
    'rate_limit_per_minute' => 10,
],

'mtls' => [
    'enabled'         => env('SECUREAPI_MTLS_ENABLED', false),
    'trusted_proxies' => [],          // required when enabled
    'cert_header'     => 'ssl-client-cert',
    'verify_header'   => 'ssl-client-verify',
],

'jwt' => [
    'algorithm'   => 'RS256',         // 'RS256' or 'HS256'
    'public_key'  => env('SECUREAPI_JWT_PUBLIC_KEY'),
    'private_key' => env('SECUREAPI_JWT_PRIVATE_KEY'),
    'key_id'      => env('SECUREAPI_JWT_KEY_ID'),
    'issuer'      => env('SECUREAPI_JWT_ISSUER', env('APP_URL')),
    'audience'    => env('SECUREAPI_JWT_AUDIENCE'),
    'ttl'         => 3600,
],

'audit' => [
    'retention_days' => 90,           // null = keep forever; pruned by model:prune
],

'rate_limit' => [
    'default_per_minute' => null,     // null = unlimited
],

'hmac' => [
    'timestamp_window' => 300,        // seconds; requests outside ±window are rejected
],

'replay' => [
    'cache_store' => null,            // null = app default; use 'redis' for multi-node
],
```

---

Scopes, Rate Limiting &amp; Audit
---------------------------------

[](#scopes-rate-limiting--audit)

Full reference: [docs/scopes-rate-limiting-audit.md](https://github.com/samireltabal/secureapi/blob/main/docs/scopes-rate-limiting-audit.md)

---

Security Model
--------------

[](#security-model)

- **API key secrets** are stored as SHA-256 hashes. Plaintext is shown once on creation and never stored.
- **HMAC secrets** are stored encrypted at rest (AES-256-CBC via Laravel's `encrypted` cast) so they can be retrieved for signing operations.
- **OAuth client secrets** are stored as SHA-256 hashes. Plaintext is shown once on creation and never stored.
- **JWT** credentials carry no secret; the server signs tokens with the RSA private key from config.
- HMAC nonces are tracked in the Laravel cache for the replay window. Use a shared cache (Redis) in multi-node deployments.
- mTLS is **fail-loud by design**: if `secureapi.mtls.enabled` is `true` but `trusted_proxies` is empty, the middleware throws `MtlsNotEnabled` (HTTP 500) rather than silently returning 401. This surfaces proxy misconfiguration immediately.
- JWT `alg:none` tokens are unconditionally rejected.
- IP allow-listing uses `Symfony\Component\HttpFoundation\IpUtils` (supports IPv4, IPv6, and CIDR).

---

Testing
-------

[](#testing)

```
composer test          # Pest suite (164 tests)
composer analyse       # PHPStan level 8
composer format        # Laravel Pint
```

---

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md).

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](https://github.com/samireltabal/secureapi/security/policy) to report security vulnerabilities responsibly.

Credits
-------

[](#credits)

- [Samir M. Eltabal](https://github.com/Samireltabal)
- [All Contributors](https://github.com/samireltabal/secureapi/contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [LICENSE.md](LICENSE.md) for more information.

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance93

Actively maintained with recent releases

Popularity6

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 84.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ce130846d5686203d0a241cb7cdbf64ce9c6e8127ff9d7f927670dcca5a8d318?d=identicon)[syncit](/maintainers/syncit)

---

Top Contributors

[![Samireltabal](https://avatars.githubusercontent.com/u/17733615?v=4)](https://github.com/Samireltabal "Samireltabal (11 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (1 commits)")[![samir-eltabal](https://avatars.githubusercontent.com/u/120480750?v=4)](https://github.com/samir-eltabal "samir-eltabal (1 commits)")

---

Tags

laravelSamir M. Eltabalsecureapi

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/samireltabal-secureapi/health.svg)

```
[![Health](https://phpackages.com/badges/samireltabal-secureapi/health.svg)](https://phpackages.com/packages/samireltabal-secureapi)
```

###  Alternatives

[spatie/laravel-permission

Permission handling for Laravel 12 and up

12.9k102.4M1.5k](/packages/spatie-laravel-permission)[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.8M48](/packages/spatie-laravel-pdf)[spatie/laravel-passkeys

Use passkeys in your Laravel app

472890.7k40](/packages/spatie-laravel-passkeys)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.8k](/packages/rawilk-profile-filament-plugin)[harris21/laravel-fuse

Circuit breaker for Laravel queue jobs. Protect your workers from cascading failures.

45955.7k](/packages/harris21-laravel-fuse)[vormkracht10/laravel-mails

Laravel Mails can collect everything you might want to track about the mails that has been sent by your Laravel app.

24957.5k](/packages/vormkracht10-laravel-mails)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
