I'm having trouble using a custom user provider with my custom user class. As highlighted in the log below, I believe the problem exists somewhere in my handler (of course, custom). It doesn't appear to be grabbing the email address (used for authentication: system_user_email_address).
app/config/dev.log
[2014-07-17 09:48:42] request.INFO: Matched route "login_check" (parameters: "_route": "login_check") [] []
[2014-07-17 09:48:42] security.DEBUG: Read SecurityContext from the session [] []
[2014-07-17 09:48:42] security.DEBUG: Reloading user from user provider. [] []
[2014-07-17 09:48:42] doctrine.DEBUG: SELECT t0.system_user_id AS system_user_id1, t0.first_name AS first_name2, t0.last_name AS last_name3, t0.enabled AS enabled4, t0.salt AS salt5, t0.password AS password6, t0.last_login AS last_login7, t0.locked AS locked8, t0.expired AS expired9, t0.expires_at AS expires_at10, t0.confirmation_token AS confirmation_token11, t0.password_requested_at AS password_requested_at12, t0.credentials_expired AS credentials_expired13, t0.credentials_expire_at AS credentials_expire_at14, t0.customer_id AS customer_id15, t0.system_user_phone_number_id AS system_user_phone_number_id16, t0.system_user_email_address_id AS system_user_email_address_id17 FROM system_user t0 WHERE t0.system_user_id = ? [16] []
[2014-07-17 09:48:42] security.DEBUG: Username "" was reloaded from user provider. [] []
[2014-07-17 09:48:42] doctrine.DEBUG: SELECT s0_.system_user_id AS system_user_id0, s0_.first_name AS first_name1, s0_.last_name AS last_name2, s0_.enabled AS enabled3, s0_.salt AS salt4, s0_.password AS password5, s0_.last_login AS last_login6, s0_.locked AS locked7, s0_.expired AS expired8, s0_.expires_at AS expires_at9, s0_.confirmation_token AS confirmation_token10, s0_.password_requested_at AS password_requested_at11, s0_.credentials_expired AS credentials_expired12, s0_.credentials_expire_at AS credentials_expire_at13, s0_.customer_id AS customer_id14, s0_.system_user_phone_number_id AS system_user_phone_number_id15, s0_.system_user_email_address_id AS system_user_email_address_id16 FROM system_user s0_ WHERE s0_.system_user_email_address_id = ? **["NONE_PROVIDED"]** []
[2014-07-17 09:48:42] security.INFO: Authentication request failed: Bad credentials [] []
I'm getting a response correctly from the SystemUserRepository class:
{"success":false,"message":"Bad credentials"}
I have been through the other "Bad Credential" posts, and double checked the problems listed there. I'm using SHA512, the password fields are long enough, etc.
app/config/security.yml
security:
encoders:
System\Bundle\DataBundle\Entity\SystemUser:
algorithm: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]
providers:
administrators:
entity: { class: SystemDataBundle:SystemUser }
firewalls:
dashboard:
pattern: ^/
form_login:
check_path: /Dashboard/Login/Check
login_path: /Dashboard/Login
use_forward: false
success_handler: system_user.security.authentication_handler
failure_handler: system_user.security.authentication_handler
anonymous: true
logout:
path: /Dashboard/Logout
target: /
admin_area:
pattern: ^/admin
http_basic: ~
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: /_wdt/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: /_profiler/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/Dashboard/(Login|LoginCheck|Logout)$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
System/Bundle/Entity/SystemUserRepository.php
namespace System\Bundle\DataBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
class SystemUserRepository extends EntityRepository implements UserProviderInterface
{
public function loadUserByUsername($emailAddress)
{
$q = $this
->createQueryBuilder('u')
->where('u.systemUserEmailAddress = :email')
->setParameter('email', $emailAddress)
->getQuery();
try {
// The Query::getSingleResult() method throws an exception
// if there is no record matching the criteria.
$SystemUser = $q->getSingleResult();
} catch (NoResultException $e) {
$message = sprintf(
'Unable to find an active admin AcmeUserBundle:User object identified by "%s".',
$emailAddress
);
throw new UsernameNotFoundException($message, 0, $e);
}
return $user;
}
public function refreshUser(UserInterface $systemUser)
{
$class = get_class($systemUser);
if (!$this->supportsClass($class)) {
throw new UnsupportedUserException(
sprintf(
'Instances of "%s" are not supported.',
$class
)
);
}
return $this->find($systemUser->getSystemUserId());
}
public function supportsClass($class)
{
return $this->getEntityName() === $class
|| is_subclass_of($class, $this->getEntityName());
}
}
System/Bundle/UserBundle/Security/AuthenticationHandler.php
namespace System\Bundle\UserBundle\Security;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
class AuthenticationHandler implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{
private $router;
private $session;
public function __construct( RouterInterface $router, Session $session )
{
$this->router = $router;
$this->session = $session;
}
public function onAuthenticationSuccess( Request $request, TokenInterface $token )
{
// if AJAX login
if ( $request->isXmlHttpRequest() ) {
$array = array( 'success' => true ); // data to return via JSON
$response = new Response( json_encode( $array ) );
$response->headers->set( 'Content-Type', 'application/json' );
return $response;
// if form login
} else {
if ( $this->session->get('_security.main.target_path' ) ) {
$url = $this->session->get( '_security.main.target_path' );
} else {
$url = $this->router->generate( 'dashboard_home_homepage' );
} // end if
return new RedirectResponse( $url );
}
}
public function onAuthenticationFailure( Request $request, AuthenticationException $exception )
{
// if AJAX login
if ( $request->isXmlHttpRequest() ) {
$array = array( 'success' => false, 'message' => $exception->getMessage() ); // data to return via JSON
$response = new Response( json_encode( $array ) );
$response->headers->set( 'Content-Type', 'application/json' );
return $response;
// if form login
} else {
// set authentication exception to session
$request->getSession()->set(SecurityContextInterface::AUTHENTICATION_ERROR, $exception);
return new RedirectResponse( $this->router->generate( 'login_path' ) );
}
}
}
System/Bundle/UserBundle/Controller/SystemUserController/registerAction (bits and pieces)
...
$tempSalt = uniqid(mt_rand());
...
$SystemUser
...
->setSalt($tempSalt)
->setPassword(hash('sha512', $tempSalt . $parameters['password']))
...
I think these are all the relevant files. I'll happily add anything else that might be relevant. I think I'm confusing one of the references, but have torn my hair out enough. There are a few pieces of messy stuff (early development obviously) but nothing that interferes with the fundamental operation here (I hope!). Not all of my routes are finished, but I'd be more than happy to be redirected to the wrong place...
I'm a little over-normalized for the typical web project:
SystemUser
PK SystemUserID
FK SystemUserEmailAddressID
FK SystemUserPhoneNumberID
...
I have no problem hydrating or dealing with these objects however, and the relationships seem to be correct based on the security.DEBUG logs above.
If there is a better method of salt/hash I will take that advice as well.
Thanks!