PHPackages                             nagibmahfuj/laravel-security-policies - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. nagibmahfuj/laravel-security-policies

ActiveLibrary

nagibmahfuj/laravel-security-policies
=====================================

Laravel Security Policies: session, password, MFA

v1.8.3(5mo ago)011MITPHPPHP ^8.0

Since Nov 10Pushed 5mo agoCompare

[ Source](https://github.com/nagibmahfuj/laravel-security-policies)[ Packagist](https://packagist.org/packages/nagibmahfuj/laravel-security-policies)[ RSS](/packages/nagibmahfuj-laravel-security-policies/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (3)Versions (15)Used By (0)

Laravel Security Policies
=========================

[](#laravel-security-policies)

A Laravel package that enforces organization-grade security policies:

- Strong session policy
- Password policy (complexity, expiry, history reuse prevention)
- Email-based multi-factor authentication (MFA) with trusted devices

Supports Laravel 8/9/10/11/12 and PHP 8.0+.

Features
--------

[](#features)

- Strong session policy
    - Idle timeout: force logout after X minutes of inactivity
    - Require MFA after X days since last verification
- Password policy
    - Configurable complexity: min length, digits, symbols, lowercase, uppercase
    - Password expiry: require change after X days
    - Password history: restrict reuse of last X passwords
- MFA via email OTP
    - OTP generation, TTL, max attempts, resend
    - Remember/trust device with cookie + DB record

Installation
------------

[](#installation)

Install via Composer:

```
composer require nagibmahfuj/laravel-security-policies
```

Then dump autoload:

```
composer dump-autoload -o
```

If auto-discovery is not active, register the service provider in `config/app.php`:

```
NagibMahfuj\LaravelSecurityPolicies\LaravelSecurityPoliciesServiceProvider::class,
```

Clear caches if needed:

```
php artisan config:clear && php artisan route:clear
```

Publish config
--------------

[](#publish-config)

Publish config:

```
php artisan vendor:publish --provider="NagibMahfuj\LaravelSecurityPolicies\LaravelSecurityPoliciesServiceProvider" --tag=security-policies-config
```

This will create a `config/security-policies.php` file with default values. You can modify these values as per your requirements. Check below for the configuration options.

Configuration
-------------

[](#configuration)

`config/security-policies.php` options (grouped):

### Session

[](#session)

KeyTypeDefaultDescription`session.idle_timeout_minutes`integer`30`Minutes of inactivity before forcing logout.`session.redirect_on_idle_to`string (route name)`login`Route to redirect to after idle timeout.`session.last_activity_store`string`session`Where to store last activity timestamp: 'session' or 'database'### MFA

[](#mfa)

KeyTypeDefaultDescription`mfa.enabled`bool`true`Enable/disable MFA enforcement.`mfa.mode`string`trusted_only``'trusted_only'` or `'grace_or_trusted'` (see below).`mfa.redirect_when_not_needed`string`'/'`URL or route name to redirect to when MFA is not required (user already verified).`mfa.grace_days_after_login`integer`30`Require MFA again if last verification is older than X days.`mfa.otp_length`integer`6`Length of the OTP code.`mfa.otp_ttl_minutes`integer`10`OTP validity window in minutes.`mfa.max_attempts`integer`5`Max verify attempts before requiring a new OTP.`mfa.throttle_per_minute`integer`5`Intended per-minute throttle (implement rate limiting as needed).`mfa.device_remember_days`integer`60`Days to trust a device when “remember this device” is selected.`mfa.remember_device_cookie`string`mfa_trusted_device`Cookie name for trusted device fingerprint.`mfa.device_session_control`string`multiple`Control device access: `'single'` or `'multiple'`.`mfa.single_device_action`string`logout_previous`Action when single device mode and new login detected: `'logout_previous'` or `'prevent_new'`.#### MFA Modes

[](#mfa-modes)

- **trusted\_only** (default)
    - Bypass MFA only if the request contains a trusted device cookie that matches a verified TrustedDevice record within `mfa.device_remember_days`.
    - If no trusted match exists, user is redirected to MFA verification on every login.
- **grace\_or\_trusted**
    - First, the middleware checks for a trusted device as above; if found, bypass MFA.
    - If not trusted, it allows access if the user's `user_columns.last_mfa_at` is within `mfa.grace_days_after_login`.
    - Otherwise, redirects to MFA verification.

#### Device Session Control

[](#device-session-control)

When `mfa.device_session_control` is set to `'single'`, users can only be logged in on one device at a time. This feature leverages the existing `trusted_devices` table to track active sessions.

- **single\_device\_action: logout\_previous** (default)

    - When a user logs in from a new device, all previously trusted devices are automatically invalidated by setting their `verified_at` timestamp to null.
    - The new device becomes the only active trusted device.
- **single\_device\_action: prevent\_new**

    - When a user tries to log in from a new device while already logged in elsewhere, the new login attempt is blocked.
    - The user is logged out and redirected to the login page with an error message explaining that single device access is enabled.
- **device\_session\_control: multiple** (default)

    - No device restrictions - users can be logged in on multiple devices simultaneously.

Device fingerprinting uses IP address, User-Agent, Accept-Language, and Accept headers to uniquely identify devices. The system automatically tracks device activity and updates last seen timestamps.

### Password

[](#password)

KeyTypeDefaultDescription`password.min_length`integer`12`Minimum password length.`password.min_digits`integer`1`Minimum digits required.`password.min_symbols`integer`1`Minimum symbols required.`password.min_lowercase`integer`1`Minimum lowercase letters required.`password.min_uppercase`integer`1`Minimum uppercase letters required.`password.allowed_symbols`string`!@#$%^&*()_+-={}[]:;'",.?/\|~`Allowed symbol set counted by StrongPassword and used to reject disallowed characters.`password.expire_days`integer`90`Force password change after X days.`password.history`integer`5`Disallow reuse of last X passwords.`password.require_history`bool`false`If true, user must have at least one password history entry; otherwise redirected to change page.`password.redirect_on_expired_to`string (route name)`password.request`Route to redirect to when password is expired or when history is required but missing.### User Columns

[](#user-columns)

KeyTypeDefaultDescription`user_columns.last_mfa_at`string`last_mfa_at`User model column that stores when MFA was last completed.`user_columns.password_changed_at`string`password_changed_at`User model column that stores when password was last changed.`user_columns.last_activity_at`string`last_active_at`User model column that stores the last activity timestamp when using database storage.Publish migrations:

```
php artisan vendor:publish --provider="NagibMahfuj\LaravelSecurityPolicies\LaravelSecurityPoliciesServiceProvider" --tag=security-policies-migrations
php artisan migrate
```

Register middleware aliases in `app/Http/Kernel.php`
----------------------------------------------------

[](#register-middleware-aliases-in-apphttpkernelphp)

If aliases are not already present, add these to `$middlewareAliases`:

```
use NagibMahfuj\LaravelSecurityPolicies\Http\Middleware\IdleTimeoutMiddleware;
use NagibMahfuj\LaravelSecurityPolicies\Http\Middleware\RequireRecentMfaMiddleware;
use NagibMahfuj\LaravelSecurityPolicies\Http\Middleware\PasswordExpiredMiddleware;

protected $middlewareAliases = [
    // ... existing aliases ...
    'security.idle'             => IdleTimeoutMiddleware::class,
    'security.mfa'              => RequireRecentMfaMiddleware::class,
    'security.password_expired' => PasswordExpiredMiddleware::class,
];
```

Add Middlewares
---------------

[](#add-middlewares)

The package registers aliases for convenience. In your route groups, add:

```
Route::middleware(['web', 'auth', 'security.mfa', 'security.password_expired'])->group(function () {
    // Protected routes...
});
```

The idle timeout is typically applied to the `web` group:

```
protected $middlewareGroups = [
    'web' => [
        // ...
        \NagibMahfuj\LaravelSecurityPolicies\Http\Middleware\IdleTimeoutMiddleware::class,
        // ...
    ],
];
```

Alternatively, use the alias `security.idle` in specific groups.

Use the Validation Rules
------------------------

[](#use-the-validation-rules)

In Laravel 9+, Apply these rules where users set or change passwords:

```
use NagibMahfuj\LaravelSecurityPolicies\Rules\StrongPassword;
use NagibMahfuj\LaravelSecurityPolicies\Rules\NotInRecentPasswords;

$request->validate([
    'password' => ['required', 'confirmed', new StrongPassword, new NotInRecentPasswords],
]);
```

For Laravel 8 and below, Apply these rules where users set or change passwords:

```
use NagibMahfuj\LaravelSecurityPolicies\Rules\StrongPasswordRule;
use NagibMahfuj\LaravelSecurityPolicies\Rules\NotInRecentPasswordsRule;

$request->validate([
    'password' => ['required', 'confirmed', new StrongPasswordRule, new NotInRecentPasswordsRule],
]);
```

MFA Routes and Views
--------------------

[](#mfa-routes-and-views)

The package provides routes:

- `GET /mfa/verify`: show OTP form
- `POST /mfa/verify`: verify OTP
- `POST /mfa/resend`: resend OTP

Views are loaded from the package and can be published/overridden:

```
php artisan vendor:publish --provider="NagibMahfuj\LaravelSecurityPolicies\LaravelSecurityPoliciesServiceProvider" --tag=security-policies-views
```

Database Schema
---------------

[](#database-schema)

- `password_histories`: user\_id, password\_hash, timestamps
- `mfa_challenges`: user\_id, code, expires\_at, consumed\_at, attempts, timestamps
- `trusted_devices`: user\_id, device\_fingerprint, user\_agent, ip\_address, verified\_at, last\_seen\_at, timestamps
- Alters `users` table (defaults): `last_mfa_at`, `password_changed_at`, `last_active_at`
    - You may rename these columns in your own migrations and set the names via `user_columns.*` in the config.

### Enabling Database Storage for Last Activity

[](#enabling-database-storage-for-last-activity)

To use database storage for last activity tracking:

1. Ensure your `users` table has a timestamp column for last activity (default: `last_active_at`). The published migration will add this if needed.
2. Set the following in `config/security-policies.php`: ```
    'session' => [
        'last_activity_store' => 'database', // or 'session' for the default behavior
    ],
    'user_columns' => [
        'last_activity_at' => 'last_active_at', // customize column name if needed
    ],
    ```
3. The middleware will now track last activity in the database instead of the session.

Events &amp; Listeners
----------------------

[](#events--listeners)

- Listens to `Illuminate\Auth\Events\PasswordReset`
    - Stores the updated hashed password into `password_histories`
    - Sets the configured `user_columns.password_changed_at = now()`

If you are using a custom password change flow, you can trigger the event manually after updating the password:

```
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Support\Facades\Hash;

// Update password
$user->update([
    'password' => Hash::make($request->password),
]);

// Trigger event
event(new PasswordReset($user));
```

Security Considerations
-----------------------

[](#security-considerations)

- Ensure mail is properly configured for OTP delivery.
- Consider enabling rate-limiting for the MFA verify/resend endpoints.
- Trusted device cookie is set with a fingerprint; adjust `device_remember_days` to your risk tolerance.
- Always keep Laravel and dependencies updated.

Testing
-------

[](#testing)

- Feature tests can assert middleware redirects when conditions are not met (MFA required, password expired, idle timeout).
- Unit tests for validation rules (StrongPassword, NotInRecentPasswords).

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE).

###  Health Score

34

—

LowBetter than 77% of packages

Maintenance70

Regular maintenance activity

Popularity5

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

14

Last Release

176d ago

PHP version history (3 changes)v1.0.0PHP ^8.2

v1.7.5PHP ^8.1

v1.7.6PHP ^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/1c692988733e3db3033b824f363cc6fd87548bd8a27959b852064f066acea931?d=identicon)[nagibmahfuj](/maintainers/nagibmahfuj)

---

Top Contributors

[![nagibmahfuj](https://avatars.githubusercontent.com/u/12795882?v=4)](https://github.com/nagibmahfuj "nagibmahfuj (22 commits)")

---

Tags

laravellaravel-frameworklaravel-packagelaravel-securitymfa-policymulti-factor-authenticationpassword-policyphpsession-policyphplaravellaravel-packageLaravel SecurityMulti Factor Authenticationlaravel-frameworkpassword policysession-policymfa-policy

### Embed Badge

![Health badge](/badges/nagibmahfuj-laravel-security-policies/health.svg)

```
[![Health](https://phpackages.com/badges/nagibmahfuj-laravel-security-policies/health.svg)](https://phpackages.com/packages/nagibmahfuj-laravel-security-policies)
```

###  Alternatives

[yadahan/laravel-authentication-log

Laravel Authentication Log provides authentication logger and notification for Laravel.

416632.8k5](/packages/yadahan-laravel-authentication-log)[imanghafoori/laravel-password-history

A package to keep a history of all password changes of users

70190.9k1](/packages/imanghafoori-laravel-password-history)[joisarjignesh/bigbluebutton

BigBlueButton Server API Library for Laravel

162145.5k1](/packages/joisarjignesh-bigbluebutton)[sbsaga/toon

🧠 TOON for Laravel — a compact, human-readable, and token-efficient data format for AI prompts &amp; LLM contexts. Perfect for ChatGPT, Gemini, Claude, Mistral, and OpenAI integrations (JSON ⇄ TOON).

6115.6k](/packages/sbsaga-toon)[waad/laravel-profanity-filter

Laravel Profanity Filter - Powerful PHP package for detecting, filtering, and masking profanity in multiple languages. Supports leet speak, custom word lists, case sensitivity, and seamless Laravel integration.

202.9k](/packages/waad-laravel-profanity-filter)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
