NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #2874 most downloaded on Packagist
Create secure link for access to private data or login in Laravel without password
Last release 10 days ago
28 Sep 2026
Release timing varies
gaps range from 2 weeks to 7 months
Rarely documented
notes for 14 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
9 years old
65 releases · first in 2017
Deprecate running magic links without MagiclinkMiddleware by @cesargb in #162
Full Changelog: v2.28.1...v2.29.0
MagicLinkController now prefers the MagicLink that MagiclinkMiddleware already validated
and stored on the request, under MagiclinkMiddleware::REQUEST_ATTRIBUTE, instead of looking
the token up itself. When that attribute is missing — a custom route, a replaced or extended
MagiclinkMiddleware — it falls back to the previous behavior, but that fallback is now
deprecated: it will be removed in 3.0, where every request will require
MagiclinkMiddleware to have run.
MagiclinkMiddleware, returned a 500 error. It now returns the configured invalid-link
response, same as everywhere else.MagiclinkMiddleware, an expired magic link is now rejected. It
used to run regardless of available_at. This also applies if you replaced
MagiclinkMiddleware with your own middleware (in magiclink.middlewares or on a custom
route): even if your middleware allows a grace period or has its own badResponse(), the
controller will now reject an expired link with the configured invalid_response. Make
sure MagiclinkMiddleware runs, or set its REQUEST_ATTRIBUTE yourself, if you need
different behavior.MagicLinkController now uses the HandlesInvalidResponse trait, which declares a
protected function badResponse(). If you extend MagicLinkController and already declare
a badResponse() method that isn't compatible (private, static, or with required
parameters), this will cause a fatal error when the class is loaded. Rename your method or
make it compatible (protected/public, no required parameters).MagicLinkController::access() without a MagicLink resolved by
MagiclinkMiddleware is deprecated (triggers E_USER_DEPRECATED). This fires on every
request that takes the fallback path, and Laravel routes it to your deprecations log
channel (LOG_DEPRECATIONS_CHANNEL, null by default). If your test suite fails on
deprecations (withoutDeprecationHandling(), PHPUnit's failOnDeprecation), this may
start failing tests that exercise a route without MagiclinkMiddleware. In 3.0 this will
be rejected outright, with no fallback lookup. If you use a custom route or a
custom/extended middleware, make sure MagiclinkMiddleware runs and sets its request
attribute — see "Custom controller" in the README.MagicLink::getMagicLinkByToken() does not check expiration or visit limits. Use
getValidMagicLinkByToken() instead, unless you intend to bypass those checks yourself.MagicLink from the request attribute when using a
custom controller (disable_default_route). See the "Custom controller" section in the README.MagicLinkController::access() will reject any request without a MagicLink resolved by
MagiclinkMiddleware, instead of falling back to an unchecked token lookup. Concretely, that
removes the current fallback's gaps: without the middleware, max_visits and the access code
are still not enforced today (only expiration and unknown tokens are, since this release).access()'s signature may change to receive the Request explicitly, now that the
fallback lookup (its only reason for reading the token on its own) is going away.One column per quarter.
This release fixes two security issues. Upgrading is recommended for all users.
This release fixes two security issues. Upgrading is recommended for all users.
Access-code guesses on protected magic links were not limited by default, so an attacker holding a leaked link could brute-force its access code.
429 with a Retry-After header.MagicLink\Events\MagicLinkAccessCodeFailed event, fired on every wrong access code, so you can log or alert on guessing attempts.Links with max_visits could be used more times than configured under concurrent requests. The visit counter is now incremented atomically, and a request that loses the race gets the invalid-link response.
POST, so codes no longer end up in access logs, browser history or Referer headers. A new POST route is registered on the magic-link path; GET ?access-code= keeps working.access_code.view config key is now honored. Before this release, only the undocumented access-code.view key worked; it is still supported.No migration required. Existing published config/magiclink.php files keep working: the new keys fall back to safe defaults in code.
The limiter uses your application's default cache store. It must be shared across all processes serving the app (e.g. redis, database, memcached), not array.
To tune or disable the limiter, add the new keys to your published config or use the env vars:
'access_code' => [
'view' => 'magiclink::ask-for-access-code-form',
'max_attempts' => env('MAGICLINK_ACCESS_CODE_MAX_ATTEMPTS', 5), // 0 or 'none' disables it
'decay_seconds' => env('MAGICLINK_ACCESS_CODE_DECAY_SECONDS', 300),
],MagicLink::visited() now returns bool (false when the link has no visits left). This only matters if you call or override it yourself.
The access code is only as strong as the value you pass to protectWithAccessCode(). Prefer long, random codes, especially with LoginAction.
Full Changelog: v2.28.0...v2.28.1
Add InlineFileAction to serve private files inline by @cesargb in #161
Full Changelog: v2.27.1...v2.28.0
InlineFileAction, to serve private files inline in the browser instead of forcing a
download (#161).Bump ramsey/composer-install from 3 to 4 by @dependabot [bot] in #155
Full Changelog: v2.27.0...v2.27.1
Add automatic pruning support with MassPrunable trait and configurable cleanup by @cesargb in #149
Full Changelog: v2.26.0...v2.27.0
MassPrunable trait (#149).delete_massive config key. delete_expired_when_created now controls cleanup
on creation, and its default changed from true to false (#154).MAGICLINK_DELETE_EXPIRED_WHEN_CREATED=true explicitly, or use php artisan model:prune.Add configurable allowed classes for secure action deserialization with TypeError handling by @cesargb in #151
Full Changelog: v2.25.1...v2.26.0
allowed_classes (and MAGICLINK_ALLOWED_CLASSES env var) to allowlist object
properties in custom actions, with a TypeError when an action uses a class that isn't
allowed (#151).Migrate to Pint for code linting by @cesargb in #147
Full Changelog: v2.25.0...v2.25.1
php artisan magiclink:migrate --dry-run
php artisan magiclink:migrate
Migrate actions add option --dry-run and doc by @cesargb in #146
magiclink:migrate --dry-run option, to simulate the legacy-action migration and report how
many links would be affected before running it for real (#146).Fix Migration Command for Legacy Actions in PostgreSQL by @cesargb in #145
Full Changelog : v2.24.5...v2.24.6
Full Changelog: v2.24.5...v2.24.6
fix tests to postgres by @cesargb in #143
magiclink:migrate command, to migrate legacy serialized actions to the new format (#144).Full Changelog : v2.24.3...v2.24.4
Full Changelog: v2.24.3...v2.24.4
allowed_classes deserialization check.Compatibility fix for the legacy action format.
Insecure deserialization of MagicLink actions — GHSA-r33w-fg8j-9c94 (High). Actions were stored as raw PHP-serialized objects, without integrity valid
magic_links table. Actions are now stored as HMAC-signed JSON, with class allowlisting
restricted to ActionAbstract subclasses and framework classes. Legacy serialized data is
still read for backward compatibility (see Migrate actions).Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Nothing published for this version
Your coding agent can read these notes before it upgrades. Set up the MCP server →