Two-way authentication in Symfony 2 project - php

I need to implement two-way authentication process in one of my Symfony 2 projects according to this algorithm:
User enters his username and password in the authentication form and submits it.
System first check his username and password the default way (all users are stored with Doctrine ORM).
If previous step failed, calling an external API and passing to it username and md5(password).
If previous step succeeded, creating a new User entity and using it as authenticated user.
If step #3 failed, then authentication is considered failed.
I already have a service that can be called to authenticate a user by he's username and password using external API, I'm just looking for a way to use it in authentication process.
What is the simplest way to implement this behavior? I just need to be pointed in the right direction.
Update
Is implementing a "custom authenticator" is a good solution to this problem? Or is there a better approach?
Looking at the documentation, I will have to implement both steps of authentication in my custom authenticator. Is it possible to implement only additional step?

Yes, that's the way to go. If your web service can give you user just by username then you could do it in UserProvider only, since in it's scope you have only username. If you must query by un/pw then you must do it in authenticator since in that scope you have password. So, with simple form it will look something like this
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
try {
$user = $userProvider->loadUserByUsername($token->getUsername());
} catch (UsernameNotFoundException $e) {
throw new AuthenticationException('Invalid username or password');
}
$encoder = $this->encoderFactory->getEncoder($user);
$passwordValid = $encoder->isPasswordValid(
$user->getPassword(),
$token->getCredentials(),
$user->getSalt()
);
if ($passwordValid) {
return new UsernamePasswordToken(
$user,
$user->getPassword(),
$providerKey,
$user->getRoles()
);
}
// remote users fallback
$webUser = $this->externalUserService->getByUsernamePassword(
$token->getUsername(),
$token->getCredentials()
);
if ($webUser) {
return new UsernamePasswordToken(
$webUser,
$token->getCredentials(),
$providerKey,
$webUser->getRoles()
);
}
throw new AuthenticationException('Invalid username or password');
}
Ofc there are too many ifs in this class and it's responsible for more then one thing, so to be tidy you could apply composite pattern and have 3 authenticators, one generic composite, second local db authenticator and third external service authenticator, and build it from the services config like this.
# services.yml
my_app.authenticator.main:
class: MyApp/Security/Core/Authentication/CompisiteAuthenticator
calls:
- [ add, [#my_app.authenticator.locale]]
- [ add, [#my_app.authenticator.remote]]
my_app.authenticator.locale:
class: MyApp/Security/Core/Authentication/LocalAuthenticator
arguments: [#security.encoder_factory]
my_app.authenticator.remote:
class: MyApp/Security/Core/Authentication/RemoteAuthenticator
arguments: [#my_app.remote_user_service]
Composite
<?php
namespace MyApp/Security/Core/;
class CompositeAuthenticator implements SimpleFormAuthenticatorInterface
{
/** #var SimpleFormAuthenticatorInterface[] */
protected $children = array();
public function add(SimpleFormAuthenticatorInterface $authenticator)
{
$this->children[] = $authenticator;
}
public function createToken(Request $request, $username, $password, $providerKey)
{
return new UsernamePasswordToken($username, $password, $providerKey);
}
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof UsernamePasswordToken
&& $token->getProviderKey() === $providerKey;
}
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
$result = null;
foreach ($this->children as $authenticator)
{
$result = $authenticator->authenticateToken($token, $userProvider, $providerKey);
if ($result) {
return $result;
}
}
throw new AuthenticationException('Invalid username or password');
}
}
And local and remote authenticators are I guess trivial now

Related

Error fetching OAuth credentials: "OAuthException: This authorization code has been used."

I have found many tutorials about this issue. But no one of them solved my problem. So, i'm trying on Symfony 4 to follow this tutorial for OAuth2 Facebook
When i click on my button "Connexion with Facebook", i have a blank page with the message :
Error fetching OAuth credentials: "OAuthException: This authorization
code has been used.".
I saw on some tutorials that is a problem about accessToken, longliveAccessToken, etc.
But i have no idea what to do in my code to solve this issue.
Here is my code of my FacebookAuthenticator.php :
<?php
namespace App\Security;
use App\Entity\User; // your user entity
use Doctrine\ORM\EntityManagerInterface;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use KnpU\OAuth2ClientBundle\Client\Provider\FacebookClient;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class FacebookAuthenticator extends SocialAuthenticator
{
private $clientRegistry;
private $em;
public function __construct(ClientRegistry $clientRegistry, EntityManagerInterface $em)
{
$this->clientRegistry = $clientRegistry;
$this->em = $em;
}
public function supports(Request $request)
{
// continue ONLY if the current ROUTE matches the check ROUTE
return $request->attributes->get('_route') === 'connect_facebook_check';
}
public function getCredentials(Request $request)
{
// this method is only called if supports() returns true
// For Symfony lower than 3.4 the supports method need to be called manually here:
// if (!$this->supports($request)) {
// return null;
// }
return $this->fetchAccessToken($this->getFacebookClient());
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
/** #var FacebookUser $facebookUser */
$facebookUser = $this->getFacebookClient()
->fetchUserFromToken($credentials);
// 1) have they logged in with Facebook before? Easy!
$existingUser = $this->em->getRepository(User::class)
->findOneBy(['facebookId' => $facebookUser->getId()]);
if ($existingUser) {
return $existingUser;
}
// 2) do we have a matching user by email?
$user = $this->em->getRepository(User::class)
->findOneBy(['email' => $email]);
// 3) Maybe you just want to "register" them by creating
// a User object
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!##$%&*_";
if(!$user){
$user = new User();
}
$user->setFacebookId($facebookUser->getId());
$user->setUsername($facebookUser->getEmail());
$user->setPassword(password_hash(substr( str_shuffle( $chars ), 0, 10), PASSWORD_DEFAULT));
$user->setPrenom($facebookUser->getFirstName());
$user->setNom($facebookUser->getLastName());
$user->setEmail($facebookUser->getEmail());
$user->setEnabled(true);
$user->setSocialAuthentication(true);
$this->em->persist($user);
$this->em->flush();
return $user;
}
/**
* #return FacebookClient
*/
private function getFacebookClient()
{
return $this->clientRegistry
// "facebook_main" is the key used in config/packages/knpu_oauth2_client.yaml
->getClient('facebook_main');
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// on success, let the request continue
return null;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$message = strtr($exception->getMessageKey(), $exception->getMessageData());
return new Response($message, Response::HTTP_FORBIDDEN);
}
/**
* Called when authentication is needed, but it's not sent.
* This redirects to the 'login'.
*/
public function start(Request $request, AuthenticationException $authException = null)
{
return new RedirectResponse(
'/connect/', // might be the site, where users choose their oauth provider
Response::HTTP_TEMPORARY_REDIRECT
);
}
}
My user is created in my database, with correct data, but can't authenticate with it.
Thanks for help me, if you want the code for FacebookController.php , tell me, then i will edit my post.
EDIT :
EDIT 2 :
public function getUser($credentials, UserProviderInterface $userProvider)
{
/** #var FacebookUser $facebookUser */
$client = $this->clientRegistry->getClient('facebook_main');
$accessToken = $client->getAccessToken();
if ($accessToken && !$accessToken->getToken()) {
dump("User is not found!"); die;
}
$provider = $client->getOAuth2Provider();
$longLivedToken = $provider->getLongLivedAccessToken($accessToken);
//I get the user by using long lived token
$facebookUser = $client->fetchUserFromToken($longLivedToken);
$email = $facebookUser->getEmail();
// 1) have they logged in with Facebook before? Easy!
$existingUser = $this->em->getRepository(User::class)
->findOneBy(['facebookId' => $facebookUser->getId()]);
if ($existingUser) {
return $existingUser;
}
// 2) do we have a matching user by email?
$user = $this->em->getRepository(User::class)
->findOneBy(['email' => $email]);
// 3) Maybe you just want to "register" them by creating
// a User object
$chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!##$%&*_";
if(!$user) {
$user = new User();
$user->setFacebookId($facebookUser->getId());
$user->setUsername($facebookUser->getEmail());
$user->setPassword(password_hash(substr(str_shuffle($chars), 0, 10), PASSWORD_DEFAULT));
$user->setPrenom($facebookUser->getFirstName());
$user->setNom($facebookUser->getLastName());
$user->setEmail($facebookUser->getEmail());
$user->setEnabled(true);
$user->setSocialAuthentication(true);
}
$this->em->persist($user);
$this->em->flush();
return $user;
}
I had same problem and I've refered the long live access token with this way. It might be a solution. Here you are.
$client = $clientRegistry->getClient('facebook_main');
$accessToken = $client->getAccessToken();
if ($accessToken && !$accessToken->getToken()) {
dump("User is not found!"); die;
}
$provider = $client->getOAuth2Provider();
$longLivedToken = $provider->getLongLivedAccessToken($accessToken);
//I get the user by using long lived token
$facebookUser = $client->fetchUserFromToken($longLivedToken);
The first error you have:
Error fetching OAuth credentials: "OAuthException: This authorization code has been used.".
Thats because you reload your Page (after e.g. change Code and refresh). The Error Message said that the Auth Code in Url Params used before.
Instead refresh your Site 1. go Back to the site where your button is placed and 2. get a new Auth Code by Clicking the FB Login Button.
Now this error is gone and you can go forward to debug your code.

Symfony 3.3 Web Service Login form

We are working in a login form, using simfony and a REST Webservice.
We have been searching in this link (http://symfony.com/doc/current/security/custom_provider.html)
The goal is login in with the form and the REST web service, updating my session data, like, name, doc, email, etc.
And with this data allow or deny the access to some pages or functions.
When we submit the form, we donĀ“t know how to use the data returned by the webservice, also if there are response or not.
This is our code:
SecurityController.php
<?php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends Controller {
// public function loginAction(AuthenticationUtils $authenticationUtils) {
public function loginAction(Request $request, AuthenticationUtils $authenticationUtils) {
// $authenticationUtils = $this->get('security.authentication_utils');
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('AppBundle:Security:login.html.twig', array('last_username' => $lastUsername, 'error' => $error));
// return $this->render('AppBundle:Security:login.html.twig');
}
public function loginCheckAction() {
$ca = $this->get('webservice_user_provider');
print_r($ca);
exit;
}
}
Login.html.twig-----
<form class="form-signin" action="{{ path('app_user_login_check') }}" method="POST">
Security.yml-----------------------
webservice:
id: webservice_user_provider
Archivo services.yml----------------------------
webservice_user_provider:
class: AppBundle\Security\User\WebserviceUserProvider
WebserviceUserProvider.php-----------------------------
<?php
// src/AppBundle/Security/User/WebserviceUserProvider.php
namespace AppBundle\Security\User;
use AppBundle\Security\User\WebserviceUser;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Unirest;
class WebserviceUserProvider implements UserProviderInterface {
protected $user;
public function __contsruct(UserInterface $user) {
$this->user = $user;
}
public function loadUserByUsername($username) {
// make a call to your webservice here
print_r("Estoy en el controlador de usuario");
exit;
$headers = array('Accept' => 'application/json');
$password = $this->request->get('password');
$query = array('user' => $username, 'password' => _password);
$userData = Unirest\Request::post('http://127.0.0.10:8888/login', $headers, $query);
// pretend it returns an array on success, false if there is no user
if ($userData) {
$datos = $userData->raw_body;
// print_r($userData);
// print_r($userData->body);
// print_r($userData->raw_body);
$username = $datos['ldap']['document'];
$password = $datos['ldap']['document'];
$salt = $datos['ldap']['document'];
$roles = $datos['ldap']['document'];
$doc = $datos['ldap']['document'];
$full_name = $datos['ldap']['document'];
$userLdap = $datos['ldap']['document'];
$userEpersonal = $datos['ldap']['document'];
$mail = $datos['ldap']['document'];
$position = $datos['ldap']['document'];
return new WebserviceUser($username, $password, $salt, $roles, $documento, $full_name, $userLdap, $userEpersonal, $mail, $position);
}
throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
}
public function refreshUser(UserInterface $user) {
if (!$user instanceof WebserviceUser) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class) {
return WebserviceUser::class === $class;
}
}
I will give you a general overview, for implementation details you may want to ask another question.
A REST web service should be stateless, i.e. (to simplify a bit) should have no session. To implement ACL you may have different strategies.
The easiest one is to perform authentication on each request. You may use http authentication, or you can use an API key as many webservices do. Your webservice will always authenticate the user as the first step in each request.
A slightly more secure strategy is to have authentication return you a temporary token. I.e. first you request the login action with whatever authentication system you choose (you can even have more than one) and you get back a randomly generated token associated with your user. In the next requests you include this token and the system know who you are.

Symfony Custom UserProvider Auth + Parse SDK

Heyo!
I know it's a common problem people having problems with custom providers and web service authentication. I'm spending hours trying to figure out how to do that but I'm almost freaking out.
So, the thing is: I'm using the Symfony Firewalls with a custom UserProvider and a AbstractGuardAuthenticator as well. The problem is in the loadUserByUsername($username) function inside the UserProvider implementation.
For security reasons I can't retrieve the user password from Parse (my web service), and the loadUserByUsername($username) function asks for that. Even in the documentation about how to create a custom user provider using web services they are retrieving the user password from the database.
So what's the solution in that case? What can I do when I don't have access to the user password?
My current code is something like that:
$app['app.authenticator'] = function () {
return new Authenticator($app);
};
$app['security.firewalls'] = array(
'login' => array(
'pattern' => '^/login/$',
),
'secured' => array(
'pattern' => '^.*$',
'form' => array('login_path' => '/login/', 'check_path' => '/login/auth/'),
'logout' => array('logout_path' => '/logout/', 'invalidate_session' => true),
'guard' => array(
'authenticators' => array(
'app.authenticator'
),
),
'users' => function () use ($app) {
return new UserProvider($app);
},
)
);
The Authenticator.php is quite big code because extends the AbstractGuardAuthenticator class. But I'm basically using this one from Symfony docs. The only thing Is that I'm sending to the UserProvider class the username AND the password as well, because that way I can check if the user and password are right. Like this:
public function getUser($credentials, UserProviderInterface $userProvider) {
return $userProvider->loadUserByUsername($credentials);
}
And my UserProvider class is the default one, I'm just checking inside the loadUserByUsername function if the credentials comming from my Authenticator are right. Something like this:
public function loadUserByUsername($credentials) {
$encoder = new BCryptPasswordEncoder(13);
try {
$user = ParseUser::logIn($credentials['username'], $credentials['password']);
} catch (ParseException $error) {
throw new UsernameNotFoundException(sprintf('Invalid Credentials.'));
}
return new User($credentials['username'], $encoder->encodePassword($credentials['password'], ''), explode(',', 'ROLE_USER'), true, true, true, true);
}
The problem is: after the login (everything with the login is working fine), Silex calls the loadUserByUsername function in every page which needs to be secured, but just sending the username parameter.
So basically, I don't know what to do guys. I'm really trying to figure out how to get this thing working.
Thanks for your help!
I have a similar implementation and this issue is well known. In my user provider I have the methods loadUserByUsername($username) and refreshUser(UserInterface $user). Since I have the same issue like you, I don't check the user in loadUserByUsername but simple return a new Object with only the username in it to not disturb the flow. loadUserByUsername doesn't make sense for external APIs, so I simply jump over it. The method refreshUser is either beeing called on every request, this is usefull.
In your AbstractGuardAuthenticator you have the method createAuthenticatedToken, which returns an token. There you should have the full authentificated user:
abstract class AbstractGuardAuthenticator implements GuardAuthenticatorInterface
{
/**
* Shortcut to create a PostAuthenticationGuardToken for you, if you don't really
* care about which authenticated token you're using.
*
* #param UserInterface $user
* #param string $providerKey
*
* #return PostAuthenticationGuardToken
*/
public function createAuthenticatedToken(UserInterface $user, $providerKey)
{
//do login stuff
//save password in user
return new PostAuthenticationGuardToken(
$user,
$providerKey,
$user->getRoles()
);
}
}
Then, I would't use loadUserByUsername but refreshUser instead. Both are called on every request:
/**
* Don't use
* #codeCoverageIgnore
* #param string $username
* #return User
*/
public function loadUserByUsername($username)
{
return new User($username, null, '', ['ROLE_USER'], '');
}
/**
* Refresh user on every subrequest after login
* #param UserInterface $user
* #return User
*/
public function refreshUser(UserInterface $user)
{
$password = $user->getPassword();
$username = $user->getUsername();
//login check
}

Symfony2 Deny User Login Based on Custom Status

I've followed the guide for implementing authentication/authorization and I can login.
I have one main difference though from what's in the guide. Instead of an isActive property I have a status table in my database.
I'm at a loss as to how I would deny/accept logins based on the values in the status table rather than the isActive property referenced in the guide.
I'm not sure what code to post because it works as it does in the guide and I'm pretty sure the Symfony security system handles all the authentication stuff where I can't see it.
Even if you just point me in the right direction I would be grateful.
Edit:
Using ChadSikorra's advice I came up with this code to implement the AdvancedUserInterface functions:
public function isAccountNonExpired()
{
$status = $this->getTblStatus()->getStatustext();
switch ($status){
case "expired":
return false;
default:
return true;
}
}
public function isAccountNonLocked()
{
$status = $this->getTblStatus()->getStatustext();
switch ($status){
case "locked":
return false;
case "suspended":
return false;
case "registered":
return false;
default:
return true;
}
}
public function isCredentialsNonExpired()
{
return $this->pwdexpired;
}
public function isEnabled()
{
$status = $this->getTblStatus()->getStatustext();
if($status != 'active')
return false
else
return true;
}
The next question I have then is how do I handle the exceptions that are thrown when a user has one of the statuses?
Based on what I have so far I think this is doable by catching the errors in the loginAction. What I don't know how to do is identify the errors, but I'll keep digging.
/**
* #Route("/Login", name="wx_exchange_login")
* #Template("WXExchangeBundle:User:login.html.twig")
* User login - Open to public
* Authenticates users to the system
*/
public function loginAction(Request $request)
{
$session = $request->getSession();
if ($this->get('security.context')->isGranted('IS_AUTHENTICATED_REMEMBERED'))
{
// redirect authenticated users to homepage
return $this->redirect($this->generateUrl('wx_exchange_default_index'));
}
// get the login error if there is one
if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(
SecurityContext::AUTHENTICATION_ERROR
);
} else {
$error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
$session->remove(SecurityContext::AUTHENTICATION_ERROR);
}
if($error instanceof LockedException)
{
}
return $this->render(
'WXExchangeBundle:User:login.html.twig',
array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
)
);
}
I am now able to check for the type of Exception, but I'm at a loss as to how to get the specific status so that I can redirect to the correct place. This is the last piece of the puzzle.
You could add mapping to your custom status table on the user entity, like so:
/**
* #ORM\OneToOne(targetEntity="AccountStatus")
* #ORM\JoinColumn(name="status_id", referencedColumnName="id", nullable=true)
*/
private $accountStatus;
This would also require creating an entity describing the custom status table. Then you could use this mapping in your user entity by implementing Symfony\Component\Security\Core\User\AdvancedUserInterface as referenced in the guide you linked. Then implement the isEnabled function something like this...
public function isEnabled()
{
return $this->getAccountStatus()->getIsActive(); /* Or whatever you named it */
}
EDIT:
Based on the API Doc for AdvancedUserInterface, if you want to do custom logic for handling the different statuses you'll need to register an exception listener...
If you need to perform custom logic for any of these situations, then
you will need to register an exception listener and watch for the
specific exception instances thrown in each case. All exceptions are a
subclass of AccountStatusException
There's a pretty good Cookbook article for creating something like this here. The basic process in this instance would be to create the class for the listener...
src/Acme/DemoBundle/EventListener/AcmeExceptionListener.php
namespace Acme\DemoBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Exception\DisabledException;
use Symfony\Component\Security\Core\Exception\LockedException;
class AcmeExceptionListener
{
public function onKernelException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
if ($exception instanceof DisabledException) {
// Customize your response object to display the exception details
$response = new Response();
$response->setContent('<html><body><h1>Custom disabled page!</h1></body></html>');
// Send the modified response object to the event
$event->setResponse($response);
}
elseif ($exception instanceof LockedException) {
// Or render a custom template as a subrequest instead...
$kernel = $event->getKernel();
$response = $kernel->forward('AcmeDemoBundle:AccountStatus:locked', array(
'exception' => $exception,
));
$event->setResponse($response);
}
// ... and so on
}
}
The above are just basic examples, but it gives you the gist anyway. Technically I guess you could also make custom exceptions by extending AccountStatusException and then throw them in your logic for your AdvancedUserInterface implementation. Then you would know exactly which status you are catching. Anyway, then make sure to register the listener as a service.
app/config/config.yml
services:
kernel.listener.your_listener_name:
class: Acme\DemoBundle\EventListener\AcmeExceptionListener
tags:
- { name: kernel.event_listener, event: kernel.exception, method: onKernelException }
Another way to go about this would be to implement some sort of a custom User Checker. See this question: Symfony2 custom user checker based on accepted eula

Silex : Bad Credentials with custom UserProvider

I'm new to Silex, I used this tutorial to set up Doctrine ORM.
But now when I'm trying to log in, i got "Bad credentials" error.
It happens when I use the default "login_check" controller.
If I use a custom one, it works but I don't know how to redirect the user to the page he was looking for. (I tried whith $request->headers->get('referer') in my login controller but it's empty.)
Here's my custom login_check contorller :
$app->post('/login-check-perso', function(Request $request) use ($app){
$route = $request->request->filter('route');
$password = $request->get('_password');
$username = $request->get('_email');
$userProvider = new \Lib\Provider\UserProvider($app);
$user = null;
try {
$user = $userProvider->loadUserByUsername($username);
}
catch (UsernameNotFoundException $e)
{
}
$encoder = $app['security.encoder_factory']->getEncoder($user);
// compute the encoded password
$encodedPassword = $encoder->encodePassword($password, $user->getSalt());
// compare passwords
if ($user->getPassword() == $encodedPassword)
{
// set security token into security
$token = new UsernamePasswordToken($user, $password, 'yourProviderKeyHere', array('ROLE_ADMIN'));
$app['security']->setToken($token);
// redirect or give response here
} else {
// error feedback
echo "wrong password";
die();
}
// replace url by the one the user requested while he wasn't logged in
return $app->redirect('/web/index_dev.php/admin/');
})->bind('login_check_perso');
So if someone can explain how to use the default "login_check", or explain to me how can I redirect user to the page he was trying to visit while not logged, it'll be great.
Thanks
EDIT:
I think the "Bad Credentials" is caused by a wrong encoders setting, I used this
to configure mine :
$app['security.encoder.digest'] = $app->share(function ($app) {
// use the sha1 algorithm
// don't base64 encode the password
// use only 1 iteration
return new MessageDigestPasswordEncoder('sha1', false, 1);
});
$app['security.encoder_factory'] = $app->share(function ($app) {
return new EncoderFactory(
array(
'Symfony\Component\Security\Core\User\UserInterface' => $app['security.encoder.digest'],
'Entity\User' => $app['security.encoder.digest'],
)
);
});
Is that correct ?
You can extend the DefaultAuthenticationSuccessHandler, which will be called after a successfull login, I doing it like this:
// overwrite the default authentication success handler
$app['security.authentication.success_handler.general'] = $app->share(function () use ($app) {
return new CustomAuthenticationSuccessHandler($app['security.http_utils'], array(), $app);
});
Create a CustomAuthenticationSuccessHandler:
use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Silex\Application;
class CustomAuthenticationSuccessHandler extends DefaultAuthenticationSuccessHandler
{
protected $app = null;
/**
* Constructor
*
* #param HttpUtils $httpUtils
* #param array $options
* #param unknown $database
*/
public function __construct(HttpUtils $httpUtils, array $options, Application $app)
{
parent::__construct($httpUtils, $options);
$this->app = $app;
}
/**
* (non-PHPdoc)
* #see \Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler::onAuthenticationSuccess()
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
$data = array(
'last_login' => date('Y-m-d H:i:s')
);
// save the last login of the user
$this->app['account']->updateUser($user->getUsername(), $data);
return $this->httpUtils->createRedirectResponse($request, $this->determineTargetUrl($request));
}
}
I'm using onAuthenticationSuccess() to save the users last loging datetime. You can use the createRedirectResponse() to redirect the user to the starting point you need.

Categories