Symfony2 custom password encoder and password hash update

I’m learning Symfony2 by moving some wordpress blog to Symfony. I’m stuck with login procedure. WordPress uses non standard password hashing like $P$.... and I want to check users against old password hash when they login and when password is correct, rehash it to bcrypt. So far I created custome encoder class to use with symfony security mechanism.

<?php
namespace PkrBlogUserBundleServiceEncoder;

use PHPassLibApplicationContext;
use SymfonyComponentSecurityCoreEncoderBCryptPasswordEncoder;
use SymfonyComponentSecurityCoreEncoderPasswordEncoderInterface;
use SymfonyComponentSecurityCoreUtilSecureRandom;

class WpTransitionalEncoder implements PasswordEncoderInterface
{

    public function __construct($cost = 13)
    {
        $secure = new SecureRandom();
        $this->_bcryptEncoder = new BCryptPasswordEncoder($secure, $cost);
    }

    public function isPasswordValid($encoded, $raw, $salt)
    {
        if (preg_match('^$P$', $encoded)) {
            $context = new Context();
            $context->addConfig('portable');
            return $context->verify($raw, $encoded);
        }
        return $this->_bcryptEncoder->isPasswordValid($encoded, $raw, $salt);
    }

    public function encodePassword($raw, $salt)
    {
        return $this->_bcryptEncoder->encodePassword($raw, $salt);
    }
}

I’m using it as a service:

Read More
#/src/Pkr/BlogUserBundle/Resources/config/services.yml
services:
    pkr_blog_user.wp_transitional_encoder:
        class: PkrBlogUserBundleServiceEncoderWpTransitionalEncoder

And in security.yml:

#/app/config/security.yml
security:
encoders:
    PkrBlogUserBoundleEntityUser:
        id:   pkr_blog_user.wp_transitional_encoder
        cost: 15

My questions are:

How do I pass parameters to my encoder service form within security.yml?

I’m asking because cost: 15 does not work.

Where should I put password hash update logic? I was thinking that maby just after password validation something like this:

public function isPasswordValid($encoded, $raw, $salt)
{
    if (preg_match('^$P$', $encoded)) {
        $context = new Context();
        $context->addConfig('portable');
        $isValid = $context->verify($raw, $encoded);
        if ($isValid) {
            // put logic here...
        }
        return $isValid;
    }
    return $this->_bcryptEncoder->isPasswordValid($encoded, $raw, $salt);
}

but it seem somehow like wrong place for it. So what is the right way?

Related posts

Leave a Reply

1 comment

  1. I’ll answer my own question.

    I placed parameters for my encoder service inside config.yml

    pkr_blog_user:
        password_encoder:
            cost: 17
    

    They will be passed to my bundle extension class:

    # /src/Pkr/BlogUserBundle/DependencyInjection/PkrBlogUserExtension.php
    namespace PkrBlogUserBundleDependencyInjection;
    
    use SymfonyComponentDependencyInjectionContainerBuilder;
    use SymfonyComponentConfigFileLocator;
    use SymfonyComponentHttpKernelDependencyInjectionExtension;
    use SymfonyComponentDependencyInjectionLoader;
    
    /**
    * This is the class that loads and manages your bundle configuration
    *
    * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html}
    */
    class PkrBlogUserExtension extends Extension
    {
        /**
        * {@inheritDoc}
        */
        public function load(array $configs, ContainerBuilder $container)
        {
            $configuration = new Configuration();
            $config = $this->processConfiguration($configuration, $configs);
    
            $loader = new LoaderYamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
            $loader->load('services.yml');
    
            if ($config['password_encoder']['cost'] < 10) {
                $config['password_encoder']['cost'] = sprintf('%02d', $config['password_encoder']['cost']);
            }
            $container->setParameter('pkr_blog_user.wp_transitional_encoder.cost', $config['password_encoder']['cost']);
    
        }
    }
    

    I found out that I could use my own authentication success handler so there is a good place to put password rehash logic. Unfortunately when using custom handler symfony2 won’t pass config to class constructor but I found a way to make it work. I described it here:

    https://stackoverflow.com/a/15988399/1089412