最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

php - Laravel Sanctum Email Verification Not Updating email_verified_at Field - Stack Overflow

programmeradmin6浏览0评论

I'm building an API using Laravel 10 with Sanctum for authentication, and I'm encountering an issue with email verification. After the user clicks the verification link, the email_verified_at field remains null.

This is my register component

import React from "react";
import { useNavigate } from "react-router-dom";

function Register() {
  const [name, setName] = React.useState("");
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");
  const navigate = useNavigate();

  React.useEffect(() => {
    if (localStorage.getItem("token")) {
      navigate("/account");
      window.location.reload();
    }
  }, []);

  async function register() {
    const item = { name, email, password };

    let result = await fetch("http://localhost:8000/api/register", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: JSON.stringify(item),
    });

    const data = await result.json();

    if (result.ok) {
      alert("Please verify your email before logging in.");
      navigate("/login");
    } else {
      alert(data.message || "Error registering.");
    }
  }

  return (
    <div className="py-16">
      <div className="flex items-center flex-col justify-center gap-6">
        <div className="flex items-center justify-center">
          <h1>Register Form</h1>
        </div>

        <div className="flex items-center flex-col gap-5 justify-center">
          <input
            type="text"
            placeholder="Name"
            value={name}
            onChange={(e) => setName(e.target.value)}
          />
          <input
            type="email"
            placeholder="Email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
          <input
            type="password"
            placeholder="Password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
          />
        </div>

        <div>
          <button onClick={register} className="border-2 border-black p-2">
            Register
          </button>
        </div>
      </div>
    </div>
  );
}

export default Register;

This is my auth controller

<?php

namespace App\Http\Controllers;
use Illuminate\Validation\ValidationException; //manually imported
use Illuminate\Support\Facades\Hash; //manually imported
use Illuminate\Http\Request;
use App\Models\User; //manualy imported
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Carbon\Carbon;
class AuthController extends Controller
{
    //

    public function verifyEmail(Request $request, $id, $hash)
    {
        $user = User::find($id);

        if (!$user) {
            return response()->json(['message' => 'User not found.'], 404);
        }


        if (!hash_equals(sha1($user->getEmailForVerification()), $hash)) {
            return response()->json(['message' => 'Invalid verification link.'], 400);
        }


        if ($user->email_verified_at) {
            return response()->json(['message' => 'Email already verified.']);
        }

        $user->email_verified_at = Carbon::now();
        $user->save();

        return response()->json(['message' => 'Email verified successfully.']);
    }
    public function resendEmailVerification(Request $request)
    {
        if ($request->user()->hasVerifiedEmail()) {
            return response()->json(['message' => 'Email already verified.']);
        }

        $request->user()->sendEmailVerificationNotification();

        return response()->json(['message' => 'Verification email sent.']);
    }

    public function register(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|unique:users',
            'password' => 'required|string|min:6'
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password)
        ]);


        $user->sendEmailVerificationNotification();



        $token = $user->createToken('auth_token')->plainTextToken;

        return response()->json(['token' => $token, 'user' => $user], 201);
    }

    public function login(Request $request)
    {
        $request->validate([
            'email' => 'required|email',
            'password' => 'required'
        ]);

        $user = User::where('email', $request->email)->first();

        if (!$user || !Hash::check($request->password, $user->password)) {
            return ["error" => "Invalid credentials"];
        }

        $token = $user->createToken('auth_token')->plainTextToken;

        return response()->json(['token' => $token, 'user' => $user]);
    }

    public function logout(Request $request)
    {
        $request->user()->tokens()->delete();

        return response()->json(['message' => 'Logged out']);
    }

    public function user(Request $request)
    {
        return response()->json($request->user());
    }



}

These are my api routes

<?php

use App\Http\Controllers\AuthController;
use App\Http\Controllers\DisplayController;
use App\Http\Controllers\OrderController;
use App\Http\Controllers\StripeController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});


Route::post("/login",[AuthController::class,"login"]);
Route::post("/register",[AuthController::class,"register"]);

Route::middleware('auth:sanctum')->post('/addOrder', [OrderController::class, 'addOrder']);

Route::middleware('auth:sanctum')->post('/pay', [StripeController::class, 'pay']);

Route::middleware('auth:sanctum')->get('/displayOrders', [DisplayController::class, 'displayOrders']);



Route::get('/email/verify/{id}/{hash}', [AuthController::class, 'verifyEmail'])
    ->middleware(['signed']) 
    ->name('verification.verify');

And these are my web routes

<?php

use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use App\Http\Controllers\AuthController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});


Route::get('/login', function () {
    return redirect('http://localhost:5173/login');
})->name('login');
Route::get('/email/verify/{id}/{hash}', [AuthController::class, 'verifyEmail'])
    ->middleware(['auth:sanctum', 'signed'])
    ->name('verification.verify');

I already made sure that the user model has the email verification thing enabled. Simply , after I receive the confirmation email on mailtrap and click the button I am getting redirected to the login page and then when checking the data base the "email_verified_at" entry is still empty... pls help

I'm building an API using Laravel 10 with Sanctum for authentication, and I'm encountering an issue with email verification. After the user clicks the verification link, the email_verified_at field remains null.

This is my register component

import React from "react";
import { useNavigate } from "react-router-dom";

function Register() {
  const [name, setName] = React.useState("");
  const [email, setEmail] = React.useState("");
  const [password, setPassword] = React.useState("");
  const navigate = useNavigate();

  React.useEffect(() => {
    if (localStorage.getItem("token")) {
      navigate("/account");
      window.location.reload();
    }
  }, []);

  async function register() {
    const item = { name, email, password };

    let result = await fetch("http://localhost:8000/api/register", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
      body: JSON.stringify(item),
    });

    const data = await result.json();

    if (result.ok) {
      alert("Please verify your email before logging in.");
      navigate("/login");
    } else {
      alert(data.message || "Error registering.");
    }
  }

  return (
    <div className="py-16">
      <div className="flex items-center flex-col justify-center gap-6">
        <div className="flex items-center justify-center">
          <h1>Register Form</h1>
        </div>

        <div className="flex items-center flex-col gap-5 justify-center">
          <input
            type="text"
            placeholder="Name"
            value={name}
            onChange={(e) => setName(e.target.value)}
          />
          <input
            type="email"
            placeholder="Email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
          />
          <input
            type="password"
            placeholder="Password"
            value={password}
            onChange={(e) => setPassword(e.target.value)}
          />
        </div>

        <div>
          <button onClick={register} className="border-2 border-black p-2">
            Register
          </button>
        </div>
      </div>
    </div>
  );
}

export default Register;

This is my auth controller

<?php

namespace App\Http\Controllers;
use Illuminate\Validation\ValidationException; //manually imported
use Illuminate\Support\Facades\Hash; //manually imported
use Illuminate\Http\Request;
use App\Models\User; //manualy imported
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use Carbon\Carbon;
class AuthController extends Controller
{
    //

    public function verifyEmail(Request $request, $id, $hash)
    {
        $user = User::find($id);

        if (!$user) {
            return response()->json(['message' => 'User not found.'], 404);
        }


        if (!hash_equals(sha1($user->getEmailForVerification()), $hash)) {
            return response()->json(['message' => 'Invalid verification link.'], 400);
        }


        if ($user->email_verified_at) {
            return response()->json(['message' => 'Email already verified.']);
        }

        $user->email_verified_at = Carbon::now();
        $user->save();

        return response()->json(['message' => 'Email verified successfully.']);
    }
    public function resendEmailVerification(Request $request)
    {
        if ($request->user()->hasVerifiedEmail()) {
            return response()->json(['message' => 'Email already verified.']);
        }

        $request->user()->sendEmailVerificationNotification();

        return response()->json(['message' => 'Verification email sent.']);
    }

    public function register(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|unique:users',
            'password' => 'required|string|min:6'
        ]);

        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => Hash::make($request->password)
        ]);


        $user->sendEmailVerificationNotification();



        $token = $user->createToken('auth_token')->plainTextToken;

        return response()->json(['token' => $token, 'user' => $user], 201);
    }

    public function login(Request $request)
    {
        $request->validate([
            'email' => 'required|email',
            'password' => 'required'
        ]);

        $user = User::where('email', $request->email)->first();

        if (!$user || !Hash::check($request->password, $user->password)) {
            return ["error" => "Invalid credentials"];
        }

        $token = $user->createToken('auth_token')->plainTextToken;

        return response()->json(['token' => $token, 'user' => $user]);
    }

    public function logout(Request $request)
    {
        $request->user()->tokens()->delete();

        return response()->json(['message' => 'Logged out']);
    }

    public function user(Request $request)
    {
        return response()->json($request->user());
    }



}

These are my api routes

<?php

use App\Http\Controllers\AuthController;
use App\Http\Controllers\DisplayController;
use App\Http\Controllers\OrderController;
use App\Http\Controllers\StripeController;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});


Route::post("/login",[AuthController::class,"login"]);
Route::post("/register",[AuthController::class,"register"]);

Route::middleware('auth:sanctum')->post('/addOrder', [OrderController::class, 'addOrder']);

Route::middleware('auth:sanctum')->post('/pay', [StripeController::class, 'pay']);

Route::middleware('auth:sanctum')->get('/displayOrders', [DisplayController::class, 'displayOrders']);



Route::get('/email/verify/{id}/{hash}', [AuthController::class, 'verifyEmail'])
    ->middleware(['signed']) 
    ->name('verification.verify');

And these are my web routes

<?php

use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Auth\EmailVerificationRequest;
use App\Http\Controllers\AuthController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});


Route::get('/login', function () {
    return redirect('http://localhost:5173/login');
})->name('login');
Route::get('/email/verify/{id}/{hash}', [AuthController::class, 'verifyEmail'])
    ->middleware(['auth:sanctum', 'signed'])
    ->name('verification.verify');

I already made sure that the user model has the email verification thing enabled. Simply , after I receive the confirmation email on mailtrap and click the button I am getting redirected to the login page and then when checking the data base the "email_verified_at" entry is still empty... pls help

Share Improve this question edited Mar 21 at 1:23 desertnaut 60.5k32 gold badges155 silver badges181 bronze badges asked Mar 21 at 0:23 TinoOoTinoOo 13 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

Your email_verified_at stays null because the verification link likely hits the web.php route with auth:sanctum, which fails since the user isn’t logged in. Remove that route and use only api.php with signed middleware. Simplify verifyEmail like this:

// api.php
Route::get('/email/verify/{id}/{hash}', [AuthController::class, 'verifyEmail'])
    ->middleware('signed')
    ->name('verification.verify');

// AuthController.php
public function verifyEmail(EmailVerificationRequest $request)
{
    $request->fulfill();
    return response()->json(['message' => 'Email verified!']);
}

Ensure User implements MustVerifyEmail. Test the link (http://localhost:8000/api/email/verify/...)—it should update the field now. Check laravel.log if it doesn’t.

发布评论

评论列表(0)

  1. 暂无评论