Symfony 5.4 LDAP and User-Entity Password mixed - symfony

Is ist possible to configure user authentification for Symfony 5.4 using either User/Password stored in the User entity oder LDAP depending on a boolean field or the password being null in the User entity?
I need to create some users that have to log on but are not contained in the customers LDAP structure. LDAP is more a comfort thing (single credentials for all apps) here than a security one (no one may logon if not defined in LDAP).
Perhaps I can get around programming the security things from the scatch and just combine two different providers.

Meanwhile I solved it and it was quite easy by using the "normal" password authenticator and modifying a bit of code. The strategy is:
Check if its an LDAP user. If not, use password authentication
Search the user in the LDAP directory
Bail out if not found
Bail out if not unique
Check credentials
The steps I took:
I added a boolean field to the entity USER called ldap_flag
I added variables to .env to specify the LDAP parameters
I modified Security/LoginFormAuthenticator:checkCredentials like this:
if ($user->getLDAPFlag()) {
if ($conn = ldap_connect($_ENV['LDAP_HOST'])) {
ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, $_ENV['LDAP_PROTOCOL_VERSION']);
ldap_set_option($conn, LDAP_OPT_REFERRALS, 0);
if ($_ENV['LDAP_CERT_CHECK'] == 0)
ldap_set_option($conn, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER);
$dn = $_ENV['LDAP_BIND_DN'];
$pw = $_ENV['LDAP_BIND_PW'];
if (ldap_bind($conn, $dn, $pw)) {
// Search user
$res = ldap_search($conn, $_ENV['LDAP_SEARCH_DN'], "(&(uid=" . $user->getUserName() . ")(objectClass=inetOrgPerson))", array('dn'));
$entries = ldap_get_entries($conn, $res);
if ($entries["count"] == 1)
return ldap_bind($conn, $entries[0]['dn'], $credentials['password']);
else if ($entries["count"] > 0)
throw new CustomUserMessageAuthenticationException('Benutzer im LDAP nicht eindeutig!');
else
throw new CustomUserMessageAuthenticationException('Benutzer auf dem LDAP Server nicht gefunden!');
} else
// cannot bind
throw new CustomUserMessageAuthenticationException('Kann nicht an LDAP-Server binden!');
ldap_unind($conn);
} else {
// no LDAP Connection
throw new CustomUserMessageAuthenticationException('Keine Verbindung zum LDAP-Server');
}
} else
// internal password-check
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
The error messages are in German but it should be easy to adapt them to an other language as they explain within their context.

I have found another solution, which makes use of Symfony's services. But it is not a one liner. One has to define several configurations, override some services and create two custom classes.
But this advices should be relatively complete.
# config/packages/security.yaml
security:
enable_authenticator_manager: true
providers:
all_users:
chain:
providers: [ldap_users, local_users]
local_users:
entity:
class: App\Entity\User
property: username
ldap_users:
# in services.yml Symfony's provider is overwritten with
# App\Security\LdapUserProvider
ldap:
service: Symfony\Component\Ldap\Ldap # see services.yml
base_dn: '%env(LDAP_BASE_DN)%'
search_dn: '%env(LDAP_SEARCH_DN)%'
search_password: '%env(LDAP_SEARCH_PASSWORD)%'
default_roles: ROLE_USER
uid_key: '%env(LDAP_UID_KEY)%'
firewalls:
main:
pattern: ^/
lazy: true
provider: all_users
form_login_ldap:
check_path: app_login
login_path: app_login
service: Symfony\Component\Ldap\Ldap # see services.yml
dn_string: '%env(LDAP_BASE_DN)%'
search_dn: '%env(LDAP_SEARCH_DN)%'
search_password: '%env(LDAP_SEARCH_PASSWORD)%'
query_string: 'sAMAccountName={username}'
# config/services.yaml
services:
Symfony\Component\Ldap\Ldap:
arguments: ['#Symfony\Component\Ldap\Adapter\ExtLdap\Adapter']
tags:
- ldap
Symfony\Component\Ldap\Adapter\ExtLdap\Adapter:
arguments:
- host: '%env(LDAP_HOST)%'
port: 389
encryption: none
options: { protocol_version: 3, referrals: false, network_timeout: 5 }
# overwrite symfony's LdapUserProvider so that a User entity is used
# instead of the default user class of Symfony.
security.user.provider.ldap:
class: App\Security\LdapUserProvider
arguments: [~, ~, ~, ~, ~, ~, ~, ~, ~]
App\Security\AppCredentialsCheckListener:
decorates: 'security.listener.form_login_ldap.main'
arguments:
$checkLdapCredentialsListener: '#.inner'
$checkCredentialsListener: '#security.listener.check_authenticator_credentials'
// src/Security/LdapUserProvider.php
namespace App\Security;
use App\Entity\User;
use App\Repository\UserRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Ldap\Entry;
use Symfony\Component\Ldap\LdapInterface;
use Symfony\Component\Ldap\Security\LdapUserProvider as BaseLdapUserProvider;
/**
* This service is responsible for adding a user entity to the local database.
*/
class LdapUserProvider extends BaseLdapUserProvider
{
private EntityManagerInterface $entityManager;
private UserRepository $userRepo;
public function __construct(
LdapInterface $ldap,
string $baseDn,
string $searchDn,
string $searchPassword,
array $defaultRoles,
string $uidKey,
string $filter,
?string $passwordAttribute,
?array $extraFields,
EntityManagerInterface $entityManager,
UserRepository $userRepo
) {
parent::__construct($ldap, $baseDn, $searchDn, $searchPassword, $defaultRoles, $uidKey, $filter, $passwordAttribute, $extraFields);
$this->entityManager = $entityManager;
$this->userRepo = $userRepo;
}
protected function loadUser(string $username, Entry $entry)
{
$ldapUser = parent::loadUser($username, $entry);
$user = $this->userRepo->findOneBy(['username' => $ldapUser->getUsername()]);
$flush = false;
if (!$user) {
$user = new User();
$user->setUsername($ldapUser->getUsername());
$this->entityManager->persist($user);
$this->entityManager->flush();
}
return $user;
}
}
// src/Security/AppCredentialsCheckListener.php
namespace App\Security;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Ldap\Security\CheckLdapCredentialsListener;
use Symfony\Component\Ldap\Security\LdapBadge;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Http\Event\CheckPassportEvent;
use Symfony\Component\Security\Http\EventListener\CheckCredentialsListener;
/**
* This event listener is responsible for checking the password.
* First the LDAP password is checked and as a fallback the local
* password is checked
*/
class AppCredentialsCheckListener implements EventSubscriberInterface
{
private CheckLdapCredentialsListener $checkLdapCredentialsListener;
private CheckCredentialsListener $checkCredentialsListener;
public function __construct(
CheckLdapCredentialsListener $checkLdapCredentialsListener,
CheckCredentialsListener $checkCredentialsListener
) {
$this->checkLdapCredentialsListener = $checkLdapCredentialsListener;
$this->checkCredentialsListener = $checkCredentialsListener;
}
public static function getSubscribedEvents(): array
{
// priority must be higher than the priority of the Symfony listeners
return [CheckPassportEvent::class => ['onCheckPassport', 999]];
}
public function onCheckPassport(CheckPassportEvent $event)
{
try {
// Check ldap password
$this->checkLdapCredentialsListener->onCheckPassport($event);
} catch (BadCredentialsException $e) {
// Fallback to local entity password
$this->checkCredentialsListener->checkPassport($event);
// We have to mark the ldap badge as resolved. Otherwise an exception will be thrown.
/** #var LdapBadge $ldapBadge */
$ldapBadge = $event->getPassport()->getBadge(LdapBadge::class);
$ldapBadge->markResolved();
}
}
}
I have added some comments to the config and the code, which should make clear how it is achieved. I hope it helps anyone.

Related

How to implement Symfony 5 Authentication without Doctrine?

Am in the process of rewriting an existing PHP project to Symfony 5.3. I want to upgrade its authentication system to Symfony's. Only issue: Doctrine is not an option in this project.
How can I use Symfony's authentication (possibly together with the new Authenticator-based Security) without invoking Doctrine anywhere?
I know that I must implement a UserLoaderInterface, but the docs use Doctrine so heavily that I cannot figure out how to do it without.
The post I just mentioned is asking something similar, but it still uses Symfony 2 and is thus too outdated.
I have a database that has the necessary User table with the usual columns (ID, eMail, Password, Name, etc.).
To the point:
How can I use Symfony's authentication (possibly together with the new Authenticator-based Security) without Doctrine?
To configurate that is on the the official website and also on this tutorial in SymfonyCast, but basically you can authenticate the user as you want:
See the next example:
Create a file on src\App\Security folder if your configuration is using the default config and create the class TokenAuthenticator, now see the below code, in this case check the class App\Service\ExternalAuthenticator, who will be in charge to get the information from other service or api and the return.
<?php
namespace App\Security;
use App\Example\Student;
use App\Service\ExternalAuthenticator;
use App\DTO\INFORMATIONFROMOTHERSERVICE;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationCredentialsNotFoundException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Security\Core\Security;
final class TokenAuthenticator extends AbstractGuardAuthenticator
{
/** #var Security */
private $security;
/** #var ExternalAuthenticator */
private $externalAuthenticator;
/** #var UrlGeneratorInterface */
private $urlGenerator;
public function __construct(
Security $security,
ExternalAuthenticator $externalAuthenticator
) {
$this->security = $security;
$this->externalAuthenticator = $externalAuthenticator;
}
/**
* {#inheritDoc}
*/
public function supports(Request $request)
{
//on this example, this guard must be using if on the request contains the word token
$response = false;
$apiKey = $request->query->get('token');
if (!is_null($apiKey)) {
$response = true;
}
return $response;
}
/**
* {#inheritDoc}
*/
public function getCredentials(Request $request)
{
$apiKey = $request->query->get('token');
// Validate with anything you want, other service or api
/** #var INFORMATIONFROMOTHERSERVICE**/
$dtoToken = $this->externalAuthenticator->validateToken($apiKey, $simulator);
return $dtoToken;
}
/**
* #param INFORMATIONFROMOTHERSERVICE $credentials
* #param UserProviderInterface $userProvider
* #return INFORMATIONFROMOTHERSERVICE |UserInterface|null
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
return $userProvider;
}
public function checkCredentials($credentials, UserInterface $user)
{
return true;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
return new RedirectResponse($this->urlGenerator->generate('home_incorrect'));
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $providerKey)
{
return new RedirectResponse($request->getPathInfo());
}
public function start(Request $request, AuthenticationException $authException = null)
{
return new RedirectResponse($this->urlGenerator->generate('home_incorrect'));
}
public function supportsRememberMe()
{
// todo
}
}
Now the external service must return App\DTO\INFORMATIONFROMOTHERSERVICE class, but this class must implement the UserInterface, now with this in mind. We need to configurate what guard must be in charge of what routes, see the next example:
security:
encoders:
App\Entity\User:
algorithm: bcrypt
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_user_provider:
entity:
class: App\Entity\User
property: email
//You can use a
custom_provider:
id : App\DTO\INFORMATIONFROMOTHERSERVICE
# used to reload user from session & other features (e.g. switch_user)
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
survey:
anonymous: true
pattern: ^/(custom_path)/
// The
provider: custom_provider
guard:
// You can use as many authenticator that you want, but in the node entrypoint, you must choose who must be the default if only is one you could remove the entrypoint node, similar as the main firewall
authenticators:
- App\Security\TokenAuthenticator
- App\Security\OtherAuthenticator
entry_point: App\Security\OtherAuthenticator
main:
anonymous: true
lazy: true
provider: app_user_provider
logout:
path: app_logout
guard:
authenticators:
- App\Security\AppAuthenticator
Also see the next documentation, that will guide you to create the class App\DTO\INFORMATIONFROMOTHERSERVICE.
I hope this answer, help you

Why credentials are null when erase_credentials is false?

Symfony 5.3
security.yaml
security:
...
erase_credentials: false
LoginListener.php
<?php
namespace App\EventListener;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactoryInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class LoginListener
{
private $passwordHasherFactory;
private $em;
public function __construct(PasswordHasherFactoryInterface $passwordHasherFactory, EntityManagerInterface $em)
{
$this->passwordHasherFactory = $passwordHasherFactory;
$this->em = $em;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
$token = $event->getAuthenticationToken();
// Migrate the user to the new hashing algorithm if is using the legacy one
if ($user->hasLegacyPassword()) {
// Credentials can be retrieved thanks to the false value of
// the erase_credentials parameter in security.yml
$plainPassword = $token->getCredentials();
file_put_contents('darius.txt', 'test'.$plainPassword, FILE_APPEND); // why null?
}
$token->eraseCredentials();
}
}
https://symfony.com/doc/current/reference/configuration/security.html#erase-credentials
If true, the eraseCredentials() method of the user object is called
after authentication.
So probably if false it should not erase? Why it is erasing?
Password is received because login works. I just dissapears at some point.
Update
Question is why credentials are null before calling
$token->eraseCredentials();
Managed to install xdebug and found that when creating token the credentials are not set:
JsonLoginAuthenticator:
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
{
return new UsernamePasswordToken($passport->getUser(), null, $firewallName, $passport->getUser()->getRoles());
}
UsernamePasswordToken:
public function __construct($user, $credentials, string $firewallName, array $roles = [])
{
parent::__construct($roles);
if ('' === $firewallName) {
throw new \InvalidArgumentException('$firewallName must not be empty.');
}
$this->setUser($user);
$this->credentials = $credentials;
$this->firewallName = $firewallName;
parent::setAuthenticated(\count($roles) > 0);
}
So I guess that is the problem - under security - firewall there is such setup:
main:
lazy: true
provider: app_user_provider
logout:
path: logout
target: after_logout
json_login:
check_path: /login
entry_point: App\Security\AuthenticationEntryPoint
So probably I have answered my question why there is no credentials. I am just missing now how do create new password hash for password on login, but that is probably for different question.

Symfony 4 get authentification with cas and roles via database

I am having trouble finding any help, examples or tutorials that layout how to use CAS for Authorization but load the roles from a local database in Symfony 4.
I managed to authenticate via CAS using the bundle: prayno. I manage to succesfully get roles from a database via an entity and repository. But the fact is that I did not manage to make the link between the twos.
Here is my security.yaml:
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from- user-providers
providers:
cas:
id: prayno.cas_user_provider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
logout: ~
guard:
authenticators:
- prayno.cas_authenticator
The function under the prayno bundle to get authentification and the roles:
public function loadUserByUsername($username)
{
if ($username) {
$password = '...';
$salt = "";
$roles = ['ROLE_USER'];
return new CasUser($username, $password, $salt, $roles);
}
throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
}
From there, I tried to get my roles from the database using the controller I created for this occasion:
<?php
namespace App\Controller;
use App\Entity\FscDroitsBundle;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class RightsController extends AbstractController
{
/**
* #Route("/rights", name="rights")
*/
public function attribute_rights($netid)
{
$entities = $this->em->getDoctrine()->getRepository(FscDroitsBundle::class)->findRights($netid);
$rights_array = array('ROLE_USER');
foreach ($entities as $data) {
array_push($rights_array, $data->getBundle());
}
return $rights_array;
}
But, until now, I did not find any way to use this controller in the bundle, something like:
$rights = new RightsController();
$roles = $rights->attribute_rights($username);
instead of:
$roles = ['ROLE_USER'];
in my loadbyUsername function.
The problem seems to come from this line:
$entities = $this->getDoctrine()->getRepository(FscDroitsBundle::class)->findRights($netid);
The error is: Call to a member function has() on null
How is it that the controller can not access the repository in the bundle meanwhile it access it without any problem on a view ?

Sonata Admin locale change with admin user's locale

I build a i18n admin site with sonata admin bundle. Now i wanna change my locale and translation with admin user's locale set. Such as , i have two admin users one is en(userA), and another is zh(UserB). user's locale is set en/zh in User admin dashboard respectively。
My admin service :
services:
sonata.admin.post:
class: Acme\StoreBundle\Admin\PostAdmin
tags:
- { name: sonata.admin, manager_type: orm, group: "Content", label: "Project", label_translator_strategy: sonata.admin.label.strategy.underscore }
arguments:
- ~
- Acme\StoreBundle\Entity\Product
- ~
calls:
- [ setTranslationDomain, [AcmeStoreBundle]]
- [ setLabelTranslatorStrategy, [ #sonata.admin.label.strategy.native ]]
Then my Resources/translations/AcmeStoreBundle.en.xliff and Resources/translations/AcmeStoreBundle.zh.xliff just like so:
<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="file.ext">
<body>
<trans-unit id="1">
<source>label.product.name</source>
<target>Product Name</target> ##---> zh is diffrent here!!!
</trans-unit>
</body>
</file>
</xliff>
Then, i loggin admin by UserA, the message (product name ) is ok. But i loggin by UserB the message is still en locale( product name) . Of course, I can change the global locale in parameters.yml (%locale%) for userB, But this is not good for userA .
So, how can i change my site's locale(message or translation) with diffrent admin's user locale ?
Thanks in advance.
You could extend the login success handler and set the user's locale in the session. For example:
# app/config/config.yml
services:
login_success_handler:
parent: security.authentication.success_handler
class: MyVendor\MyBundle\LoginSuccessHandler
UPDATE: Make sure to point to this listener in your security.yml:
# app/config/security.yml
security:
firewalls:
secured_area:
pattern: ^/
anonymous: ~
form_login:
login_path: login
check_path: login_check
success_handler: login_success_handler
Then add login success handler class:
class LoginSuccessHandler extends DefaultAuthenticationSuccessHandler
{
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$locale = $token->getUser()->getLocale()
$request->getSession()->set('_locale', $locale);
$request->setLocale($locale);
return parent::onAuthenticationSuccess($request, $token);
}
}
Then you could create a LocaleListener similar to or exactly the same as the one in the Symfony documentation. The only difference is, if you will never define _locale in your routes you could change:
if ($locale = $request->attributes->get('_locale')) {
$request->getSession()->set('_locale', $locale);
} else {
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
to just
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
Thank you Jason. I did that as your hint.But , the login_success_handler seem not be called completely。
my config.yml:
services:
login_success_handler:
parent: security.authentication.success_handler
class: Acme\StoreBundle\EventListener\LoginSuccessHandler
acme_locale.locale_listener:
class: Acme\StoreBundle\EventListener\LocaleListener
arguments: ["%kernel.default_locale%"]
tags:
- { name: kernel.event_subscriber }
And the src/Acme/StoreBundle/EventListener/LoginSuccessHandler.php
namespace Acme\StoreBundle\EventListener;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
class LoginSuccessHandler extends DefaultAuthenticationSuccessHandler
{
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$locale = $token->getUser()->getLocale();
file_put_contents('/tmp/login.log', $locale, FILE_APPEND); ## I can't find the log file
$request->getSession()->set('_locale', $locale);
$request->setLocale($locale);
}
}
And src/Acme/StoreBundle/EventListener/LocaleListener.php
namespace Acme\StoreBundle\EventListener;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class LocaleListener implements EventSubscriberInterface
{
private $defaultLocale;
public function __construct($defaultLocale = 'en')
{
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
$request->setLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
public static function getSubscribedEvents()
{
return array(
// must be registered before the default Locale listener
KernelEvents::REQUEST => array(array('onKernelRequest', 17)),
);
}
}
What's wrong with me? Thanks.

Restrict user logins to one station

Is it possible, in Symfony2, to logout a user that has been logged in one machine, when user logs in another machine?
My User entity has a field that contains the machine identifier where user has logged in last place and I had thought using the login listener to invalidate the previous user's session, but i have no success with it.
¿Have you any idea?
Thanks
You can use AuthenticationSuccessHandlerInterface if user provides correct credentials and then check the machine id.
Take a look for example from my app:
Define your listener as service:
services:
blogjakon.user.authentication_handler:
class: BlogJakon\UserBundle\ActionListener\AuthenticationHandler
calls:
- [ setContainer, [ #service_container ] ]
security.yml:
form_login:
[...]
success_handler: blogjakon.user.authentication_handler
Listener class:
class AuthenticationHandler extends ContainerAware implements AuthenticationSuccessHandlerInterface{
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$templating = $this->container->get('templating');
$machineIdentifier = $this->container->get('machine.identifier');
if( $token->getUser()->getMachineId() == $machineIdentifier->getCurrentId() )
{
return $templating->renderResponse('yourBundle::logged.html.twig');
}
else
{
// Update user entity with new machineId and redirect.
return $templating->renderResponse('yourBundle::logged-in-new-machine.html.twig');
}
}
}
Of course service machine.identifier is your service that provides functionality to get/generate/check your machine id. You have to implement this service by yourself.
On each request you have to check if user is all the time on the same machine.
You can do it in 2 ways:
If you have your own user provider you can implement refreshUser(UserInterface $user) method.
Register request listener.
I will show you how to register request listener:
service for this listener:
services:
listener.requestresponse:
class: My\AwesomeBundle\Listener\MyListener
arguments: [ #service_container ]
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
Listener class:
class MyListener
{
protected $container;
public function __construct(ContainerInterface $container) // this is #service_container
{
$this->container = $container;
}
public function onKernelRequest(GetResponseEvent $event)
{
$securityContex = $this->get('security.context');
$machineIdentifier = $this->container->get('machine.identifier');
if( $securityContext>getToken()->getUser()>getMachineId() != $machineIdentifier->getCurrentId() )
$this->get('security.context')->setToken(NULL);
}
}
This example comes from this question.
Hope it helps.

Resources