where('user_id', $userId)->delete(); $rawToken = Str::random(64); $tokenHash = $this->hashToken($rawToken); // Support environments where the migration hasn't renamed the column yet $column = \Illuminate\Support\Facades\Schema::hasColumn('user_verification_tokens', 'token_hash') ? 'token_hash' : 'token'; DB::table('user_verification_tokens')->insert([ 'user_id' => $userId, $column => $tokenHash, 'expires_at' => now()->addHours($this->ttlHours()), 'created_at' => now(), 'updated_at' => now(), ]); return $rawToken; } public function findValidRecord(string $rawToken): ?object { $tokenHash = $this->hashToken($rawToken); $column = \Illuminate\Support\Facades\Schema::hasColumn('user_verification_tokens', 'token_hash') ? 'token_hash' : 'token'; $record = DB::table('user_verification_tokens') ->where($column, $tokenHash) ->first(); if (! $record) { return null; } if (! hash_equals((string) ($record->{$column} ?? ''), $tokenHash)) { return null; } if (now()->greaterThan($record->expires_at)) { DB::table('user_verification_tokens')->where('id', $record->id)->delete(); return null; } return $record; } private function ttlHours(): int { return max(1, (int) config('registration.verify_token_ttl_hours', 24)); } private function hashToken(string $rawToken): string { return hash('sha256', $rawToken); } }