Cannot refresh token because user has changed Syfmony 4 - EquatableInterface problem - symfony

I have made login form with Security Guide. When I try to login I have logs like below:
2019-06-10 10:16:56] security.INFO: User has been authenticated successfully. {"username":"user#example.com"} []
[2019-06-10 10:16:56] security.DEBUG: Stored the security token in the session. {"key":"_security_main"} []
[2019-06-10 10:16:56] request.INFO: Matched route "app_user_dashboard". {"route":"app_user_dashboard","route_parameters":{"_route":"app_user_dashboard","_controller":"App\\Controller\\User\\UserController::dashboard"},"request_uri":"https://127.0.0.1:8001/app/dashboard","method":"GET"} []
[2019-06-10 10:16:56] security.DEBUG: Read existing security token from the session. {"key":"_security_main","token_class":"Symfony\\Component\\Security\\Core\\Authentication\\Token\\UsernamePasswordToken"} []
[2019-06-10 10:16:57] doctrine.DEBUG: SELECT t0.id AS id_1, t0.password AS password_2, t0.email AS email_3, t0.first_name AS first_name_4, t0.last_name AS last_name_5, t0.username AS username_6, t0.referral_code AS referral_code_7, t0.referred_by_code AS referred_by_code_8, t0.roles AS roles_9, t0.active_to AS active_to_10, t0.created_at AS created_at_11, t0.updated_at AS updated_at_12 FROM users t0 WHERE t0.id = ? [15] []
[2019-06-10 10:16:57] security.DEBUG: Cannot refresh token because user has changed. {"username":"user#example.com","provider":"Symfony\\Bridge\\Doctrine\\Security\\User\\EntityUserProvider"} []
[2019-06-10 10:16:57] security.DEBUG: Token was deauthenticated after trying to refresh it. [] []
and also I use EquatableInterface. My User.php code:
namespace App\Entity\User;
use DateTime;
use Doctrine\ORM\Mapping as ORM;
use Exception;
use Serializable;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Class User
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="App\Repository\User\UserRepository")
* #ORM\HasLifecycleCallbacks
*
* #ORM\Entity
* #UniqueEntity(fields="username", message="username taken")
* #UniqueEntity(fields="email", message="email taken")
*/
class User implements UserInterface, Serializable, EquatableInterface
{
/**
* #var int
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string", length=256)
*/
private $password;
/**
* #var string
*
* #ORM\Column(type="string", length=64, unique=true)
*/
private $email;
/**
* #var string|null
*
* #ORM\Column(type="string", length=64, nullable=true)
*/
private $firstName;
/**
* #var string|null
*
* #ORM\Column(type="string", length=64, nullable=true)
*/
private $lastName;
/**
* #var string
*
* #ORM\Column(type="string", length=64, unique=true)
*/
private $username;
/**
* #var string
*
* #ORM\Column(type="string", length=64, unique=true)
*/
private $referralCode;
/**
* #var string|null
*
* #ORM\Column(type="string", length=64, nullable=true)
*/
private $referredByCode;
/**
* #var array
*
* #ORM\Column(type="array", length=64)
*/
private $roles;
/**
* #var DateTime
*
* #ORM\Column(type="datetime")
*/
private $activeTo;
/**
* #var DateTime
*
* #ORM\Column(type="datetime")
*/
private $createdAt;
/**
* #var DateTime
*
* #ORM\Column(type="datetime", nullable=true)
*/
private $updatedAt;
/**
* User constructor.
*
* #throws Exception
*/
public function __construct()
{
$this->createdAt = new DateTime();
$this->updatedAt = new DateTime();
$this->activeTo = new DateTime('now + 14 days');
$this->referralCode = substr(hash('sha256', uniqid()), 0, 5);
}
/**
* #return string
*/
public function __toString()
{
return $this->getUsername();
}
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param string $username
*/
public function setUsername(string $username): void
{
$this->username = $username;
}
/**
* #return string
*/
public function getUsername(): ?string
{
return $this->username;
}
/**
* #return null|string
*/
public function getSalt(): ?string
{
// you *may* need a real salt depending on your encoder
// see section on salt below
return null;
}
/**
* #return string
*/
public function getPassword(): ?string
{
return $this->password;
}
/**
* #param string $password
*/
public function setPassword(string $password)
{
$this->password = $password;
}
/**
* #return array
*/
public function getRoles(): array
{
// return $this->roles;
return ['ROLE_USER', 'ROLE_API_USER'];
}
/**
*
*/
public function eraseCredentials()
{
}
/**
* #see Serializable::serialize()
*/
public function serialize()
{
return serialize(array($this->id, $this->email));
}
/**
* #see Serializable::unserialize()
*
* #param $serialized
*/
public function unserialize($serialized)
{
list ($this->id, $this->email) = unserialize($serialized, array('allowed_classes' => false));
}
/**
* #return string
*/
public function getEmail(): ?string
{
return $this->email;
}
/**
* #param string $email
*/
public function setEmail(string $email): void
{
$this->email = $email;
}
/**
* #return DateTime
*/
public function getCreatedAt(): DateTime
{
return $this->createdAt;
}
/**
* #ORM\PrePersist
*
* #throws Exception
*/
public function setCreatedAt(): void
{
$this->createdAt = new DateTime();
}
/**
* #return DateTime
*/
public function getUpdatedAt(): DateTime
{
return $this->updatedAt;
}
/**
* #ORM\PreUpdate
*
* #throws Exception
*/
public function setUpdatedAt(): void
{
$this->updatedAt = new DateTime();
}
/**
* #return DateTime
*/
public function getActiveTo(): DateTime
{
return $this->activeTo;
}
/**
* #param DateTime $activeTo
*/
public function setActiveTo(DateTime $activeTo): void
{
$this->activeTo = $activeTo;
}
/**
* #return string
*/
public function getReferralCode(): string
{
return $this->referralCode;
}
/**
* #param string $referralCode
*/
public function setReferralCode(string $referralCode): void
{
$this->referralCode = $referralCode;
}
/**
* #return string|null
*/
public function getReferredByCode():? string
{
return $this->referredByCode;
}
/**
* #param string|null $referredByCode
*/
public function setReferredByCode(?string $referredByCode): void
{
$this->referredByCode = $referredByCode;
}
/**
* #return string|null
*/
public function getFirstName(): ?string
{
return $this->firstName;
}
/**
* #param string|null $firstName
*/
public function setFirstName(?string $firstName): void
{
$this->firstName = $firstName;
}
/**
* #return string|null
*/
public function getLastName(): ?string
{
return $this->lastName;
}
/**
* #param string|null $lastName
*/
public function setLastName(?string $lastName): void
{
$this->lastName = $lastName;
}
/**
* #param array $roles
*/
public function setRoles(array $roles): void
{
$this->roles = $roles;
}
/**
* The equality comparison should neither be done by referential equality
* nor by comparing identities (i.e. getId() === getId()).
*
* However, you do not need to compare every attribute, but only those that
* are relevant for assessing whether re-authentication is required.
*
* #param UserInterface $user
*
* #return bool
*/
public function isEqualTo(UserInterface $user)
{
if ($this->username !== $user->getUsername()) {
return false;
}
return true;
}
}
and security.yaml
encoders:
App\Entity\User\User:
algorithm: auto
providers:
user_provider:
entity:
class: App\Entity\User\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
http_basic: ~
anonymous: true
# logout_on_user_change: true
provider: user_provider
form_login:
login_path: app_user_login
check_path: app_user_login
default_target_path: app_user_dashboard
csrf_token_generator: security.csrf.token_manager
logout:
path: /app/logout
target: /app/login
# activate different ways to authenticate
# http_basic: true
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/app/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/app/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/app, roles: IS_AUTHENTICATED_FULLY }
I have red this post: Token was deauthenticated after trying to refresh it and that solution does not work for me. Any ideas?

You've got 2 options here:
Make your firewall stateless or
update your serialization in you User
I'm referring to https://symfony.com/doc/current/security/user_provider.html#understanding-how-users-are-refreshed-from-the-session btw, as I had the same issue.
First solution:
firewalls:
# ...
main:
http_basic: ~
anonymous: true
stateless: true
This should make Symfony ignore your serialization and just reload the whole entity from database.
Second solution:
class User implements UserInterface, Serializable, EquatableInterface
{
public function serialize()
{
return serialize(array(
$this->id,
$this->password,
$this->email,
$this->username,
$this->activeTo,
));
}
public function unserialize($serialized)
{
list (
$this->id,
$this->password,
$this->email,
$this->username,
$this->activeTo,
) = unserialize($serialized, array('allowed_classes' => false));
}
}
You should keep any information that Symfony might need in any of your user_checker classes (or the default ones).

Related

Symfony Authentication Token lost after redirect

First of all, don't bother to copy this code. This will be available on my github soon. (I'll update this post just in case someone will need it)
Hi. I'm trying to use Steam to connect in my application. So, I've tried to create a custom user provider and a custom authentication. After I click Log In button, my user (I've added one by myself) is loaded from database and I'm redirected to my custom page. In that page, my debug toolbar is telling me that I'm authenticated with my custom Token and Firewall. If I move to another page, "/search" for example, my debug toolbar is telling me that I'm not authenticated anymore...
What am I doing wrong?
I'm using Symfony 4.0.6. Thank you !
P.S.: This script is inspired by this one: https://github.com/SirWaddles/SteamAuthBundle
P.P.S: If I missed any file and you need it, please reply.
P.P.P.S: I think it's a problem with serialize() and unserialize(), but I don't know exactly.
Player.php
<?php
namespace App\Entity;
use App\Service\SteamAuth\User\SteamUserInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Class Player
* #package App\Entity
*
* #ORM\Entity(repositoryClass="App\Repository\PlayerRepository")
* #ORM\Table(name="players")
*/
class Player implements UserInterface, SteamUserInterface, AdvancedUserInterface, EquatableInterface, \Serializable
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*
* #var int
*/
private $id;
/**
* #ORM\Column(type="string", length=255, )
*
* #var string
*/
private $username;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
private $name;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
private $password;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*
* #var null|string
*/
private $avatar;
/**
* #var array
*
* #ORM\Column(type="array")
*/
private $roles;
/**
* #var \DateTime|null
*
* #ORM\Column(type="datetime", nullable=true)
*/
private $lastSync;
/**
* #var bool
*
* #ORM\Column(type="boolean")
*/
private $enabled;
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
*
* #return Player
*/
public function setId(int $id): Player
{
$this->id = $id;
return $this;
}
/**
* #return string
*/
public function getUsername(): string
{
return $this->username;
}
/**
* #param string $username
*
* #return Player
*/
public function setUsername(string $username): Player
{
$this->username = $username;
return $this;
}
/**
* #return string
*/
public function getName(): string
{
return $this->name;
}
/**
* #param string $name
*
* #return Player
*/
public function setName(string $name): Player
{
$this->name = $name;
return $this;
}
/**
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* #return array
*/
public function getRoles()
{
return $this->roles;
}
/**
* #param array $roles
* #return Player
*/
public function setRoles(array $roles): Player
{
$this->roles = $roles;
return $this;
}
/**
* #return \DateTime|null
*/
public function getLastSync(): ?\DateTime
{
return $this->lastSync;
}
/**
* #param \DateTime|null $lastSync
* #return Player
*/
public function setLastSync(?\DateTime $lastSync): Player
{
$this->lastSync = $lastSync;
return $this;
}
/**
* #return null|string
*/
public function getSalt()
{
return null;
}
/**
* #param string $password
* #return Player
*/
public function setPassword(string $password): Player
{
$this->password = $password;
return $this;
}
/**
* #return null|string
*/
public function getAvatar(): ?string
{
return $this->avatar;
}
/**
* #param null|string $avatar
*
* #return Player
*/
public function setAvatar(?string $avatar): Player
{
$this->avatar = $avatar;
return $this;
}
/**
* {#inheritdoc}
*/
public function eraseCredentials()
{
}
/**
* {#inheritdoc}
*/
public function isAccountNonExpired()
{
return true;
}
/**
* {#inheritdoc}
*/
public function isAccountNonLocked()
{
return true;
}
/**
* {#inheritdoc}
*/
public function isCredentialsNonExpired()
{
return true;
}
/**
* {#inheritdoc}
*/
public function isEnabled()
{
return $this->enabled;
}
/**
* #param bool|null $enabled
* #return Player
*/
public function setEnabled(?bool $enabled): Player
{
$this->enabled = $enabled;
return $this;
}
/**
* {#inheritdoc}
*/
public function isEqualTo(UserInterface $user)
{
if ($this->username !== $user->getUsername()) {
return false;
}
return true;
}
/**
* {#inheritdoc}
*/
public function serialize()
{
return serialize([
$this->id,
$this->username,
$this->name,
$this->avatar,
$this->password,
$this->enabled
]);
}
/**
* {#inheritdoc}
*/
public function unserialize($data)
{
list($this->id, $this->username, $this->name, $this->avatar, $this->password, $this->enabled) = unserialize($data);
}
/**
* #return string
*/
public function __toString()
{
return $this->getUsername() ?? '-';
}
}
SteamToken.php
<?php
namespace App\Service\SteamAuth\Token;
use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
/**
* Class SteamToken
* #package App\Service\SteamAuth\Token
*/
class SteamToken extends AbstractToken
{
/**
* {#inheritdoc}
*/
public function __construct(array $roles = [])
{
parent::__construct($roles);
$this->setAuthenticated(count($roles) > 0);
}
/**
* {#inheritdoc}
*/
public function setAttributes(array $attributes)
{
foreach ($attributes as $key => $attribute) {
$key = str_replace("openid_", "openid.", $key);
$this->setAttribute($key, $attribute);
}
return $this;
}
/**
* {#inheritdoc}
*/
public function getCredentials()
{
}
/**
* {#inheritdoc}
*/
public function serialize()
{
return serialize([
$this->getUser(),
$this->isAuthenticated(),
$this->getAttributes()
]);
}
/**
* {#inheritdoc}
*/
public function unserialize($data)
{
$data = unserialize($data);
$this->setUser($data[0]);
$this->setAuthenticated($data[1]);
$this->setAttributes($data[2]);
}
}
SteamListener.php
<?php
namespace App\Service\SteamAuth\Firewall;
use App\Service\SteamAuth\Token\SteamToken;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
/**
* Class SteamListener
* #package App\Service\SteamAuth
*/
class SteamListener implements ListenerInterface
{
/**
* #var TokenStorageInterface
*/
private $tokenStorage;
/**
* #var AuthenticationManagerInterface
*/
private $authentication;
/**
* SteamListener constructor.
*
* #param TokenStorageInterface $tokenStorage
* #param AuthenticationManagerInterface $authentication
*/
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authentication)
{
$this->tokenStorage = $tokenStorage;
$this->authentication = $authentication;
}
public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();
if($request->get('_route') === 'login_check') {
$token = new SteamToken();
$token->setUser(str_replace("http://steamcommunity.com/openid/id/", "", $request->query->get('openid_claimed_id')));
$token->setAttributes($request->query->all());
try {
$authToken = $this->authentication->authenticate($token);
$this->tokenStorage->setToken($authToken);
return;
} catch (AuthenticationException $exception) {
}
}
$response = new Response();
$response->setStatusCode(Response::HTTP_FORBIDDEN);
$event->setResponse($response);
return;
}
}
SteamProvider.php
<?php
namespace App\Service\SteamAuth\Authentication;
use App\Service\SteamAuth\Token\SteamToken;
use GuzzleHttp\Client;
use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
/**
* Class SteamProvider
* #package App\Service\SteamAuth\Provider
*/
class SteamProvider implements AuthenticationProviderInterface
{
/**
* #var UserProviderInterface
*/
private $userProvider;
/**
* #var Client
*/
private $client;
/**
* SteamProvider constructor.
*
* #param UserProviderInterface $userProvider
* #param Client $client
*/
public function __construct(UserProviderInterface $userProvider, Client $client)
{
$this->userProvider = $userProvider;
$this->client = $client;
}
/**
* {#inheritdoc}
*/
public function authenticate(TokenInterface $token)
{
if ($token->getAttribute('openid.ns') !== "http://specs.openid.net/auth/2.0") {
throw new AuthenticationException("Invalid token !");
}
$checkAuth = $token->getAttributes();
$checkAuth['openid.mode'] = 'check_authentication';
$response = $this->client->request('GET', 'login', ['query' => $checkAuth]);
if ((string)$response->getBody() === "ns:http://specs.openid.net/auth/2.0\nis_valid:true\n") {
$user = $this->userProvider->loadUserByUsername($token->getUsername());
$authToken = new SteamToken($user->getRoles());
$authToken->setUser($user);
return $authToken;
}
throw new AuthenticationException("Invalid token !");
}
/**
* {#inheritdoc}
*/
public function supports(TokenInterface $token)
{
return $token instanceof SteamToken;
}
}
SteamUserProvider.php
<?php
namespace App\Service\SteamAuth\User;
use App\Entity\Player;
use App\Service\SteamAuth\SteamUserService;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
/**
* Class SteamUserProvider
* #package App\Service\SteamAuth\User
*/
class SteamUserProvider implements UserProviderInterface
{
/**
* #var EntityManager
*/
private $entityManager;
/**
* #var string
*/
private $userClass;
/**
* #var SteamUserService
*/
private $userService;
/**
* SteamUserProvider constructor.
*
* #param EntityManager $entityManager
* #param SteamUserService $userService
* #param string $userClass
*/
public function __construct(EntityManager $entityManager, SteamUserService $userService, string $userClass)
{
$this->entityManager = $entityManager;
$this->userService = $userService;
$this->userClass = $userClass;
}
/**
* {#inheritdoc}
*/
public function loadUserByUsername($username)
{
$repository = $this->entityManager->getRepository($this->userClass);
$player = $repository->findOneBy(['username' => $username]);
if (!$player) {
/**
* #var $player Player
*/
$player = new $this->userClass();
$player->setUsername($username);
$player->setPassword(md5(random_bytes(15)));
$player->setRoles(['ROLE_USER']);
$player->setEnabled(1);
$player = $this->userService->updateUserEntry($player);
$this->entityManager->persist($player);
$this->entityManager->flush($player);
}
/// if last update....
return $player;
}
/**
* {#inheritdoc}
*/
public function refreshUser(UserInterface $user)
{
if (!$user instanceof SteamUserInterface) {
throw new UnsupportedUserException("User not supported!");
}
return $this->loadUserByUsername($user->getUsername());
}
/**
* {#inheritdoc}
*/
public function supportsClass($class)
{
return $class === $this->userClass;
}
}
services.yaml
services:
...
# Aliases
GuzzleHttp\Client: '#eight_points_guzzle.client.login'
# Log In System
app.steam_user.service:
class: App\Service\SteamAuth\SteamUserService
arguments: ['#eight_points_guzzle.client.steam', '%steam_api_key%']
app.steam_user.provider:
class: App\Service\SteamAuth\User\SteamUserProvider
arguments:
$entityManager: '#doctrine.orm.default_entity_manager'
$userService: '#app.steam_user.service'
$userClass: '%steam_user_class%'
app.steam.provider:
class: App\Service\SteamAuth\Authentication\SteamProvider
arguments:
$userProvider: '#app.steam_user.provider'
$client: '#eight_points_guzzle.client.login'
app.steam.listener:
class: App\Service\SteamAuth\Firewall\SteamListener
security.yaml
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
steamauth:
id: app.steam_user.provider
firewalls:
steam_auth:
pattern: ^/login_check
stateless: true
steam: ~
form_login:
csrf_token_generator: security.csrf.token_manager
remember_me:
secret: '%kernel.secret%'
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login_check, roles: IS_AUTHENTICATED_ANONYMOUSLY }
EDIT: If I dump($this->get('session')->all()) and that page before refresh, I get this: "_security_steam_auth" => "C:38:"App\Service\SteamAuth\Token\SteamToken":40:{a:4:{i:0;N;i:1;b:0;i:2;a:0:{}i:3;a:0:{}}}"
I had a similar problem.
I had a missing attribute in a my serialization process: isActive
I never understand why, but when this attribute is my serialization/unserialization process, it works fine, and when not, it don't work at all.
here is my source: https://github.com/matthieuleorat/documentManager/blob/master/src/Entity/User.php#L253
The doc: http://symfony.com/doc/current/security/entity_provider.html#security-serialize-equatable
Hope it help.
Do you have module_suhosin7 enabled?
We experienced problems on session with suhosin7 enabled.
In fact it adds some rules on session and cookie management.
If enabled, try and disable it and check if it works.
There is a known issue about session encryption with suhosin7 :
https://github.com/sektioneins/suhosin7/issues/21
This lines in SteamListener.php prevents any other routes to work.
$request = $event->getRequest();
if($request->get('_route') === 'login_check') {
[...]
}
$response = new Response();
$response->setStatusCode(Response::HTTP_FORBIDDEN);
$event->setResponse($response);
Security Firewall handle() function is called each times you navigate to a page, so if you set other pages status as forbidden, you won't be able to navigate on other pages than login.
This should work by removing it. Let me know if problem solved
After a lot of work, I decided to make a research again for a Steam Authentication bundle and I found one for Symfony 4 and I confirm that it works.
Link: https://github.com/knojector/SteamAuthenticationBundle

Login without FOSuser

I'm trying to make a login form without FOSuser, as I need to override a lot in the entity User. I tried to find a lot of informations with the cookbook, and on several sites, but the mere informations were for SF2, which is quite annoying as the code may be different.
I managed to make a registration form, which records the User on the db, but my login form doesn't want to work whatever I can do. I have only one error message, "Invalid credential". I thought it could be because of a bad algorithm, but I changed it three or four times (bcrypt, sha512, none...), no success.
Here's my security.yml (I know that there's a lot a useless code but I don't want to remove anything that could be useful):
security:
encoders:
Symfony\Component\Security\Core\User\User:
algorithm: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
# http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
providers:
in_memory:
memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
our_db_provider:
entity:
class: FrontBundle:User
property: username
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/login$
security: false
login:
pattern: ^/register$
security: false
main:
anonymous: false
pattern: ^/
form_login:
login_path: login
check_path: login_check
logout:
path: /logout
target: /
# activate different ways to authenticate
# http_basic: ~
# http://symfony.com/doc/current/book/security.html#a-configuring-how-your-users-will-authenticate
# form_login: ~
# http://symfony.com/doc/current/cookbook/security/form_login_setup.html
access_control:
- { path: ^/register$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login_check, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_ADMIN }
Here's my entity User (excuse my french, it's a school project :) ):
<?php
namespace FrontBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="FrontBundle\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=255)
*/
private $nom;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=255, unique=true)
*/
private $username;
/**
* #var string
*
* #ORM\Column(name="prenom", type="string", length=255)
*/
private $prenom;
/**
* #var string
*
* #ORM\Column(name="mail", type="string", length=255, unique=true)
*/
private $mail;
/**
* #Assert\NotBlank()
* #Assert\Length(max=4096)
*/
private $plainPassword;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255)
*/
private $password;
/**
* #var string
*
* #ORM\Column(name="salt", type="string", length=255, nullable=true)
*/
private $salt;
/**
* #var string
*
* #ORM\Column(name="photo", type="string", length=255, nullable=true)
*/
private $photo;
/**
* #var string
*
* #ORM\Column(name="ville", type="string", length=255, nullable=true)
*/
private $ville;
/**
* #var array
*
* #ORM\Column(type="array", nullable=false)
*/
protected $roles;
/**
* #ORM\ManyToOne(targetEntity="Entreprise", inversedBy="users", cascade={"remove"})
* #ORM\JoinColumn(name="entreprise_id", referencedColumnName="id")
*/
protected $entreprise;
/**
* #ORM\ManyToOne(targetEntity="Promotion", inversedBy="users", cascade={"remove"})
* #ORM\JoinColumn(name="promotion_id", referencedColumnName="id")
*/
protected $promotion;
/**
* #ORM\OneToMany(targetEntity="Annonce", mappedBy="user", cascade={"remove", "persist"})
*/
protected $annonces;
/**
* #ORM\OneToMany(targetEntity="Commentaire", mappedBy="user", cascade={"remove", "persist"})
*/
protected $commentaires;
/**
* #ORM\OneToMany(targetEntity="Article", mappedBy="user", cascade={"remove", "persist"})
*/
protected $articles;
/**
* #ORM\OneToMany(targetEntity="Evenement", mappedBy="user", cascade={"remove", "persist"})
*/
protected $evenements;
/**
* #ORM\ManyToMany(targetEntity="Evenement", inversedBy="users")
* #ORM\JoinTable(name="evenement_user")
*/
private $participe;
/**
* #ORM\ManyToMany(targetEntity="Groupe", inversedBy="users")
* #ORM\JoinTable(name="groupe_user")
*/
private $groupes;
public function __construct() {
$this->participe = new \Doctrine\Common\Collections\ArrayCollection();
$this->groupes = new \Doctrine\Common\Collections\ArrayCollection();
$this->roles = array("ROLE_USER");
$this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set nom
*
* #param string $nom
*
* #return User
*/
public function setNom($nom)
{
$this->nom = $nom;
return $this;
}
/**
* Get nom
*
* #return string
*/
public function getNom()
{
return $this->nom;
}
/**
* Set prenom
*
* #param string $prenom
*
* #return User
*/
public function setPrenom($prenom)
{
$this->prenom = $prenom;
return $this;
}
/**
* Get prenom
*
* #return string
*/
public function getPrenom()
{
return $this->prenom;
}
/**
* Set mail
*
* #param string $mail
*
* #return User
*/
public function setMail($mail)
{
$this->mail = $mail;
return $this;
}
/**
* Get mail
*
* #return string
*/
public function getMail()
{
return $this->mail;
}
/**
* Set password
*
* #param string $password
*
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set photo
*
* #param string $photo
*
* #return User
*/
public function setPhoto($photo)
{
$this->photo = $photo;
return $this;
}
/**
* Get photo
*
* #return string
*/
public function getPhoto()
{
return $this->photo;
}
/**
* Set ville
*
* #param string $ville
*
* #return User
*/
public function setVille($ville)
{
$this->ville = $ville;
return $this;
}
/**
* Get ville
*
* #return string
*/
public function getVille()
{
return $this->ville;
}
/**
* Set entreprise
*
* #param \FrontBundle\Entity\Entreprise $entreprise
*
* #return User
*/
public function setEntreprise(\FrontBundle\Entity\Entreprise $entreprise = null)
{
$this->entreprise = $entreprise;
return $this;
}
/**
* Get entreprise
*
* #return \FrontBundle\Entity\Entreprise
*/
public function getEntreprise()
{
return $this->entreprise;
}
/**
* Set promotion
*
* #param \FrontBundle\Entity\Promotion $promotion
*
* #return User
*/
public function setPromotion(\FrontBundle\Entity\Promotion $promotion = null)
{
$this->promotion = $promotion;
return $this;
}
/**
* Get promotion
*
* #return \FrontBundle\Entity\Promotion
*/
public function getPromotion()
{
return $this->promotion;
}
/**
* Add annonce
*
* #param \FrontBundle\Entity\Annonce $annonce
*
* #return User
*/
public function addAnnonce(\FrontBundle\Entity\Annonce $annonce)
{
$this->annonces[] = $annonce;
return $this;
}
/**
* Remove annonce
*
* #param \FrontBundle\Entity\Annonce $annonce
*/
public function removeAnnonce(\FrontBundle\Entity\Annonce $annonce)
{
$this->annonces->removeElement($annonce);
}
/**
* Get annonces
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getAnnonces()
{
return $this->annonces;
}
/**
* Add commentaire
*
* #param \FrontBundle\Entity\Commentaire $commentaire
*
* #return User
*/
public function addCommentaire(\FrontBundle\Entity\Commentaire $commentaire)
{
$this->commentaires[] = $commentaire;
return $this;
}
/**
* Remove commentaire
*
* #param \FrontBundle\Entity\Commentaire $commentaire
*/
public function removeCommentaire(\FrontBundle\Entity\Commentaire $commentaire)
{
$this->commentaires->removeElement($commentaire);
}
/**
* Get commentaires
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCommentaires()
{
return $this->commentaires;
}
/**
* Add article
*
* #param \FrontBundle\Entity\Article $article
*
* #return User
*/
public function addArticle(\FrontBundle\Entity\Article $article)
{
$this->articles[] = $article;
return $this;
}
/**
* Remove article
*
* #param \FrontBundle\Entity\Article $article
*/
public function removeArticle(\FrontBundle\Entity\Article $article)
{
$this->articles->removeElement($article);
}
/**
* Get articles
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getArticles()
{
return $this->articles;
}
/**
* Add evenement
*
* #param \FrontBundle\Entity\Evenement $evenement
*
* #return User
*/
public function addEvenement(\FrontBundle\Entity\Evenement $evenement)
{
$this->evenements[] = $evenement;
return $this;
}
/**
* Remove evenement
*
* #param \FrontBundle\Entity\Evenement $evenement
*/
public function removeEvenement(\FrontBundle\Entity\Evenement $evenement)
{
$this->evenements->removeElement($evenement);
}
/**
* Get evenements
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEvenements()
{
return $this->evenements;
}
/**
* Add participe
*
* #param \FrontBundle\Entity\Evenement $participe
*
* #return User
*/
public function addParticipe(\FrontBundle\Entity\Evenement $participe)
{
$this->participe[] = $participe;
return $this;
}
/**
* Remove participe
*
* #param \FrontBundle\Entity\Evenement $participe
*/
public function removeParticipe(\FrontBundle\Entity\Evenement $participe)
{
$this->participe->removeElement($participe);
}
/**
* Get participe
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getParticipe()
{
return $this->participe;
}
/**
* Add groupe
*
* #param \FrontBundle\Entity\Groupe $groupe
*
* #return User
*/
public function addGroupe(\FrontBundle\Entity\Groupe $groupe)
{
$this->groupes[] = $groupe;
return $this;
}
/**
* Remove groupe
*
* #param \FrontBundle\Entity\Groupe $groupe
*/
public function removeGroupe(\FrontBundle\Entity\Groupe $groupe)
{
$this->groupes->removeElement($groupe);
}
/**
* Get groupes
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getGroupes()
{
return $this->groupes;
}
public function getRoles() {
return array($this->roles);
}
public function getUsername() {
return $this->username;
}
public function getSalt() {
return $this->salt;
}
public function eraseCredentials() {
// Ici nous n'avons rien à effacer.
// Cela aurait été le cas si nous avions un mot de passe en clair.
}
/**
* Set username
*
* #param string $username
*
* #return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Set salt
*
* #param string $salt
*
* #return User
*/
public function setSalt($salt)
{
$this->salt = $salt;
return $this;
}
/**
* Set roles
*
* #param string $roles
*
* #return User
*/
public function setRoles($roles)
{
$this->roles = $roles;
return $this;
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function setPlainPassword($password)
{
$this->plainPassword = $password;
}
}
Here's my SecurityController:
namespace FrontBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
class SecurityController extends Controller
{
/**
* #Route("/login", name="login")
* #Template()
*/
public function loginAction(Request $request)
{
$authenticationUtils = $this->get('security.authentication_utils');
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('FrontBundle:Security:login.html.twig', array(
'last_username' => $lastUsername,
'error' => $error,
));
}
}
Here are my routes:
login:
path: /login
defaults: { _controller: FrontBundle:Security:login}
login_check:
path: /login_check
I think these are the only files needed to make it work, please tell me if you need anything else.
Thanks if you can help me :)

FOSUserBundle: Unrecognized field: usernameCanonical

First of all, I know SO is full of questions like this but I tried to combine different config values according to those responses with no luck.
I'm using FOSUserBundle with my own User class and when submiting login form I get this error:
Unrecognized field: usernameCanonical
Here are some bits of my code:
doctrine:
auto_generate_proxy_classes: "%kernel.debug%"
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
# mappings:
# FOSUserBundle: ~
fos_user:
service:
mailer: fos_user.mailer.twig_swift
db_driver: orm
firewall_name: main
user_class: AppBundle\Entity\User
Some variations tested include setting auto_mapping: false and/or uncommenting mappings.FOSUserBundle: ~
This is my user class:
<?php
namespace AppBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* AppBundle\Entity\User
*
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User extends BaseUser implements UserInterface
{
const ROLE_DEFAULT = 'ROLE_ADMIN';
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=100)
*/
protected $name;
/**
* #ORM\Column(type="string", length=40)
* #Assert\Email()
*/
protected $login;
/**
* #ORM\Column(type="string", length=255)
*/
protected $password;
/**
* #ORM\Column(type="string", length=255)
*/
protected $salt;
/**
* #ORM\Column(type="array", length=255)
*/
protected $roles;
/**
* Método requerido por la interfaz UserInterface
*/
public function equals(\Symfony\Component\Security\Core\User\UserInterface $user)
{
return $this->getLogin() == $user->getLogin();
}
/**
* Método requerido por la interfaz UserInterface
*/
public function eraseCredentials()
{
}
/**
* Método requerido por la interfaz UserInterface
*/
public function getUsername()
{
return $this->getLogin();
}
public function __toString()
{
return $this->getName();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set login
*
* #param string $login
*/
public function setLogin($login)
{
$this->login = $login;
}
/**
* Get login
*
* #return string
*/
public function getLogin()
{
return $this->login;
}
/**
* Set password
*
* #param string $password
*/
public function setPassword($password)
{
$this->password = $password;
}
/**
* Get salt
*
* #return string
*/
public function getSalt()
{
return $this->salt;
}
/**
* Set salt
*
* #param string $salt
*/
public function setSalt($salt)
{
$this->salt = $salt;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Adds a role to the user.
*
* #param string $role
*/
public function addRole($role)
{
$role = strtoupper($role);
if ($role === static::ROLE_DEFAULT) {
return;
}
if (!in_array($role, $this->roles, true)) {
$this->roles[] = $role;
}
}
/**
* Returns the user roles
*
* Implements SecurityUserInterface
*
* #return array The roles
*/
public function getRoles()
{
$roles = $this->roles;
foreach ($this->getGroups() as $group) {
$roles = array_merge($roles, $group->getRoles());
}
// we need to make sure to have at least one role
$roles[] = static::ROLE_DEFAULT;
return array_unique($roles);
}
/**
* Set roles
*
* #param string $roles
*/
public function setRoles(array $roles)
{
$this->roles = $roles;
}
/**
* Never use this to check if this user has access to anything!
*
* Use the SecurityContext, or an implementation of AccessDecisionManager
* instead, e.g.
*
* $securityContext->isGranted('ROLE_USER');
*
* #param string $role
* #return Boolean
*/
public function hasRole($role)
{
return in_array(strtoupper($role), $this->getRoles(), true);
}
}
Login (layout.html.twig actually) template has been overriden and apparently renders properly, my versions are:
Symonfy: Symfony version 2.8.2 - app/dev/debug
"friendsofsymfony/user-bundle": "^1.3"
console doctrine:schema:updatehas been executed and it doesn't detect any more changes, although usernameCanonical or email do not exist in the DB table.
Thanks
With FOSUserBundle 1.3.x you have to extend FOS\UserBundle\Entity\User instead of FOS\UserBundle\Model\User (see http://symfony.com/doc/1.3.x/bundles/FOSUserBundle/index.html#a-doctrine-orm-user-class).

Symfony using AdvancedUserInterface

I am trying to implement a login system with Symfony2. I am implementing the AdvancedUserInterface for my User entity, and keep getting the following error when I try to log in. Any help is appreciated.
Error:
$user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string.
User Entity:
<?php
namespace Flc\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use Serializable;
/**
* User
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="Flc\UserBundle\Entity\UserRepository")
*/
class User implements AdvancedUserInterface, Serializable {
/**
* #var integer
*
* #ORM\Column(name="userId", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $userId;
/**
* #var string
*
* #ORM\Column(name="userEmail", type="string", length=50)
*/
private $userEmail;
/**
* #var string
*
* #ORM\Column(name="userName", type="string", length=20)
*/
private $userName;
/**
* #var string
*
* #ORM\Column(name="userPassword", type="string", length=60)
*/
private $userPassword;
/**
* #var string
*
* #ORM\Column(name="userSalt", type="string", length=60, nullable=true)
*/
private $userSalt;
/**
* #var string
*
* #ORM\Column(name="userFirstName", type="string", length=40)
*/
private $userFirstName;
/**
* #var string
*
* #ORM\Column(name="userLastName", type="string", length=40)
*/
private $userLastName;
/**
* #var string
*
* #ORM\Column(name="userPhone", type="string", length=10)
*/
private $userPhone;
/**
* #var \DateTime
*
* #ORM\Column(name="created", type="datetime")
*/
private $created;
/**
* #var boolean
*
* #ORM\Column(name="isActive", type="boolean")
*/
private $isActive;
/**
* #var array
*
* #ORM\Column(name="roles", type="json_array", nullable=true)
*/
private $roles;
#########################
## GETTER - SETTER FUNCTIONS ##
#########################
/**
* Get userId
*
* #return integer
*/
public function getUserId() {
return $this->userId;
}
/**
* Set userEmail
*
* #param string $userEmail
* #return User
*/
public function setUserEmail($userEmail) {
$this->userEmail = $userEmail;
return $this;
}
/**
* Get userEmail
*
* #return string
*/
public function getUserEmail() {
return $this->userEmail;
}
/**
* Set userName
*
* #param string $userName
* #return User
*/
public function setUserName($userName) {
$this->userName = $userName;
return $this;
}
/**
* Get userName
*
* #return string
*/
public function getUserName() {
return $this->userName;
}
/**
* Set userPassword
*
* #param string $userPassword
* #return User
*/
public function setUserPassword($userPassword) {
$this->userPassword = $userPassword;
return $this;
}
/**
* Get userPassword
*
* #return string
*/
public function getUserPassword() {
return $this->userPassword;
}
/**
* Set userSalt
*
* #param string $userSalt
* #return User
*/
public function setUserSalt($userSalt) {
$this->userSalt = $userSalt;
return $this;
}
/**
* Get userSalt
*
* #return string
*/
public function getUserSalt() {
return $this->userSalt;
}
/**
* Set userFirstName
*
* #param string $userFirstName
* #return User
*/
public function setUserFirstName($userFirstName) {
$this->userFirstName = $userFirstName;
return $this;
}
/**
* Get userFirstName
*
* #return string
*/
public function getUserFirstName() {
return $this->userFirstName;
}
/**
* Set userLastName
*
* #param string $userLastName
* #return User
*/
public function setUserLastName($userLastName) {
$this->userLastName = $userLastName;
return $this;
}
/**
* Get userLastName
*
* #return string
*/
public function getUserLastName() {
return $this->userLastName;
}
/**
* Set userPhone
*
* #param string $userPhone
* #return User
*/
public function setUserPhone($userPhone) {
$this->userPhone = $userPhone;
return $this;
}
/**
* Get userPhone
*
* #return string
*/
public function getUserPhone() {
return $this->userPhone;
}
/**
* Set created
*
* #param \DateTime $created
* #return User
*/
public function setCreated($created) {
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated() {
return $this->created;
}
/**
* Set isActive
*
* #param boolean $isActive
* #return User
*/
public function setIsActive($isActive) {
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* #return boolean
*/
public function getIsActive() {
return $this->isActive;
}
/**
* Set roles
*
* #param boolean $roles
* #return User
*/
public function setRoles(array $roles) {
$this->roles = $roles;
return $this;
}
#############################################
## USER INTERFACE FUNCTIONS IMPLEMENTATION ##
#############################################
public function getRoles() {
#return $this->roles;
$roles = $this->roles;
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function eraseCredentials() {
//will be implemented
}
public function getSalt() {
return $this->getUserSalt();
}
public function getPassword() {
return $this->getUserPassword();
}
public function isAccountNonExpired() {
return true;
}
public function isAccountNonLocked() {
return true;
}
public function isCredentialsNonExpired() {
return true;
}
public function isEnabled() {
return $this->isActive;
}
#####################################################
## SERIALIZABLE INTERFACE FUNCTIONS IMPLEMENTATION ##
#####################################################
public function serialize() {
return serialize(array(
$this->userId,
$this->userName,
$this->userPassword
));
}
public function unserialize($serialized) {
list(
$this->userId,
$this->userName,
$this->userPassword) = unserialize($serialized);
}
}
User Repository:
<?php
namespace Flc\UserBundle\Entity;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
/**
* UserRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class UserRepository extends EntityRepository implements UserProviderInterface {
public function findOneByUsernameOrEmail($username) {
return $this->createQueryBuilder('u')
->andWhere('u.userName = :username OR u.userEmail = :email')
->setParameter('username', $username)
->setParameter('email', $username)
->getQuery()
->getOneOrNullResult();
}
public function loadUserByUsername($username) {
//$user = $this->findOneByUsernameOrEmail($username);
$user = $this->findOneBy(array("userName"=>$username));
if (!$user) {
throw new UsernameNotFoundException('No user found for ' . $username);
}
return $user;
}
public function refreshUser(UserInterface $user) {
$class = get_class($user);
if (!$this->supportsClass($class)) {
throw new UnsupportedUserException(sprintf(
'Instances of "%s" are not supported.', $class
));
}
if (!$refreshedUser = $this->find($user->getUserId())) {
throw new UsernameNotFoundException(sprintf('User with id %s not found'), json_encode($refreshedUser));
}
}
public function supportsClass($class) {
return $this->getEntityName() === $class || is_subclass_of($class, $this->getEntityName());
}
}
Security YML:
security:
encoders:
Flc\UserBundle\Entity\User: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
flc_users:
entity: { class: FlcUserBundle:User }
firewalls:
secured_area:
pattern: ^/
form_login:
login_path: login_form
check_path: login_check
logout:
path: logout
target: /
anonymous: ~
access_control:
- { path: ^/app, roles: ROLE_USER }
try adding __toString method on your User entity:
public function __toString() {
return (string) $this->getUsername();
}
In User Repository you need to replace :
public function findOneByUsernameOrEmail($username) {
return $this->createQueryBuilder('u')
->andWhere('u.userName = :username OR u.userEmail = :email')
->setParameter('username', $username)
->setParameter('email', $username)
->getQuery()
->getOneOrNullResult();
}
public function loadUserByUsername($username) {
//$user = $this->findOneByUsernameOrEmail($username);
$user = $this->findOneBy(array("userName"=>$username));
if (!$user) {
throw new UsernameNotFoundException('No user found for ' . $username);
}
return $user;
}
with :
public function loadUserByUsername($username)
{
$q = $this
->createQueryBuilder('u')
->where('u.username = :username OR u.email = :email')
->setParameter('username', $username)
->setParameter('email', $username)
->getQuery();
try {
$user = $q->getSingleResult();
} catch (NoResultException $e) {
throw new UsernameNotFoundException(sprintf('Unable to find an active admin AcmeUserBundle:User object identified by "%s".', $username), 0, $e);
}
return $user;
}

Symfony2 $user must be an instanceof UserInterface

I am having some trouble with login and authentication in Symfony2. The exception is "$user must be an instanceof UserInterface, an object implementing a __toString method, or a primitive string."
Debugging my code I could notice that the user I am trying to log in my application can get authenticated successfully (app/log/dev.log) but the credentials var is null:
The user variable from AbstractToken has the user data from database.
I continue debugging in the ContextListener->refreshUser function and I get these values:
Everything has the value null and on the Symfony\Bridge\Doctrine\Security\User\EntityUserProvider->refreshUser function returns the variable $refreshedUser as null, so when the function $token->setUser($refreshedUser) on the ContextListener class fails and throws the exception.
I write down my security.yml and my entities I am using:
security.yml:
security:
encoders:
Pladuch\BackBundle\Entity\BaseUser:
algorithm: sha512
encode_as_base64: false
iterations: 1
providers:
sga:
entity: { class: 'PladuchBackBundle:Usuario', property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
sga:
pattern: ^/gestion
anonymous: ~
form_login:
login_path: pladuch_login_sga
check_path: pladuch_login_check
default_target_path: pladuch_sga_index
csrf_provider: form.csrf_provider
provider: sga
logout:
path: pladuch_logout_sga
target: pladuch_login_sga
access_control:
- { path: ^/gestion/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/gestion, roles: ROLE_ADMIN }
Abstract class BaseUser:
<?php
namespace Pladuch\BackBundle\Entity;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
abstract class BaseUser implements AdvancedUserInterface, \Serializable
{
protected $id;
protected $salt;
protected $username;
protected $password;
public function __construct()
{
$this->isActive = true;
$this->salt = $this->generateSalt();
}
public function serialize()
{
return serialize(array($this->id, $this->username, $this->password));
}
public function unserialize($serialized)
{
list($this->id, $this->username, $this->password) = unserialize($serialized);
}
public function getRoles()
{
return array('ROLE_ADMIN');
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getUsername()
{
return $this->username;
}
public function eraseCredentials()
{
}
public function setSalt($salt)
{
$this->salt = $salt;
return $this;
}
public function getSalt()
{
return $this->salt;
}
public function generateSalt()
{
return base_convert(sha1(uniqid(mt_rand(), true)), 16, 36);
}
public function isAccountNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
public function isEnabled()
{
return true;
}
}
Class Usuario:
<?php
namespace Pladuch\BackBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Usuario
*
* #ORM\Table(name="usuario",
* uniqueConstraints={
* #ORM\UniqueConstraint(name="username", columns={"username"})
* },
* indexes={#ORM\Index(name="FK_USUARIO_ROL", columns={"rol_id"})})
* #ORM\Entity(repositoryClass="Pladuch\BackBundle\Repository\UsuarioRepository")
*/
class Usuario extends BaseUser
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id()
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=255, nullable=false)
*/
protected $username;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=1024, nullable=false)
*/
protected $password;
/**
* #var string
*
* #ORM\Column(name="salt", type="string", length=1024, nullable=false)
*/
protected $salt;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255, nullable=false)
*/
protected $email;
/**
* #var Rol
*
* #ORM\ManyToOne(targetEntity="Rol", inversedBy="id")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="rol_id", referencedColumnName="id")
* })
*/
protected $rol;
/**
* #var bool
*
* #ORM\Column(name="activo", type="boolean", nullable=true)
*/
protected $activo = true;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set username
*
* #param string $username
* #return Usuario
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Get username
*
* #return string
*/
public function getUsername()
{
return $this->username;
}
/**
* Set password
*
* #param string $password
* #return Usuario
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set salt
*
* #param string $salt
* #return Usuario
*/
public function setSalt($salt)
{
$this->salt = $salt;
return $this;
}
/**
* Get salt
*
* #return string
*/
public function getSalt()
{
return $this->salt;
}
/**
* Set email
*
* #param string $email
* #return Usuario
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set rol
*
* #param Rol $rol
* #return Usuario
*/
public function setRol(Rol $rol = null)
{
$this->rol = $rol;
return $this;
}
/**
* Get rol
*
* #return Rol
*/
public function getRol()
{
return $this->rol;
}
/**
* #return array|\Symfony\Component\Security\Core\Role\Role[]
*/
public function getRoles()
{
return array($this->getRol()->getRol());
}
/**
* Set activo
*
* #param $activo
* #return $this
*/
public function setActivo($activo)
{
$this->activo = $activo;
return $this;
}
/**
* Get activo
*
* #return bool
*/
public function getActivo()
{
return $this->activo;
}
}
The UsuarioRepository where I implements the three functions loadUserByUsername, refreshUser and supportsClass:
class UsuarioRepository extends EntityRepository implements UserProviderInterface
{
public function loadUserByUsername($username)
{
$q = $this->createQueryBuilder('u')
->where('u.username = :username')
->setParameter('username', $username)
->getQuery();
try {
$user = $q->getSingleResult();
} catch (NoResultException $e) {
$message = sprintf('Unable to find an active Usuario object identified by %s', $username);
throw new UsernameNotFoundException($message, 0, $e);
}
return $user;
}
public function refreshUser(UserInterface $userInterface)
{
$class = get_class($userInterface);
if (! $this->supportsClass($class)) {
throw new UnsupportedUserException(sprintf('Instances of %s are not suppoted', $class));
}
}
public function supportsClass($class)
{
return $this->getEntityName() === $class || is_subclass_of($class, $this->getEntityName());
}
}
Thank you for your help.
Kind regards.
P.S: I am using Symfony 2.5.6
Ok, I forgot to add the return sentence on refreshUser function in my repository...
return $this->find($userInterface->getId());
or
return $userInterface;
$userInterface has the authenticated user so I didn't need the $this->find() method. That solved everything

Resources