2

I am trying to authenticate user against database. It works well for plaintext as password encoding. But when I am using bccrypt to encrypt password it gives 'bad credentials'.

Here is my security.yml

security:
    firewalls:
        secured_area:
            pattern:    ^/
            anonymous: ~
            form_login:
                login_path:  /login
                check_path:  /login_check
            logout:
                path:   /logout
                target: /

    role_hierarchy:
            ROLE_ADMIN: ROLE_USER

    access_control:
        - { path: ^/admin, roles: ROLE_USER }

    providers:
        chain_provider:
            chain:
                providers: [in_memory, user_db]
        in_memory:
            memory:
                users:
                    foo: { password: test , roles: 'ROLE_USER' }
        user_db:
            entity: { class: versionR\userBundle\Entity\User, property: username }

    encoders:
        versionR\userBundle\Entity\User:
            algorithm: bcrypt
            cost: 5
        Symfony\Component\Security\Core\User\User: plaintext

Entity class

<?php
// src/Acme/UserBundle/Entity/User.php
namespace versionR\userBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;

/**
 * Acme\UserBundle\Entity\User
 *
 * @ORM\Table(name="acme_users")
 * @ORM\Entity(repositoryClass="Acme\UserBundle\Entity\UserRepository")
 */
class User implements UserInterface, \Serializable
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=25, unique=true)
     */
    private $username;

    /**
     * @ORM\Column(type="string", length=64)
     */
    private $password;

    /**
     * @ORM\Column(type="string", length=60, unique=true)
     */
    private $email;

    /**
     * @ORM\Column(name="is_active", type="boolean")
     */
    private $isActive;

    /**
     * @ORM\Column(name="address", type="string")
     */
    private $address;

    public function __construct()
    {
        $this->isActive = true;
        // may not be needed, see section on salt below
        // $this->salt = md5(uniqid(null, true));
    }

    /**
     * @inheritDoc
     */
    public function getUsername()
    {
        return $this->username;
    }

    /**
     * @inheritDoc
     */
    public function getSalt()
    {
        // you *may* need a real salt depending on your encoder
        // see section on salt below
        return null;
    }

    /**
     * @inheritDoc
     */
    public function getPassword()
    {
        return $this->password;
    }

    /**
     * @inheritDoc
     */
    public function getRoles()
    {
        return array('ROLE_USER');
    }

    /**
     * @inheritDoc
     */
    public function eraseCredentials()
    {
    }

    /**
     * @see \Serializable::serialize()
     */
    public function serialize()
    {
        return serialize(array(
            $this->id,
            $this->username,
            $this->password,
            // see section on salt below
            // $this->salt,
        ));
    }

    /**
     * @see \Serializable::unserialize()
     */
    public function unserialize($serialized)
    {
        list (
            $this->id,
            $this->username,
            $this->password,
            // see section on salt below
            // $this->salt
        ) = unserialize($serialized);
    }

    /**
     * Set username
     *
     * @param string $username
     * @return User
     */
    public function setUsername($username)
    {
        $this->username = $username;

        return $this;
    }

    /**
     * Set password
     *
     * @param string $password
     * @return User
     */
    public function setPassword($password)
    {
        $this->password = $password;

        return $this;
    }

    /**
     * Set email
     *
     * @param string $email
     * @return User
     */
    public function setEmail($email)
    {
        $this->email = $email;

        return $this;
    }

    /**
     * Get email
     *
     * @return string 
     */
    public function getEmail()
    {
        return $this->email;
    }

    /**
     * Set address
     *
     * @param string $address
     * @return User
     */
    public function setAddress($address)
    {
        $this->address = $address;

        return $this;
    }

    /**
     * Get address
     *
     * @return string 
     */
    public function getAddress()
    {
        return $this->address;
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }
}

and user repository class

<?php

namespace versionR\userBundle\Entity;

use Doctrine\ORM\EntityRepository;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
/**
 * UserRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class UserRepository extends EntityRepository implements UserProviderInterface
{
    /**
     * @param string $username
     * @return \versionR\userBundle\Entity\User
     */
    public function loadUserByUsername($username)
    {
        return $this->findOneBy(array('username' => $username));
    }

    public function refreshUser(UserInterface $user)
    {
        $class = get_class($user);
        if (!$this->supportsClass($class)) {
            throw new UnsupportedUserException(
                sprintf(
                    'Instances of "%s" are not supported.',
                    $class
                )
            );
        }

        return $this->find($user->getId());
    }

    public function supportsClass($class)
    {
        return $this->getEntityName() === $class
        || is_subclass_of($class, $this->getEntityName());
    }
}

I use this command class to create test users

<?php

namespace versionR\userBundle\Command;

use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use versionR\userBundle\Entity\User;

    class userCommand extends ContainerAwareCommand
    {
        protected function configure()
        {
            $this
                ->setName('versionR:users')
                ->setDescription('Add Jobeet users')
                ->addArgument('username', InputArgument::REQUIRED, 'The username')
                ->addArgument('password', InputArgument::REQUIRED, 'The password')
                ->addArgument('email', InputArgument::REQUIRED, 'The email')
                ->addArgument('address', InputArgument::REQUIRED, 'The address')
            ;
        }

        protected function execute(InputInterface $input, OutputInterface $output)
        {
            $username = $input->getArgument('username');
            $password = $input->getArgument('password');
            $email = $input->getArgument('email');
            $address = $input->getArgument('address');

            $em = $this->getContainer()->get('doctrine')->getManager();

            $user = new User();
            $user->setUsername($username);
            // encode the password
            $factory = $this->getContainer()->get('security.encoder_factory');
            $encoder = $factory->getEncoder($user);
            $encodedPassword = $encoder->encodePassword($password, $user->getSalt());
            $user->setPassword($encodedPassword);
            $user->setEmail($email);
            $user->setAddress($address);
            $em->persist($user);
            $em->flush();

            $output->writeln(sprintf('Added %s user with password %s', $username, $password));
        }
    }

Can anyone explain why this is not working? thanks.

Ruwanka De Silva
  • 3,555
  • 6
  • 35
  • 51

1 Answers1

1

Since code is correct. Problem was in the database, to use bcrypt there should be enough space in the database password field. According to this SO answer password field should be either CHAR(60) or BINARY(60). If it is less than that half of the encrypted string will be saved and it turns out bad credentials.

Community
  • 1
  • 1
Ruwanka De Silva
  • 3,555
  • 6
  • 35
  • 51