laravel-rebel-step-up
GitHub repository · Composer: padosoft/laravel-rebel-step-up · MIT
Confirm the action, not just the session. Before a sensitive operation runs, step-up asks the user to re-prove themselves at an assurance level that matches the risk — and for payments it binds the confirmation to the exact amount and payee.
Step-up is not the login. It is the per-action confirmation layered on top of an existing session — “you are signed in, but this transfer needs a stronger check right now”.
What it is
laravel-rebel-step-up gates a sensitive action behind a fresh, risk-appropriate confirmation. Each action has a purpose and a policy that says what assurance (AAL/AMR) it requires; the package starts a challenge, lets a driver satisfy it, and only then allows the action to proceed. For payments it adds PSD2/SCA dynamic linking: the confirmation is bound to the transaction details, so if the amount or payee changes, the confirmation no longer applies.
The problem it solves
A logged-in session is not enough authority to move money or change security settings. Most apps either over-ask (re-prompt for everything) or under-protect (trust the session for everything). Step-up gives you a precise middle ground: a per-purpose policy decides when to escalate and how strong the confirmation must be, driven by risk. And because the confirmation is dynamically linked to the transaction, a confirmed €10 cart can’t be silently turned into a €1,000 transfer.
What you get
- Per-action / per-purpose confirmation governed by a policy, not a global flag.
- AAL/AMR assurance enforcement — the action states the level it needs, the challenge proves it.
- Risk-based escalation: step up only when the risk warrants it.
- PSD2/SCA dynamic linking via
TransactionContext— confirmation bound to amount + payee; if the total changes, it expires. - Pluggable drivers (
StepUpDriver) — the bundled email-OTP driver, plus the bridge packages (fortify, passkeys, totp, otpz) as additional drivers. - Route protection through the
EnsureStepUpmiddleware.
When to use it
- You have sensitive actions — payments, transfers, security-setting changes — that need more than a valid session.
- You must satisfy PSD2/SCA with dynamic linking to the transaction.
- You want confirmation strength to scale with risk instead of prompting for everything.
- You need to choose the confirmation factor per app via swappable drivers.
Worked example
composer require padosoft/laravel-rebel-step-up
php artisan vendor:publish
php artisan migrate
Protect a sensitive route with the bundled EnsureStepUp middleware and let the per-purpose policy decide the required assurance:
// routes/web.php
Route::post('/transfers', [TransferController::class, 'store'])
->middleware(EnsureStepUp::class);
For payments, capture the transaction in a TransactionContext so the confirmation is dynamically linked: change the amount or payee and the prior confirmation no longer satisfies the challenge.
How it fits
Step-up builds on padosoft/laravel-rebel-core (the AAL/AMR assurance model and the audit trail) and on padosoft/laravel-rebel-email-otp, whose driver ships as the default confirmation factor. The bridge packages (fortify, passkeys, totp, otpz) plug in as additional StepUpDrivers, so you can confirm with whatever factor fits the action’s required assurance. Every challenge outcome flows through the core audit trail.
A policy-driven, dynamically linked step-up beats a hand-rolled re-auth prompt — see Why Rebel.
Reference
Runtime files
src\Config\StepUpConfigValidator.phpsrc\Contracts\HasStepUpEmail.phpsrc\Contracts\StepUpDriver.phpsrc\Drivers\EmailOtpStepUpDriver.phpsrc\Enums\StepUpStatus.phpsrc\Exceptions\NoAvailableDriverException.phpsrc\Http\Middleware\EnsureStepUp.phpsrc\Models\StepUpChallenge.phpsrc\Results\StepUpResult.phpsrc\Results\StepUpStartResult.phpsrc\Sca\TransactionContext.phpsrc\Testing\FakeStepUpDriver.phpsrc\DriverRegistry.phpsrc\PolicyRepository.phpsrc\PurposePolicy.phpsrc\RebelStepUp.phpsrc\RebelStepUpServiceProvider.phpsrc\StepUpContext.php
Service providers
src\RebelStepUpServiceProvider.php
Services and managers
src\DriverRegistry.phpsrc\PolicyRepository.phpsrc\RebelStepUpServiceProvider.php
Contracts
src\Contracts\HasStepUpEmail.phpsrc\Contracts\StepUpDriver.php
Controllers
None detected in the package tree.
Middleware
src\Http\Middleware\EnsureStepUp.php
Models
src\Models\StepUpChallenge.php
Config
config\rebel-step-up.php
Migrations
database\migrations\create_rebel_step_up_challenges_table.php
Routes
None detected in the package tree.
Commands
None detected in the package tree.
Composer requirements
| Dependency | Constraint |
|---|---|
illuminate/contracts |
`^12.0 |
illuminate/support |
`^12.0 |
padosoft/laravel-rebel-core |
^0.1 |
padosoft/laravel-rebel-email-otp |
^0.1 |
php |
^8.3 |
spatie/laravel-package-tools |
^1.92 |
Development requirements
| Dependency | Constraint |
|---|---|
larastan/larastan |
^3.0 |
laravel/pint |
^1.18 |
orchestra/testbench |
`^10.0 |
pestphp/pest |
^4.0 |
pestphp/pest-plugin-laravel |
^4.0 |
ADR
Problem: keep laravel-rebel-step-up replaceable
Decision: document its public responsibility and use Rebel core contracts at integration boundaries.
Consequences: applications can adopt the package without coupling every other Rebel module to its internals.
Problem: package-specific behavior must remain auditable
Decision: all security-significant outcomes should emit or feed audit events through the core vocabulary.
Consequences: admin API, admin UI and AI guard can reason across packages without bespoke parsers for every provider.
Test & verification surface
tests\Feature\StepUpConfigValidatorTest.phptests\Feature\StepUpEmailDriverTest.phptests\Feature\StepUpManagerTest.phptests\Feature\StepUpMiddlewareTest.phptests\Pest.phptests\TestCase.php
Do not copy internal test-only classes into an application. Treat file lists as a source map for maintainers and auditors, not as an installation recipe by themselves.