Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel2Step Laravel Package

jeremykenedy/laravel2step

Laravel2step adds two-step verification to Laravel apps, providing middleware, routes, views, config, and database support for requiring an extra verification step after login. It helps secure user accounts with minimal setup and a customizable flow.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. laravel2step is a thin, opinionated second-factor layer: a middleware, a controller, a notification, a model trait, a migration, and a publishable config/views set. It slots into the standard Laravel HTTP kernel and auth flow rather than introducing its own guard or service provider lifecycle, so it composes cleanly with existing apps. v6.0.0 materially improves this fit by removing the dependency on the host application's App\Http\Controllers\Controller — the package no longer breaks when the skeleton's base controller changes shape, which was the root cause of it being unusable on Laravel 11+. That is the single most important structural change in this release: the package is now decoupled from application scaffolding conventions it never controlled.

Integration feasibility. High for greenfield or recently-upgraded apps on Laravel 12/13 and PHP 8.2+. The upgrade is a single composer require jeremykenedy/laravel2step:^6.0 because nothing on ^5.0 or below resolves to it automatically — the upgrade is opt-in and cannot be pulled in transitively, which is good for change control and bad for patching cadence (see Operational Impact). There is no reported schema migration for the hasOne → belongsTo fix, but that correction implies the relationship now reads userId on the two-step table; the actual column name and whether existing installs already match it needs verification before we promise a no-migration upgrade. Two integration surfaces are frozen at publish time: config and views. Published copies do not receive any of this release's fixes, so any app that has published either inherits the code changes but not the CSP, CDN, jQuery, or sender fixes.

Technical risk. Moderate, and concentrated in behaviour rather than compatibility:

  • Security-positive but observable. The middleware previously called $next($request) before checking verification, meaning protected controller actions executed in full for unverified users and the response was discarded. This release fixes the ordering. The risk to us is retrospective, not forward-looking: any side effects those actions performed — writes, audit entries, outgoing mail, third-party API calls, rate-limit consumption — were happening pre-verification in production. We should treat this as a potential past exposure and ask whether any protected route had state-changing side effects.
  • Subclass breakage. TwoStepController's base class changed to Illuminate\Routing\Controller. Any app that extended it and relied on inherited $this->authorize() or $this->validate() will fatal. This is a narrow but hard failure, and it will not surface in CI unless CI exercises the verification routes.
  • CSP/CDN. The Bootstrap default moved from maxcdn.bootstrapcdn.com (3.3.7) to cdnjs.cloudflare.com (3.4.1). A Content Security Policy whitelisting only the old host will render the verification page unstyled. Markup-compatible, so cosmetic only — the flow still works, which is the right failure mode for a security interstitial.
  • Database connection. The hardcoded mysql connection now falls back to the app default. Multi-connection apps whose two-step table lives on mysql need LARAVEL_2STEP_DATABASE_CONNECTION=mysql set explicitly or they get table-not-found errors at the worst possible moment.
  • Stale jQuery in published views. Bundled jQuery moved 3.2.1 → 3.7.1. Apps that published views keep 3.2.1, which carries known XSS advisories. This is the one item in the release notes that is a standing security liability rather than an inconvenience, and it is invisible to anyone who doesn't diff published assets.
  • Unvalidated support matrix. Composer still permits Laravel 6–11 and PHP 7.3, but CI covers only PHP 8.2–8.5 against Laravel 12/13. The stated reason — that those framework versions now carry published advisories so Composer refuses to install them on a clean machine — is honest and reasonable, but the practical effect is that the declared matrix overstates support.

Key questions.

  1. Which Laravel and PHP versions are actually in our fleet, and how many apps sit on 11 or below (i.e. outside CI coverage and advised to stay on v5)?
  2. Does any of our code extend TwoStepController, and did any protected route perform writes, logging, notifications, or outbound calls before the verification redirect?
  3. Have we published the config or views in any app? If so, who owns reconciling them against the 6.0 defaults, and are those views carrying jQuery 3.2.1?
  4. Do we have a CSP that whitelists maxcdn.bootstrapcdn.com, and is the two-step table on a non-default DB connection anywhere?
  5. Is this package still the right build-vs-buy call given Laravel's own starter kits and Fortify ship first-party two-factor support? v6.0.0 is a maintenance rescue, not a feature investment.
  6. What is the mail-from configuration in each consuming app, and do we rely on the package's sender or the app's MAIL_FROM_ADDRESS?

Integration Approach

Stack fit. Best fit is Laravel 12 or 13 on PHP 8.2–8.5 — the only combination with real CI coverage. Laravel 11 is a judgement call: the release notes explicitly say to test it yourself or stay on v5. Laravel 10 and below is effectively unsupported despite the permissive constraint, because those framework versions cannot be installed cleanly enough to test against. Treat the composer constraint as a compatibility floor, not a support statement.

Migration path. Because 6.0.0 is opt-in and does not auto-resolve, the safe pattern is: pin ^5.0 on the current branch, cut an upgrade branch, composer require jeremykenedy/laravel2step:^6.0, then run the regression checklist below before merging. No data migration is described, but confirm the relationship column change against the existing schema on a restored production-shaped database rather than a fresh one.

Compatibility checklist before merge:

  • Grep for extends TwoStepController and any use of authorize()/validate() inside it.
  • Diff published config/laravel2step.php and resources/views/vendor/laravel2step against 6.0 defaults; decide per-file whether to re-publish, hand-merge, or consciously accept the old version. The jQuery bump and CDN change both only reach us through this step.
  • Confirm CSP style-src allows cdnjs.cloudflare.com, or pin LARAVEL_2STEP_BOOTSTRAP_CSS_CDN to whatever the policy already allows.
  • Confirm LARAVEL_2STEP_DATABASE_CONNECTION is either correct or unnecessary.
  • Confirm whether LARAVEL_2STEP_EMAIL_FROM is set. If not, the package now defers to application mail config — verify the resulting sender passes SPF/DKIM for the domain we send from.
  • Note that LARAVEL_2STEP_EMAIL_SUBJECT is documented as removed; it was never a real key, so if it appears in our .env files it was always dead and can be deleted.

Sequencing. (1) Inventory consuming apps and their Laravel/PHP versions. (2) Audit for TwoStepController subclasses and pre-verification side effects — this is the part with retrospective risk. (3) Reconcile published config and views. (4) Upgrade the lowest-risk app first, on a branch, with an end-to-end manual pass of enrol → verify → session → logout. (5) Explicitly test that an unverified user hitting a protected route is now blocked and that the blocked action left no trace. (6) Roll out app by app. Do not batch this with other dependency work; the middleware ordering change deserves its own blast radius.

Operational Impact

Maintenance. This is the release that makes the package maintainable rather than merely working: 81 Pest tests on in-memory SQLite, GitHub Actions replacing a dead Travis config, Dependabot scoped to actions, a working Scrutinizer config, Pint, composer audit, and a weekly scheduled run so a new Laravel release surfaces as a red build rather than a production incident. For a TPM that weekly cron is the most valuable line in the changelog — it converts "will this break silently?" into a monitored signal. The counterweight is bus factor: the release is three PRs from a single maintainer, and the project's health depends on that individual's continued attention. Worth a periodic check that the scheduled runs are still green, not just that the package is installed.

Support. Genuinely bifurcated. Laravel 12/13 is tested and supported. Laravel 11 and below is explicitly "test it yourself or stay on v5," which is honest but means a meaningful share of the installed base has no supported upgrade path to the security fixes in this release — most notably the middleware ordering fix. If we have apps on 11 or below, the correct move is to schedule the framework upgrade first and take the package upgrade second, not to run v5 indefinitely.

Scaling. The per-request cost is a middleware check plus a relationship load against the two-step table. Fine for typical authenticated-app volumes; the release gives no indication of caching, and none was promised. The test suite running on in-memory SQLite means query behaviour under production MySQL/Postgres — indexes, connection handling, the new default-connection fallback — is not exercised by CI. Low risk given the query is a simple keyed lookup, but it is untested rather than proven.

Failure modes and their blast radius:

  • Unstyled verification page (CSP blocks the new CDN) — cosmetic, flow still completes. Recoverable by setting the env override; no user is locked out.
  • Table-not-found on the wrong connection — hard failure at verification time. Highest-severity configuration failure in this release, and the one most likely to be missed because the default changed silently.
  • Fatal in a TwoStepController subclass — hard failure confined to apps that extended it; surfaces immediately on deploy, so it fails loudly rather than subtly.
  • Mail sender misconfiguration — the old TypeError crash is fixed, but the new fallback to app mail config can produce a sender that fails SPF/DKIM, so verification emails land in spam. This changes a loud failure into a quiet one, which is the harder operational mode.
  • Stale published views with jQuery 3.2.1 — silent, persistent, and security-relevant, because nothing in the upgrade path touches published assets.
  • Pre-verification side effects in the past — not a forward failure mode, but the one item that may require a security conversation rather than an engineering ticket.

Ramp-up. Low. One config file, one trait, one middleware registration, a documented env surface, and .env examples that now accurately describe which values are optional. The cognitive load is not in learning the package; it is in the frozen-asset problem — understanding that publishing config or views opts you out of future fixes. That needs to be stated in our internal runbook, because it is not obvious from the README and it silently applies to every future release, not just this one.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel