PHPackages                             3brs/sylius-enterprise-security-plugin - 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. [Security](/categories/security)
4. /
5. 3brs/sylius-enterprise-security-plugin

ActiveSylius-plugin[Security](/categories/security)

3brs/sylius-enterprise-security-plugin
======================================

Advanced security plugin for Sylius — 2FA, password policies, account protection, GDPR

043[1 PRs](https://github.com/3BRS/sylius-enterprise-security-plugin/pulls)PHPCI passing

Since Apr 13Pushed 1w agoCompare

[ Source](https://github.com/3BRS/sylius-enterprise-security-plugin)[ Packagist](https://packagist.org/packages/3brs/sylius-enterprise-security-plugin)[ RSS](/packages/3brs-sylius-enterprise-security-plugin/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependenciesVersions (27)Used By (0)

 [ ![](https://camo.githubusercontent.com/bb0c16caf210bea68e0e59fc11e0844dc47097f9aeb8b388c3de5f1414d4d1c0/68747470733a2f2f33627273312e667261312e63646e2e6469676974616c6f6365616e7370616365732e636f6d2f336272732f6c6f676f2f334252532d6c6f676f2d73796c6975732d3230302e706e67) ](https://www.3brs.com)

 Enterprise Security Plugin
 [ ![](https://camo.githubusercontent.com/7fb584f71473ea5d540e8efea4b7a9eb6519cfa767b977ed38a7ed3c28b46785/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f336272732f73796c6975732d656e74657270726973652d73656375726974792d706c7567696e2e737667) ](https://packagist.org/packages/3brs/sylius-enterprise-security-plugin "License") [ ![](https://camo.githubusercontent.com/6d28f2006cf59bf7c704c719ff9f9123202833a4b65428186abc4673e42441a1/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f336272732f73796c6975732d656e74657270726973652d73656375726974792d706c7567696e2e737667) ](https://packagist.org/packages/3brs/sylius-enterprise-security-plugin "Version") [ ![](https://github.com/3BRS/sylius-enterprise-security-plugin/actions/workflows/ci.yml/badge.svg) ](https://github.com/3BRS/sylius-enterprise-security-plugin/actions "Build status")
=======================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================

[](#----enterprise-security-plugin----------------------------------------------------)

3BRS Enterprise Security
------------------------

[](#3brs-enterprise-security)

- Password Policy
- Password History
- Password Expiration
- Password Change Notifications
- Two-Factor Authentication
- 3rd-party OAuth (Social Login)
- Magic Link Login
- Passkey Login (WebAuthn / FIDO2)
- Account Lockout &amp; Rate Limiting
- Session Management &amp; Login Notifications
- Centralized Security Settings UI
- Self-Service Account Deletion (GDPR)
- Admin IP Whitelist
- Admin IP Blacklist
- Admin Customer Management
- Password Login

[![Two-factor authentication setup with QR code and recovery secret](docs/images/two-factor-setup.png)](docs/images/two-factor-setup.png)

[![Active sessions list with per-device sign-out and revocation](docs/images/active-sessions.png)](docs/images/active-sessions.png)

[![Admin customer Security section — force password reset, block, sessions and login history](docs/images/customer-security-section.png)](docs/images/customer-security-section.png)

[![Centralized Security Settings admin UI — password policy, history and rate limiting](docs/images/security-settings.png)](docs/images/security-settings.png)

Features
--------

[](#features)

Most features ship **disabled by default** (the exception is **Password Login**, which is on so existing stores keep working) — enable and tune the ones you need (each linked doc covers its options and defaults).

FeatureWhat it doesDocPassword PolicyConfigurable password complexity (min/max length + uppercase / lowercase / number / special-character rules), separately per customer and admin group; overrides Sylius's weak 3-character default.[password-policy](docs/password-policy.md)Password HistoryPrevents reuse of recent passwords, with a configurable remembered count per group; customer and admin history stored separately.[password-history](docs/password-history.md)Password ExpirationForces a password change after a configurable number of days, with an optional `force_change` on next login; independent for customers and admins. Existing accounts are measured from their creation date until the first change, so enabling it won't reset everyone at once.[password-expiration](docs/password-expiration.md)Password Change NotificationsEmails the user on every password change (account settings, forgot-password reset, admin-initiated) with the timestamp, IP address and a secure-account link.[password-change-notifications](docs/password-change-notifications.md)Two-Factor AuthenticationTOTP 2FA (Google Authenticator, Authy, 1Password, …) with QR-code setup, single-use recovery codes, trusted devices and per-group `disabled` / `allowed` / `enforced` modes. Built on `scheb/2fa-bundle`.[two-factor-authentication](docs/two-factor-authentication.md)3rd-party OAuth (Social Login)Google, Apple and Microsoft sign-in per group, with account-link takeover protection (a single-use, time-limited emailed code), optional domain-gated auto-registration and an extensible provider registry.[oauth-social-login](docs/oauth-social-login.md)Magic Link LoginPasswordless email sign-in with single-use, hashed, time-limited tokens, anti-enumeration, timing-attack padding and rate limiting. As a passwordless method it bypasses 2FA (the second factor guards password login only).[magic-link-login](docs/magic-link-login.md)Passkey Login (WebAuthn / FIDO2)Passwordless passkeys (Touch ID / Windows Hello / Android lock / hardware keys), multiple labelled keys per user, built on `web-auth/webauthn-lib`. As a passwordless method it bypasses 2FA (the second factor guards password login only).[passkey-login](docs/passkey-login.md)Account Lockout &amp; Rate LimitingPersistent per-user account lockout after N failed sign-ins (auto- or admin-unlock) plus ephemeral request rate limiting (login, password reset, registration, magic link).[account-lockout-rate-limiting](docs/account-lockout-rate-limiting.md)Session Management &amp; Login NotificationsActive-session listing with manual revocation (single or all-other), plus optional email alerts on sign-in from a previously unseen device; pluggable GeoIP lookup.[session-management-login-notifications](docs/session-management-login-notifications.md)Centralized Security Settings UIA single admin page (`/admin/security-settings`) to configure every feature at runtime — values persist in the database and apply on the next request, no YAML edits or redeploys.[centralized-security-settings-ui](docs/centralized-security-settings-ui.md)Self-Service Account Deletion (GDPR)Customer-driven erasure (GDPR right to be forgotten) with a configurable grace period, admin-side cancellation and a cron that anonymizes name / email / phone / address.[account-deletion-gdpr](docs/account-deletion-gdpr.md)Admin IP WhitelistRestrict admin-panel access to allowed IPs / CIDR ranges, with a team-wide global list plus optional per-admin lists.[admin-ip-whitelist](docs/admin-ip-whitelist.md)Admin IP BlacklistBlock specific IPs / CIDR ranges from the admin panel with a global deny-list — always wins over the whitelist, is identity-agnostic, and fails open when empty.[admin-ip-blacklist](docs/admin-ip-blacklist.md)Admin Customer ManagementA Security section on the Sylius customer detail page — force password reset, block / unblock, sign out of all or individual sessions, plus active-sessions and login-history tables.[admin-customer-management](docs/admin-customer-management.md)Password LoginGlobal per-group (customer / admin) switch — **on by default** — for classic email + password sign-in and registration. Turn it off for a group and its login / registration forms are hidden (including the checkout inline sign-in), its forgotten-password pages are closed, the admin panel creates that group's accounts without a password, everyone must use a magic link, passkey or social login, and that group's password expiration / force-change pause too.[password-login](docs/password-login.md)Installation (into an existing Sylius application)
--------------------------------------------------

[](#installation-into-an-existing-sylius-application)

This section is for **consuming** the plugin in your own Sylius project — you register the bundle/plugin and wire the config yourself. If you instead want to **work on the plugin itself**, skip to **Development** below: its bundled test application already has the bundle, plugin and routes registered, so you don't repeat these steps.

> **This plugin requires the standalone `3brs/enterprise-security-bundle` package and does not work without it.** The bundle is the framework-agnostic core (security validators, services, entity contracts); the plugin is the thin Sylius integration layer on top (admin / shop UI, controllers, routes, fixtures). That's why step 1 installs **both** packages (the bundle comes in as a dependency of the plugin) and step 2 registers them, and the entity traits in step 5 implement interfaces that live in the bundle.

> Almost every feature ships **disabled by default** (see each feature's doc under [`docs/`](docs/)) — the one exception is **Password Login**, which is **on** so existing stores keep working. You enable only what you need in step 3, and the firewall / entity wiring in steps 5–6 is only required for the features you turn on.

1. Require the plugin (its standalone bundle and the Scheb 2FA bundle are pulled in automatically as dependencies):

    ```
    composer require 3brs/sylius-enterprise-security-plugin
    ```
2. Register the bundles in `config/bundles.php` (the plugin, its standalone bundle, and the Scheb 2FA bundle it builds on):

    ```
    return [
        // ...
        Scheb\TwoFactorBundle\SchebTwoFactorBundle::class => ['all' => true],
        ThreeBRS\EnterpriseSecurityBundle\ThreeBRSEnterpriseSecurityBundle::class => ['all' => true],
        ThreeBRS\SyliusEnterpriseSecurityPlugin\ThreeBRSSyliusEnterpriseSecurityPlugin::class => ['all' => true],
    ];
    ```
3. Import the plugin configuration and enable the features you want by creating `config/packages/threebrs_sylius_enterprise_security_plugin.yaml`:

    ```
    imports:
        - { resource: "@ThreeBRSSyliusEnterpriseSecurityPlugin/Resources/config/config.yaml" }

    three_brs_sylius_enterprise_security:
        # Turn on and tune the features you need — each feature's doc under
        # docs/ documents its options and defaults (everything is off by default,
        # except Password Login, which is on).
    ```
4. Import the plugin routes by creating `config/routes/three_brs_enterprise_security.yaml` (without this none of the plugin endpoints — passkey, magic link, 2FA setup, OAuth, account deletion, settings UI — are registered):

    ```
    three_brs_enterprise_security:
        resource: "@ThreeBRSSyliusEnterpriseSecurityPlugin/Resources/config/routes.yaml"
    ```
5. Add the relevant traits to your `ShopUser` and `AdminUser` entities. Include **only** the traits for the features you enabled — `PasswordExpiration*` (Password Expiration), `TwoFactorAuth*` (Two-Factor Authentication), `Lockable*` (Account Lockout). (Password Expiration also reads the account creation date via `getCreatedAt()` as its fallback for users who have never changed their password — Sylius's base `ShopUser` / `AdminUser` already expose it, so no extra wiring is needed here.) The full set:

    ```
    // src/Entity/User/ShopUser.php
    use Sylius\Component\Core\Model\ShopUser as BaseShopUser;
    use ThreeBRS\EnterpriseSecurityBundle\Lockout\LockableShopUserInterface;
    use ThreeBRS\EnterpriseSecurityBundle\PasswordExpiration\PasswordExpirationShopUserInterface;
    use ThreeBRS\EnterpriseSecurityBundle\TwoFactor\TwoFactorAuthShopUserInterface;
    use ThreeBRS\SyliusEnterpriseSecurityPlugin\Model\LockableShopUserTrait;
    use ThreeBRS\SyliusEnterpriseSecurityPlugin\Model\PasswordExpirationShopUserTrait;
    use ThreeBRS\SyliusEnterpriseSecurityPlugin\Model\TwoFactorAuthShopUserTrait;

    class ShopUser extends BaseShopUser implements PasswordExpirationShopUserInterface, TwoFactorAuthShopUserInterface, LockableShopUserInterface
    {
        use PasswordExpirationShopUserTrait;
        use TwoFactorAuthShopUserTrait;
        use LockableShopUserTrait;
    }
    ```

    ```
    // src/Entity/User/AdminUser.php
    use Sylius\Component\Core\Model\AdminUser as BaseAdminUser;
    use ThreeBRS\EnterpriseSecurityBundle\Lockout\LockableAdminUserInterface;
    use ThreeBRS\EnterpriseSecurityBundle\PasswordExpiration\PasswordExpirationAdminUserInterface;
    use ThreeBRS\EnterpriseSecurityBundle\TwoFactor\TwoFactorAuthAdminUserInterface;
    use ThreeBRS\SyliusEnterpriseSecurityPlugin\Model\LockableAdminUserTrait;
    use ThreeBRS\SyliusEnterpriseSecurityPlugin\Model\PasswordExpirationAdminUserTrait;
    use ThreeBRS\SyliusEnterpriseSecurityPlugin\Model\TwoFactorAuthAdminUserTrait;

    class AdminUser extends BaseAdminUser implements PasswordExpirationAdminUserInterface, TwoFactorAuthAdminUserInterface, LockableAdminUserInterface
    {
        use PasswordExpirationAdminUserTrait;
        use TwoFactorAuthAdminUserTrait;
        use LockableAdminUserTrait;
    }
    ```

    > Magic link, passkey, OAuth, session management, login notifications and account deletion keep their data in their own tables (foreign-keyed to `ShopUser` / `AdminUser`) and need **no** traits.
6. Configure the firewall for the features you enabled, in `config/packages/security.yaml` (and `config/packages/scheb_2fa.yaml` for 2FA). Each feature's doc under `docs/` contains the exact block to copy:

    - **Two-Factor Authentication** — the `scheb_2fa.yaml` config, the shop `success_handler`, and the `two_factor` blocks on the `shop` / `admin` firewalls.
    - **3rd-party OAuth**, **Magic Link Login**, **Passkey Login** — the `PUBLIC_ACCESS` `access_control` entries that expose their login endpoints.
7. Update the database schema to create the plugin tables (`three_brs_*`) and the trait columns added in step 5:

    ```
    bin/console doctrine:schema:update --complete --force
    ```

    In production generate and run a migration with your usual workflow instead.
8. Install the bundled assets (e.g. the passkey browser script):

    ```
    bin/console assets:install
    ```

Upgrading from 1.x
------------------

[](#upgrading-from-1x)

Per-user password-login control has been replaced by a global per-scope switch, managed on the **Security Settings** admin screen. The admin-side trait and interface it required are gone, so a consumer whose `AdminUser` still follows the earlier README hits a class-not-found error on `composer update` — before the schema is even reached. Migrate by hand:

1. Remove `PasswordLoginControlAdminUserTrait` and `PasswordLoginControlAdminUserInterface` (both under `ThreeBRS\SyliusEnterpriseSecurityPlugin\Model\`) from your `AdminUser` entity — the trait `use`, the `implements`, and their two imports.
2. Run `bin/console doctrine:schema:update --complete --force` (or a migration in production) to drop the now-orphaned `sylius_admin_user.password_login_allowed` column and `three_brs_customer_login_preference` table. Their stored per-user preferences are discarded on purpose — the global switch supersedes them.

Troubleshooting
---------------

[](#troubleshooting)

### `Cannot create union with both "object" and class type` during cache clear / warmup

[](#cannot-create-union-with-both-object-and-class-type-during-cache-clear--warmup)

If `bin/console cache:clear` (or any route / API metadata warmup) fails with:

```
Cannot create union with both "object" and class type.

```

this is an **upstream api-platform regression, not a plugin bug**. API Platform's property-metadata scanner (Symfony's `PhpStanExtractor` → `TypeInfo`) chokes on generic `@template T of object` PHPDoc present in some of the plugin's transitive dependencies (e.g. `web-auth/webauthn-lib`), trying to build an `object|SomeClass` union that `TypeInfo` rejects. Because API Platform is enabled by default in Sylius 2, you hit it right after installing the plugin.

It affects api-platform `4.3.x` (reproduced on 4.3.5–4.3.7; no fixed release exists at the time of writing). Until an upstream fix ships, work around it by decorating the property-info extractors with a wrapper that swallows the `TypeInfo` exception.

Add the decorator class to your application — use the plugin's [`SafePhpStanExtractor`](tests/Application/src/PropertyInfo/SafePhpStanExtractor.php) as a reference implementation. It implements every property-info extractor interface (on Symfony 7.3+ also `ConstructorArgumentTypeExtractorInterface`) and returns `null` whenever the inner extractor throws `Symfony\Component\TypeInfo\Exception\InvalidArgumentException`. Then register it over both Symfony's and API Platform's extractor services:

```
# config/services.yaml
services:
    App\PropertyInfo\SafePhpStanExtractor:
        arguments: { $inner: '@.inner' }
        decorates: property_info.phpstan_extractor
        decoration_on_invalid: ignore

    app.property_info.safe_php_doc_extractor:
        class: App\PropertyInfo\SafePhpStanExtractor
        arguments: { $inner: '@.inner' }
        decorates: property_info.php_doc_extractor
        decoration_on_invalid: ignore

    app.property_info.safe_reflection_extractor:
        class: App\PropertyInfo\SafePhpStanExtractor
        arguments: { $inner: '@.inner' }
        decorates: property_info.reflection_extractor
        decoration_on_invalid: ignore

    # API Platform registers its own parallel extractor services — decorate those too:
    app.property_info.api_platform_safe_phpstan_extractor:
        class: App\PropertyInfo\SafePhpStanExtractor
        arguments: { $inner: '@.inner' }
        decorates: api_platform.property_info.phpstan_extractor
        decoration_on_invalid: ignore

    app.property_info.api_platform_safe_php_doc_extractor:
        class: App\PropertyInfo\SafePhpStanExtractor
        arguments: { $inner: '@.inner' }
        decorates: api_platform.property_info.php_doc_extractor
        decoration_on_invalid: ignore

    app.property_info.api_platform_safe_reflection_extractor:
        class: App\PropertyInfo\SafePhpStanExtractor
        arguments: { $inner: '@.inner' }
        decorates: api_platform.property_info.reflection_extractor
        decoration_on_invalid: ignore
```

> The decorator is harmless once the upstream bug is fixed (it only catches an exception that no longer fires), but remove it after you upgrade to a fixed api-platform release to keep your container clean.

### `Unable to generate an IRI for the item of type ...` on API responses

[](#unable-to-generate-an-iri-for-the-item-of-type--on-api-responses)

If, after adding the decorator above, an API endpoint that embeds related resources fails with a 400 such as:

```
Unable to generate an IRI for the item of type "App\Entity\Product\ProductImage"

```

— for example `GET /api/v2/shop/products` (embedded product images) or `GET /api/v2/admin/product-variants/{code}` (embedded channel pricings) — disable API Platform's constructor property-info extractor:

```
# config/packages/property_info.yaml
framework:
    property_info:
        with_constructor_extractor: false
```

On api-platform `4.3.x` this extractor runs before Doctrine's and resolves relation properties such as `Image::$owner` to a bare `object` (from their `@var object|null` PHPDoc), hiding the concrete related class that API Platform needs to build nested IRIs like `/shop/products/{code}/images/{id}`. Turning it off lets the Doctrine extractor resolve the concrete class again. `false` is also Symfony's current default for this setting, so this only makes it explicit (and silences the related Symfony 7.3 deprecation); the Symfony recipe ships it pre-enabled, which is why you may need to flip it.

Development
-----------

[](#development)

This section is **only** for contributing to / developing the plugin — not for installing it into your own app (that's the *Installation* section above). The bundled **test application** under [`tests/Application/`](./tests/Application/) already has the bundle, plugin, routes and feature config registered (it's part of this repo), so you do **not** repeat the Installation steps — `make init` brings the whole stack up ready to go.

### Usage

[](#usage)

- Develop the plugin in `/src`; the framework-agnostic core lives in the separate [`3brs/enterprise-security-bundle`](https://packagist.org/packages/3brs/enterprise-security-bundle) package (its own repository)
- See [`bin/`](./bin) and [`Makefile`](./Makefile) for useful commands

### Bootstrapping the dev environment

[](#bootstrapping-the-dev-environment)

Spin up the dockerized test application (DB, PHP, assets, migrations, fixtures wiring) in one go:

```
make init
```

This builds the containers, runs `composer install`, creates the database, applies all migrations (including the plugin's `three_brs_*_social_account_link` tables) and builds the frontend assets. Use `make init-tests` for the `test` environment.

In the dev environment both Google and Apple OAuth providers are swapped for a fake in-memory provider (see [`tests/Application/config/services_dev.yaml`](./tests/Application/config/services_dev.yaml)) so the social-login buttons work end-to-end without any external credentials. To exercise the real Google/Apple flows locally, comment out the `FakeOAuthProvider` override and fill in your credentials in `tests/Application/.env.local`.

### Testing

[](#testing)

After your changes you must ensure that the tests are still passing.

```
make ci
```

License
-------

[](#license)

MIT License. See [LICENSE](./LICENSE) for details.

Credits
-------

[](#credits)

Developed by [3BRS](https://3brs.com)

###  Health Score

28

—

LowBetter than 52% of packages

Maintenance64

Regular maintenance activity

Popularity11

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity24

Early-stage or recently created project

 Bus Factor1

Top contributor holds 62.8% 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.

### Community

Maintainers

![](https://www.gravatar.com/avatar/ab23998c28b111996e4b3357f20929d6904a97ac21724750e1dfafd6a09791e4?d=identicon)[ondrej-kuhnel](/maintainers/ondrej-kuhnel)

---

Top Contributors

[![FilipHoracek](https://avatars.githubusercontent.com/u/231852167?v=4)](https://github.com/FilipHoracek "FilipHoracek (27 commits)")[![FapLipos](https://avatars.githubusercontent.com/u/210017008?v=4)](https://github.com/FapLipos "FapLipos (14 commits)")[![ondrej-kuhnel](https://avatars.githubusercontent.com/u/6840281?v=4)](https://github.com/ondrej-kuhnel "ondrej-kuhnel (2 commits)")

### Embed Badge

![Health badge](/badges/3brs-sylius-enterprise-security-plugin/health.svg)

```
[![Health](https://phpackages.com/badges/3brs-sylius-enterprise-security-plugin/health.svg)](https://phpackages.com/packages/3brs-sylius-enterprise-security-plugin)
```

###  Alternatives

[mews/purifier

Laravel 5/6/7/8/9/10 HtmlPurifier Package

2.0k18.7M144](/packages/mews-purifier)[paragonie/ecc

PHP Elliptic Curve Cryptography library

24820.0k41](/packages/paragonie-ecc)

PHPackages © 2026

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