wolfcode/authenticator
A Laravel authentication package for wolfcode/authenticator, offering a simple way to add login, registration, and access control to your PHP applications. Lightweight and easy to integrate.
wolfcode/authenticator is a small, unopinionated PHP library for the "authenticator app" half of two-factor authentication — the TOTP/HOTP style codes you'd scan into Google Authenticator, Authy, 1Password, etc. It is not a Laravel package with guards, middleware, migrations, or a service provider you can lean on; treat it as a primitive you wrap in your own application code.
Minimal path in a Laravel app:
composer require wolfcode/authenticator and confirm the autoloader picks it up (composer dump-autoload).vendor/wolfcode/authenticator/src/ first. There is no meaningful documentation surface at 0 stars — the source is the spec. Identify the entry class and note: does it return an otpauth:// provisioning URI, a raw Base32 secret, or both? Does it generate QR images or only URIs? Does it accept a window/drift parameter on verification?composer.json for extra.laravel and any illuminate/* requirements. If there is no service provider, there's nothing to publish — you'll instantiate the class yourself and bind it in a TwoFactorService of your own.bacon/bacon-qr-code or endroid/qr-code, and require one successful code entry before flipping the user to two_factor_enabled = true.The fastest sanity check before writing any app code is a throwaway route that generates a secret, prints the otpauth:// URI, adds it to your phone, and verifies a code. If that round-trip works, the library does what you need and everything after is your own plumbing.
Wrap it, don't sprinkle it. Define an interface in your domain (TwoFactorProvider with generateSecret(), provisioningUri(), verify()) and implement it against this package. Given the maturity signals here, you want a one-file swap to pragmarx/google2fa if you ever need to move. Bind it in a service provider and inject it.
Enrollment flow (the one you'll actually write):
encrypted (Laravel's encrypted cast uses APP_KEY). A TOTP secret is a long-lived shared credential — treating it as plaintext is the most common mistake in this space.two_factor_secret set but two_factor_confirmed_at null until the first valid code. Otherwise users lock themselves out with a mistyped scan.Hash::make), and show them once.Login flow:
Auth::login() until the code checks out.FormRequest or a dedicated action, not in the controller body.two_factor_last_used_at (or the TOTP time-step) and clear the pending state.Replay protection. A TOTP code is valid for its whole 30-second window (plus any drift window the library allows). Persist the last accepted time-step and reject anything <= it. Otherwise a code captured from a shoulder-surf or a logged request is reusable for up to a minute and a half.
Rate limiting. Codes are 6 digits. Without throttling, an attacker with a valid password brute-forces the second factor in a few thousand requests. Use Laravel's RateLimiter keyed on user ID + IP (5 attempts / 5 minutes is a reasonable start), and lock the challenge after N failures requiring a fresh password login.
Recovery codes belong in their own table or JSON column, each marked consumed on use, with the same rate limiting and an audit log entry.
Testing. Freeze time so tests are deterministic — Carbon::setTestNow() combined with the library's verification call. Cover: valid current code, code from one step ago (should pass only if your window allows), code from ten steps ago (must fail), a code already consumed (must fail), and a malformed/whitespace-padded input. If the library exposes a step-based API, test against the RFC 6238 test vectors rather than trusting generated values.
Blade/UI integration. Show the QR plus the manual Base32 secret as a copyable string — a meaningful share of users are on desktop and want to type it into a phone app. Also surface the otpauth:// URI as a fallback link for mobile.
Server clock drift is the #1 support ticket. TOTP is entirely time-based. If your app server's clock is off by 40 seconds, every code fails and you'll blame the library. Ensure NTP (chrony/systemd-timesyncd) is running on every app node, and set a drift window of ±1 step in the verifier. Don't widen it beyond ±1 — each extra step roughly doubles the brute-force surface.
APP_KEY rotation invalidates every enrolled secret. If you use the encrypted cast, rotating the key without re-encrypting (or keeping the old key in APP_PREVIOUS_KEYS) silently locks out every 2FA user. Plan for it or store the secret with a dedicated key managed separately.
Base32 input hygiene. Users paste secrets with spaces, lowercase letters, and padding = characters. Normalize before verifying: uppercase, strip whitespace and =. Most libraries are strict, and a mismatch looks like "the code is wrong" rather than "the secret is malformed."
Zero stars and no dependents means zero independent scrutiny. This is a security-sensitive primitive handling a shared secret. Before shipping: read every line of src/ (it's small — that's the upside), confirm it uses random_bytes/random_int for secret generation and hash_equals for comparison, and check that the release tag matches what you reviewed. Pin an exact version ("wolfcode/authenticator": "1.0.0") rather than a caret range, and add it to whatever dependency-review process you have.
Check the Base32 alphabet and secret length. TOTP secrets should be ≥160 bits (32 Base32 chars) per RFC 4226. If the generator defaults to something shorter, generate your own and feed it in.
QR codes are probably not included. Most lean packages in this space emit the otpauth:// URI only. Budget for bacon/bacon-qr-code (pure PHP, no Imagick requirement) or endroid/qr-code. Build the URI yourself if needed: otpauth://totp/{issuer}:{account}?secret={base32}&issuer={issuer}&algorithm=SHA1&digits=6&period=30 — URL-encode the label, because email addresses contain @ and issuers contain spaces.
No service provider means no config file. Don't hunt for config/authenticator.php. Put your settings (window, issuer, digits) in your own config/auth.php or a dedicated config/two_factor.php that you author and that your wrapper reads.
Don't gate the whole app behind 2FA on day one. Ship it as opt-in, then enforce per-role once you've watched failure rates and support volume for a couple of weeks. An aggressive rollout with a clock-drift bug produces a login outage that looks like a breach.
Have an admin reset path. Support staff need a way to clear a user's two_factor_secret and confirmed_at after identity verification. Build it before launch, log it loudly, and rate-limit it — it's the softest target in any 2FA implementation.
How can I help you explore Laravel packages today?