I have the following authentication handler:
class LoginAuthSuccessHandler implements AuthenticationSuccessHandlerInterface, AuthenticationFailureHandlerInterface
{
private $router;
private $container;
/**
* Constructor
* #param RouterInterface $router
*/
public function __construct(RouterInterface $router, $container)
{
$this->router = $router;
$this->container = $container;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
if ($request->isXmlHttpRequest()) {
$user = $this->container->get('security.context')->getToken()->getUser();
$result = array('success' => true, 'user' => $user);
return new Response(json_encode($result));
} else {
$route = $this->router->generate('ShopiousMainBundle_profile');
$referrer_url = $request->server->get('HTTP_REFERER');
if (strstr($referrer_url, '/items/')) {
$route = $referrer_url;
}
return new RedirectResponse($route);
}
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
if ($request->isXmlHttpRequest()) {
$result = array('success' => false, 'message' => $exception->getMessage());
return new Response(json_encode($result));
} else {
// Handle non XmlHttp request here
}
}
}
why is it that:
$user = $this->container->get('security.context')->getToken()->getUser();
returns null? how do i get the authenticated user at this point?
You should use the $token variable you are receiving as an argument instead of $this->container->get('security.context')->getToken().
$user = $token->getUser();
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
}
that's all ;)
maibe you don't set your provider in security.yml.
security:
provider:
example:
entity: {class Acme\AuctionBundle\Entity\User, property: username}
Replace the Bundle and the entity by yours.
The only way I found was to inject the entityManager,
calls:
- [setEntityManager,[#doctrine.orm.entity_manager]]
get the username from the request and query for the user, using that username.
$userRepo->findByEmail($request->get('_username'));
Related
I want to manage my users without Database, and check their JWT
I use Guard and a Custom Service to Validate my JWT.
I Want to use even a custom user provider, And If user doesn't have JWT I want to redirect to a new page or call an exception
this is my code
class WebserviceUserProvider implements UserProviderInterface {
/**
* #var \Symfony\Component\Routing\RouterInterface
*/
private $router;
private $requestStack;
/**
* WebserviceUserProvider constructor.
* #param RequestStack $router
*/
public function __construct($router,RequestStack $requestStack) {
$this->router = $router;
$this->requestStack = $requestStack;
}
public function refreshUser(UserInterface $user) {
if (!$user instanceof User) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
return $this->loadUserByUsername($user->getUsername());
}
public function loadUserByUsername($username) {
if (!$token = $this->requestStack->getCurrentRequest()->headers->get('Authorization') ?: $this->requestStack->getCurrentRequest()->query->get('token')) {
throw new UsernameNotFoundException('Could not find user. Sorry!');
}
$user = new \AppBundle\Entity\User('MyUsername');
$user->setJwtToken($token);
return $user;
}
public function supportsClass($class) {
return $class === 'AppBundle\Security\User\WebserviceUser';
}
}
And this is my service.yml
app.webservice_user_provider:
class: AppBundle\Security\WebserviceUserProvider
arguments: ['#router','#request_stack']
And My security.yml
user-providers
providers:
webservice:
id: app.webservice_user_provider
When I visit a url with token in queryString
http://127.0.0.1:8000/app_dev.php/?token=my.token.jwt
My User is logged.
When visit
http://127.0.0.1:8000/app_dev.php/
I have an user anon. ( My token is set to Anon. ) and I don't have an exception
If I change loadUserByUsername with
public function loadUserByUsername($username) {
if (!$token = $this->requestStack->getCurrentRequest()->headers->get('Authorization') ?: $this->requestStack->getCurrentRequest()->query->get('token')) {
$url = $this->router->generate('errorPage');
return new RedirectResponse($url);
}
$user = new \AppBundle\Entity\User('MyUsername');
$user->setJwtToken($token);
return $user;
}
I have this error
Attempted to call an undefined method named "getUsername" of class
"Symfony\Component\HttpFoundation\RedirectResponse". 500 Internal
Server Error - UndefinedMethodException
How can I redirect to a page if I dont'have a JWT ( or is not valid ), or have an exception ?
I'm creating an E-commerce app and I'm building a custom redirection with the Handler.
Let's say my user is on his shopping's cart and he is not logged. He validates his cart to continue the buying process. In this situation, My app redirect the user to the login page. But when He is finally logged I would like to redirect him to the previous route (ie the cart page).
(Sorry for my poor english, I hope you will understand what I say)
This is my AuthentificationSuccessHandler :
class AuthentificationSuccessHandler implements AuthenticationSuccessHandlerInterface
{
protected $router;
protected $security;
/**
* AfterLoginRedirection constructor.
* #param Router $router
* #param AuthorizationChecker $security
*/
public function __construct(Router $router, AuthorizationChecker $security)
{
$this->router = $router;
$this->security = $security;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$session = $request->getSession();
if ($this->security->isGranted('ROLE_SUPER_ADMIN')) {
$response = new RedirectResponse($this->router->generate('_homepage_admin'));
} else {
$referer_url = $request->headers->get('referer');
$response = new RedirectResponse($referer_url);
}
$session->getFlashBag()->add('success', 'Connexion réussi !');
return $response;
}
As you can see, I use headers->get('referer'); like I saw in the documentation, but this is not working. The user is redirect to the login page (but the authentification works fine)
So i'm stuck right now..
Tell me if you need more code to help me for my issue.
Any clues or advices ?
Thanks.
Well, I found a solution, I don't know if it's the better way but at least it's works.
So : into my listener I storage a attribute in the session's object, This attribue is update if the user is on a path in my list.
My Listener :
class RedirectionListener
{
/**
* RedirectionListener constructor.
* #param ContainerInterface $container
* #param Session $session
*/
public function __construct(ContainerInterface $container, Session $session)
{
$this->session = $session ;
$this->router = $container->get('router') ;
$this->securityContext = $container->get('security.context') ;
}
/**
* #param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event){
$request = $event->getRequest() ;
$list_route = array('_cart', '_category', '_delivery','_validate','_product','_homepage');
$route = $request->attributes->get('_route') ;
$route_params = $request->attributes->get('_route_params') ;
if(in_array($route, $list_route)){
$this->setRouteSession($request, $route, $route_params);
}
if($route == "_delivery" || $route =="_validate"){
if ($this->session->has('cart')){
if(count($this->session->get('cart')) == 0){
$this->session->getFlashBag()->add('info', 'Votre panier étant vide, vous ne pouvez pas continuer le processus d\'achat ');
$event->setResponse(new RedirectResponse($this->router->generate('_cart')));
}
}
if(!is_object($this->securityContext->getToken()->getUser())){
$this->session->getFlashBag()->add('info', 'Vous devez vous identifier');
$event->setResponse(new RedirectResponse($this->router->generate('fos_user_security_login')));
}
}
}
/**
* #param $request
* #param $route
* #param null $param
*/
private function setRouteSession($request, $route, $param){
$session = $request->getSession() ;
$session->set('lastPath', array(
'route' => $route,
'params' => $param
));
}
}
My Handler :
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$session = $request->getSession();
if(!$session->has('lastPath')){
$route = '_homepage';
} else {
$route = $session->get('lastPath') ;
}
if ($this->security->isGranted('ROLE_SUPER_ADMIN')) {
$response = new RedirectResponse($this->router->generate('_homepage_admin'));
} else {
if($route['params'] != null){
$response = new RedirectResponse($this->router->generate($route['route'],$route['params']));
} else {
$response = new RedirectResponse($this->router->generate($route['route']));
}
}
$session->getFlashBag()->add('success', 'Connexion réussi !');
return $response;
}
after user is logged in correctly you can get the referer url using
$this->getRequest()->headers->get('referer')
The problem
I want users to be authenticated via an access token that is supplied as a GET parameter to the first request.
I have never implemented such a thing in Symfony, so I followed the steps outlined in How to Create a custom Authentication Provider, but it 'doesn't work'. The authenticate method of the AuthenticationProviderInterface is not triggered.
What I have tried
Because it is a lot of configuration mostly, I don't even know how to debug this. This is what I have concluded so far: Only the AccessTokenProvider gets constructed, nothing else.
The code
These are the relevant parts of the system:
security.yml
security:
# Snip default (empty) in_memory provider
firewalls:
# Snip dev and main (symfony default)
accesstoken_secured:
pattern: ^/admin/
accesstoken: true
services.yml
services:
accesstoken.security.authentication.provider:
class: AppBundle\Security\Authentication\Provider\AccessTokenProvider
arguments:
- '' # User Provider
- '%kernel.cache_dir%/security/nonces'
public: false
accesstoken.security.authentication.listener:
class: AppBundle\Security\Firewall\AccessTokenListener
arguments: ['#security.token_storage', '#security.authentication.manager']
public: false
AccessTokenFactory
class AccessTokenFactory implements SecurityFactoryInterface
{
public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
{
$providerId = 'security.authentication.provider.accesstoken.'.$id;
$container
->setDefinition($providerId, new DefinitionDecorator('accesstoken.security.authentication.provider'))
->replaceArgument(0, new Reference($userProvider))
;
$listenerId = 'security.authentication.listener.accesstoken.'.$id;
$container->setDefinition($listenerId, new DefinitionDecorator('accesstoken.security.authentication.listener'));
return array($providerId, $listenerId, $defaultEntryPoint);
}
public function getPosition()
{
return 'pre_auth';
}
public function getKey()
{
return 'accesstoken';
}
public function addConfiguration(NodeDefinition $node)
{
}
}
AccessTokenProvider
class AccessTokenProvider implements AuthenticationProviderInterface
{
private $userProvider;
public function __construct(UserProviderInterface $userProvider)
{
$this->userProvider = $userProvider;
}
public function authenticate(TokenInterface $token)
{
$user = $this->userProvider->loadUserByAccessToken($token->getAttribute('token'));
if ($this->isTokenValid($token)) {
$authenticatedToken = new AccessToken(['role_user']);
$authenticatedToken->setUser($user);
return $authenticatedToken;
}
throw new AuthenticationException('The WSSE authentication failed.');
}
protected function isTokenValid(AccessToken $token)
{
//TODO: Implement
return (bool)$token->token;
}
public function supports(TokenInterface $token)
{
return $token instanceof AccessToken;
}
}
AccessTokenListener
class AccessTokenListener
{
protected $tokenStorage;
protected $authenticationManager;
/**
* AccessTokenListener constructor.
* #param TokenStorageInterface $tokenStorage
* #param AuthenticationManagerInterface $authenticationManager
*/
public function __construct(TokenStorageInterface $tokenStorage, AuthenticationManagerInterface $authenticationManager)
{
$this->tokenStorage = $tokenStorage;
$this->authenticationManager = $authenticationManager;
}
public function handle(GetResponseEvent $event)
{
$request = $event->getRequest();
$accesstoken = $request->get('accesstoken');
$token = new AccessToken();
$token->token = $accesstoken;
try {
$authToken = $this->authenticationManager->authenticate($token);
$this->tokenStorage->setToken($authToken);
return;
} catch (AuthenticationException $failed) {
// ... you might log something here
}
// By default deny authorization
$response = new Response();
$response->setStatusCode(Response::HTTP_FORBIDDEN);
$event->setResponse($response);
}
}
AccessToken
class AccessToken extends AbstractToken
{
public $token;
/**
* AccessToken constructor.
* #param array $roles
*/
public function __construct(array $roles = array())
{
parent::__construct($roles);
// If the user has roles, consider it authenticated
$this->setAuthenticated(count($roles) > 0);
}
/**
* Returns the user credentials.
*
* #return mixed The user credentials
*/
public function getCredentials()
{
return '';
}
}
I eventually tried to implement it in another way, using the tutorial at How to Create a Custom Authentication System with Guard.
This uses Symfony's new Guard system. It was actually very easy to setup!
i'm working on symfony2 project and i get this exception. anybody have an idea on what is causing it ?
Uncaught exception 'Symfony\Component\Security\Core\Exception\AccessDeniedException' with message 'Access Denied' in /data/apache/www/emploipublic-sf/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/AccessListener.php:70\n
class AccessListener implements ListenerInterface
{
private $context;
private $accessDecisionManager;
private $map;
private $authManager;
private $logger;
public function __construct(SecurityContextInterface $context, AccessDecisionManagerInterface $accessDecisionManager, AccessMapInterface $map, AuthenticationManagerInterface $authManager, LoggerInterface $logger = null)
{
$this->context = $context;
$this->accessDecisionManager = $accessDecisionManager;
$this->map = $map;
$this->authManager = $authManager;
$this->logger = $logger;
}
/**
* Handles access authorization.
*
* #param GetResponseEvent $event A GetResponseEvent instance
*/
public function handle(GetResponseEvent $event)
{
if (null === $token = $this->context->getToken()) {
throw new AuthenticationCredentialsNotFoundException('A Token was not found in the SecurityContext.');
}
$request = $event->getRequest();
list($attributes, $channel) = $this->map->getPatterns($request);
if (null === $attributes) {
return;
}
if (!$token->isAuthenticated()) {
$token = $this->authManager->authenticate($token);
$this->context->setToken($token);
}
if (!$this->accessDecisionManager->decide($token, $attributes, $request)) {
throw new AccessDeniedException(); // this is line 70
}
}
}
Look at your security.yml file (app/config/security.yml).
You may have some secure path which you do not have access to. Check out
security -> access_control
section.
How to manage Full authentication is required to access this resource.?
I want to redirect user when he is not authenticated.
I have custom uthenticater which authenticate user depending on session data, and i want to redirect user when hes not authenticatet.
My authenticator class:
/**
* #Service("sso_authenticator")
*/
class SsoAuthenticator implements SimplePreAuthenticatorInterface
{
/**
* #var SsoUserProvider
*/
protected $userProvider;
/**
* #InjectParams({
* "userProvider" = #Inject("sso_user_provider")
* })
*/
public function __construct(SsoUserProvider $userProvider)
{
$this->userProvider = $userProvider;
}
public function createToken(Request $request, $providerKey)
{
$user = $request->getSession()->get('sso_user');
if (!$user) {
throw new BadCredentialsException('No user found');
}
return new PreAuthenticatedToken(
'anon.', $user, $providerKey
);
}
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
$user = $token->getCredentials();
if (!is_array($user)) {
$user = $token->getUser();
}
if (!$user) {
throw new AuthenticationException('User does not exist.');
}
$ssoUser = $this->userProvider->loadUser($user);
return new PreAuthenticatedToken(
$ssoUser, $user, $providerKey, $ssoUser->getRoles()
);
}
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
}
}
i set the login path to logout path like this:
secured_area:
form_login:
login_path : main_user_logout
then i wrote custom logout handler:
/**
* #Service("sso_authentication_handler")
*/
class SsoAuthenticationHandler implements LogoutSuccessHandlerInterface
{
/**
* #var Router
*/
private $router;
/**
* #var array
*/
protected $ssoUrls;
/**
* #InjectParams({
* "ssoUrls" = #Inject("%wordpress_sso%"),
* "router" = #Inject("router")
* })
*/
public function __construct(array $ssoUrls, Router $router)
{
$this->ssoUrls = $ssoUrls;
$this->router = $router;
}
public function onLogoutSuccess(Request $request)
{
$locale = $request->getLocale();
if ($locale === 'pl') {
$url = $this->ssoUrls[$locale];
} else {
$url = $this->ssoUrls['en'];
}
$url .= '?returnUrl=' . $this->router->generate('main');
return new RedirectResponse($url);
}
}
so with this combination i achive behavior like when youser is not authenticated or when he logout i will redirect him to other site to login, in my example to wordpress.