bearer-tokenemail-verificationlaravel-10

Laravel 10 Email verification link not verifying via the email link, just in postman


What I want to happen : Verify the user's email when clicking the button in the verification email.

What happens : The authentication is failing because laravel is not retrieving the bearer token of the user. It returns "Unauthenticated".

What I've done : Tested the verification in Postman and it's properly verifying the user, using the verification link sent in the email.

http://127.0.0.1:8000/verify/1/bd2c82278d7a76fc882d47348d0e981bfbcac894?expires=1678537895&signature=59ed9520ddd74820380afe47bd752d7be424ec89c485e035f8c6f72094f706e5.

In postman, I included the bearer token in the Authorization tab. So the authentication of the email verification pushed through.

I have enabled MustVerifyEmail in my User model.

How do I pass through the bearer token so that laravel can access it when the link is clicked from the user email ?

My VerificationRequest

class VerificationRequest extends FormRequest
{
    public function authorize(): bool
    {
        // $user = User::find($this->route('id'));

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

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

        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
     */
    public function rules(): array
    {
        return [];
    }

    public function fulfill() {
        if (! $this->user()->hasVerifiedEmail()) {
            $this->user()->markEmailAsVerified();
            $this->user()->update([
                'isVerified' => true
            ]);

            event(new Verified($this->user()));
        }
    }
}

My Route

Route::get('/verify/{id}/{hash}', function(VerificationRequest $request) {
    $request->fulfill();

    return redirect('profile');
})  ->middleware(['auth', 'signed'])
    ->name('verification.verify');

The URL sent in the email


If you're having trouble clicking the "nyoom" button, copy and paste the URL below into your web browser: http://127.0.0.1:8000/verify/1/bd2c82278d7a76fc882d47348d0e981bfbcac894?expires=1678537895&signature=59ed9520ddd74820380afe47bd752d7be424ec89c485e035f8c6f72094f706e5

Solution

  • There might be exceptions but I believe that generally its like this:

    Bearer token is passed through communication just as a header data, more importantly there is no automatic mechanics which would set that header (unlike cookies). If you want to use this type of authentication in your application, then you also at the same time require from users of your application, that they would have to explicitly add that authentication header to every requests in order to access protected resources. Now you can see why this type of authentication is used exclusively for communication with API servers.

    Email verification is not what API servers do directly, in case you need such thing its abstracted away under another application, which acts as a middleware layer (which sets the authentication token and do the request on behalf the user).

    In regards of the email verification itself you could split it into two categories: 1) link, 2) retype.

    Both categories use tokens associated with the user/email, without association it wouldn't be called token anymore, so if you have the token you can get the user/email to which the token belong to, that is why user being logged in doesn't have to be required, token itself already carry that information (by association).

    The requirement or expectation that the user use the same client(browser) or same device is just wrong (unless that is especially decided), because you have the user identity in your application which is not bound to device or client but human and humans can have multiple devices or browsers so you should want to allow it.

    To fulfill that idea its clear that for link category you do not require that the user is logged in because otherwise you brake the rule of users not having to use the same client/device, that is why the token which is provided for the link verification is very long, so nobody could guess it (even machine guesser). Now for example, user could use PC to register, then grab phone where email notification pop up and use the link on the phone. You see why this example works and wouldn't when logged in was required right ?

    The retype category is different, because the token is carried by human (like literally using human brain as information holder), so you can require logged in user as part of email verification process by displaying form to which the user retype the token information, while still letting the users to use different clients/devices. Advantage of requiring users to be logged in is that the token could be short, the token guessing is not concerning as before because its no longer possible to do it on behalf some other user.

    You are trying to use link method while requiring logged in user and on top of that using authentication method which is not possible to use for clicking on link.