Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Magic Sign-in Links Are Simpler Than You Think

--  redirect("/blog")
-- by Newton Job
-- Apr 8, 2025

I recently logged in to Slack, and noticed that they offer the option of authenticating via a magic login link. Neat! I made a mental note.

Fast forward, and for one of my side projects, I decided to offer a similar functionality. In my case, administrators would create user accounts and send them a link for their first-time password-less login, after which they'd set up a password. Let me show you how I accomplished it!

What is a magic sign-in link?

A magic sign-in link offers users a way to securely sign in to their account without a password. Effectively, it prepares a one-time token they can click to sign-in without a password.

Let's imagine how this might work. We technically just need to generate a link that contains a token. When the user clicks this link, we'll verify the token, sign-in the user, and wrap up by deleting the token.

But, wait a minute: doesn't this sound familiar to you? This is exactly how password resets work in Laravel!

  • A link, containing a unique token, is sent to the user.

  • The user clicks the link and is presented with a form to provide a new password.

  • The token is added to the form as a hidden field.

  • The form is submitted with the token and new password.

  • Laravel verifies the token and allows the password to be reset.

For our use-case, we only require the first and last steps from above. So let's explore how we might achieve this using Laravel's PasswordBroker which already does most of the heavy lifting for us.

Preparing the Controller

Let's create a MagicLoginLinkController that contains two actions. The create method creates and sends the user an email that contains the relevant link. Clicking it will direct the user to the store method, which verifies the token and signs the user in.

Here, I am assuming that the user instance is known (since an admin does the sending), but of course, you could simply rely on an email address, especially if you're offering this as the primary method of logging into your application.

class MagicLoginLinkController extends Controller
{
    /**
     * Generate and send a magic login link to the user.
     */
    public function create(Request $request, User $user)
    {
        $user->notify(new MagicLoginLink(Password::createToken($user)));

        return response()->json(['message' => 'Magic login link sent.']);
    }

    /**
     * Verify the magic link and log the user in.
     */
    public function store(Request $request, User $user): RedirectResponse
    {
        if (! Password::tokenExists($user, $request->token)) {
            abort(404);
        }

        Auth::login($user);

        Password::deleteToken($user);

        return redirect()->intended('dashboard');
    }
}

The Notification

Our notification class is just a basic regular notification that can be generated using the php artisan make:notification command. It accepts the token and generates the full URL to the MagicLoginLinkController::store() action.

class MagicLoginLink extends Notification
{
    use Queueable;

    public function __construct(#[SensitiveParameter] protected string $token) {}

    /**
     * Get the notification's delivery channels.
     */
    public function via($notifiable): array
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     */
    public function toMail($notifiable): MailMessage
    {
        return (new MailMessage)
            ->greeting('Use this link to sign in')
            ->line("Here's a link to login to your account without a password.")
            ->line('Note that this link expires in 24 hours and can only be used once.')
            ->action('Sign in', route('magic-login', [$notifiable, 'token' => $this->token]));
    }
}

Believe it or not, that's... all there is to it! I configured the tokens to expire in 24 hours in config/auth.php, but feel free to set that to any value you like.

Conclusion

Hopefully, you can now appreciate how easy it is to implement magic authentication into your applications by leveraging Laravel's Password facade. Of course, there are a few Laravel packages out there that offer similar functionality. But as you know, I try to exhaust my options with what the framework provides out of the box before reaching for third-party dependencies.

Until next time!