I’m currently discovering symfony.I am trying to code some user account. There are two role : ADMIN_ROLE or USER_ROLE. When I try to connect an account, I have this message « Authentication request could not be processed due to a system problem. » even when I enter a wrong username/password !
I have been looking for a solution for days (in docs and on internet).
I can fill my database with my user thanks to the fixture (I can see it in SQLyog). I tried to erase ma database to create it again.
This is my security.yaml :
# 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: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
form_login:
login_path: login
check_path: login
logout:
path: app_logout
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/, roles: ROLE_USER }
This is my UtilisateurFixtures (to create false data)
<?php
namespace App\DataFixtures;
use App\Entity\Utilisateur;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\User\User;
//use Symfony\Bridge\Doctrine\Tests\Fixtures\User;
class UtilisateurFixtures extends Fixture
{
/**
* #var UserPasswordEncoderInterface
*/
private $encoder;
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->encoder = $encoder;
}
public function load(ObjectManager $manager)
{
$user = new Utilisateur();
$user->setUsername('demo3');
$user->setPassword($this->encoder->encodePassword($user,'demo3'));
$user->setRoles(['ROLE_USER', 'ROLE_ADMIN']);
$manager->persist($user);
$manager->flush();
}
}
This is my SecurityController :
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
/**
* #Route("/login", name="login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}
/**
* #Route("/logout", name="app_logout", methods={"GET"})
*/
public function logout()
{
// controller can be blank: it will never be executed!
throw new \Exception('Don\'t forget to activate logout in security.yaml');
}
}
This is my Utilisateur.php :
<?php
namespace App\DataFixtures;
use App\Entity\Utilisateur;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\User\User;
//use Symfony\Bridge\Doctrine\Tests\Fixtures\User;
class UtilisateurFixtures extends Fixture
{
/**
* #var UserPasswordEncoderInterface
*/
private $encoder;
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->encoder = $encoder;
}
public function load(ObjectManager $manager)
{
$user = new Utilisateur();
$user->setUsername('demo3');
$user->setPassword($this->encoder->encodePassword($user,'demo3'));
$user->setRoles(['ROLE_USER', 'ROLE_ADMIN']);
$manager->persist($user);
$manager->flush();
}
}
This is my login.htlml.twig (where there is my form) :
{% extends 'base.html.twig' %}
{% block title %}Log in!{% endblock %}
{% block body %}{% if error %}
{{ error.messageKey|trans(error.messageData, 'security') }}
{% endif %}
<form action="{{ path('login') }}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />
<input type="hidden" name="_target_path" value="/" />
{#
If you want to control the URL the user
is redirected to on success (more details below)
<input type="hidden" name="_target_path" value="/account" />
#}
<button type="submit">login</button>
</form>
{% endblock %}
This is UtilisateurRepository :
<?php
namespace App\Repository;
use App\Entity\Utilisateur;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Symfony\Bridge\Doctrine\Security\User\UserLoaderInterface;
use Symfony\Bridge\Doctrine\RegistryInterface;
/**
• #method Utilisateur|null find($id, $lockMode = null, $lockVersion = null)
• #method Utilisateur|null findOneBy(array $criteria, array $orderBy = null)
• #method Utilisateur[] findAll()
• #method Utilisateur[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UtilisateurRepository extends ServiceEntityRepository
{
public function __construct(RegistryInterface $registry)
{
parent::__construct($registry, Utilisateur::class);
}
// /**
// * #return Utilisateur[] Returns an array of Utilisateur objects
// /
/
public function findByExampleField($value)
{
return $this->createQueryBuilder('u')
->andWhere('u.exampleField = :val')
->setParameter('val', $value)
->orderBy('u.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?Utilisateur
{
return $this->createQueryBuilder('u')
->andWhere('u.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}
Thank you for your help ! Don't hesitate if you need anything.
Related
I'm posting here because I had searching for hours now. When I was in dev mode, everything was totally working and now, I had push my app on the server, the login form do not work.
When I click on "login" we can see the password and the email on the URL (and we stay on the login page) like if I done get method:
"https://website/login?email=blablablamail%40gmail.com&password=paswword&_csrf_token=9fI5G0acHOHQ8rmpO4chM2XrI0Tm8HSN32ghrtRzb38"
I'm using fortrabbit server
-> my swiftmailer function is not working too but I don't know why because the .env is correctly done and the swiftmailer.yaml is ok too, maybe there is a link between this 2 errors.
thanx by advance
here is my .env
###> symfony/framework-bundle ###
APP_ENV=prod
APP_SECRET=959a3e56d7f63b190f107a24fc7eade4
#TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
#TRUSTED_HOSTS='^localhost|example\.com$'
###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#conne$
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# For a PostgreSQL database, use: "postgresql://db_user:db_password#127.0.0.1:5432/db_name?serverVersion=11&charset=utf8"
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
DATABASE_URL=mysql://root:root#127.0.0.1:3306/mysuper?serverVersion=10.4.8
###< doctrine/doctrine-bundle ###
###> nelmio/cors-bundle ###
CORS_ALLOW_ORIGIN=^https?://(localhost|127\.0\.0\.1)(:[0-9]+)?$
###< nelmio/cors-bundle ###
here is my security.yaml
security:
encoders:
App\Entity\User:
algorithm: auto
# 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
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
provider: app_user_provider
form_login:
login_path: app_login
check_path: app_login
guard:
authenticators:
- App\Security\UserAuthenticator
logout:
path: app_logout
# where to redirect after logout
# target: app_any_route
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/user, roles: ROLE_USER }
role_hierarchy:
ROLE_ADMIN: ROLE_USER
here is my user.php
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass=UserRepository::class)
* #UniqueEntity(fields={"email"}, message="There is already an account with this email")
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
*#Assert\Regex(
* pattern="/^[a-zA-Z0-9_.-]+#[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/",
* message="Veuillez indiquer un mail valide"
* )
*/
private $email;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string" )
*/
private $password;
/**
* #ORM\Column(type="string", length=25, nullable=true)
* #Assert\Regex(
* pattern="/^[^0-9-<>()\[\]\\.,;:\s#\']+[ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçêëìíîïðòóôõöùúûüýÿ\s]{2,}$/" ,
* match=false,
* message="Ton prénom ne peux pas contenir de caractères spéciaux"
* )
*/
private $prenom;
/**
* #ORM\Column(type="string", length=25, nullable=true)
* #Assert\Regex(
* pattern="/^[^0-9-<>()\[\]\\.,;:\s#\']+[ÀÁÂÃÄÅÇÈÉÊËÌÍÎÏÒÓÔÕÖÙÚÛÜÝàáâãäåçêëìíîïðòóôõöùúûüýÿ\s]{2,}$/" ,
* match=false,
* message="Ton nom ne peux pas contenir de caractères spéciaux"
* )
*/
private $nom;
/**
* #ORM\Column(type="string", length=10, nullable=true)
*
*
*
* * #Assert\Regex(
* pattern="/^
(?:(?:\+|00)33|0) # Dialing code
\s*[1-9] # First number (from 1 to 9)
(?:[\s.-]*\d{2}){4} # End of the phone number
$/" ,
* match=false,
* message="Veuillez écrire un numéro valide"
* )
*/
private $phone;
/**
* #ORM\Column(type="boolean", nullable=true)
*/
private $phoneok;
/**
* #ORM\OneToMany(targetEntity=Mission::class, mappedBy="User", orphanRemoval=true)
*/
private $missions;
public function __construct()
{
$this->missions = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
public function getPrenom(): ?string
{
return $this->prenom;
}
public function setPrenom(?string $prenom): self
{
$this->prenom = $prenom;
return $this;
}
public function getNom(): ?string
{
return $this->nom;
}
public function setNom(?string $nom): self
{
$this->nom = $nom;
return $this;
}
public function getPhone(): ?string
{
return $this->phone;
}
public function setPhone(?string $phone): self
{
$this->phone = $phone;
return $this;
}
public function getPhoneok(): ?bool
{
return $this->phoneok;
}
public function setPhoneok(?bool $phoneok): self
{
$this->phoneok = $phoneok;
return $this;
}
/**
* #return Collection|Mission[]
*/
public function getMissions(): Collection
{
return $this->missions;
}
public function addMission(Mission $mission): self
{
if (!$this->missions->contains($mission)) {
$this->missions[] = $mission;
$mission->setUser($this);
}
return $this;
}
public function removeMission(Mission $mission): self
{
if ($this->missions->contains($mission)) {
$this->missions->removeElement($mission);
// set the owning side to null (unless already changed)
if ($mission->getUser() === $this) {
$mission->setUser(null);
}
}
return $this;
}
}
here is my security controller
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
/**
* #Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
// if ($this->getUser()) {
// return $this->redirectToRoute('target_path');
// }
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}
/**
* #Route("/logout", name="app_logout")
*/
public function logout()
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}
here is my login form :
{% extends 'base.html.twig' %}
{% block title %}Se connecter{% endblock %}
{% block body %}
<form action="{{ path('app_login') }}" method="post " class="mt-4">
{% if error %}
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
{% if app.user %}
<div class="mb-3">
Vous tes connect en tant que {{ app.user.prenom }}, Logout
</div>
{% endif %}
<div class="container-fluid ">
<h1 class="h3 mb-3 font-weight-normal">Veuillez vous connecter</h1>
<label for="inputEmail" class="m-2">Email</label>
<input type="email" value="{{ last_username }}" name="email" id="inputEmail" class="form-control container-fluid " req$
<label for="inputPassword" class="m-2">Mot de passe</label>
<input type="password" name="password" id="inputPassword" class="form-control container-fluid " required>
<br>
<input type="hidden" name="_csrf_token"
value="{{ csrf_token('authenticate') }}"
>
{#
Uncomment this section and add a remember_me option below your firewall to activate remember me functionality.
See https://symfony.com/doc/current/security/remember_me.html
<div class="checkbox mb-3">
<label>
<input type="checkbox" name="_remember_me"> Remember me
</label>
</div>
#}
<button class="btn btn-lg btn-primary m-2" type="submit">
Connexion
</button>
</div>
</form>
{% endblock %}
Evening everyone , I've been stack with this problem since few days and whatever I change it remains .. basically on login when I put an invalid email it says email not found , when I put invalid password it says wrong password( I edited it of course ) but when they're both correct it shows Invalid credentials for some reason , I m really disparate .. help me please I can link any page code but Im 90% sure the code is correct ,thanks in advance
SecurityController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
/**
* #Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
if ($this->getUser()) {
return $this->redirectToRoute('/index');
}
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]);
}
/**
* #Route("/logout", name="app_logout")
*/
public function logout()
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}
security.yaml
security:
encoders:
App\Entity\User5:
algorithm: bcrypt
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
app_user_provider:
entity:
class: 'App\Entity\User5'
property: 'email'
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
provider: app_user_provider
form_login:
login_path: app_login
check_path: app_login
guard:
authenticators:
- App\Security\LoginFormAuthenticator
entry_point: App\Security\LoginFormAuthenticator
logout:
path: app_logout
# where to redirect after logout
# target: app_any_route
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
#access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
# - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
login.html.twig
{% extends 'home.html.twig' %}
{% block title %}Log in!{% endblock %}
{% block body %}
<form method="post" action="{{ path('app_login')}}">
{% if error %}
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
{% if app.user %}
<div class="mb-3">
You are logged in as {{ app.user.username }}, Logout
</div>
{% endif %}
<h1 class="h3 mb-3 font-weight-normal">Please sign in</h1>
<label for="inputEmail">Email</label>
<input type="email" value="{{ last_username }}" name="email" id="inputEmail" class="form-control" required autofocus>
<label for="inputPassword">Password</label>
<input type="password" name="password" id="inputPassword" class="form-control" required>
<input type="hidden" name="_csrf_token"
value="{{ csrf_token('authenticate') }}"
>
{#
Uncomment this section and add a remember_me option below your firewall to activate remember me functionality.
See https://symfony.com/doc/current/security/remember_me.html
<div class="checkbox mb-3">
<label>
<input type="checkbox" name="_remember_me"> Remember me
</label>
</div>
#}
<button class="btn btn-lg btn-primary" type="submit" >
Sign in
</button>
</form>
{% endblock %}
LoginFormAuthenticator
<?php
namespace App\Security;
use App\Entity\User5;
use Doctrine\ORM\EntityManagerInterface;
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\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
use TargetPathTrait;
private const LOGIN_ROUTE = 'app_login';
private $entityManager;
private $urlGenerator;
private $csrfTokenManager;
private $passwordEncoder;
public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager, UserPasswordEncoderInterface $passwordEncoder)
{
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
$this->passwordEncoder = $passwordEncoder;
}
public function supports(Request $request)
{
return self::LOGIN_ROUTE === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'email' => $request->request->get('email'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['email']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $this->entityManager->getRepository(User5::class)->findOneBy(['email' => $credentials['email']]);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Email could not be found.');
}
$user = $this->entityManager->getRepository(User5::class)->findOneBy(['password' => $credentials['password']]);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Wrong password.');
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
return $this->passwordEncoder->isPasswordValid($user, $credentials['password']);
}
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function getPassword($credentials): ?string
{
return $credentials['password'];
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
// For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
return new RedirectResponse($this->urlGenerator->generate('index'));
throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate('app_login');
}
}
User5.php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity()
* #UniqueEntity("email")
*/
/**
* #ORM\Entity(repositoryClass="App\Repository\User5Repository")
* #UniqueEntity(
* fields={"email"},
* message="That Email is already taken , try another "
* )
*/
class User5 implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(unique=true, type="string", nullable=false)
*/
private $email;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string")
*/
private $password;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Role", mappedBy="Users")
*/
private $userRoles;
public function __construct()
{
$this->userRoles = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function getSalt()
{
// not needed when using the "bcrypt" algorithm in security.yaml
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
/**
* #return Collection|Role[]
*/
public function getUserRoles(): Collection
{
return $this->userRoles;
}
public function addUserRole(Role $userRole): self
{
if (!$this->userRoles->contains($userRole)) {
$this->userRoles[] = $userRole;
$userRole->addUser($this);
}
return $this;
}
public function removeUserRole(Role $userRole): self
{
if ($this->userRoles->contains($userRole)) {
$this->userRoles->removeElement($userRole);
$userRole->removeUser($this);
}
return $this;
}
}
You should remove this lines, the password is encrypted in the database so you can't retrieve a user by his password, Symfony will do this job (comparing the two passwords).
$user = $this->entityManager->getRepository(User5::class)->findOneBy(['password' => $credentials['password']]);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Wrong password.');
}
I solved it my self , apparently you cant use guard and form login in the same time you gotta pick one of them because both do the same job one is easy and one is customized also I forgot something very important in the register function , forgot to encode the password , now it works just fine
heres the new register function
public function register(Request $request , EntityManagerInterface $entityManager, UserPasswordEncoderInterface $encoder)
{
$user = new User5();
$form = $this->createForm(Form::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$hashed = $encoder->encodePassword($user, $user->getPassword());
$user->setPassword($hashed);
$entityManager->persist($user);
$entityManager->flush();
$this->addFlash("success", "Welcome to our application");
return $this->redirectToRoute("app_login");
}
return $this->render('account/registration.html.twig',[
'form' => $form->createView()
]);
}
new security.yaml
security:
encoders:
App\Entity\User5:
algorithm: bcrypt
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
app_user_provider:
entity:
class: 'App\Entity\User5'
property: 'email'
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
provider: app_user_provider
form_login:
login_path: app_login
check_path: app_login
# default_target_path: dashboard
logout:
path: logout_user
target: index
# target: app_any_route
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
#access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
# - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
# - { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
If a page needs authentication and no User is found Symfony simply redirects or shows the login page. So simple enough I got that working.
Next, I would like to send a custom message (or html) if the User makes an Ajax call inside a page that requires authentication, but the session has died for instance (the User is not authenticated anymore).
security.yml
security:
encoders:
AppBundle\Entity\User:
algorithm: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
providers:
db_provider:
entity:
class: AppBundle:User
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
pattern: ^/
form_login:
login_path: security_login
check_path: security_login
use_forward: false
failure_handler: AppBundle\Security\AuthenticationHandler
logout:
path: /logout
target: /
access_denied_handler: AppBundle\Security\AccessDeniedHandler
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: ROLE_ADMIN }
I have tried to intercept an event error by using access_denied_handler or failure_handler.
AppBundle\Security\AccessDeniedHandler.php
namespace AppBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Authorization\AccessDeniedHandlerInterface;
class AccessDeniedHandler implements AccessDeniedHandlerInterface {
public function handle(Request $request, AccessDeniedException $exception) {
return new JsonResponse([
'success' => 0,
'error' => 1,
'message' => $exception -> getMessage(),
'from' => 'AccessDeniedHandler'
]);
}
}
AppBundle\Security\AuthenticationHandler.php
namespace AppBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
class AuthenticationHandler implements AuthenticationFailureHandlerInterface {
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) {
return new JsonResponse(['error' => 1, 'from' => 'AuthenticationHandler']);
}
}
None of those classes are accessed. What am I missing?
Notes
Created for a Symfony 3.4 project, should be compatible with Symfony 4, but I haven't tested;
All services are auto-wired, so there's nothing to add in services.yml
I'm not using FOSUserBundle;
I'm not following Symfony coding standars;
I've made notes, here and there; also I've put some comments in the code itself;
The important part is at the end (LoginFormAuthenticator), I'm posting the whole code, hopefully someone will have an easier time than me.
Source of inspiration:
https://symfony.com/doc/3.4/security.html
https://symfonycasts.com/screencast/symfony3-security
https://www.sitepoint.com/easier-authentication-with-guard-in-symfony-3/
Wall of code
security.yml
Security configuration
For the "memory" user the username and password is "admin"
security:
encoders:
Symfony\Component\Security\Core\User\User:
algorithm: bcrypt
AppBundle\Entity\User:
algorithm: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
chain_provider:
chain:
providers: [memory_provider, db_provider]
memory_provider:
memory:
users:
admin:
password: '$2y$13$21gXkzksqlR68HhAYB2WLOqcQvJZzgIrSH/KRq1aEzkkOnjI7lR9e'
roles: 'ROLE_SUPER_ADMIN'
db_provider:
entity:
class: AppBundle: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
main:
anonymous: ~
pattern: ^/
logout:
path: /logout
target: /
guard:
authenticators:
- AppBundle\Security\LoginFormAuthenticator
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, role: ROLE_USER }
Template app/Resources/views/Security/_content.login.html.twig
{% set form_action = path('security_login') %}
<form action="{{ form_action }}" method="post" autocomplete="off" id="login_f">
{% if error %}
<div class="login_form_error">{{ error.messageKey }}</div>
{% endif %}
<div class="closed">
<input type="hidden" name="_csrf_token" value="{{ csrf_token(login_csrf_token) }}" />
</div>
<div class="login_field login_field_0">
<label for="login_username" class="login_l">
<i class="fas fa-user"></i>
</label>
<input type="text" class="login_i" id="login_username" name="_username" placeholder="Username" />
</div>
<div class="login_field login_field_1">
<label for="login_password" class="login_l">
<i class="fas fa-key"></i>
</label>
<input type="password" class="login_i" id="login_password" name="_password" placeholder="Password" />
</div>
<div>
<input type="submit" class="login_bttn" id="_submit" value="Login" />
</div>
</form>
Template app/Resources/views/Security/login.html.twig
No need for base.html.twig
{% extends 'base.html.twig' %}
{% block content %}
<div id="login_c">
{% include 'Security/_content.login.html.twig' %}
</div>
{% endblock %}
The service
Renders the login page or the login content
Replace the CSRF_TOKEN constant value with your own
namespace AppBundle\Services\User;
use Symfony\Component\Templating\EngineInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class LoginFormService {
private $templatingEngine;
private $authenticationUtils;
const CSRF_TOKEN = 'login:token:w4kSzA3v5VJyb4aWLbV7stAY92cNwgL77J6QrXpU!';
function __construct(
EngineInterface $templatingEngine,
AuthenticationUtils $authenticationUtils) {
$this -> templatingEngine = $templatingEngine;
$this -> authenticationUtils = $authenticationUtils;
}
function getHtml($contentOnly = False) {
// last username entered by the user
$lastUsername = $this -> authenticationUtils -> getLastUsername();
// get the login error if there is one
$error = $this -> authenticationUtils -> getLastAuthenticationError();
$html_vars = array(
'lastUsername' => $lastUsername,
'error' => $error,
'login_csrf_token' => self::CSRF_TOKEN,
);
$html_template = 'Security/login.html.twig';
if ( $contentOnly ) {
$html_template = 'Security/_content.login.html.twig';
}
$html = $this -> templatingEngine -> render($html_template, $html_vars);
return $html;
}
}
The login controller
A simple buffer controller to render the login page if the user will access /login
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use AppBundle\Services\User\LoginFormService;
class SecurityController extends Controller {
/**
#Route("/login", name="security_login")
*/
public function loginAction(LoginFormService $loginFormService, Request $request) {
return new Response($loginFormService -> getHtml());
}
/**
#Route("/logout", name="security_logout")
*/
public function logoutAction() {}
}
The Guard authenticator
Instead of "project_homepage_route" use whatever route you want
namespace AppBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Symfony\Component\Routing\RouterInterface;
use AppBundle\Services\User\LoginFormService;
class LoginFormAuthenticator extends AbstractGuardAuthenticator {
private $router;
private $templatingEngine;
private $passwordEncoder;
private $csrfTokenManager;
private $loginService;
protected $auth_error_csrf = 'Invalid CSRF token!!!';
protected $auth_error_message = 'Invalid credentials!!!';
function __construct(
RouterInterface $router,
UserPasswordEncoderInterface $passwordEncoder,
CsrfTokenManagerInterface $csrfTokenManager,
LoginFormService $loginService) {
$this -> router = $router;
$this -> passwordEncoder = $passwordEncoder;
$this -> csrfTokenManager = $csrfTokenManager;
$this -> loginService = $loginService;
}
/* Methods */
protected function loginResponse(Request $request, $forbidden = False) {
// The javascript library must set the 'X-Requested-With' header
// xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
if ( $request -> isXmlHttpRequest() ) {
$response = new JsonResponse([
'error' => 1,
'html' => $this -> loginService -> getHtml(True)
]);
} else {
$html = $this -> loginService -> getHtml();
$response = new Response($html);
}
if ($forbidden) {
$response -> setStatusCode(Response::HTTP_FORBIDDEN);
}
return $response;
}
/* AbstractGuardAuthenticator methods */
public function supports(Request $request) {
return $request -> attributes -> get('_route') === 'security_login' && $request -> isMethod('POST');
}
public function getCredentials(Request $request) {
// Add csrf protection
$csrfData = $request -> request -> get('_csrf_token');
$csrfToken = new CsrfToken(LoginFormService::CSRF_TOKEN, $csrfData);
if ( !$this -> csrfTokenManager -> isTokenValid($csrfToken) ) {
throw new InvalidCsrfTokenException( $this -> auth_error_csrf );
}
return array(
'username' => $request -> request -> get('_username'),
'password' => $request -> request -> get('_password'),
);
}
public function getUser($credentials, UserProviderInterface $userProvider) {
$username = $credentials['username'];
try {
return $userProvider -> loadUserByUsername($username);
} catch (UsernameNotFoundException $e) {
throw new CustomUserMessageAuthenticationException( $this -> auth_error_message );
}
return null;
}
public function checkCredentials($credentials, UserInterface $user) {
$is_valid_password = $this -> passwordEncoder -> isPasswordValid($user, $credentials['password']);
if ( !$is_valid_password ) {
throw new CustomUserMessageAuthenticationException( $this -> auth_error_message );
return;
}
return True;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $authException) {
$session = $request -> getSession();
$session -> set(Security::AUTHENTICATION_ERROR, $authException);
$session -> set(Security::LAST_USERNAME, $request -> request -> get('_username'));
// Shows the login form instead of the page content
return $this -> loginResponse($request, True);
// If you want redirect make sure the line below is used
// return new RedirectResponse($this -> router -> generate('security_login'));
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) {
if ( $request -> isXmlHttpRequest() ) {
return new JsonResponse([
'success' => 1,
'message' => 'Authentication success!'
]);
}
return new RedirectResponse($this -> router -> generate('project_homepage_route'));
}
public function start(Request $request, AuthenticationException $authException = null) {
// Shows the login form instead of the page content
return $this -> loginResponse($request);
// If you want redirect make sure the line below is used
// return new RedirectResponse($this -> router -> generate('security_login'));
}
public function supportsRememberMe() {
return false;
}
}
I created a login page with Symfony and Twig. Now, I'm trying to retrieve the Username after the login. But it seems, that the User Object which I return from my SecurityController is empty and so Twig has a rendering problem. What do I need to do to get that UserObject?
security.yml
security:
encoders:
UserBundle\Entity\User:
algorithm: bcrypt
providers:
in_memory:
memory: ~
user_db_provider:
entity:
class: UserBundle:User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
http_basic: ~
provider: user_db_provider
form_login:
login_path: /login
check_path: /login_check
logout:
path: /logout
target: /
SecurityController
class SecurityController extends Controller{
/**
* #Route("/login", name="login_form")
*/
public function loginAction(){
$authenticationUtils = $this->get('security.authentication_utils');
$error = $authenticationUtils->getLastAuthenticationError();
$user = $this->getUser();
return $this->render(':security:login.html.twig',
array(
'user' => $user,
'error'=> $error,
)
);
}
/**
* #Route("/login_check", name="login_check")
*/
public function loginCheckAction() {}
/**
* #Route ("/logout", name="logout")
*/
public function logoutAction(){}}
User (Serialize, getter/setter methods are implemented)
/**
* #Table("users")
* #Entity
*/
class User implements UserInterface, \Serializable {
/**
* #Column(name="id", type="integer")
* #Id
* #GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #Column(length=25)
*/
private $username;
/**
* #Column(length=64)
*/
private $password;
/**
* #var array
* #Column(name="roles", type="json_array")
*/
private $roles = array();
Twig-Template
{% if is_granted('IS_AUTHENTICATED_REMEMBERED') %}
<div class="form-group">
{{ user.username }}
Logout
</div>
{% else %}
<form class="navbar-form navbar-left" action="{{ path('login_check') }}" method="post">
<div class="form-group">
<input type="text" class="form-control" name="_username" placeholder="Username"/>
<input type="password" class="form-control" name="_password" placeholder="Password"/>
<button type="submit" class="btn btn-default">Log In</button>
</div>
</form>
{% endif %}
{% if error is defined %}
<p class="navbar-text navbar-right">{{ error.messageKey|trans(error.messageData, 'security')}}</p>
{% endif %}
Symfony have global template variables check this.
use:
{{ app.user.username }}
I always get message: "Bad credentials" when I try to login in symfony2. I am doing this based to http://symfony.com/doc/current/cookbook/security/custom_provider.html. Please help me to figure out, where the problem is? Thanks in advance.
security.yml looks like this
security:
encoders:
Zags\UserBundle\Security\User\User: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
webservice:
id: zags_user_provider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login_firewall:
pattern: ^/login$
anonymous: ~
secured_area:
pattern: ^/
anonymous: ~
form_login:
login_path: /login
check_path: /login_check
#anonymous: ~
#http_basic:
# realm: "Secured Demo Area"
access_control:
- { path: ^/gender_type, roles: ROLE_USER }
#- { path: ^/_internal/secure, roles: IS_AUTHENTICATED_ANONYMOUSLY, ip: 127.0.0.1 }
I have added these lines to routing.yml
login:
pattern: /login
defaults: { _controller: ZagsUserBundle:Security:login }
login_check:
pattern: /login_check
User.php class looks like this
<?php
namespace Zags\UserBundle\Security\User;
use Symfony\Component\Security\Core\User\UserInterface;
class User implements UserInterface
{
private $username;
private $password;
private $salt;
private $roles;
public function __construct($username, $password, $salt, array $roles)
{
$this->username = $username;
$this->password = $password;
$this->salt = $salt;
$this->roles = $roles;
}
public function getRoles()
{
return $this->roles;
}
public function getPassword()
{
return $this->password;
}
public function getSalt()
{
return $this->salt;
}
public function getUsername()
{
return $this->username;
}
public function eraseCredentials()
{
}
public function equals(UserInterface $user)
{
return $user->getUsername() === $this->username;
}
}
?>
So this is my UserProvider.php class
<?php
namespace Zags\UserBundle\Security\User;
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;
class UserProvider implements UserProviderInterface
{
public function loadUserByUsername($username)
{
// make a call to your webservice here
$userData = array("username" => "latysh", "password" => "123", "salt" => "123", "roles" => array('ROLE_USER'));
// pretend it returns an array on success, false if there is no user
if ($userData) {
$username = $userData['username'];
$password = $userData['password'];
$salt = $userData['salt'];
$roles = $userData['roles'];
// ...
return new User($username, $password, $salt, $roles);
}
throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
}
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 supportsClass($class)
{
return $class === 'Zags\UserBundle\Security\User\User';
}
}
?>
and services.yml looks like this
parameters:
zags_user_provider.class: Zags\UserBundle\Security\User\UserProvider
services:
zags_user_provider:
class: "%zags_user_provider.class%"
SecurityController.php
<?php
namespace Zags\UserBundle\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('ZagsUserBundle:Security:login.html.twig', array(
// last username entered by the user
'last_username' => $session->get(SecurityContext::LAST_USERNAME),
'error' => $error,
));
}
}
?>
and login.html.twig
{% if error %}
<div>{{ error.message }}</div>
{% endif %}
<form action="{{ path('login_check') }}" method="post">
<label for="username">Username:</label>
<input type="text" id="username" name="_username" value="{{ last_username }}" />
<label for="password">Password:</label>
<input type="password" id="password" name="_password" />
{#
If you want to control the URL the user is redirected to on success (more details below)
<input type="hidden" name="_target_path" value="/account" />
#}
<button type="submit">login</button>
</form>
Has found answer to my question. Thanks to machour for responce. The problem was with SALT. So I updated the User.php class to
public function getSalt()
{
return '';
}
Then it logs in correctly or I should have encoded password with salt to successfully login. If one know how to do it, please write it as answer, YAHOO ))
$factory = $this->get('security.encoder_factory');
$encoder = $factory->getEncoder($user);
$pass = $encoder->encodePassword($user->getPassword(), $user->getSalt());