diff --git a/app/Http/Controllers/Auth/VerifyEmailController.php b/app/Http/Controllers/Auth/VerifyEmailController.php index a78fba87..360ab140 100644 --- a/app/Http/Controllers/Auth/VerifyEmailController.php +++ b/app/Http/Controllers/Auth/VerifyEmailController.php @@ -4,39 +4,42 @@ namespace App\Http\Controllers\Auth; use App\Enums\UserType; use App\Http\Controllers\Controller; +use App\Models\User; use Illuminate\Auth\Events\Verified; -use Illuminate\Foundation\Auth\EmailVerificationRequest; +use Illuminate\Http\Request; use Illuminate\Http\RedirectResponse; +use Illuminate\Support\Facades\Auth; class VerifyEmailController extends Controller { /** - * Mark the authenticated user's email address as verified. + * Mark the user's email address as verified via signed link. */ - public function __invoke(EmailVerificationRequest $request): RedirectResponse + public function __invoke(Request $request): RedirectResponse { - $adminDashboard = route('dashboard', absolute: false) . '?verified=1'; - $studentDashboard = route('student.index', ['tab' => 'courses'], absolute: false) . '?verified=1'; + $user = User::find($request->route('id')); - if ($request->user()->hasVerifiedEmail()) { - if ($request->user()->role === UserType::STUDENT->value) { - return redirect()->intended($studentDashboard); - } else { - return redirect()->intended($adminDashboard); - } + if (!$user) { + abort(404); } - if ($request->user()->markEmailAsVerified()) { - /** @var \Illuminate\Contracts\Auth\MustVerifyEmail $user */ - $user = $request->user(); + if (!hash_equals(sha1($user->getEmailForVerification()), (string) $request->route('hash'))) { + abort(403); + } + if (!$user->hasVerifiedEmail()) { + $user->markEmailAsVerified(); event(new Verified($user)); } - if ($request->user()->role === UserType::STUDENT->value) { - return redirect()->intended($studentDashboard); - } else { - return redirect()->intended($adminDashboard); + if (Auth::check()) { + $redirect = Auth::user()->role === UserType::STUDENT->value + ? route('student.index', ['tab' => 'courses'], absolute: false) + : route('dashboard', absolute: false); + + return redirect()->intended($redirect . '?verified=1'); } + + return redirect()->route('login')->with('status', __('auth.email_verified_successfully')); } } diff --git a/app/Services/UserService.php b/app/Services/UserService.php index 99594abc..a64e7be5 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -4,12 +4,11 @@ namespace App\Services; use App\Enums\UserType; use App\Models\User; -use App\Notifications\ResetPasswordNotification; +use App\Notifications\VerifyEmailNotification; use Illuminate\Support\Facades\DB; use Illuminate\Pagination\LengthAwarePaginator; use Illuminate\Database\Eloquent\Collection; use Illuminate\Support\Facades\Hash; -use Illuminate\Support\Facades\Password; use Illuminate\Support\Str; class UserService @@ -51,10 +50,8 @@ class UserService 'password' => Hash::make(Str::random(32)), ]); - $token = Password::createToken($user); - - DB::afterCommit(function () use ($user, $token) { - $user->notify(new ResetPasswordNotification($token)); + DB::afterCommit(function () use ($user) { + $user->notify(new VerifyEmailNotification()); }); return $user; diff --git a/resources/js/pages/dashboard/users/Partials/table-columns.tsx b/resources/js/pages/dashboard/users/Partials/table-columns.tsx index 173e3bef..3cc07f52 100644 --- a/resources/js/pages/dashboard/users/Partials/table-columns.tsx +++ b/resources/js/pages/dashboard/users/Partials/table-columns.tsx @@ -1,6 +1,7 @@ import DeleteModal from '@/components/inertia/delete-modal'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; import { ColumnDef } from '@tanstack/react-table'; import { ArrowUpDown, Pencil, Trash2 } from 'lucide-react'; import EditForm from './edit-form'; @@ -44,6 +45,15 @@ const TableColumn = (translate: LanguageTranslations): ColumnDef[] => { ), }, + { + accessorKey: 'email_verified_at', + header: table.verified, + cell: ({ row }) => ( + + {row.original.email_verified_at ? common.verified : common.not_verified} + + ), + }, { accessorKey: 'role', header: table.role, diff --git a/resources/js/types/lang/common.d.ts b/resources/js/types/lang/common.d.ts index d5d64b66..73965d03 100644 --- a/resources/js/types/lang/common.d.ts +++ b/resources/js/types/lang/common.d.ts @@ -41,6 +41,8 @@ interface CommonLang { off: string; free: string; completed: string; + verified: string; + not_verified: string; // Common Actions & Account Management search: string; diff --git a/resources/js/types/lang/table.d.ts b/resources/js/types/lang/table.d.ts index fd738b4b..893c1e20 100644 --- a/resources/js/types/lang/table.d.ts +++ b/resources/js/types/lang/table.d.ts @@ -36,6 +36,7 @@ interface TableLang { category_child: string; meta_description: string; meta_keywords: string; + verified: string; // Table Action Labels action: string; diff --git a/routes/auth.php b/routes/auth.php index 96234e0d..c3357b46 100644 --- a/routes/auth.php +++ b/routes/auth.php @@ -25,6 +25,10 @@ Route::post('password-reset', [NewPasswordController::class, 'store']) ->name('password.store') ->middleware('checkSmtp'); +Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) + ->middleware(['signed', 'throttle:6,1']) + ->name('verification.verify'); + Route::middleware(['guest', 'authConfig'])->group(function () { Route::get('register', [RegisteredUserController::class, 'create']) ->name('register'); @@ -44,10 +48,6 @@ Route::middleware('auth')->group(function () { Route::get('verify-email', EmailVerificationPromptController::class) ->name('verification.notice'); - Route::get('verify-email/{id}/{hash}', VerifyEmailController::class) - ->middleware(['signed', 'throttle:6,1']) - ->name('verification.verify'); - Route::post('email/verification-notification', [EmailVerificationNotificationController::class, 'store']) ->middleware(['checkSmtp', 'throttle:6,1']) ->name('verification.send'); diff --git a/storage/app/lang/default/auth.php b/storage/app/lang/default/auth.php index 9c49f7a5..6450975b 100644 --- a/storage/app/lang/default/auth.php +++ b/storage/app/lang/default/auth.php @@ -53,4 +53,5 @@ return [ 'verify_title' => 'Verify email', 'verify_description' => 'Please verify your email address by clicking on the link we just emailed to you.', 'verification_sent' => 'A new verification link has been sent to the email address you provided during registration.', + 'email_verified_successfully' => 'Your email has been verified. You can now sign in.', ]; diff --git a/storage/app/lang/default/common.php b/storage/app/lang/default/common.php index 8b91e05f..1e332ee7 100644 --- a/storage/app/lang/default/common.php +++ b/storage/app/lang/default/common.php @@ -51,10 +51,12 @@ return [ 'default' => 'default', 'closed' => 'Closed', 'paused' => 'Paused', - 'on' => 'On', - 'off' => 'Off', - 'free' => 'Free', - 'completed' => 'Completed', + 'on' => 'On', + 'off' => 'Off', + 'free' => 'Free', + 'completed' => 'Completed', + 'verified' => 'Verified', + 'not_verified' => 'Not Verified', // Common Actions & Account Management 'search' => 'Search', diff --git a/storage/app/lang/default/dashboard.php b/storage/app/lang/default/dashboard.php index d3ed782c..24d47141 100644 --- a/storage/app/lang/default/dashboard.php +++ b/storage/app/lang/default/dashboard.php @@ -251,8 +251,8 @@ return [ 'update_user' => 'Update User', 'select_approval_status' => 'Select the approval status', 'invite_user' => 'Invite User', - 'invite_user_description' => 'Add a user and email them a password reset link to join.', - 'user_invited' => 'User invited successfully. A password reset link has been sent.', + 'invite_user_description' => 'Add a user and email them a verification link.', + 'user_invited' => 'User invited successfully. A verification email has been sent.', // Course Progress 'course_progress' => 'Course Progress', diff --git a/storage/app/lang/default/table.php b/storage/app/lang/default/table.php index cd60c529..b489bed6 100644 --- a/storage/app/lang/default/table.php +++ b/storage/app/lang/default/table.php @@ -48,6 +48,7 @@ return [ 'category_child' => 'Category Child', 'meta_description' => 'Meta Description', 'meta_keywords' => 'Meta Keywords', + 'verified' => 'Verified', // Table Action Labels 'action' => 'Action', diff --git a/storage/app/lang/groups/auth.php b/storage/app/lang/groups/auth.php index 4b871ea5..7ad58913 100644 --- a/storage/app/lang/groups/auth.php +++ b/storage/app/lang/groups/auth.php @@ -92,6 +92,7 @@ return [ 'verify_title' => 'Verify email', 'verify_description' => 'Please verify your email address by clicking on the link we just emailed to you.', 'verification_sent' => 'A new verification link has been sent to the email address you provided during registration.', + 'email_verified_successfully' => 'Your email has been verified. You can now sign in.', ] ], ]; diff --git a/storage/app/lang/groups/common.php b/storage/app/lang/groups/common.php index 94dcf206..5e986641 100644 --- a/storage/app/lang/groups/common.php +++ b/storage/app/lang/groups/common.php @@ -66,6 +66,8 @@ return [ 'off' => 'Off', 'free' => 'Free', 'completed' => 'Completed', + 'verified' => 'Verified', + 'not_verified' => 'Not Verified', ] ], diff --git a/storage/app/lang/groups/dashboard.php b/storage/app/lang/groups/dashboard.php index e90bdf45..ebd0b51f 100644 --- a/storage/app/lang/groups/dashboard.php +++ b/storage/app/lang/groups/dashboard.php @@ -394,8 +394,8 @@ return [ 'update_user' => 'Update User', 'select_approval_status' => 'Select the approval status', 'invite_user' => 'Invite User', - 'invite_user_description' => 'Add a user and email them a password reset link to join.', - 'user_invited' => 'User invited successfully. A password reset link has been sent.', + 'invite_user_description' => 'Add a user and email them a verification link.', + 'user_invited' => 'User invited successfully. A verification email has been sent.', ] ], diff --git a/storage/app/lang/groups/table.php b/storage/app/lang/groups/table.php index a3016f7e..515ec4e4 100644 --- a/storage/app/lang/groups/table.php +++ b/storage/app/lang/groups/table.php @@ -58,6 +58,7 @@ return [ 'category_child' => 'Category Child', 'meta_description' => 'Meta Description', 'meta_keywords' => 'Meta Keywords', + 'verified' => 'Verified', ] ],