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

Two Fa Laravel Package

kind-work/two-fa

A Laravel package for adding two-factor authentication to your applications. It helps secure user accounts with an additional verification step and integrates into existing auth flows, giving teams a simple way to strengthen login protection.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package

    composer require kind-work/two-fa
    

    Note the current line is 2.0.0-beta.2 — pin the version explicitly ("kind-work/two-fa": "2.0.0-beta.2") until v2 is stable.

  2. Publish config and migrations

    php artisan vendor:publish --provider="KindWork\TwoFa\TwoFaServiceProvider"
    php artisan migrate
    

    The publish step drops a config/two-fa.php (issuer name, digits, time window, recovery-code count) and the columns needed to store the TOTP secret and recovery codes on your users table. Read the config file first — it is the fastest way to understand the package's feature surface.

  3. Opt the User model in Add the package trait (e.g. HasTwoFactorAuthentication) to App\Models\User. This gives you helpers such as enableTwoFactorAuth(), confirmTwoFactorAuth($code), disableTwoFactorAuth(), and the recovery-code APIs.

  4. First use case: enrolment The canonical first flow is user enables 2FA:

    • Generate a secret for the user.
    • Render the QR code / provisioning URI so they can scan it with an authenticator app.
    • Ask for a 6-digit code and confirm it before persisting "2FA is on".
    • Show the recovery codes exactly once and prompt the user to store them.

    Don't start with the login challenge; start with enrolment. It's the part with the most UX and persistence decisions, and it validates that your config, migration, and trait wiring are correct.

  5. Where to look first

    • README.md for the exact trait/facade/middleware names (they changed across v1 → v2).
    • config/two-fa.php after publishing.
    • The package's migration to confirm column names before writing any code against the model.

Implementation Patterns

Enrolment as a two-step state machine. Store the secret but keep 2FA "inactive" until the user confirms a generated code. This prevents locking users out with a secret they never actually scanned. Route: POST /two-fa/enable → render QR → POST /two-fa/confirm → flip active flag and emit recovery codes.

Login challenge as middleware or a dedicated route. Two common patterns:

  • Middleware: the package ships middleware you append to your web group that redirects authenticated-but-unverified users to a challenge screen.
  • Explicit route: after Auth::attempt(), check $user->hasTwoFactorAuthEnabled() and redirect to a /two-fa/challenge GET/POST pair; store a short-lived session flag on success.

Pick one and be consistent — mixing both is a common source of bypass bugs.

Recovery codes. Generate them server-side, hash them at rest if the package supports it (or store encrypted), show them once, and treat each as single-use. Implement a "regenerate recovery codes" endpoint behind password confirmation — users lose these constantly.

Enforcing 2FA by policy. Rather than bolting this onto every controller, wrap it in a middleware group or a RequireTwoFactor middleware applied per-route-group. For admin panels, enforce it globally and allow a grace period via a two_fa_grace_until timestamp.

Localization. As of 2.0.0-beta.2 the package ships a German translation (PR #37). Publish translations with the vendor publish tag and you'll get lang/vendor/two-fa/de/…. To add another locale, copy the en files into lang/vendor/two-fa/{locale}/ and translate the keys — no package fork required. Set config('two-fa.locale') (or rely on app()->getLocale()) and keep your challenge/enrolment views using __('two-fa::…') so the package strings resolve correctly.

Blade/Inertia integration. The QR code is typically rendered as an inline SVG or data URI from the provisioning URI. If you're on Inertia/Livewire, return the provisioning URI + QR SVG from your controller and let the front end render it; don't rely on package views if you have a JS UI.

Testing. Fake time with Carbon::setTestNow() when asserting TOTP codes — codes are time-windowed. Build a small test helper that generates a valid code for a given user so feature tests for the login challenge stay deterministic.

Gotchas and Tips

  • This is a beta. 2.0.0-beta.2 is a moving target; the only change in this release is a German translation, but the v2 line as a whole may still shift trait/middleware names. Pin the exact version and read the changelog before bumping.
  • Don't store the TOTP secret in plaintext if you can avoid it. Use the package's cast/encryption support, or add an encrypted cast on the secret column yourself. A DB leak with plaintext TOTP secrets is a full account-takeover vector.
  • Clock drift. Default windows are usually ±1 step (30s). If your infrastructure has skewed clocks or users complain about "invalid code", widen the window in config/two-fa.php before assuming a bug in your flow.
  • Config cache blindness. After editing config/two-fa.php in production, run php artisan config:cache. Forgetting this is the #1 reason issuer/name changes "don't take effect."
  • Migrations on existing users. Adding 2FA columns to a large users table can lock it. Add nullable columns in a separate migration and backfill nothing (null = disabled), rather than adding non-null defaults.
  • Recovery code reuse and casing. Normalize input (strip spaces/dashes, uppercase) before comparing; otherwise users will report valid codes as broken. Enforce single-use atomically (transaction or whereNull('used_at') update) to avoid race conditions.
  • Session invalidation. After a successful 2FA challenge, regenerate the session ID (session()->regenerate()) to prevent fixation. The package won't do this for you.
  • Translations are additive and safe. The German translation in this release is non-breaking. If you've published and customized lang/vendor/two-fa/en/…, re-publish or diff after upgrading — a new release may add keys your local copies don't have.
  • Extension points. Customize the QR rendering, the challenge view, and the recovery-code generator through the published config/views first; only reach for binding replacements in a service provider if the config doesn't expose what you need. Overriding package classes is the fastest way to break on the next beta.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib