PHPackages                             secureprompt/laravel-device-approval - 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. secureprompt/laravel-device-approval

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

secureprompt/laravel-device-approval
====================================

Laravel package for trusted-device login approval and device-approval security workflows.

v0.1.0-beta.1(4d ago)01↓100%MITPHPPHP ^8.2CI passing

Since Aug 13Pushed 2d agoCompare

[ Source](https://github.com/DiveshR/laravel-device-approval)[ Packagist](https://packagist.org/packages/secureprompt/laravel-device-approval)[ RSS](/packages/secureprompt-laravel-device-approval/feed)WikiDiscussions main Synced today

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

SecurePrompt Laravel
====================

[](#secureprompt-laravel)

**Composer:** `secureprompt/laravel-device-approval`
**Current release:** `v0.1.0-beta.1`

Trusted-device login approval for Laravel applications: primary credentials remain the responsibility of the host application, while SecurePrompt adds a cryptographic trusted-device approval layer with number matching and a short-lived, one-time approval grant.

Status: `v0.1.0-beta.1`
-----------------------

[](#status-v010-beta1)

SecurePrompt is currently in beta.

It is not yet a production-complete `v1.0` release.

### Included in this beta

[](#included-in-this-beta)

- Trusted-device enrollment and revocation
- Cryptographic device credentials
- Approval challenges
- Number matching
- Requester proof
- One-time approval grants with concurrency-safe consumption
- Eloquent persistence
- Laravel HTTP protocol transport
- Opt-in Laravel web/session authentication adapter
- Composer package auto-discovery
- Consumer-install verification using a separate Laravel application
- SQLite package testing
- MySQL consumer-flow verification

### Explicit beta limitations

[](#explicit-beta-limitations)

- No Web Push / FCM / APNs / real notification delivery yet
- No PWA / service worker yet
- No Sanctum / API-token authentication adapter yet
- No production first-device enrollment bootstrap policy
- No `SECUREPROMPT_KEY` rotation/versioning
- No complete cross-database CI matrix yet
- No real phone push-notification test yet

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

[](#requirements)

- PHP 8.2+
- Laravel 12 or 13
- Dedicated `SECUREPROMPT_KEY` (**not** `APP_KEY`)
- **HTTPS in production** (requester proofs, device credentials, and grant secrets travel in HTTP headers)

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

[](#installation)

```
composer require secureprompt/laravel-device-approval
```

Laravel discovers `SecurePromptServiceProvider` automatically. Do **not** register it manually.

The package source is publicly available on GitHub. Packagist distribution is being prepared for the beta release. A Composer path repository may still be used for local package development.

### 1. Generate key

[](#1-generate-key)

```
php artisan secureprompt:key
```

Add to `.env`:

```
SECUREPROMPT_KEY=base64:...
```

Do not commit this value. Changing it later invalidates device credentials and verifier digests. **Key rotation is not implemented yet.**

### 2. Migrate

[](#2-migrate)

```
php artisan migrate
```

Package migrations load automatically (no manual copy required).

### 3. Optional config publish

[](#3-optional-config-publish)

```
php artisan vendor:publish --tag=secureprompt-config
```

Publishing is optional. Defaults work for local development once `SECUREPROMPT_KEY` is set.

ApprovalSubject setup
---------------------

[](#approvalsubject-setup)

Any authenticatable model may participate. SecurePrompt does **not** assume `App\Models\User`.

```
use SecurePrompt\Laravel\Concerns\UsesSecurePrompt;
use SecurePrompt\Laravel\Contracts\ApprovalSubject;

class User extends Authenticatable implements ApprovalSubject
{
    use UsesSecurePrompt;
}
```

Critical: do not `Auth::attempt()` before SecurePrompt
------------------------------------------------------

[](#critical-do-not-authattempt-before-secureprompt)

`Auth::attempt()` creates an authenticated Laravel session immediately. Using it before SecurePrompt approval defeats SecurePrompt as a second factor:

```
// ❌ WRONG — user is already logged in before device approval
Auth::attempt($credentials);
```

Correct host flow:

```
host validates primary credentials (UserProvider / existing auth)
        ↓
Authenticatable candidate obtained (NOT logged in)
        ↓
BeginPendingAuthentication($user, $guard)
        ↓
StartApprovalFromPendingAuthentication (or HTTP start)
        ↓
device confirm → number match → grant
        ↓
POST /secureprompt/session/complete
        ↓
Auth::login + session regenerate

```

### Session auth integration sketch

[](#session-auth-integration-sketch)

Enable the adapter:

```
SECUREPROMPT_SESSION_ENABLED=true
```

```
use Illuminate\Support\Facades\Auth;
use SecurePrompt\Laravel\Application\Session\BeginPendingAuthentication;
use SecurePrompt\Laravel\Application\Session\StartApprovalFromPendingAuthentication;

$provider = Auth::guard('web')->getProvider();
$user = $provider->retrieveByCredentials($credentials);

if ($user === null || ! $provider->validateCredentials($user, $credentials)) {
    abort(401);
}

app(BeginPendingAuthentication::class)->begin($user, 'web');
$started = app(StartApprovalFromPendingAuthentication::class)->start();
// Keep requester proof in the Laravel session — never in the query string.
```

After the trusted device approves and the requester obtains a grant:

```
POST /secureprompt/session/complete
X-SecurePrompt-Grant-Secret: ...
{ "grant_id": "..." }
```

See the sibling `secureprompt-demo` application for a Sail + MySQL consumer example.

Trusted devices
---------------

[](#trusted-devices)

SecurePrompt does **not** auto-trust the first device. Host applications must implement a deliberate enrollment policy (re-authentication, passkey, existing device approval, etc.).

```
use SecurePrompt\Laravel\Application\TrustedDevice\EnrollTrustedDevice;
use SecurePrompt\Laravel\Domain\ValueObjects\DeviceIdentifier;

$enrolled = app(EnrollTrustedDevice::class)->enroll(
    $user->securePromptSubjectReference(),
    DeviceIdentifier::from('phone-1'),
    now()->toDateTimeImmutable(),
);

// Deliver $enrolled->credential() once. Never store plaintext in the database.
```

Configuration
-------------

[](#configuration)

Env / configPurpose`SECUREPROMPT_KEY` → `secureprompt.crypto.key`Dedicated HMAC key`SECUREPROMPT_ENABLED` → `secureprompt.enabled`When `false`, HTTP routes are not registered. Orchestration services remain available.`secureprompt.http.prefix`Route prefix (default `secureprompt`)`secureprompt.http.middleware`Middleware for device + generic consume routes (default `[]`)`SECUREPROMPT_SESSION_ENABLED` → `secureprompt.session.enabled`Opt-in Laravel session auth adapter (default `false`)`secureprompt.session.middleware`Middleware for requester + session-complete routes (default `['web']`)HTTP protocol
-------------

[](#http-protocol)

Routes load when `secureprompt.enabled=true`.

MethodPathAuth material`POST``/secureprompt/approvals`Subject resolver (pending session when session auth enabled)`POST``/secureprompt/approvals/{challenge}/confirm``device_id` + `X-SecurePrompt-Device-Credential``POST``/secureprompt/approvals/{challenge}/reject`device id + credential`POST``/secureprompt/approvals/{challenge}/number-match`device + `{ "number": 47 }``POST``/secureprompt/approvals/{challenge}/status``X-SecurePrompt-Requester-Proof``POST``/secureprompt/approvals/{challenge}/grant`requester proof`POST``/secureprompt/grants/{grant}/consume``X-SecurePrompt-Grant-Secret` (authorization only — no `Auth::login()`)`POST``/secureprompt/session/complete`grant id + secret (**session auth only** — consume + login)Status is **POST** because it requires a secret proof and may persist expiry. Never put secrets in query strings.

Security architecture (summary)
-------------------------------

[](#security-architecture-summary)

- Possession secrets travel only in dedicated headers
- Clients cannot assert `subject_id` / `subject_type` to impersonate another subject
- Session pending context is server-side; grant theft without matching pending session is denied by the session adapter
- RequesterProof remains mandatory even with Laravel session binding
- This package never sets `Access-Control-Allow-Origin: *`
- Host rate limiting is recommended (not built-in yet)

Development
-----------

[](#development)

Docker-first package workflow:

```
docker compose run --rm php composer install
docker compose run --rm php composer check
```

Individual scripts: `composer test`, `composer lint`, `composer analyse`, `composer validate --strict`.

License
-------

[](#license)

MIT

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance100

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity31

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% 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

4d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4d95552fcf9867d1336b979cacffcd4de52f4616d21ec03d8bc8e0b0c3b8cc3d?d=identicon)[DiveshR](/maintainers/DiveshR)

---

Top Contributors

[![DiveshR](https://avatars.githubusercontent.com/u/25860707?v=4)](https://github.com/DiveshR "DiveshR (2 commits)")

---

Tags

laravelsecurityAuthenticationMFAtrusted-devicelogin approvaldevice-approval

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/secureprompt-laravel-device-approval/health.svg)

```
[![Health](https://phpackages.com/badges/secureprompt-laravel-device-approval/health.svg)](https://phpackages.com/packages/secureprompt-laravel-device-approval)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.4M354](/packages/psalm-plugin-laravel)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.6k31.8M160](/packages/laravel-cashier)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

79227.1M227](/packages/laravel-mcp)[api-platform/laravel

API Platform support for Laravel

58190.1k21](/packages/api-platform-laravel)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k16.3M154](/packages/laravel-pulse)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9922.4M146](/packages/roots-acorn)

PHPackages © 2026

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