Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Advanced Encoder Bundle Laravel Package

friendsofsymfony/advanced-encoder-bundle

A Symfony bundle that provides an advanced password encoder factory, letting different users use different encoders. Useful for migrating legacy password hashes and gradually upgrading users to stronger algorithms without forcing password resets or locking accounts out.

View on GitHub
Deep Wiki
Context7

Getting Started

What it actually solves. Symfony's stock security.encoder_factory resolves a password encoder by class (get_class($user)). That means every Acme\UserBundle\Entity\User row gets the same algorithm. This bundle swaps in a factory that first asks the user object itself which encoder to use, falling back to the class-based lookup. The canonical use case is a password migration: old rows hashed with md5 (or sha512), new rows with bcrypt or pbkdf2, both verified correctly while you backfill.

Install (legacy Symfony 2.x only).

composer require friendsofsymfony/advanced-encoder-bundle
// app/AppKernel.php
new FOS\AdvancedEncoderBundle\FOSAdvancedEncoderBundle(),

Nothing else is required if the bundle's compiler pass wires itself over security.encoder_factory; otherwise import the service config from Resources/config/.

Version note (v1.1.0). The 1.1.0 release adds explicit support for Symfony 2.3+'s bcrypt and pbkdf2 encoders and adds CI coverage on PHP 5.5, 5.6 and HHVM. This does not change the integration contract — it only means the modern-encoder side of a migration can now be bcrypt/pbkdf2 rather than being limited to sha512-style digest encoders. If you are on Symfony < 2.3, those two encoder types are unavailable and the release note does not apply to you.

Where to look first, in this order:

  1. README.md — short, but it states the intended contract.
  2. DependencyInjection/Configuration.php — the only trustworthy source for the config tree keys; do not guess them.
  3. Security/Encoder/ — the factory implementation and the marker interface your User entity must implement (EncoderAwareInterface-style, exposing getEncoderName()).
  4. Resources/config/*.xml — confirms whether the bundle decorates or replaces security.encoder_factory, which determines whether you need to keep security.encoders populated.

First use case. Add a nullable encoder column to your user table. Seed legacy rows with the legacy encoder name (sha512, or whatever your pre-migration default was), new rows with bcrypt. Implement the interface to return that value (or null to fall through). On successful login, if the stored name differs from the current default, rehash and persist. That's the whole win: no forced password resets.

Implementation Patterns

Per-instance override on the entity. The cleanest integration is on a base user class so every concrete user inherits it:

use FOS\AdvancedEncoderBundle\Security\Encoder\EncoderAwareInterface;

class User implements EncoderAwareInterface
{
    /** @ORM\Column(type="string", nullable=true) */
    private $encoder;

    public function getEncoderName()
    {
        return $this->encoder ?: null; // null => default class-based encoder
    }
}

Everything downstream (DaoAuthenticationProvider, your custom login form, security.encoder_factory consumers) picks this up without changes — that's the main appeal of doing it at the factory layer rather than in your auth code.

Migration listener, not a controller. Hook security.interactive_login (or your success handler) and only after authentication succeeds:

public function onLogin(InteractiveLoginEvent $event)
{
    $user = $event->getAuthenticationToken()->getUser();
    $current = $this->defaultEncoderName; // e.g. 'bcrypt', from your security config

    if ($user->getEncoderName() !== $current) {
        $encoder = $this->encoderFactory->getEncoder($user);
        $user->setPassword($encoder->encodePassword(
            $event->getRequest()->get('_password'),
            $user->getSalt()   // must be null/'' for bcrypt & pbkdf2
        ));
        $user->setEncoderName($current);
        $this->em->flush();
    }
}

Never rehash before verification — you'd be trusting an unauthenticated password.

Configuring the modern encoders. In security.yml the 2.3+ encoder keys are bcrypt and pbkdf2; you can also use the FQCNs (Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder, Pbkdf2PasswordEncoder) if you need to pass constructor arguments. Keep the class-level entry pointing at the new encoder so freshly created users and null-returning users get the modern hash:

security:
    encoders:
        Acme\UserBundle\Entity\User:
            algorithm: bcrypt
            cost: 12

Multi-tenant / externally provisioned users. If users arrive with hashes produced elsewhere, store the algorithm identifier alongside the hash and let the entity report it. This is the second legitimate day-to-day use: one user table, several provisioning sources, each with its own hashing policy. With 1.1.0 those sources can now include bcrypt/PBKDF2 systems without you writing a custom encoder wrapper.

Keep the class-based default in security.encoders. Even when overriding per instance, leave a sane default configured for the class so users that return null still authenticate. The bundle's factory should fall back; verify that against the source before relying on it.

Test at the factory boundary. Unit-test getEncoder($user) with plain mock users returning different names — and include bcrypt and pbkdf2 in the matrix, now that 1.1.0 claims support. Add one functional login test per encoder. This is cheap and catches config typos that otherwise only surface on a real user's first login.

Gotchas and Tips

  • Archived and unmaintained. Target is Symfony 2.x. On Symfony 4.3+ this problem is solved natively by MigratingPasswordEncoder/PasswordHasherFactory; on Symfony 2.6+ an EncoderAwareInterface exists in the core. 1.1.0 shows the project was still receiving occasional maintenance at that point, but it does not extend the supported platform — before adopting, check whether your Symfony version already does this, since you may only need the ~50 lines of the factory, copied and owned.
  • The bundle predates service decoration (Symfony 2.4). It likely replaces the security.encoder_factory class parameter or uses a compiler pass. Confirm which, because a replace-style override silently breaks if a second bundle does the same.
  • bcrypt/PBKDF2 and the salt column. Both algorithms embed their own salt. Symfony 2.3's BCryptPasswordEncoder rejects a non-null salt passed to encodePassword(), and PBKDF2 likewise generates its own. Users on these encoders must have getSalt() return null/empty — the classic failure is a legacy salt column that is still populated, so login works for legacy users and hashing throws for new ones. Decide this per encoder, not globally.
  • bcrypt's 72-byte ceiling. Passwords longer than 72 bytes are truncated by bcrypt (and Symfony 2.3's implementation may reject them outright). If your legacy system accepted long passphrases, verify the migration path for those accounts and log a warning rather than silently reducing their entropy.
  • pbkdf2 cost is a CPU decision. The default iteration count can be slow on the PHP 5.5-era hardware this bundle targets; measure your login latency. Likewise tune cost for bcrypt rather than accepting defaults — this is the one place where 1.1.0's new support is a config decision, not a code one.
  • Stale/garbage encoder names in the DB throw. A typo in the stored value produces a "no encoder found" style exception at login. Whitelist the value on write (including the new bcrypt/pbkdf2 names) and treat unknown values as null on read; never let a user-editable field reach the factory unchecked.
  • Empty string vs. null. The fallback contract depends on returning a falsy value. getEncoderName() returning '' from a default-initialized column is the classic bug — normalize in the getter.
  • Don't confuse this with authorization. The encoder name only affects hash verification. It must never influence roles, voters, or isGranted decisions; keep it out of anything security-sensitive beyond password checking.
  • Session/serialization. The value must survive user serialization into the token. If you use a custom user provider or Serializable implementation, add the property explicitly or the override disappears mid-session.
  • Debugging recipe: debug:container security.encoder_factory to confirm the override is live; dump get_class($user) and getEncoderName() in the login path; run a password_verify-equivalent check manually against a known legacy row and a known bcrypt row.
  • Rehash edge cases: skip when there's no plaintext credential (remember-me, token auth, programmatic login). Guard on the credentials being present, not just on the event firing.
  • PHP 5.5+ native option. The CI matrix added in 1.1.0 confirms 5.5 support, which also means password_hash()/password_verify() are available. If you are not on Symfony's encoder API anyway, a thin custom factory wrapping the native functions is a viable, dependency-free alternative — and sidesteps the salt-column and cost-config friction above.
  • Extension point if the interface is too rigid: subclass/wrap their factory and resolve the name via a convention (e.g., a getEncoderName() method discovered by reflection) instead of forcing the interface onto every user class.
  • Composer friction: old constraints will fight modern symfony/security-bundle and PHP 7+. Budget for a fork or vendoring rather than expecting a clean resolve; 1.1.0 does not loosen any of those constraints.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package