Download the PHP package padosoft/laravel-rebel-step-up without Composer
On this page you can find all versions of the php package padosoft/laravel-rebel-step-up. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download padosoft/laravel-rebel-step-up
More information about padosoft/laravel-rebel-step-up
Files in padosoft/laravel-rebel-step-up
Package laravel-rebel-step-up
Short Description Step-up authentication per Laravel Rebel: conferma di un'azione/purpose con assurance AAL/AMR, risk-based, e PSD2/SCA dynamic linking. Parte di padosoft/laravel-rebel-*.
License MIT
Homepage https://github.com/padosoft/laravel-rebel-step-up
Informations about the package laravel-rebel-step-up
Laravel Rebel — Step-Up
Ask for a strong re-confirmation only when it truly matters. The user is already logged in, but is about to perform a sensitive action (change their email, download an invoice, confirm a credit order): Rebel Step-Up asks them for a targeted second factor (email OTP, passkey, TOTP…), with the AAL/AMR security level chosen for that action and — for payments — PSD2/SCA dynamic linking (the confirmation is bound to amount+payee). It is part of the
padosoft/laravel-rebel-*suite.
Table of contents
- What it is (and what it is NOT)
- Quick glossary (read it, it takes 1 minute)
- Why Rebel Step-Up — the moats
- Rebel Step-Up vs the "do-it-yourself"
- How it works (the flow, step by step)
- Installation (junior-proof)
- Configuration (every option)
- Usage examples
- 1. Protect a route with the middleware
- 2. Manual control (without middleware)
- 3. Payment with PSD2/SCA dynamic linking
- 4. Start and confirm a challenge (API/mobile)
- 5. Choose the driver (passkey-first, OTP fallback)
- 6. Bind the confirmation to the device
- Validating the config in CI
.env.example- Security (what it guarantees you)
- Testing & License
What it is (and what it is NOT)
It is the "control plane" that decides when an already-authenticated user must re-prove who they are before a sensitive action, with what strength (assurance), and binding that confirmation to the specific action (for payments: to amount and payee). You declare a policy for each purpose (action) and Rebel enforces the rule — via middleware or via API.
It is NOT:
- a login system (to sign in there is
laravel-rebel-email-otp, or Fortify vialaravel-rebel-bridge-fortify); step-up assumes a user already logged in; - a standalone OTP generator: for email OTP it uses the engine of
laravel-rebel-email-otp; for passkey/TOTP it uses the drivers oflaravel-rebel-bridge-fortify. Step-Up orchestrates them, it does not reimplement them.
It depends on padosoft/laravel-rebel-core (assurance, contracts, keyed hashing) and on padosoft/laravel-rebel-email-otp (default OTP driver). For the big picture of the ecosystem, start from the core README.
Quick glossary (read it, it takes 1 minute)
| Term | In plain words |
|---|---|
| Step-up | "You're already in, but for THIS thing I'm asking you for one more proof." |
| Purpose | The name of the protected action, e.g. change-email, download-invoice, checkout-credit-order. You associate a rule with each purpose. |
| AAL (Authenticator Assurance Level) | How "strong" the proof is, per the NIST standard. aal1 = one factor (e.g. email OTP); aal2 = two factors / more robust. |
| AMR | Authentication Methods References: the list of methods used, e.g. ['otp','email'], ['webauthn']. |
| Phishing-resistant | A proof that phishing cannot steal: typically passkey/FIDO2. An email OTP is not. |
| Driver | The "way" the proof is performed: email_otp, fortify_passkey_confirm, fortify_totp… Each one declares its own assurance. |
| Binding / Dynamic linking | The confirmation is glued to the details of the operation (amount, currency, payee, order). If they change, the confirmation lapses: this is mandated by the European PSD2/SCA for payments. |
| Challenge | The open step-up "case": it has an id, an expiry, attempts, a status. |
| Confirmation window (TTL) | How long a confirmation stays valid after success (then it must be redone). |
Why Rebel Step-Up — the moats
| ★ | What | In short |
|---|---|---|
| ★★★ | Per-purpose policy | Decide for each action the required level, the allowed drivers, the TTL. No ifs scattered through the code. |
| ★★★ | Assurance enforcement | A driver below the threshold is rejected upfront. And if you raise the policy, the older, weaker confirmations lapse immediately. |
| ★★★ | PSD2/SCA dynamic linking | Confirmation bound to amount+payee with a keyed hash; anti-injection canonicalization (no collisions from separators). |
| ★★ | Pluggable drivers | Email OTP included; passkey/TOTP via bridge-fortify; your own custom drivers by implementing an interface. |
| ★★ | Atomic & anti-replay | Verification in a transaction with lockForUpdate, single-use, max attempts, expiry. |
| ★★ | Device binding | The confirmation can be bound to the device: no cross-device reuse. |
| ★★ | Multi-tenant & audit | Everything is scoped per tenant; every step (StepUpRequired/Verified/Failed) is audited. |
| ★ | Config validated in CI | php artisan rebel:validate-config blocks insecure configurations before deploy. |
Rebel Step-Up vs the "do-it-yourself"
| Rebel Step-Up | Shopify | Laravel's password.confirm middleware |
Fortify-native password confirmation | Hand-rolled "re-enter password" | |
|---|---|---|---|---|---|
| Configurable strength per action (AAL/AMR) | ✅ | ❌ | ❌ (password only) | ❌ (password only) | ❌ |
| Passkey / TOTP / email OTP interchangeable | ✅ | ❌ | ❌ | ❌ | ❌ |
| PSD2/SCA dynamic linking (amount+payee) | ✅ | ❌ | ❌ | ❌ | ❌ |
| Confirmation that lapses if the amount changes | ✅ | ❌ | ❌ | ❌ | ❌ |
| Device binding | ✅ | ➖ | ❌ | ❌ | ❌ |
| Per-purpose, multiple protected actions | ✅ | ❌ | ➖ (single global window) | ➖ (single global window) | ❌ |
| Multi-tenant + audit trail | ✅ | ❌ | ❌ | ❌ | ❌ |
| Config validation in CI | ✅ | ❌ | ❌ | ❌ | ❌ |
Legend: ✅ built-in · ➖ partial / hosted-only / not exposed to you · ❌ not available. Note on Shopify: it is a hosted, closed commerce platform you can neither self-host nor extend — it exposes none of these step-up primitives to your own Laravel app, so it's a black box you don't control.
How it works (the flow, step by step)
What happens if…
- the user gets the code wrong too many times → the challenge goes to
failed(max attempts configurable); - the amount changes between
startandconfirm→binding_mismatch, you start over (the SCA mandates it); - you raise the policy from
aal1toaal2after a confirmation → the oldaal1confirmation no longer counts; - the factor provider goes down during
start→ the challenge is cancelled (no orphan "pending" entries).
Installation (junior-proof)
Prerequisites: Laravel 12 or 13, PHP 8.3+, with
padosoft/laravel-rebel-coreandpadosoft/laravel-rebel-email-otpalready installed (they are pulled in as dependencies).
1) Require the package
2) Publish config and migration
3) Configure the pepper (if you haven't already done so for the core)
Step-up uses the core's keyed hashing for the SCA binding. In your .env:
4) Define your protected actions in config/rebel-step-up.php (see below) and protect a route:
Done: the route now requires a step-up for the change-email purpose.
Configuration (every option)
File config/rebel-step-up.php. Global keys:
| Key | Default | What it does | When to change it |
|---|---|---|---|
default_ttl_seconds |
600 |
Default duration of the confirmation window (how long a successful confirmation stays valid). | Very sensitive actions → lower it (e.g. 120). |
challenge_ttl_seconds |
300 |
Expiry of the single challenge (how long you have to enter the code). | Align it with the channel's OTP duration. |
max_attempts |
5 |
Wrong attempts before marking the challenge failed. |
Stricter → lower it to 3. |
redirect_route |
null |
For web (non-JSON) requests: the route name of the confirmation page. null ⇒ abort(423). |
Set your own challenge route. |
purposes |
see below | Your protected actions and their respective rules. | Always: this is where you declare what to protect. |
Each purposes entry accepts:
| Purpose key | Default | What it does |
|---|---|---|
required_assurance |
aal1 |
Minimum required AAL level (aal1 / aal2). |
require_phishing_resistant |
false |
If true, allows only phishing-resistant drivers (e.g. passkey). |
reject_restricted |
false |
If true, rejects NIST "restricted" authenticators (e.g. SMS). |
drivers |
['email_otp'] |
Allowed drivers, in order of preference. The first available and eligible one is chosen. |
ttl_seconds |
default_ttl_seconds |
Override of the confirmation window for THIS purpose. |
always_require |
true |
Reserved for the risk-based hook (coming soon): today step-up is always required. Setting false does not yet skip verification — it will once the risk evaluator is wired up. |
sca.dynamic_linking |
false |
If true, enables binding to amount+payee (for payments). |
Example:
⚠️ If a purpose requires
aal2+require_phishing_resistantbut lists onlyemail_otp(which isaal1, not phishing-resistant), the config is insecure:rebel:validate-configfails in CI before deploy (see below).
Usage examples
1. Protect a route with the middleware
-
JSON / API request without a valid confirmation →
423 Locked: - Web request without a confirmation → redirect to
redirect_route(if set) orabort(423).
2. Manual control (without middleware)
When you want to handle the flow yourself in a controller:
3. Payment with PSD2/SCA dynamic linking
The confirmation is bound to amount+currency+payee+order. If the user confirms €100 and then someone tries to push the order through at €999, the confirmation does not count.
4. Start and confirm a challenge (API/mobile)
A two-endpoint pattern, perfect for mobile apps (Sanctum tokens):
5. Choose the driver (passkey-first, OTP fallback)
The policy lists the drivers in order of preference; you can also force one:
6. Bind the confirmation to the device
Pass a deviceId (e.g. derived from the Sanctum token or from hash(ip|user-agent)): the confirmation will count only for that device.
A confirmation made on device A does not unlock the action on device B.
Validating the config in CI
Step-up extends the core's command:
It exits with a code ≠ 0 if a purpose is configured insecurely, for example:
- it requires an assurance that none of the listed drivers can reach;
- it demands
phishing_resistantbut lists only non-phishing-resistant drivers; - it points to an unregistered driver.
Put it in your CI pipeline so you don't ship rules to production that can't be satisfied:
.env.example
The package commits an .env.example with all the variables used. The essential ones:
Security (what it guarantees you)
- Atomic & single-use verification:
confirmruns in a transaction withlockForUpdate; two concurrent confirmations don't both pass. - Assurance enforcement against the CURRENT policy: a successful confirmation saves the achieved assurance; if the policy is raised, the "old", weaker confirmation lapses.
- PSD2/SCA dynamic linking: keyed
HMACbinding (withkey_versionfor rotation) over amount+currency+payee+order; anti-injection JSON canonicalization (no collisions from separators in the fields). - Symmetric device binding: a context without a device ⇒ only deviceless confirmations; with a device ⇒ only that device. No cross reuse.
- Tenant isolation: every query is scoped per tenant (null-safe).
- Fail-closed: missing/corrupted assurance data ⇒ the confirmation is not valid; an invalid amount (NaN/∞/negative) ⇒ an immediate exception.
- Audit:
StepUpRequired,StepUpVerified,StepUpFailedrecorded via the core'sAuditLogger.
🔋 Vibe coding with batteries included
This package ships AI batteries — so you (and your AI agent) can extend it correctly on the first try:
CLAUDE.md— a concise AI working guide (purpose, conventions, architecture, how to extend, Definition of Done). Plain Markdown, so Claude Code, Cursor, Copilot and Codex all read it.AGENTS.md— the agent/workflow contract (branch → PR → CI → tag/release, the gates)..claude/skills/— invocable skills (at leastrebel-package-dev) encoding the suite's TDD loop, the PHPStan-level-max recipes, the security/telemetry rules, and the release discipline.
Open the repo in your AI editor and just start — the rules, guardrails and extension recipes come
with it. PRs that follow the shipped CLAUDE.md pass CI (PHPStan max + Pest + Pint) and review the
first time around.
Testing & License
The suite covers: start/confirm, wrong code + max attempts, no eligible driver, dynamic linking (amount change, separator collision), TTL expiry, policy raising, device binding, cancellation on driver crash, middleware 423→OK, config validation, and the real integration with the email_otp driver.
License: MIT — see padosoft/laravel-rebel suite.
All versions of laravel-rebel-step-up 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-email-otp Version ^0.1
spatie/laravel-package-tools Version ^1.92