Symfony2 enable user login with either username or e-mail

In project, I am using standard Symfony2 authentication mechanism. So, by default, I created entity User and added it to security.yml file like this:
providers:
main:
entity: { class: Surgeworks\CoreBundle\Entity\User, property: username}
firewalls:
login:
pattern: ^/admin/login$
security: false
secured_area:
pattern: ^/admin/
form_login:
check_path: /admin/login_check
login_path: /admin/login
always_use_default_target_path: true
default_target_path: /admin/login_after
logout:
path: /admin/logout
target: /admin/
#anonymous: ~
http_basic:
realm: "Secured Admin Area"
In meantime, the needs for project was changed and I had to implement possibility for user to insert either username or e-mail address into login form’s username field. But, I could not intercept login_check call inside controller to load username from e-mail provided and then do the login check. After some brainstorming and testing possibilities I found easy solution for that problem:
I added class UserRepository and implemented UserProvider interface:
<?php
namespace Surgeworks\CoreBundle\Entity;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class UserRepository extends EntityRepository
implements UserProviderInterface
{
public function loadUserByUsername($username) {
return $this->getEntityManager()
->createQuery('SELECT u FROM
SurgeworksCoreBundle:User u
WHERE u.username = :username
OR u.email = :username')
->setParameters(array(
'username' => $username
))
->getOneOrNullResult();
}
public function refreshUser(UserInterface $user) {
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class) {
return $class === 'Surgeworks\CoreBundle\Entity\User';
}
}
And my user class looks like this:
<?php
namespace Surgeworks\CoreBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints as DoctrineAssert;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity(repositoryClass="Surgeworks\CoreBundle\Entity\UserRepository")
* @ORM\Table(name="user")
* @DoctrineAssert\UniqueEntity(fields={"username"}, message="username.already.exist" )
* @DoctrineAssert\UniqueEntity(fields={"email"}, message="email.already.exist" )
*/ class User implements UserInterface
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue(strategy="AUTO")
* @var integer $id
*/
protected $id;
/**
* @ORM\OneToMany(targetEntity="Favorite", mappedBy="user")
*/
protected $favorites;
/**
* @ORM\Column(type="string", length="255", name="first_name")
*
* @var string $firstName
*/
protected $firstName;
/**
* @ORM\Column(type="string", length="255", name="last_name")
*
* @var string $lastName
*/
protected $lastName;
/**
* @ORM\Column(type="string", length="255",unique=true)
* @var string $email
*/
protected $email;
/**
* @ORM\Column(type="string", length="255", unique=true)
* @var string username
*/
protected $username;
/**
* @ORM\Column(type="string", length="255")
*
* @var string password
*/
protected $password;
//...
}
Of course one more thing inside security.yml, remove property:username to get the whole thing work:
#...
providers:
main:
entity: { class: Surgeworks\CoreBundle\Entity\User}
#...
That solved the problem. Now my users can login with e-mail or username.
Cheers!
10 comments
The above code isn’t working for me, its always showing ‘Bad Credentials’ with exosting user name…why?
Hi Martin,
How you solved the above problem of The Doctrine repository “Doctrine\ORM\EntityRepository” must implement UserProviderInterface.
Can you please show any example
Can you help me the view of this login, please? Can I download full package of this? Thanks much.
What is interesting, authorization system denies username with ‘.’ (user@gg.com is ok, but user.dummy@gg.com isn’t). Thought loading user from repository is ok.
What happens if one user has the username me@example.com and another one has the email adress me@example.com
assumed your username allows an . and @ for specialchaits ?
It looks like very interesting.
Thank you.
Ok, I found the issue, I am using the entity mapping User.orm.yml and the repositoryClass was missing there!
That fixed the problem!
Thanks,
-Martin
I am not sure if this is ok. but the root cause of the issue rely on $userProvider in DaoAuthenticationProvider (Symfony\Component\Security\Core\Authentication\Provider)
when constructing the UserProviderInterface object is Symfony\Bridge\Doctrine\Security\User\EntityUserProvider (not UserProvider) I am not fully sure if this is correct or not. the thing is in the mefhod retrieveUser an exception is thrown by
$user = $this->userProvider->loadUserByUsername($username);
so the custom provider seems not to be instantiated correctly. 🙁
Hope this can guide to help me on my issue
Cheers,
-Martin
Hey,
I have done all stpes but when I remove the property: username from provider definition I get this error:
The Doctrine repository “Doctrine\ORM\EntityRepository” must implement UserProviderInterface.
Thank you very much, that’s what i was looking for!