Summer sale! Save 50% on access to our entire library of courses.Join here →
Laravel

Prevent email verification if the email is already verified

Howdy! 🤠

I am trying to prevent a user from verifying their email if it has already been verified.

It doesn't hurt to verify it again, nevertheless, that's what I'm trying to achieve here. I am using Laravel Fortify.

app()->singleton(VerifyEmailResponseContract::class, VerifyEmailResponse::class);

class VerifyEmailResponse implements VerifyEmailResponseContract
{
    public function toResponse($request): Response|JsonResponse|RedirectResponse
    {
        return $request->wantsJson()
            ? response()->json([
                'metadata' => [
                    'message' => trans('responses.email_verified_successfully'),
                ],
            ])
            : redirect()->intended(Config::get('fortify.home'));
    }
}

My first thought was to check whether the email had been verified and return a different response. Something like this:

class VerifyEmailResponse implements VerifyEmailResponseContract
{
    public function toResponse($request): Response|JsonResponse|RedirectResponse
    {
        return $request->wantsJson()
            ? response()->json([
                'metadata' => [
                    'message' => $request->user()->hasVerifiedEmail()
                        ? trans('responses.email_already_verified')
                        : trans('responses.email_verified_successfully'),
                ],
            ])
            : redirect()->intended(Config::get('fortify.home'));
    }
}

This didn't go quite to plan. This strangely always returns responses.email_already_verified regardless of the value. This is because, by the time it returns the response, the email has been verified.

In a nutshell, this is what I am trying to achieve:

  • Allow an email to be verified if it has NOT verified, email_verified_at is NULL
  • Prevent an email from being verified if it has ALREADY been verified, email_verified_at is NOT NULL
  • Show the appropriate message
  • Handle the action accordingly, don't silently re-verify and return a message that the email has been already verified

My second attempt was to try and override EmailVerificationRequest to throw a validation message.

class EmailVerificationRequest extends BaseEmailVerificationRequest
{
    public function authorize(): bool
    {
        if ($this->user()->hasVerifiedEmail()) {
            $response = response()->json(
                [
                    'metadata' => [
                        'message' => 'You have already verified your email.',
                    ],
                ],
                Response::HTTP_UNPROCESSABLE_ENTITY,
            );

            throw new HttpResponseException($response);
        }

        return parent::authorize();
    }
}

app()->bind(EmailVerificationRequest::class, CustomEmailVerificationRequest::class);

It doesn't look like CustomEmailVerificationRequest is taking effect though.

Any ideas?

It might be better to expire the link once it has been used. Not sure. This would be consistent, and it makes sense because it will have the same behaviour after the link has expired.

FWIW, I am trying to avoid overriding routes.

Edit:

Perhaps this is overkill. Setting up another column just to track when an email was verified so we can return a more accurate response and handle the action properly. All for the duration of the email signature is active (like 60 minutes?)... Hmm.

so we can return a more accurate response and handle the action properly

Or rather, to throw a 419.

Haz
Haz
Moderator
0
0
147