Emeka Mbah
2 min readDec 28, 2024

Laravel Custom Email Verification — Fix Call to a member function getKey() on null

Route:

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\VerificationController;

Route::controller(VerificationController::class)
->middleware('guest')
->prefix('email')
->name('email.')
->group(function ()
{
Route::get('/verify/{id}/{hash}', 'verify')
->middleware('signed')
->name('verify');
});

VerifyRequest:

<?php

namespace App\Http\Requests;

use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Http\FormRequest;
use App\Models\User;

class VerifyRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
if (! hash_equals((string) $this->user()->getKey(), (string) $this->route('id'))) {
return false;
}

if (! hash_equals(sha1($this->user()->getEmailForVerification()), (string) $this->route('hash'))) {
return false;
}

return true;
}

/**
* Fulfill the email verification request.
*
* @return void
*/
public function fulfill(): void
{
if (! $this->user()->hasVerifiedEmail()) {
$this->user()->markEmailAsVerified();
event(new Verified($this->user()));
}
}

public function prepareForValidation(): void
{
//set user
$this->setUserResolver(function () {
return User::find($this->route('id'));
});
}
}

Controller:

<?php

namespace App\Http\Controllers;

use App\Http\Requests\VerifyRequest;
use Digitlimit\Alert\Facades\Alert;

/**
* Class VerificationController
*
* @package App\Modules\Auth\Http\Controllers
*/
class VerificationController
{
public function verify(VerifyRequest $request)
{
$request->fulfill();

Alert::modal('Your account has been verified. You can now login.')
->title('Account Verified')
->centered()
->flash();

return redirect('/');
}
}

VerifyEmail Notification:

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\URL;

class VerifyEmail extends Notification // implements ShouldQueue
{
use Queueable;

/**
* Get the verify email notification mail message for the given URL.
*/
public function url(object $notifiable): string
{
$expire = Config::get('auth.email.expire', 60);

return URL::temporarySignedRoute(
'email.verify',
Carbon::now()->addMinutes($expire),
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
]
);
}

/**
* Get the notification's delivery channels.
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return ['mail'];
}

/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
$url = $this->url($notifiable);

return (new MailMessage)
->subject(Lang::get('Verify Email Address'))
->line(Lang::get('Please click the button below to verify your email address.'))
->action(Lang::get('Verify Email Address'), $url)
->line(Lang::get('If you did not create an account, no further action is required.'));
}

/**
* Get the array representation of the notification.
*
* @return array<string, mixed>
*/
public function toArray(object $notifiable): array
{
return [
//
];
}
}
Emeka Mbah
Emeka Mbah

Written by Emeka Mbah

Emeka is a seasoned software developer passionate about contributing to open-source projects.

No responses yet