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

Google2Fa Laravel Package

pragmarx/google2fa

Google2FA is a PHP implementation of Google Two-Factor Authentication, supporting HOTP (RFC 4226) and TOTP (RFC 6238). It lets you generate secret keys, QR codes, and verify one-time passwords from Google Authenticator and similar apps, with an optional Laravel bridge.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • Single-purpose cryptography library: TOTP (RFC 6238) and HOTP (RFC 4226) generation and verification. It is deliberately framework-agnostic (new Google2FA()), which means it slots into a Laravel service container cleanly as a singleton binding with zero coupling to Eloquent, HTTP, or session state.
  • The package owns no persistence, no routes, no middleware, and no migrations. All schema (secret column, replay timestamp, recovery codes, enrollment state) is ours to design. This is the right shape for a TPM-scoped integration — small surface, clear ownership boundary — but it also means the "product" (an actual 2FA feature) is roughly 20% library and 80% our implementation.
  • It does not generate QR codes. Enrollment requires a companion (bacon/bacon-qr-code, google2fa-qrcode, or endroid/simple-qrcode). That is a second dependency with its own maintenance posture and its own failure modes (Imagick/GD availability, renderer API changes between majors).
  • A first-party Laravel bridge (google2fa-laravel) exists and provides config, facade, middleware, and a basic enrollment controller. For a Laravel stack this is the higher-leverage integration point, but it adds a second package to track and is maintained by the same single author.

Integration feasibility

  • Effort is low: composer require pragmarx/google2fa plus a QR backend, a nullable encrypted column, a verify endpoint, and middleware. Realistically 3–8 engineering days for a hardened implementation (enrollment, verification, recovery codes, admin reset, rate limiting, audit logging, tests) versus hours for a naive one.
  • The API is stable and memorable: generateSecretKey(), getQRCodeUrl(), verifyKey($secret, $code, $window), verifyKeyNewer().
  • PHP matrix is broad (7.4 through 8.5, CI-tested against 8.6 beta) and comfortably covers currently supported Laravel releases. MIT license removes legal friction.
  • Replay protection is available but opt-in: verifyKeyNewer() returns a timestamp that the caller must persist. Nothing in the package prevents the same valid code being accepted twice unless we wire that up.

Technical risk

  • v9 breaking change is the single largest upgrade hazard. Default secret length moved from 16 to 32 characters (80 → 160 bits entropy). Any column defined as string('google2fa_secret', 16), any max:16 validation rule, any test asserting length 16, and any UI constrained to 16 characters will break or silently truncate on upgrade. Silent truncation of a secret is catastrophic — the user enrolls successfully and can never authenticate again.
  • Single-maintainer / bus-factor risk. One author across the core package, the Laravel bridge, and the QR bridge. Version support policy is documented and sane (9.x active, 8.x maintenance, 7.x unsupported), but there is no vendor, no SLA, and no commercial support path. Security disclosure relies on a SECURITY.md process.
  • Data provenance discrepancy. The supplied metadata reports 1 star and 0 dependents, which contradicts the repository's own Packagist download badge, the existence of two companion packages, and the breadth of public usage. Before adopting, confirm the artifact resolves to the expected Packagist vendor namespace (pragmarx/google2fa, historically renamed from the antonioribeiro GitHub org). Typosquat/namespace-confusion on auth libraries is a genuine supply-chain vector, and the metadata as given is not internally consistent.
  • Cryptographic configuration is our responsibility, not the library's. Algorithm (SHA1 default vs SHA256/512 — note non-SHA1 is not universally supported by consumer authenticator apps), window size, and secret storage encryption are all caller decisions. A misconfiguration here fails open in subtle ways.

Key questions

  1. Which Laravel and PHP versions are in scope, and does the bridge package support the same matrix?
  2. Is 2FA mandatory for all users or role-gated (admin/privileged)? This determines rollout complexity by an order of magnitude.
  3. Do we need WebAuthn/passkeys on the roadmap? If yes, avoid hard-coding TOTP as the only factor — abstract behind a TwoFactorProvider interface now.
  4. What is the recovery story — recovery codes, support-desk identity verification, or admin reset? Locked-out users are the dominant support cost.
  5. Who owns secret-at-rest encryption and key rotation, and what happens to enrolled users if the app key rotates?
  6. Are we accepting SHA1 (widely compatible) or requiring SHA256/512 (reduced app compatibility)?
  7. Is there an existing identity provider (SSO/IdP) that already offers MFA, making this redundant?

Integration Approach

Stack fit

  • Composer-only install, zero infrastructure footprint, no daemon, no external service. Bind Google2FA::class as a container singleton; inject rather than instantiate so tests can mock verification.
  • Recommended dependency set: pragmarx/google2fa (pin ^9.0), one QR backend (bacon/bacon-qr-code), optionally pragmarx/google2fa-laravel for the middleware and config scaffolding. Pin exact minor versions in composer.lock and treat auth-path dependency updates as security-review-gated.
  • Schema additions: google2fa_secret (encrypted cast, length ≥ 64 to absorb base32 32-char secrets plus ciphertext overhead), google2fa_enabled_at, google2fa_last_ts (for verifyKeyNewer replay protection), and a separate two_factor_recovery_codes table or encrypted JSON column.
  • Use Laravel's encrypted cast on the secret. Do not store base32 secrets in plaintext — database read access would otherwise equal full 2FA bypass.

Migration path

  • Additive only: nullable new columns, no backfill required, no downtime. Existing sessions and password auth are untouched until enforcement is switched on.
  • Existing 16-character secrets remain fully functional under v9; no re-enrollment needed for already-enrolled users. Only new enrollments get 32-character secrets — so the column must be sized for 32 from day one even if the org currently runs v8.
  • If migrating off another TOTP implementation, secrets are portable (base32) provided algorithm and period (30s) match; verify compatibility per-user before cutover rather than assuming.

Compatibility

  • Blocking prerequisite: TLS everywhere (TOTP codes are bearer tokens for one window), NTP-synchronized application servers, and a shared cache (Redis) for rate limiting across nodes.
  • Confirm QR rendering works in the target runtime — ImagickImageBackEnd requires the Imagick extension; the GD/SVG backends are lighter. Validate in the container image, not just locally.
  • Authenticator app compatibility: Google Authenticator, Authy, 1Password, Microsoft Authenticator all consume the standard otpauth:// URI emitted by getQRCodeUrl(). Provide a manual base32 entry fallback for users whose camera or app fails.

Sequencing

  1. Spike: install, bind, generate a secret, verify end-to-end with a real authenticator app in a staging container image. Confirm QR rendering and Imagick/GD availability.
  2. Schema + encryption: add nullable columns and casts, sized for 32-character secrets. Ship with no enforcement.
  3. Enrollment UX: QR + manual key fallback + one confirmation code before persisting enabled_at. Never mark enabled until the user proves possession.
  4. Verification middleware on a single low-traffic route; enable for internal staff and privileged/admin roles first.
  5. Add replay protection (verifyKeyNewer + persisted timestamp) and per-user/per-IP throttling before any broad rollout.
  6. Recovery codes and support-desk runbook before mandatory enforcement.
  7. Org-wide enforcement behind a feature flag, with a documented grace period.

Operational Impact

Maintenance

  • Low but non-zero. The package is a leaf dependency with no transitive weight; the ongoing cost is version tracking, not operation. Expect one major every few years with a documented breaking change (v9 being the current example).
  • Treat the auth path as a security-critical dependency: subscribe to GitHub security advisories, review changelogs before bumping, and never let a lockfile update to this package ship without a diff review and a smoke test of enrollment + verification.
  • Single-maintainer projects occasionally go dormant. Mitigation: the code is small and MIT-licensed, so a fork-and-maintain fallback is realistic. Budget for that possibility rather than assuming perpetual upstream support.

Support

  • No vendor SLA, no commercial escalation. Internal ownership must be explicit: one team owns the 2FA feature, the runbook, and the upgrade cadence.
  • The dominant support ticket category will not be library bugs — it will be locked-out users (lost/replaced device, wiped authenticator, factory-reset phone). Recovery-code UX and a support-desk identity-verification procedure are mandatory deliverables, not nice-to-haves.
  • Secondary category: clock drift ("my code is always wrong"), which is a server-side NTP issue, not a user issue.

Scaling

  • Negligible compute cost — HMAC-SHA1 over a 30-second counter is microseconds. No horizontal scaling concern; verification is stateless beyond the secret fetch and the replay-timestamp write.
  • The one scaling-sensitive element is rate limiting: a 6-digit code with a window of 1 gives roughly 3 valid codes out of 1,000,000 at any instant. Without throttling, an attacker with valid credentials can brute-force. Throttling state must live in shared cache (Redis), not per-node. Note the trade-off explicitly: increasing $window to 8 (as documented) expands accepted codes from 3 to 17 and meaningfully widens the brute-force surface — default to window 1 and only raise it in response to measured clock-drift complaints.
  • Replay-timestamp writes add one row update per verification; trivial, but it serializes concurrent verification attempts for the same user — acceptable.

Failure modes

  • Clock drift on application servers → total authentication outage for all 2FA users. Highest-severity, lowest-probability-mitigation item. Enforce NTP/chrony, monitor drift, and alert on it. Document a break-glass disable path.
  • Secret truncation on v9 upgrade → silent, permanent per-user lockout that looks like a user error. Caught only by schema review and enrollment smoke tests.
  • Encryption key rotation without re-encryption → every stored secret becomes undecryptable; org-wide lockout.
  • QR backend removal or API change → enrollment breaks while verification keeps working; users can log in but new users cannot enroll. Decoupled failure, easy to miss in monitoring.
  • Replay protection omitted → a phished code is reusable for the remainder of the window. Silent security degradation, not a visible failure.
  • Recovery path missing → any device loss becomes a manual support escalation with no safe verification procedure.
  • Package substitution / typosquat → worst-case credential compromise. Lock the vendor namespace and audit the lockfile.

Ramp-up

  • For an engineer familiar with Laravel: half a day to a working prototype, 3–5 days to a production-ready enrollment + verification flow with tests.
  • For the organization: 2–4 weeks end-to-end including security review, support-desk training, recovery procedures, and a staged rollout to privileged users before general enforcement.
  • Ramp-up cost is dominated by product and process decisions (enforcement policy, recovery, UX), not by the library. Budget accordingly — the package itself will not be the constraint, and that is precisely why it is a reasonable choice.
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.
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table