Symfony 2.5 and FOSRestBundle: How to log in user - symfony

I want to programmatically log in an user using the FOSRestBundle (I am not using FOSUserBundle). It seems to work, but when I logged in successfully and try to access an secured endpoint, Symfony throws an AccessDeniedException.
This is my security.yml:
security:
providers:
main:
entity:
class: DepartureMonitor\RestBundle\Entity\User
property: email
role_hierarchy:
ROLE_EDITOR: [ ROLE_USER ]
ROLE_VU_ADMIN: [ ROLE_EDITOR, ROLE_USER ]
ROLE_ADMIN: [ ROLE_VU_ADMIN ]
encoders:
DepartureMonitor\RestBundle\Entity\User:
algorithm: bcrypt
cost: 13
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
rest:
pattern: ^/api
provider: main
anonymous: true
form_login:
login_path: login
check_path: login_check
logout: ~
access_control:
- { path: ^/api/users/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/users/password, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: ROLE_USER }
In my UsersController I have the following action:
/**
* #Post("/users/login")
*/
public function loginAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('DepartureMonitorRestBundle:User')->findOneBy(array('email' => $request->request->get('username')));
if ($user instanceof User) {
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$pw = $encoder->encodePassword($request->request->get('password'), $user->getSalt());
if($pw === $user->getPassword()) {
$token = new UsernamePasswordToken($user, $pw, "main", $user->getRoles());
$this->get('security.context')->setToken($token);
$event = new \Symfony\Component\Security\Http\Event\InteractiveLoginEvent($request, $token);
$this->get('event_dispatcher')->dispatch('security.interactive_login', $event);
if(!($this->getUser() instanceof User)) {
return $this->view(array('code' => HttpStatusCode::NOT_FOUND, 'message' => $this->get('translator')->trans('error.messages.bad_credentials')), HttpStatusCode::NOT_FOUND);
}
$view = $this->view($user, HttpStatusCode::CREATED);
$view->setSerializationContext(SerializationContext::create()->setGroups(array('login')));
return $this->handleView($view);
}
}
return $this->view(array('code' => HttpStatusCode::NOT_FOUND, 'message' => $this->get('translator')->trans('error.messages.bad_credentials')), HttpStatusCode::NOT_FOUND);
}
I don't know what the problem here is.
Any help is very appreciated.

Related

HWIOAuthBundle custom user provider will not login user

I'm using HWIOAuthBundle with Symfony 4.1.
My custom user provider gets called and returns a valid user, however the user is not logged in after the redirect. The web profiler shows: logged in as anon; anonymous token; firewall main
I've simplified the provider class below for brevity and it's only considering twitter right now. The conditions around $source are so I can add more later.
I have used xdebug to make sure loadUserByOauthuserResponse() is being called and that users are both being created in the case of a new user, or the existing user is being returned when it exists.
Question: If loadUserbyOauthUserResponse() IS returning a valid user entity, then what could be preventing it from creating a valid session with this user?
<?php
namespace App\Security;
...
class OAuthProvider extends OAuthUserProvider
{
...
public function loadUserByOAuthUserResponse(UserResponseInterface $response): User
{
/** #var EntityManager $em */
$em = $this->container->get('doctrine.orm.entity_manager');
$repo = $em->getRepository('App:User');
$source = $response->getResourceOwner()->getName();
$email = null;
$data = [];
$newUser = false;
// Set email and socialUser.
if ($source === 'twitter') {
$data = $response->getData();
$email = $data['email'];
}
// Check if this user already exists in our app.
$user = $repo->findOneBy(['email' => $email]);
if ($user === null) {
$newUser = true;
$user = new User();
$user->setPassword($this->strand(32));
}
// Set session and user data based on source.
if ($source === 'twitter') {
$name = $data['name'];
if ($newUser) {
$user->setNickName($name);
$user->setEmail($email);
$em->persist($user);
$em->flush();
}
}
return $user;
}
}
hwi_oauth.yaml
hwi_oauth:
# list of names of the firewalls in which this bundle is active, this setting MUST be set
firewall_names: [main]
# https://github.com/hwi/HWIOAuthBundle/blob/master/Resources/doc/2-configuring_resource_owners.md
resource_owners:
twitter:
type: twitter
client_id: '%env(TWITTER_ID)%'
client_secret: '%env(TWITTER_SECRET)%'
options:
include_email: true
services.yaml:
app.oauth_aware.user_provider.service:
class: App\Security\OAuthProvider
arguments: ['#service_container']
security.yaml:
security:
# https://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
encoders:
App\Entity\User: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
app_users:
entity: { class: App\Entity\User, property: email }
hwi:
id: app.oauth_aware.user_provider.service
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
provider: app_users
anonymous: ~
remember_me:
secret: "%env(APP_SECRET)%"
lifetime: 2592000
path: /
guard:
authenticators:
- App\Security\LoginFormAuthenticator
entry_point: App\Security\LoginFormAuthenticator
logout:
path: /logout
target: /
oauth:
resource_owners:
twitter: "/login/check-twitter"
default_target_path: /
login_path: /
failure_path: /user-login
oauth_user_provider:
service: app.oauth_aware.user_provider.service
switch_user: ~
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/recover, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
I had the same problem until I found in logs "Token was deauthenticated after trying to refresh it."
And after that I found this solution.
Token was deauthenticated after trying to refresh it
I added isEqualTo and did not logged me out.

symfony fosuserbundle redirection after login

i have a login form what i want to do that if a user have role user tries to login he's redirected to page voiture_new and if an admin eventually has a role admin he's redirected to the admin page
PS : i'm using easyadminbundle
here's what i've added to the loginaction of my controller
$authChecker = $this->container- >get('security.authorization_checker');
$router = $this->container->get('router');
if ($authChecker->isGranted('ROLE_ADMIN')) {
return new RedirectResponse($router->generate('admin'), 307);
}
if ($authChecker->isGranted('ROLE_USER')) {
return new RedirectResponse($router->generate('voiture_new'), 307);
}
and here's my security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_token_generator: security.csrf.token_manager
always_use_default_target_path: false
default_target_path: /voiture/new
check_path: fos_user_security_check
# if you are using Symfony < 2.8, use the following config instead:
# csrf_provider: form.csrf_provider
logout: true
anonymous: true
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
- { path: ^/marque/, role: ROLE_ADMIN }
- { path: ^/modele/, role: ROLE_ADMIN }
- { path: ^/user/, role: ROLE_ADMIN }
- { path: ^/voiture/, role: ROLE_USER }
- { path: ^/profile/, role: ROLE_USER }
- { path: ^/interventions/, role: ROLE_USER }
but always i'mredirected to voiture_new even if the user have a role admin waht i'm missing ?
What you need to do is to create Authenticator Class and then tell symfony that use this while trying to authenticate. Inside this class is a method onAuthenticationSuccess you can then use this to perform all the redirect.
For example inside security.yml under firewall which is called main in this case. Tell it that you want to use guard and then mention the service which in this example is called app.form_login_authenticator
main:
pattern: ^/
http_basic: ~
anonymous: ~
logout:
path: logout
guard:
authenticators:
- app.form_login_authenticator
# by default, use the start() function from FormLoginAuthenticator
entry_point: app.form_login_authenticator
Inside your services.yml make sure this service is listed
app.form_login_authenticator:
class: AppBundle\Security\FormLoginAuthenticator
arguments: ["#service_container"]
and then this is the class example
class FormLoginAuthenticator extends AbstractFormLoginAuthenticator
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getCredentials(Request $request)
{
if ($request->getPathInfo() != '/login_check') {
return;
}
$username = $request->request->get('_username');
$request->getSession()->set(Security::LAST_USERNAME, $username);
$password = $request->request->get('_password');
return array(
'username' => $username,
'password' => $password
);
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$username = $credentials['username'];
$userRepo = $this->container
->get('doctrine')
->getManager()
->getRepository('AppBundle:User');
return $userRepo->findOneByUsername($username);
}
public function checkCredentials($credentials, UserInterface $user)
{
$plainPassword = $credentials['password'];
$encoder = $this->container->get('security.password_encoder');
if (!$encoder->isPasswordValid($user, $plainPassword)) {
return false;
}
return true;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
// AJAX! Maybe return some JSON
if ($request->isXmlHttpRequest()) {
return new JsonResponse(
// you could translate the message
array('message' => $exception->getMessageKey()),
403
);
}
// for non-AJAX requests, return the normal redirect
return parent::onAuthenticationFailure($request, $exception);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
//Perform your redirects here for example
$response = '';
if($this->container->get('security.authorization_checker')->isGranted('ROLE_ADMIN')){
$response = $this->container->get('router')->generate('admin_dashboard');
}
if($this->container->get('security.authorization_checker')->isGranted('ROLE_USER')){
$response = $this->container->get('router')->generate('user_dashboard');
}
return $response;
}
protected function getLoginUrl()
{
return $this->container->get('router')
->generate('login');
}
}
Hopefully this should put you in right path to implementing what you are looking for,

Login form not work

I try to create a connection system with form.
But I still get the message: "Bad credentials."
security:
encoders:
Symfony\Component\Security\Core\User\User: plaintext
Tp\Bundle\AppBundle\Entity\User: plaintext
providers:
in_memory:
memory:
users:
user: { password: userpass, roles: [ 'ROLE_USER' ] }
admin: { password: adminpass, roles: [ 'ROLE_ADMIN' ] }
tp:
entity: { class: TpAppBundle:User, property: email }
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
tp:
pattern: ^/
provider: tp
#anonymous: ~
form_login:
login_path: login
check_path: login_check
default_target_path: user_index
always_use_default_target_path: true
logout:
path: logout
target: login
<?php
namespace Tp\Bundle\AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
class SecurityController extends Controller
{
public function loginAction()
{
$request = $this->getRequest();
$session = $request->getSession();
// 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);
}
return $this->render('TpAppBundle:Security:login.html.twig', array(
// last username entered by the user
'email' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
));
}
}
class User implements UserInterface {
...
In my date base,
User
email:test#test.com
password: test
But unable to connect.
I still have the same message: Bad credentials.
Have you any idea?
Do you need more?
Thank you very much

my website /home for authenticated users and anonymous

I don't understand my problem. I just want :
/ redirected /home
/home is not secured but logged user is able to navigate into
the whole website.
Non authenticated user is only able to see the homepage
People can register an account to access the whole website
So it's my security.yml config :
security:
encoders:
Siriru\AntBundle\Entity\User: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
main:
entity: { class: Siriru\AntBundle\Entity\User, property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
root:
pattern: ^/$
security: false
home:
pattern: ^/home$
security: false
login:
pattern: ^/login$
security: false
register:
pattern: ^/account/
security: false
secured_area:
pattern: ^/
form_login:
check_path: /login_check
login_path: /login
username_parameter: username
password_parameter: password
logout:
path: /logout
target: /home
Registration is ok, login too. But after the redirection to the homepage, user is not authenticated (in the symfony profiler "You are not authenticated."). If I reach the secured area, i'm logged but not authenticated.
<?php
namespace Siriru\AntBundle\Controller;
use Siriru\AntBundle\Form\Model\Registration;
use Siriru\AntBundle\Form\Type\RegistrationType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\Response;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use JMS\SecurityExtraBundle\Annotation\Secure;
class AccountController extends Controller
{
/**
* #Route("/login", name="login")
* #Template()
*/
public function loginAction()
{
if ($this->get('request')->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
$error = $this->get('request')->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
} else {
$error = $this->get('request')->getSession()->get(SecurityContext::AUTHENTICATION_ERROR);
}
return array(
'last_username' => $this->get('request')->getSession()->get(SecurityContext::LAST_USERNAME),
'error' => $error,
);
}
/**
* #Route("/login_check", name="login_check")
*/
public function securityCheckAction()
{
// The security layer will intercept this request
}
/**
* #Route("/logout", name="logout")
*/
public function logoutAction()
{
// The security layer will intercept this request
}
/**
* #Route("/account/register", name="account_register")
* #Template()
*/
public function registerAction()
{
$form = $this->createForm(new RegistrationType(), new Registration());
return array('form' => $form->createView());
}
/**
* #Route("/account/create", name="account_create")
* #Template()
*/
public function createAction()
{
$em = $this->getDoctrine()->getEntityManager();
$form = $this->createForm(new RegistrationType(), new Registration());
$form->bind($this->getRequest());
if ($form->isValid()) {
$registration = $form->getData();
$user = $registration->getUser();
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$password = $encoder->encodePassword($user->getPassword(), $user->getSalt());
$user->setPassword($password);
$em->persist($user);
$em->flush();
return $this->redirect($this->generateUrl('homepage'));
}
return $this->render('SiriruAntBundle:Account:register.html.twig', array('form' => $form->createView()));
}
}
I need some help =) Thank you.
Try to change your firewall configuration to catch all the urls, then set anonymous: ~ and use access_control to restrict all the urls to ROLE_USER.
The problem is that the security session is not shared by default between different firewalls.
Something like this should work:
security:
encoders:
Siriru\AntBundle\Entity\User: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
main:
entity: { class: Siriru\AntBundle\Entity\User, property: username }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
anonymous: ~
form_login:
check_path: /login_check
login_path: /login
username_parameter: username
password_parameter: password
logout:
path: /logout
target: /home
access_control:
- { path: ^/$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/home$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }

Manual authenticate user

I try to authenticate user:
<?php
/**
* #Route("/testLogin", name="testLogin")
*/
public function testLoginAction()
{
$em = $this->getDoctrine()->getEntityManager();
$user = $em->getRepository('ApplicationDefaultBundle:User')->findOneBy(array('id' => 126));
$providerKey = 'main';
$token = new UsernamePasswordToken($user, null, $providerKey, $user->getRoles());
$this->container->get('security.context')->setToken($token);
return $this->redirect($this->generateUrl('testCheck'));
}
/**
* #Route("/testCheck", name="testCheck")
*/
public function testCheckAction()
{
if (false === $this->get('security.context')->isGranted(
'IS_AUTHENTICATED_REMEMBERED'
)) {
return new Response('Not logged');
}
$user = $this->container->get('security.context')->getToken()->getUser();
return new Response($user->getUsername.' is logged');
}
But I get permanent 302 redirect to /login page.
security:
encoders:
Application\Bundle\DefaultBundle\Entity\User:
algorithm: sha512
iterations: 24
encode_as_base64: true
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SPECIALIST: ROLE_USER
ROLE_EMPLOYER: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
main:
entity: { class: Application\Bundle\DefaultBundle\Entity\User, property: username }
firewalls:
secured_area:
remember_me:
key: MySecretKeyBlablabla
lifetime: 36000000
path: /
domain: ~
pattern: ^/
form_login:
check_path: /login_check
login_path: /login
provider: main
logout:
path: /logout
target: /
anonymous: true
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
Code for authenticate I take from https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Controller/RegistrationController.php
Error in app/logs/dev.log:
redirecting to authentication entry point (No Authentication Provider found for token of class "Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken".) [] []
I can't access to site until I clean cookies.
Try code from this answer. In your case firewall name is secured_area:
// your controller action
public function myAction()
{
// Authenticating user
$token = new UsernamePasswordToken($user, null, 'secured_area', $user->getRoles());
$this->get('security.token_storage')->setToken($token);
//For Symfony <= 2.3
//$this->get('security.context')->setToken($token);
$this->get('session')->set('_security_secured_area', serialize($token));
}
Verify that you have configured encoders section in security.yml, as configuration there has changed not very long time ago and it affects your problem.

Resources