Download the PHP package padosoft/laravel-rebel-bridge-otpz without Composer
On this page you can find all versions of the php package padosoft/laravel-rebel-bridge-otpz. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download padosoft/laravel-rebel-bridge-otpz
More information about padosoft/laravel-rebel-bridge-otpz
Files in padosoft/laravel-rebel-bridge-otpz
Package laravel-rebel-bridge-otpz
Short Description Bridge the benbjurstrom/otpz email one-time-password package into Laravel Rebel step-up. Exposes OTP email magic-code as a step-up driver (AAL2, AMR otp). Part of padosoft/laravel-rebel-*.
License MIT
Homepage https://github.com/padosoft/laravel-rebel-bridge-otpz
Informations about the package laravel-rebel-bridge-otpz
Official documentation: https://doc.laravel-rebel.padosoft.com
laravel-rebel-bridge-otpz
Step-up driver that bridges benbjurstrom/otpz (email one-time-passcode login) into the Laravel Rebel step-up framework. A user who has already logged in can re-confirm sensitive actions by entering a short code emailed to them — no password required, fully audited, offline-testable.
Glossary
| Term | Meaning |
|---|---|
| Step-up | A re-confirmation challenge issued to an already-authenticated user before a sensitive action (e.g. wire transfer, delete account). The user is not logged out; they just prove fresh intent. |
| OTP | One-Time Password — a short numeric or alphanumeric code that is valid exactly once and expires in minutes. |
| Magic code | The term otpz uses for its OTP: a 10-char uppercase code sent by email; hashed at rest; consumed on first successful use. |
| AAL | Authenticator Assurance Level (NIST SP 800-63B). AAL1 = one factor; AAL2 = two distinct factors; AAL3 = hardware key. |
| AMR | Authentication Methods Reference — a list of how the user proved identity, e.g. ['otp']. |
| Phishing-resistant | A method that cannot be replayed on a fake site in real time. Email OTP is NOT phishing-resistant (the attacker can forward it). Passkeys are. |
| DriverRegistry | Laravel Rebel step-up component that holds all registered step-up drivers. |
| AuditLogger | Core service that writes security events to rebel_auth_events (DB), never to session or log files. |
How it works
Why not use AttemptOtp::handle()?
The otpz package's AttemptOtp action validates an HTTP signed URL and the browser session ID.
Neither exists in a step-up flow (the challenge/response is managed by the Rebel layer, not HTTP).
This bridge therefore calls CreateOtp for issue and re-implements the same business rules
(status, expiry, attempt limits, Hash::check) in OtpzBrokerImpl::verify() — all in-process,
no HTTP coupling.
Requirements
| Dependency | Version |
|---|---|
| PHP | ^8.3 |
| Laravel | ^12.0 or ^13.0 |
| padosoft/laravel-rebel-core | ^0.1 |
| padosoft/laravel-rebel-step-up | ^0.1 |
| benbjurstrom/otpz | ^0.7 (optional — see install) |
Installation
1. Install this bridge
2. Install and configure otpz
3. Make your User model implement Otpable
4. Publish the bridge config (optional)
That's it. The OtpzStepUpDriver registers itself automatically on boot.
Configuration
File: config/rebel-bridge-otpz.php
| Key | Default | Effect |
|---|---|---|
drivers.otpz |
true |
Register the OtpzStepUpDriver into DriverRegistry. Set false to disable without uninstalling the bridge. |
options.max_attempts |
3 |
Maximum failed verification attempts before the OTP is invalidated (mirrors otpz's hard-coded value; for reference). |
The otpz package's own config (config/otpz.php) controls expiry time, rate-limit thresholds,
mailable class, and user resolver.
Usage examples
Example 1 — Trigger step-up before a destructive action
Example 2 — Verify the submitted code
Example 3 — Check availability before starting
Example 4 — Testing with FakeOtpzBroker (offline, no email)
Example 5 — Sanctum API (mobile/SPA)
The step-up driver is transport-agnostic: start() and verify() work identically over a
Sanctum-protected API endpoint. Store the returned reference (UUID) in the response body; the
mobile client submits it back with the code.
Audit events
All events are written to rebel_auth_events by the core AuditLogger. They are never stored
in the session or PHP log files.
| Event type | When emitted | Key fields |
|---|---|---|
stepup.otpz.started |
start() succeeds (OTP sent) |
channel=email, aal=aal2, amr=['otp'], subjectType, subjectId, purpose |
stepup.otpz.verified |
verify() returns true |
same |
stepup.otpz.failed |
verify() returns false (wrong/expired/consumed code, null reference, or broker error) |
same |
The OTP code itself is never present in any audit event.
Security notes
- Fail closed: any
\Throwableinstart()returnsnull; any\Throwableinverify()returnsfalse. The driver never propagates exceptions to the caller. - Single-use: once verified, the OTP record is marked
USEDand cannot be redeemed again. - Attempt limiting: after 3 failed attempts the record is marked
ATTEMPTED(invalidated). - Expiry: default 5 minutes (configurable in
config/otpz.php). - Hashed at rest: otpz uses Laravel's
hashedcast on thecodecolumn — the plaintext code is never persisted. - Reference is opaque: the step-up layer stores only the OTP UUID as its reference. Even if an attacker reads the reference from a compromised session, they still need to know the code.
Competitor card-battle
| Feature | laravel-rebel-bridge-otpz | Laravel Fortify | Laravel Sanctum | Shopify Multipass |
|---|---|---|---|---|
| Email OTP step-up (post-auth) | ✅ Native driver | ❌ No step-up concept | ❌ No step-up concept | ❌ Login-only, no step-up |
| AAL2 assurance declared | ✅ Aal::Aal2 |
❌ No assurance model | ❌ No assurance model | ❌ No assurance model |
| AMR metadata in audit | ✅ ['otp'] per event |
❌ | ❌ | ❌ |
| Offline-testable seam | ✅ FakeOtpzBroker |
❌ HTTP-coupled | ❌ | ❌ |
| Fail-closed on Throwable | ✅ Always false/null |
❌ Throws | ❌ | ❌ |
| Audit trail to DB | ✅ rebel_auth_events |
❌ No audit | ❌ No audit | ❌ No audit |
| Single-use + attempt limit | ✅ otpz enforces | ❌ | ❌ | ❌ |
| Code hashed at rest | ✅ otpz hashed cast |
❌ | ❌ | ❌ |
| PSD2/SCA compliance hooks | ✅ via core purpose binding | ❌ | ❌ | ❌ |
| No HTTP coupling in verify | ✅ Bypass AttemptOtp |
❌ | ❌ | ❌ |
🔋 Vibe coding with batteries included
This package ships with:
CLAUDE.md— AI-readable working guide: what it is, non-negotiable conventions, security rules, seam pattern, Definition of Done.AGENTS.md— Operational rules for AI agents and humans: branching, DoD, guardrails, security design-lock..claude/skills/rebel-package-dev/— Invocable skill for Claude Code agents: dev loop commands, PHPStan-max recipes, key design decisions, test patterns.FakeOtpzBroker— Deterministic in-memory broker for offline tests.- CI matrix — PHP 8.3/8.4/8.5 × Laravel 12/13 via GitHub Actions.
AI agent? Start with /rebel-package-dev and read CLAUDE.md. Your first composer test should
be green before you open a PR.
Contributing
PRs welcome. Follow AGENTS.md. All contributions must pass:
License
MIT — see LICENSE.
All versions of laravel-rebel-bridge-otpz with dependencies
illuminate/contracts Version ^12.0|^13.0
illuminate/support Version ^12.0|^13.0
padosoft/laravel-rebel-core Version ^0.1
padosoft/laravel-rebel-step-up Version ^0.1
spatie/laravel-package-tools Version ^1.92