i need help for the authentication in my application. I have create form and function with symfony doc and configurate the security.yaml but, when i give the credentials and after the redirection, i'm not authenticated. I don't found a similar problem on the net.
- loginController
/**
* Display login form and process login form (GET POST)
*
* @Route("/login", name="app_login")
*/
public function index(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('login/index.html.twig', [
'controller_name' => 'LoginController',
'last_username' => $lastUsername,
'error' => $error,
]);
}
-user.php
public function getUserIdentifier(): ?string
{
return $this->Role;
}
public function getRoles(){
}
public function getUsername()
{
return $this->email;
}
-security.yaml
providers:
users:
entity:
# the class of the entity that represents users
class: 'App\Entity\User'
# the property to query by - e.g. email, username, etc
property: 'Email'
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: users
form_login:
# "app_login" is the name of the route created previously
login_path: app_login
check_path: app_login
access_control:
- { path: ^/, roles: PUBLIC_ACCESS }
# - { path: ^/profile, roles: ROLE_USER }
-index.html.twig
{% block body %}
{% if error %}
<div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
<form action="{{ path('app_login') }}" method="post">
<label for="username">Email:</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
<input type="hidden" name="_target_path" value="/account"/> #}
<button type="submit">login</button>
</form>
{% endblock %}
- UsernamePasswordToken.php
parent::__construct([$roles]);
if ('' === $firewallName) {
throw new \InvalidArgumentException('$firewallName must not be empty.');
}
$this->setUser($user);
$this->credentials = $credentials ?? null;
$this->firewallName = $firewallName;
parent::setAuthenticated(\count([$roles]) > 0, false);
}
CodePudding user response:
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* @ORM\Entity(repositoryClass=UserRepository::class)
* @UniqueEntity(fields={"Email"}, message="There is already an account with this Email")
*/
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $Surname;
/**
* @ORM\Column(type="string", length=255)
*/
private $Name;
/**
* @ORM\Column(type="string", length=255)
*/
private $Email;
/**
* @ORM\Column(type="string", length=255)
*/
private $Password;
/**
* @ORM\Column(type="string", length=255)
*/
private $Roles;
/**
* @ORM\Column(type="datetime_immutable")
*/
private $Created_at;
/**
* @ORM\Column(type="datetime", nullable=true)
*/
private $Updated_at;
public function getId(): ?int
{
return $this->id;
}
public function getSurname(): ?string
{
return $this->Surname;
}
public function setSurname(string $Surname): self
{
$this->Surname = $Surname;
return $this;
}
public function getName(): ?string
{
return $this->Name;
}
public function setName(string $Name): self
{
$this->Name = $Name;
return $this;
}
public function getEmail(): ?string
{
return $this->Email;
}
public function setEmail(string $Email): self
{
$this->Email = $Email;
return $this;
}
public function getPassword(): ?string
{
return $this->Password;
}
public function setPassword(string $Password): self
{
$this->Password = $Password;
return $this;
}
public function getRoles(): ?string
{
return $this->Roles;
}
public function setRoles(string $Roles): self
{
$this->Roles = $Roles;
return $this;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->Created_at;
}
public function setCreatedAt(\DateTimeImmutable $Created_at): self
{
$this->Created_at = $Created_at;
return $this;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->Updated_at;
}
public function setUpdatedAt(?\DateTimeInterface $Updated_at): self
{
$this->Updated_at = $Updated_at;
return $this;
}
public function eraseCredentials()
{
}
public function getSalt()
{
}
public function getUserIdentifier(): ?string
{
return $this->Email;
}
public function getUsername()
{
return $this->Email;
}
}
CodePudding user response:
I've tested your code, and I believe the problem is with your getRoles
method.
getRoles
must return a non-empty array of strings e.g. ['ROLE_ADMIN', 'ROLE_USER']
). Returning an empty array or a string value seems to cause the behaviour you describe: you are redirected from the login page, but you're not logged in, and there's no error.
See if changing getRoles
to the following solves your problem:
public function getRoles(): array
{
return ['ROLE_ADMIN'];
}
If it does, either make sure every user has at least one role in the database and return those as an array of roles:
public function getRoles(): array
{
return [$this->Roles];
}
Or have getRoles
add a role to users who don't have one in the database, so getRoles
will always return a non-empty array:
public function getRoles(): array
{
$roles = $this->Roles ? [$this->Roles] : [];
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
(Not sure how you've set up the roles in the database, assuming each user has a string value for a single role in the database here, adjust if needed.)
Edit: I ignored the UsernamePasswordToken
in your opening post, but now that I look at it again, that may solve your problem too, since it also converts the roles string to an array.
To use the UsernamePasswordToken
, you need to remove the form_login
authenticator from your firewall, and use a custom authenticator instead, for example:
firewalls:
main:
# form_login:
# "app_login" is the name of the route created previously
# login_path: app_login
# check_path: app_login
custom_authenticator: App\Security\LoginFormAuthenticator
Then make this custom authenticator return the custom UsernamePasswordToken
from your opening post:
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
...
/**
* @deprecated since Symfony 5.4, use {@link createToken()} instead
*/
public function createAuthenticatedToken(PassportInterface $passport, string $firewallName): TokenInterface
{
// @deprecated since Symfony 5.4
if (!$passport instanceof UserPassportInterface) {
throw new LogicException(sprintf('Passport does not contain a user, overwrite "createToken()" in "%s" to create a custom authentication token.', static::class));
}
trigger_deprecation('symfony/security-http', '5.4', 'Method "%s()" is deprecated, use "%s::createToken()" instead.', __METHOD__, __CLASS__);
return new UsernamePasswordToken($passport->getUser(), $firewallName, $passport->getUser()->getRoles());
}
}
And getRoles
would then remain the same:
public function getRoles(): ?string
{
return $this->Roles;
}