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

gothiquo's avatar

Laravel 8 rest api email verification

After a huge search in the internet and in the forum, I just gave up...

I am develping a rest api using Laravel 8 and I am trying since week to make the email verification working using the official documentation has no title attribute for that, the email is always sent successfully once the user is registered event(new Registered($user)); The problem is that once I click on the link in the received email, I got redirected to the login page (which in this case is a post call)..

Here my routes/api.php:


Route::group(['namespace' => 'App\Http\Controllers', 'middleware' => ['api'], 'prefix' => 'auth'], function ($router) {
    Route::post('login', 'AuthController@login')->name('login');
    Route::post('register', 'AuthController@register');
    Route::post('logout', 'AuthController@logout');
    Route::post('profile', 'AuthController@profile')->middleware('verified');
    Route::post('refresh', 'AuthController@refresh');
});

Route::group(['namespace' => 'App\Http\Controllers', 'middleware' => ['api']],function ($router) {
    Route::get('/email/verify/{id}/{hash}', 'VerificationController@verify')->middleware(['auth', 'signed'])->name('verification.verify');
    Route::get('/email/resend', 'VerificationController@resend')->middleware(['auth', 'throttle:6,1'])->name('verification.send');
});

And here my VerificationController:


user()->sendEmailVerificationNotification();
        return response()->json(['message' => __('auth.email_sent')], Response::HTTP_NO_CONTENT);
    }

    public function verify(EmailVerificationRequest $request)
    {
        $request->fulfill();
        return response()->json(['message' => __('auth.user_verified_successfully')], Response::HTTP_RESET_CONTENT);
    }
}

Last but not least, I added the LogVerifiedUser event to EventServiceProvider as required.

Any suggestion plz? I tried to remove the middleware auth from verify route, but it doesn't help me...

1 like
1 reply
rodrigo.pedra's avatar

The EmailVerificationRequest expects a logged in user available in the request:

public function authorize()
{
    if (! hash_equals((string) $this->route('id'),
                      (string) $this->user()->getKey())) {
        return false;
    }

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

    return true;
}

Reference: https://github.com/laravel/framework/blob/8c26bcf46dbe29b3ec09ec83bd2c6a94c17af559/src/Illuminate/Foundation/Auth/EmailVerificationRequest.php#L15-L28

It works well with the web guard as it is expect the user to confirm their email while having their session active.

As there is no user available to the request, the authorize method on EmailVerificationRequest fails and the exception handler redirects you to a route named login (even if there isn't a GET counterpart).

The idea is that a user would confirm their email on a traditional web app, before using a mobile app, or other app that only consumes the API.

You'll need to build your own email verification mechanism to use without a session-based guard, as the built-in one relies on having a logged in user in the request.

From a quick Google search (search term: laravel email verification api) I found these two links:

Although they are not using Laravel 8, the solutions presented there would still work on Laravel 8.

A slightly modified version from the solution in the Youtube video would be using a custom FormRequest as this one:

<?php

namespace App\Http\Requests;

use App\Models\User;
use Illuminate\Foundation\Auth\EmailVerificationRequest as BaseEmailVerificationRequest;

class EmailVerificationRequest extends BaseEmailVerificationRequest
{
    public function authorize()
    {
        $user = User::query()->findOrFail($this->route('id'));

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

        $this->setUserResolver(function () use ($user) {
            return $user;
        });

        return true;
    }
}

You'd use this form request instead of the one shipped with the framework. Also, when using this, remove the auth middleware from the route as it would try to authenticate the user before running this Form Request.

Hope this helps.

4 likes

Please or to participate in this conversation.