Home > Software engineering >  Login and verify user - Call to a member function getKey() on null
Login and verify user - Call to a member function getKey() on null

Time:01-29

I am trying to create a custom verification flow, where as soon as a user clicks the verification link, it logs him in and also verifies him, instead of first making him log in and only then the verification link works.

I built a custom notification URL in my CustomVerificationNotification, including the registered user_id, to login him later:

protected function verificationUrl($notifiable)
{
    if (static::$createUrlCallback) {
        return call_user_func(static::$createUrlCallback, $notifiable);
    }

    return URL::temporarySignedRoute(
        'verification.custom-verify',
        Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
        [
            'id' => $notifiable->getKey(),
            'hash' => sha1($notifiable->getEmailForVerification()),
            'user_id' => $this->user->id
        ]
    );
}

Then in my web.php I added this route:

Route::get('/email/verify/{id}/{hash}/{user_id}','Auth\CustomVerifyController@login_and_verify')->name('verification.custom-verify');

Then in my CustomVerifyController:

public function login_and_verify(EmailVerificationRequest $request)
{
    //..
}

But I get Call to a member function getKey() on null. And I can't edit EmailVerificationRequest, so what can I do? Is it possible to somehow call Auth::login($user); before calling the EmailVerificationRequest? (Because I have the user_id from the route)

I tried to follow the best answer from this post as well: How to Verify Email Without Asking the User to Login to Laravel

But I'm not sure then how to trigger the verify() method from the web.php and send the $request when I'm first calling the verify_and_login method

CodePudding user response:

First you need verify that the URL is signed by adding the middleware signed

You don't want that anoyone having the url /email/verify/{id}/{hash}/{user_id} able to access this ressource without the signature.

web.php

Route::get('/email/verify/{id}/{hash}/{user_id}','Auth\CustomVerifyController@login_and_verify')
      ->middleware('signed')
      ->name('verification.custom-verify');

Then you need to verify that the hash correspond the user_id and for that you can use a Request or a Middleware. I think the Request fits better since Laravel already uses a Request for this.

CustomEmailVerificationRequest.php


<?php

namespace App\Http\Requests;

use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Http\FormRequest;

class EmailVerificationRequest extends FormRequest
{

    public function authorize()
    {
       
        $user = User::findOrFail($this->route('id'));


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

        return true;
    }

}

Finally you need to login with the user and set is email as verified

CustomVerifyController.php


public function login_and_verify(CustomEmailVerificationRequest $request)
{
    $user = User::findOrFail($this->route('id'));
    
    Auth::login($user);
    $user->markEmailAsVerified();

    event(new Verified($user));


    ...

}
  •  Tags:  
  • Related