This package adds an out-of-the-box, fully customizable solution for sending security notifications to users when their account is updated or accessed from a new device.
composer require zaengle/laravel-security-notifications
./vendor/bin/pest
php artisan vendor:publish --provider="Zaengle\LaravelSecurityNotifications\Providers\PackageServiceProvider" --tag="config"
In order to send security notifications, you'll need to add the Securable
trait to your user model. Additionally, you'll want to make sure you are using Laravel's Notifiable
trait.
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Zaengle\LaravelSecurityNotifications\Traits\Securable;
class User extends Authenticatable
{
use Securable, Notifiable;
}
The Securable
trait watches your user model for updates to the specified secure fields. By default, the secure fields are email
, username
, and password
. If any of these fields are updated, a security notification will be sent to the user. The original email address will be notified in the event that the email
field is updated.
You may customize the secure fields by overriding the getSecureFields
method on your user model.
public function getSecureFields(): array
{
return [
'first_name',
'last_name',
'email',
];
}
The Securable
trait also provides a logins
relationship that can be used to track user logins. This relationship tracks all IP addresses that have logged into the user's account. In order to set up secure logins, you'll need to publish and run the migration included in the package to set up a logins
table. This table name is configurable in the published security-notifications.php
config file.
php artisan vendor:publish --provider="Zaengle\LaravelSecurityNotifications\Providers\PackageServiceProvider" --tag="migrations"
php artisan migrate
You can use the IPAddress
facade to track user logins in your existing login controller. If the given IP address has already been used, it will update the login record with the login time. If the IP address is new, it will create a new login record and send a notification to the user.
<?php
namespace App\Http\Controllers;
use Zaengle\LaravelSecurityNotifications\Facades\IPAddress;
class LoginController extends Controller
{
public function login(Request $request)
{
// Your existing login logic
IPAddress::process([
'ipAddress' => $request->ip(),
'userId' => auth()->id(),
'userType' => auth()->user()->getMorphClass(),
]);
// return
}
}
If you would like to disable the entire package you can set the following config value:
'enabled' => false
If you would like to disable sending notifications you can set the following config value:
'send_notifications' => false
The package provides email notification templates out-of-the-box. However, if you would like to customize these templates to match your system, you may publish the views to your application and override them.
php artisan vendor:publish --provider="Zaengle\LaravelSecurityNotifications\Providers\PackageServiceProvider" --tag="views"
They will be available under resources/views/vendor/security-notifications
.
There are two views that can be customized. Each with access to specific data:
security-alert.blade.php
- Has access to a
$fields
array which contains the fields that were updated. It also has access to the$updated_at
variable which is a Carbon instance of when the fields were updated.
- Has access to a
login-from-new-ip.blade.php
- Has access to a
$login
variable which is an instance of theZaengle\LaravelSecurityNotifications\Models\Login
model.
- Has access to a
If you would like to further customize the notifications, you can configure the notification classes in the security-notifications.php
config file.
'notifications' => [
'secure_login' => \Zaengle\LaravelSecurityNotifications\Notifications\LoginFromNewIP::class,
'secure_fields' => \Zaengle\LaravelSecurityNotifications\Notifications\SecureFieldsUpdated::class,
],
By default, this package assumes that the user model has an email
attribute. If you would like to customize the email address that notifications are sent to, you can override the sendSecurityEmailsTo
method on the models that utilize the Securable
trait.
public function sendSecurityEmailsTo(): string
{
return $this->getOriginal('alternate_email') ?? $this->alternate_email;
}
If you would like to have full control over IP address and login handling, you can create a custom driver by implementing the Zaengle\LaravelSecurityNotifications\Services\DigestIPAddress
interface like the example below.
<?php
use Zaengle\LaravelSecurityNotifications\Services\DigestIPAddress;
readonly class CustomIPAddressDriver implements DigestIPAddress
{
public function __construct(
private readonly string $ipAddress,
private readonly string $customField,
)
{
}
public function handle(): void
{
// Custom logic to handle IP address
}
}
Be sure to update the ip_address_driver
config value to point to your custom driver.
'ip_address_driver' => \Path\To\CustomIPAddressDriver::class,
This will tell the IPAddress
facade to use your custom driver. Simply pass an array of data to IPAddress::process()
matching your __construct
definition.
IPAddress::process([
'ipAddress' => $request->ip(),
'customField' => 'customValue',
]);
While this is not recommeded for most cases, you may run into a situation where users are on a network that changes IP addresses frequently (E.g. a public school network). In this case, the user is going to receive an email every time they log in because their IP address will have changed. This is probably not ideal. To account for this, you can configure the option to allow same location logins. This means that a user may login with as many IP addresses as they want and it will assume they are the same person as long as the location remains the same. This is based on the city
and state/region
.
To enable this, add the following value to the package config:
'allow_same_location_login' => true,
Be default, this package uses the free api provided by ip-api.com. If you would like to use their paid API to increase rate limiting, you can set the following env value with your api key:
IP_API_KEY=your_api_key
The MIT License (MIT). Please see License File for more information.