pushery / email-magic-link-for-laravel
Passwordless email magic-link & OTP authentication for Laravel — standalone or with a correct, no-bypass Fortify 2FA handoff and scanner-safe link consumption.
Package info
github.com/pushery/email-magic-link-for-laravel
pkg:composer/pushery/email-magic-link-for-laravel
Requires
- php: ^8.4
- ext-filter: *
- ext-hash: *
- ext-json: *
- ext-mbstring: *
- laravel/framework: ^13.0
Requires (Dev)
- cweagans/composer-patches: ^1.7
- larastan/larastan: 3.11.0
- laravel/fortify: ^1.39
- laravel/pao: ^1.1
- laravel/pint: ^1.30
- orchestra/testbench: ^11.2
- pestphp/pest: ^5.1
- pestphp/pest-plugin-agent: ^5.0
- pestphp/pest-plugin-browser: ^5.0
- pestphp/pest-plugin-evals: ^5.0
- pestphp/pest-plugin-laravel: ^5.0
- pestphp/pest-plugin-phpstan: 5.2.1
- pestphp/pest-plugin-rector: 5.0.4
- pestphp/pest-plugin-type-coverage: ^5.0
- phpstan/phpstan: 2.2.13
- predis/predis: ^3.2
- pushery/wirekit: ^2.45
- rector/rector: 2.6.3
- spaze/phpstan-disallowed-calls: 4.14.0
Suggests
- laravel/fortify: Enables the no-bypass two-factor (TOTP) handoff: a magic-link user with confirmed 2FA completes Fortify's challenge before being logged in (^1.0).
- pushery/wirekit: Renders the sign-in screens with WireKit components when installed; the package auto-detects it and falls back to plain Blade otherwise. The screens need 2.26 or newer: the one-time-code field needs the alphabet prop (2.22) to accept a non-numeric code at all, and its case-folding validation pattern (2.26) to accept that code in lower case without scripting. Only the version pinned in require-dev is installed by the suite, so a release between that floor and the pin meets the stated requirement without a run behind it.
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v0.28.0.x-dev
- v0.28.0
- v0.27.1.x-dev
- v0.27.1
- v0.27.0
- v0.26.3
- v0.26.2
- v0.26.1
- v0.26.0
- v0.25.0
- v0.24.1
- v0.24.0
- v0.23.0
- v0.22.0
- v0.21.0
- v0.20.2
- v0.20.1
- v0.20.0
- v0.19.0
- v0.18.0
- v0.17.1
- v0.17.0
- v0.16.1
- v0.16.0
- v0.15.2
- v0.15.1
- v0.15.0
- v0.14.0
- v0.13.3
- v0.13.2
- v0.13.1
- v0.13.0
- v0.12.0
- v0.11.0
- v0.10.0
- v0.9.0
- v0.8.0
- v0.7.0
- v0.6.0
- v0.5.0
- v0.4.0
- v0.3.1
- v0.3.0
- v0.2.0
- v0.1.1
This package is auto-updated.
Last update: 2026-09-14 08:36:53 UTC
README
Email Magic Link for Laravel
Passwordless email authentication for Laravel — magic links and one-time codes — that works standalone or alongside Laravel Fortify.
Plenty of packages send a magic link. This one is built around two properties most of them get wrong:
- A correct, no-bypass Fortify two-factor handoff. A user with two-factor authentication enabled is handed to Fortify's own challenge in a not-yet-authenticated state; the login completes inside Fortify only after the code is verified. There is no path that signs a two-factor user in without the second factor.
- Scanner-safe and prefetch-safe link consumption. The emailed link is a signed, inert
GETthat only renders a confirmation page. The single-use token is spent solely by an explicitPOST, so SafeLinks, Mimecast, Proofpoint and browser prefetch cannot burn the link before the human clicks "Sign in".
Installation
composer require pushery/email-magic-link-for-laravel
Requires PHP ^8.4 and Laravel ^13.0. Laravel Fortify (^1.0) is optional and only needed for the two-factor handoff. There are no third-party runtime dependencies.
Documentation
Full docs at docs.pushery.com/email-magic-link-for-laravel
- Installation — the installer, the migration, the publish tags, and the queue worker the emails need
- Quick start — the routes you get out of the box and the flow a user walks through
- Configuration — the three Fortify setups, token lifetimes, and the invalid-link response
- Minting links and codes yourself — deliver a credential over SMS, chat, or your own transactional email
- Security model — every threat the package is designed against and the decision that addresses it
What it does
- Magic links and one-time codes — pick either, or offer both.
- A complete browser flow out of the box — the routes, the screens, and the emails; point your "log in" link at one route name.
- A Mint API — issue a signed link or a code and get it back without sending anything, to deliver over any channel you like.
- Bounded multi-use links — hand out a link redeemable N times, with the counter decremented in the same conditional
UPDATEthat consumes it, so concurrent redemptions can never exceed the limit. - Passphrase-gated links — require a shared secret, delivered out of band, before a high-value link is consumed.
- A resend guard — an escalating cooldown plus a rolling hourly cap, so a repeatedly clicked "send again" cannot flood an inbox. Reusable for your own endpoints.
- Invitations — the other half of the story. A magic link can only sign in somebody who already exists; an invitation puts an account into service for an address that has none yet. The package issues the token, supersedes the previous one when you re-invite, refuses every dead one identically, and spends it exactly once — your application decides what accepting one means.
- A JSON contract — stable statuses and error codes for first-party SPA and mobile clients.
- Multiple guards — sign in to an
adminguard alongsideweb, on an allowlist that keeps guards un-enumerable. - Bundled locales — English, German, Spanish, French, Italian, Dutch and Portuguese, plus the
en-GB,en-US,pt-PTandpt-BRregional variants under both spellings. Both spellings are shipped on purpose: Laravel's own documentation prescribes the underscore form (pt_BR), while the hyphenated language tag (pt-BR) is what a browser sends, and a file under only one of them is unreachable for a host that uses the other. If your application decides its locale from a header or a URL segment, tell Laravel how to fall back withApp::determineLocalesUsing()— otherwisept-BRresolves to nothing and the screens silently render in English. - Styled screens, or none of ours — the sign-in views render with WireKit when it is installed, fall back to dependency-free Blade when it is not, and can be published and rewritten either way. Browser support follows WireKit's floor on the styled path and the browser's own defaults on the plain one; see the documentation.
- Designed to fail closed — tokens hashed at rest, nothing serialized into a token, a single race-free conditional claim, and responses that never reveal whether an account exists.
Built by PUSHERY
This package is built and maintained by PUSHERY — a Berlin-based studio building Laravel applications, SaaS products, and open-source tools.
Want these sign-in screens to match a polished component library out of the box? They render automatically with WireKit, PUSHERY's open-source Livewire UI kit. Browse the rest of our work at pushery.com.
Security
Found a vulnerability? See SECURITY.md for the supported versions and how to report it.
Versioning
This package follows Semantic Versioning. It is in its 0.x line while the public API settles; the backward-compatibility promise begins at 1.0.0.
License
The MIT License. See LICENSE.