padosoft / laravel-rebel-sessions
Device/session registry for Laravel Rebel: session/device tracking, logout-everywhere, refresh-token rotation with reuse detection, and device trust. Part of padosoft/laravel-rebel-*.
Package info
github.com/padosoft/laravel-rebel-sessions
pkg:composer/padosoft/laravel-rebel-sessions
Requires
- php: ^8.3
- illuminate/contracts: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- padosoft/laravel-rebel-core: ^0.1
- spatie/laravel-package-tools: ^1.92
Requires (Dev)
- larastan/larastan: ^3.0
- laravel/pint: ^1.18
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^4.0
- pestphp/pest-plugin-laravel: ^4.0
This package is auto-updated.
Last update: 2026-06-03 12:24:08 UTC
README
Refresh-token rotation with reuse detection, logout-everywhere, and device trust. When a stolen refresh token is replayed, Rebel detects the reuse and burns the whole session — every token of that user — instead of silently handing the attacker a fresh one. Plus remembered-device trust to cut step-up friction. Part of the
padosoft/laravel-rebel-*suite.
Table of contents
- What it is
- Quick glossary
- Why this package
- Rebel Sessions vs the alternatives
- How rotation + reuse detection works
- Installation
- Usage
- Security notes
- Testing & License
What it is
The device/session registry for Rebel. It provides the default implementations of two
core contracts — SessionRegistry (used by OTP/step-up for logout-everywhere and reuse
checks) and DeviceTrust (remembered devices) — plus a SessionManager that does the real
work: tracking sessions/refresh tokens and rotating them safely.
Depends on padosoft/laravel-rebel-core.
Quick glossary
| Term | In plain words |
|---|---|
| Refresh token | A long-lived token exchanged for a fresh access token (e.g. on mobile). |
| Rotation | Each use of a refresh token consumes it and issues a brand-new one. |
| Reuse detection | If an already-used refresh token shows up again, it was probably stolen → react. |
| Chain | All the refresh tokens descended from one original login, sharing a root_id. |
| Device trust | "Remember this device" so it can skip step-up for a while. |
Why this package
| ★ | What | In short |
|---|---|---|
| ★★★ | Reuse detection that burns the chain | A replayed refresh token doesn't just fail — it revokes all the user's tokens (the correct theft response). |
| ★★★ | Ownership + expiry enforced | A refresh token can only be rotated by its owner, and never after it expires. |
| ★★★ | Race-safe rotation | Every rotation locks the chain root, so concurrent requests serialize and no sibling escapes a burn. |
| ★★ | Logout-everywhere | One call revokes every active session/token of a subject. |
| ★★ | Device trust | Remembered devices (by fingerprint hash) expire after N days; atomic, tenant-scoped. |
| ★★ | Drop-in contracts | Implements the core SessionRegistry + DeviceTrust — OTP/step-up use them automatically. |
Rebel Sessions vs the alternatives
| Capability | Rebel Sessions | Shopify | Sanctum / Passport (native) | Hand-rolled |
|---|---|---|---|---|
| Refresh-token rotation you control | ✅ | ❌ | ➖ (Passport rotates, Sanctum has no refresh) | ❌ |
| Reuse detection (theft signal) | ✅ | ❌ | ❌ | ❌ |
| Burns the whole chain/user on reuse | ✅ | ❌ | ❌ | ❌ |
| Owner + expiry enforced on rotate | ✅ | ➖ | ➖ | ❌ |
| Race-safe (chain-root locking) | ✅ | ❌ | ❌ | ❌ |
| Programmatic logout-everywhere API | ✅ | ➖ | ➖ | ➖ |
| Customer-facing login activity / logout | ✅ | ✅ | ❌ | ❌ |
| Remembered-device trust API | ✅ | ➖ | ❌ | ❌ |
| Multi-tenant + audit-friendly (your app) | ✅ | ❌ | ❌ | ❌ |
Legend: ✅ built-in · ➖ partial / hosted-only / not exposed to you · ❌ not available.
Note: Shopify is a hosted, closed commerce platform — it manages its own customer sessions and shows shoppers a "logged-in devices" view, but never exposes refresh-token rotation, reuse detection, or a device-trust API you can self-host or build on.
How rotation + reuse detection works
login → issue refresh R0 (root of the chain)
│
client exchanges R0 ──► rotateRefresh(R0): consume R0, issue R1 (parent=R0, root=R0)
│
client exchanges R1 ──► consume R1, issue R2 ...
│
ATTACKER replays a stolen R0 ──► rotateRefresh(R0):
R0 is already 'consumed' ⇒ REUSE ⇒ burn EVERY live token of the user
(sessions + the whole refresh chain) and return null
A rotation can only proceed if the token is active, owned by the caller, and not expired — otherwise it returns null (and, for reuse, burns the user's tokens).
Installation
composer require padosoft/laravel-rebel-sessions
php artisan vendor:publish --tag="rebel-sessions-migrations"
php artisan migrate
The package binds the core SessionRegistry and DeviceTrust contracts automatically.
Usage
use Padosoft\Rebel\Sessions\Enums\SessionType; use Padosoft\Rebel\Sessions\SessionManager; $sessions = app(SessionManager::class); // On login: open a session and issue a refresh token $session = $sessions->start($user, SessionType::Session, ttlSeconds: 3600); $refresh = $sessions->start($user, SessionType::Refresh, ttlSeconds: 60 * 60 * 24 * 30); // On token refresh: rotate (null = reject; a stolen-token replay burns the chain) $next = $sessions->rotateRefresh($refresh->id, $user); if ($next === null) { // token unknown / expired / reused → force a fresh login } // Logout everywhere $sessions->revokeAll($user);
Device trust:
use Padosoft\Rebel\Core\Context\DeviceContext; use Padosoft\Rebel\Core\Contracts\DeviceTrust; $trust = app(DeviceTrust::class); $device = new DeviceContext(fingerprintHash: $hashOfThisDevice); $trust->trust($user, $device, days: 30); // "remember this device" $trust->isTrusted($user, $device); // true until it expires $trust->untrust($user, $device);
Security notes
- Reuse = theft: a replayed refresh token revokes every live token of the subject.
- Ownership & expiry: rotation checks the token belongs to the caller and isn't expired.
- Race-safe: rotations lock the chain root row, so concurrent rotations serialize.
- UUID ids; tenant-scoped queries; device fingerprints stored as hashes (never raw).
Testing & License
composer test # Pest (rotation, reuse-burn, ownership, expiry, logout-everywhere, device trust) composer phpstan # static analysis, level max composer pint # code style
License: MIT — see LICENSE. Part of the padosoft/laravel-rebel suite.
