Laravel Passwordless Login with Magic Link

Laravel Passwordless Login with Magic Link

Log in without a password by using the magic link in Laravel.

ยท

4 min read

In this post, I will explain about login in with Magic Link in LARAVEL 9.

After Installing Laravel and Laravel Authentication open code project in a code editor & follow the steps below.

Step -1

To make passwordless login with Magic Link, we are going to use Laravel Password Less Package.

All you need to do is run the composer command.

composer require grosv/laravel-passwordless-login

Step - 2

Make Changes in the login view file, open resources/views/auth/login.blade.php and update the code below.

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('login') }}">
                        @csrf

                        @if (session('message'))
                            <div class="alert alert-info">{{ session('message') }}</div>
                        @endif

                        <div class="row mb-3">
                            <label for="username" class="col-md-4 col-form-label text-md-end">{{ __('Username') }}</label>

                            <div class="col-md-6">
                                <input id="username" type="text" class="form-control @error('username') is-invalid @enderror" name="username" value="{{ old('username') }}" required autocomplete="username" autofocus>

                                @error('username')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="row mb-3">
                            <label for="password" class="col-md-4 col-form-label text-md-end">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password"  autocomplete="current-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="row mb-3">
                            <div class="col-md-6 offset-md-4">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>

                                    <label class="form-check-label" for="remember">
                                        {{ __('Remember Me') }}
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" name="submit" class="btn btn-primary" value="login">
                                    {{ __('Login') }}
                                </button>

                                or

                                <button type="submit" class="btn btn-secondary" name="submit" value="magic-link">
                                    {{ __('Send Magic Link') }}
                                </button>
                            </div>
                        </div>

                        <div class="row mb-0 mt-3">
                            <div class="col-md-8 offset-md-4">
                                @if (Route::has('password.request'))
                                    <a class="btn btn-link" href="{{ route('password.request') }}">
                                        {{ __('Forgot Your Password?') }}
                                    </a>
                                @endif

                                <a class="btn btn-link" href="{{ route('otp.login') }}">
                                    {{ __('Login With OTP.') }}
                                </a>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Step - 3

Update the app/Http/Controllers/Auth/LoginControlller.php and add the login & Login with Magic Link Function.

<?php

namespace App\Http\Controllers\Auth;

use App\Models\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Notifications\SendMagicLinkNotification;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;

    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = RouteServiceProvider::HOME;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');
    }

    public function username()
    {
        return 'username';
    }

    public function login(Request $request)
    {


        if($request->input('submit') == 'magic-link'){
            $user = $this->loginViaMagicLink($request);

            if(!$user){
                return redirect()->route('login')
                ->withErrors(['username' => 'User with this username does not exist.'])
                ->withInput();
            }

            return redirect()->route('login')
            ->withMessage('Magic Link Sent to the registered email ID.');
        }

        $this->validateLogin($request);

        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        if (method_exists($this, 'hasTooManyLoginAttempts') &&
            $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }

        if ($this->attemptLogin($request)) {
            if ($request->hasSession()) {
                $request->session()->put('auth.password_confirmed_at', time());
            }

            return $this->sendLoginResponse($request);
        }

        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        $this->incrementLoginAttempts($request);

        return $this->sendFailedLoginResponse($request);
    }

    public function loginViaMagicLink(Request $request)
    {
        $user = User::where('username', $request->input('username'))->first();

        if ($user) {
            $user->notify(new SendMagicLinkNotification());
        }

        return $user;
    }
}

Step - 4

Create notification to send a magic link on registered email.

php artisan make:notification SendMagicLinkNotification

After creating the notification open it from app/Notifications and update the code below.

<?php

namespace App\Notifications;

use Grosv\LaravelPasswordlessLogin\LoginUrl;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class SendMagicLinkNotification extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

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

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {

        $generator = new LoginUrl($notifiable);
        $generator->setRedirectUrl('/home');
        $url = $generator->generate();

        return (new MailMessage)
                    ->subject('Your Login Magic Link!')
                    ->line('Click this link to log in!')
                    ->action('Login', $url)
                    ->line('Thank you for using our application!');
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

That's it now you visit on the login page and you will see the magic link button on the login page.

Login

After you hit send the magic link you are going to get an email.

Email

I hope this will help login with MAGIC LINK in LARAVEL 9.

Complete Video Tutorial on YouTube.

If you face any issues while implementing, please comment with your query.

Thank You for Reading

Reach Out To me. Twitter Instagram YouTube

Did you find this article valuable?

Support Shani Singh by becoming a sponsor. Any amount is appreciated!

ย