Home > Blockchain >  authentication : problem with symfony, no error but no authentication
authentication : problem with symfony, no error but no authentication

Time:09-30

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;
}
  • Related