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

Getting Started

Install the v6 line explicitly — nothing on ^5.0 or below will pull it in:

composer require jeremykenedy/laravel2step:^6.0

Then publish and migrate:

php artisan vendor:publish --provider="Jeremykenedy\Laravel2step\Laravel2stepServiceProvider"
php artisan migrate

Where to look first, in order:

  1. config/laravel2step.php — the authoritative list of knobs after publishing. Everything the README calls "default" only applies if you didn't publish this file.
  2. The translation file — the verification email subject comes from here, not from any config key (see Gotchas).
  3. README on GitHub, not in vendor/ — v6 stopped shipping tests/, CI and tooling in the composer archive, so the packaged copy is thinner than the repo.
  4. The repo's Pest suite (81 tests, in-memory SQLite) — the fastest way to see the intended wiring of middleware, routes and the TwoStepAuth relation, now that it isn't installed alongside the code.

First use case — protect a route group. Add the package's trait to your User model, then attach the middleware to the authenticated group you want gated. A verified session reaches the controller; an unverified one is redirected to the verification screen, receives a code by email, and posts it back. Check the README for the exact middleware alias and route names in v6 rather than copying an alias from a v5-era tutorial — the internals changed even though the public surface largely didn't.

The single most important thing to know before you start: on v6 the middleware blocks before the controller runs. That is the headline bug fix, and it changes what you can rely on (see Gotchas).

Implementation Patterns

Gate the group, not individual actions. Attach the middleware after auth on the route group that represents "logged in and verified". Scattering it per-controller invites the classic mistake of protecting the dashboard but forgetting the settings page.

Trait on the model, relation for reads. TwoStepAuth::user() is now a belongsTo keyed on userId, so $user->twoStepAuth resolves correctly. Use it for display/state checks; don't try to reassign the relation to model the verification state yourself.

Publish views when you need branding or CSP control. Published copies are untouched by upgrades — good for stability, bad for drift. If you publish, diff your copy against the repo's views on each major bump.

CSP-constrained apps should pin the CDN explicitly. Set LARAVEL_2STEP_BOOTSTRAP_CSS_CDN to whatever host your policy already whitelists. Otherwise v6 silently moves you to cdnjs.cloudflare.com (Bootstrap 3.4.1), and your verification page renders as unstyled HTML with a console full of CSP violations — an easy thing to miss because it only affects users mid-verification.

Queue the verification email. LARAVEL_2STEP_EMAIL_QUEUE is documented for the first time in v6. On anything with meaningful login volume, sending synchronously inside the request is the difference between a snappy redirect and a timeout when the mail provider is slow.

Non-HTML entry points need their own handling. The middleware redirects; an API client or JSON consumer gets a 302 to an HTML page. Either keep the middleware off API route groups entirely, or branch on $request->expectsJson() in your own wrapper.

Testing. The shipped suite runs against in-memory SQLite, which is now viable because the DB connection falls back to your app default instead of hardcoding mysql. For your own tests, withoutMiddleware() around the verification middleware keeps auth feature tests fast, and a dedicated test for "unverified user hits protected route → redirect, no side effects" is worth writing once — it's precisely the behaviour that changed.

Deployment checklist for the upgrade. Bump the constraint, then: (1) diff published config against the new default, (2) re-check your CSP against the new CDN host, (3) verify DB_CONNECTION vs. where the package table actually lives, (4) grep for any class extending TwoStepController.

Gotchas and Tips

Protected actions no longer execute for unverified users. This is the point of the release, but if anything relied on the old ordering — an activity log that recorded the attempt, a side effect that fired before the redirect — that behaviour is gone. Audit anything that assumed "the controller ran, then we redirected".

Extending TwoStepController is now a trap. It extends Illuminate\Routing\Controller instead of your app's base controller, so $this->authorize(), $this->validate() and any other helpers you inherited from App\Http\Controllers\Controller are missing. If you subclassed it, either move those calls out or compose rather than extend.

The DB connection default changed. Previously hardcoded to mysql; now it follows your application default. If your two-step table lives on a connection literally named mysql while DB_CONNECTION is pgsql or sqlite, you now read the wrong database — set LARAVEL_2STEP_DATABASE_CONNECTION=mysql explicitly.

Set a sender address anyway. v6 no longer throws a TypeError when LARAVEL_2STEP_EMAIL_FROM and MAIL_FROM_ADDRESS are both unset — it defers to your mail config. That's a fix, but "no crash" isn't "correct sender"; configure one deliberately or your verification mail lands in spam.

LARAVEL_2STEP_EMAIL_SUBJECT never existed. It was documentation noise and has been removed. If you set it and your subject never changed, that's why. Edit the translation file.

Remove any user() workaround. The old hasOne looked for a users.two_step_auth_id column that doesn't exist. If you added a column, an accessor, or a custom relation to paper over it, delete it before it shadows the real one.

Laravel ≤ 11 is constrained but untested. Composer still allows Laravel 6–11 and PHP 7.3; CI covers only PHP 8.2–8.5 on Laravel 12 and 13. Worse, composer now refuses to install those older framework releases at all because of published security advisories, so you can't even stand up a clean environment to test against. On Laravel 11 or below: test thoroughly yourself, or stay on v5.0.0.

Published views keep jQuery 3.2.1. The bump to 3.7.1 only affects the bundled view. Not urgent, but if your published copy is years old you're carrying known-vulnerable JS on a page every user must pass through.

Debugging tip. With the check now running before $next($request), a breakpoint or dd() in your protected controller will never be hit for an unverified user. If you're debugging "my controller isn't running", confirm verification state first — the redirect is correct, not a routing bug.

Upgrade hygiene. Because tests/ and CI files are no longer in the archive, vendor/ gives you no reference implementation and no local reproduction harness. Clone the repo when you need to trace behaviour; don't try to read intent out of the installed copy.

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.
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
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus