I am trying to use Fr3d LDAP bundle with Symfony2 to authenticate users. Also I use fosuser bundle. The first authentication is well, the user is inserted into database, but the password is null. The second authentication (after logout) is fail: "Invalid credentials". Could anybody help me?
security:
erase_credentials: false
encoders:
Ens\LunchBundle\Entity\User: plaintext
providers:
chain_provider:
chain:
providers: [fos_userbundle, fr3d_ldapbundle]
fr3d_ldapbundle:
id: fr3d_ldap.security.user.provider
fos_userbundle:
id: fos_user.user_manager
firewalls:
main:
pattern: ^/
fr3d_ldap: ~
form_login:
always_use_default_target_path: true
default_target_path: /
provider: chain_provider
logout: true
anonymous: true
default:
anonymous: ~
config:
fos_user:
db_driver: orm # other valid values are 'mongodb', 'couchdb' and 'propel'
firewall_name: main
user_class: Ens\LunchBundle\Entity\User
fr3d_ldap:
driver:
host: my host
port: 389 # Optional
# version: 3
username: +++ # Optional
password: +++ # Optional
bindRequiresDn: false # Optional
bindRequiresDn: true
accountFilterFormat: (&(samaccountname=%s))
# baseDn: OU=Users,OU=R4S,OU=SVRD-44-B,OU=SPB,OU=RU,OU=Offices,DC=tps,DC=local
# accountFilterFormat: (&(uid=%s)) # Optional. sprintf format %s will be the username
# optReferrals: false # Optional
# useSsl: false # Enable SSL negotiation. Optional
# useStartTls: true # Enable TLS negotiation. Optional
# accountCanonicalForm: 3 # ACCTNAME_FORM_BACKSLASH this is only needed if your users have to login with something like HOST\User
# accountDomainName: HOST
# accountDomainNameShort: HOST # if you use the Backslash form set both to Hostname than the Username will be converted to HOST\User
user:
baseDn: ++++
filter: (&(ObjectClass=Person))
attributes: # Specify ldap attributes mapping [ldap attribute, user object method]
- { ldap_attr: samaccountname, user_method: setusername } # Default
- { ldap_attr: name, user_method: setUsernameCanonical } # Default
- { ldap_attr: mail, user_method: setName } # Default
- { ldap_attr: mail, user_method: setEmail } # Optional
User.php
namespace Ens\LunchBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
use FR3D\LdapBundle\Model\LdapUserInterface;
/**
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User extends BaseUser implements LdapUserInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
protected $name;
/** #var string */
protected $surname;
private $dn;
/**
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* #param string $name
*/
public function setName($name)
{
$this->name = $name;
}
// public function setUsername($username){
// var_dump($username);die;
// }
/**
* #return string
*/
public function getSurname()
{
return $this->surname;
}
/**
* #param string $surname
*/
public function setSurname($surname)
{
$this->surname = $surname;
}
public function __construct()
{
parent::__construct();
if (empty($this->roles)) {
$this->roles[] = 'ROLE_USER';
}
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set Ldap Distinguished Name.
*
* #param string $dn Distinguished Name
*/
public function setDn($dn)
{
$this->dn = $dn;
}
/**
* Get Ldap Distinguished Name.
*
* #return string Distinguished Name
*/
public function getDn()
{
return $this->dn;
}
}
I'm posting in reply to this old request so that others can find the info if they need it.
The ORM column info in the comment for $dn is key. Without that you will see this behavior:
- User can log in once.
- When user logs in their user record gets created. Note that the dn field is empty.
- User can log out.
- User cannot successfully log in at this point.
In class User change the declaration for $dn to include the comment:
/**
* #ORM\Column(type="string")
*/
private $dn;
Related
Versions I Used:
using (symfony 5.2.6) with (api-platform 2.7.0) alongside (php 8.0.3) and (postgres 13)
Description
used maker bundle to generate a User Entity and configured the security.yaml to encode passwords already tried auto and bcrypt or even argon2i non of them seems to work and hash the passwords
Possible Solution
as the symfony documentation describes it should automatically encode passwords if we are using security bundle and implementing UserInterface but this one seems like a bug cause i have tried many things nothing works . maybe using UserPasswordEncoderInterface but this one should not be used when User class is implementing UserInterface anyway i hope someone check this and tell me i'm wrong otherwise seems like a bug.
security.yaml:
security:
encoders:
App\Entity\User:
algorithm: bcrypt
# 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: true
lazy: true
provider: app_user_provider
# 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: ^/profile, roles: ROLE_USER }
# or require ROLE_ADMIN or IS_AUTHENTICATED_FULLY for /admin*
# - { path: '^/admin', roles: [IS_AUTHENTICATED_FULLY, ROLE_ADMIN] }
# the 'path' value can be any valid regular expression
# (this one will match URLs like /api/post/7298 and /api/comment/528491)
# - { path: ^/api/(post|comment)/\d+$, roles: ROLE_USER }
User.php:
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use App\Entity\Trait\HasDateTime;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ApiResource
* #ORM\Entity(repositoryClass=UserRepository::class)
* #ORM\Table(name="`user`")
* #ORM\HasLifecycleCallbacks
*/
class User implements UserInterface
{
use HasDateTime;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
#[Assert\NotBlank]
#[Assert\Email]
private $email;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
#[Assert\NotBlank]
private $username;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string")
*/
#[Assert\NotBlank]
private $password;
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): string
{
return (string) $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->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
/**
* #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;
}
/**
* Returning a salt is only needed, if you are not using a modern
* hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
*
* #see UserInterface
*/
public function getSalt(): ?string
{
return null;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}
tried to save data in the database each time it saved passwords as plaintext.
also tried to remove the trait class thought it might conflict some how which seems strange(i know) (but i just gave it a try) it was not hashing passwords even after removing the trait. what am i doing wrong or missing here?
thanks in advance
here is the solution:
i had to create a class that implements DataPersister then in there we have to encode the password . here is the code:
<?php
namespace App\DataPersister;
use App\Entity\User;
use ApiPlatform\Core\DataPersister;
use Doctrine\ORM\EntityManagerInterface;
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserDataPersister implements DataPersisterInterface
{
public function __construct(private EntityManagerInterface $entityManager, private UserPasswordEncoderInterface $userPasswordEncoderInterface)
{
}
/**
* Is the data supported by the persister?
*/
public function supports($data): bool
{
return $data instanceof User;
}
/**
* #param User $data
* #return object|void Void will not be supported in API Platform 3, an object should always be returned
*/
public function persist($data)
{
if ($data->getPassword()) {
$data->setPassword(
$this->userPasswordEncoderInterface->encodePassword($data, $data->getPassword())
);
}
$this->entityManager->persist($data);
$this->entityManager->flush();
}
/**
* Removes the data.
*/
public function remove($data)
{
$this->entityManager->remove($data);
$this->entityManager->flush();
}
}
I am trying to encrypt the passwords of my Users table using the functions provided by Symfony 3.4.
My goal would be as follows:
Generate a random password of 7 characters (including special characters) ==> Done.
Encrypt the password and insert it into the database. ==> Failed
Be able to check when the user enters their password (one that is not encrypted) to authenticate. ==> Failed.
For the 1st step I used this function:
function generatePassword($nb_caractere = 7)
{
$mot_de_passe = "";
$chaine = "abcdefghjkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ023456789#!$?&";
$longeur_chaine = strlen($chaine);
for($i = 1; $i <= $nb_caractere; $i++)
{
$place_aleatoire = mt_rand(0,($longeur_chaine-1));
$mot_de_passe .= $chaine[$place_aleatoire];
}
return $mot_de_passe;
}
The password is correctly generated.
For the second step, my idea was to use this:
$encoded = $encoder->encodePassword($user, $user->getPassword());
But I've this error :
Controller
"Site\PagesBundle\Controller\UserController::activerUtilisateur()"
requires that you provide a value for the "$encoder" argument. Either
the argument is nullable and no null value has been provided, no
default value has been provided or because there is a non optional
argument after this one.
I send you the code of my files for more details :
User.php :
<?php
namespace Site\PagesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="Site\PagesBundle\Repository\UserRepository")
*/
class User
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="identifiant", type="string", length=255, nullable=true)
*/
private $identifiant;
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=255)
*/
private $nom;
/**
* #var string
*
* #ORM\Column(name="prenom", type="string", length=255)
*/
private $prenom;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255)
*/
private $email;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255, nullable=true)
*/
private $password;
/**
* #var int
*
* #ORM\Column(name="nbTelechargementsAuto", type="integer", nullable=true)
*/
private $nbTelechargementsAuto;
/**
* #var bool
*
* #ORM\Column(name="isActif", type="boolean")
*/
private $isActif=0;
/**
* #ORM\Column(type="datetime", nullable=true)
*
* #var \DateTime
*/
private $createdAt;
/**
* #var bool
*
* #ORM\Column(name="isCreated", type="boolean")
*/
private $isCreated=0;
/**
* Set createdAt
*
* #param \DateTime $createdAt
*
* #return Information
*/
public function setCreatedAt()
{
$this->createdAt = new \DateTimeImmutable();
return $this;
}
/**
* Get createdAt
*
* #return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set nom
*
* #param string $nom
*
* #return User
*/
public function setNom($nom)
{
$this->nom = $nom;
return $this;
}
/**
* Get nom
*
* #return string
*/
public function getNom()
{
return $this->nom;
}
/**
* Set nom
*
* #param string $identifiant
*
* #return User
*/
public function setIdentifiant($identifiant)
{
$this->identifiant = $identifiant;
return $this;
}
/**
* Get nom
*
* #return string
*/
public function getIdentifiant()
{
return $this->identifiant;
}
/**
* Set prenom
*
* #param string $prenom
*
* #return User
*/
public function setPrenom($prenom)
{
$this->prenom = $prenom;
return $this;
}
/**
* Get prenom
*
* #return string
*/
public function getPrenom()
{
return $this->prenom;
}
/**
* Set email
*
* #param string $email
*
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set password
*
* #param string $password
*
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set nbTelechargementsAuto
*
* #param integer $nbTelechargementsAuto
*
* #return User
*/
public function setNbTelechargementsAuto($nbTelechargementsAuto)
{
$this->nbTelechargementsAuto = $nbTelechargementsAuto;
return $this;
}
/**
* Get nbTelechargementsAuto
*
* #return int
*/
public function getNbTelechargementsAuto()
{
return $this->nbTelechargementsAuto;
}
/**
* Set isActif
*
* #param boolean $isActif
*
* #return User
*/
public function setIsActif($isActif)
{
$this->isActif = $isActif;
return $this;
}
/**
* Get isActif
*
* #return bool
*/
public function getIsActif()
{
return $this->isActif;
}
/**
* Set isCreated
*
* #param boolean $isCreated
*
* #return User
*/
public function setIsCreated($isCreated)
{
$this->isCreated = $isCreated;
return $this;
}
/**
* Get isCreated
*
* #return bool
*/
public function getIsCreated()
{
return $this->isCreated;
}
/**
* Activation du compte
*/
public function activerCompte($nbPackages, $nbHomonymes)
{
if($this->getIsCreated() == 0)
{
$unIdentifiant = $this->getNom();
$unIdentifiant .= ".";
$unIdentifiant .= $this->getPrenom();
$unIdentifiant = strtolower($unIdentifiant);
if($nbHomonymes > 0)
{
$nbHomonymes++;
$unIdentifiant .= $nbHomonymes;
}
$this->setIdentifiant(strtolower($unIdentifiant));
$this->setNbTelechargementsAuto($nbPackages);
$this->setCreatedAt();
$this->setIsCreated(true);
}
$password = $this->generatePassword();
$this->setPassword($password);
$this->setIsActif(true);
return $this;
}
public function constructionIdentifiant()
{
$unIdentifiant = $this->getNom();
$unIdentifiant .= ".";
$unIdentifiant .=$this->getPrenom();
return $unIdentifiant;
}
/**
* Désactivation du compte
*/
public function desactiverCompte()
{
$this->setIsActif(false);
$this->setCreatedAt();
return $this;
}
public function registration()
{
// Passage Nom 1ère lettre Majuscule le reste minuscule
$leNom = $this->getNom();
$leNom = strtolower($leNom);
$leNom = ucfirst($leNom);
// Passage Nom 1ère lettre Majuscule le reste minuscule
$lePrenom = $this->getPrenom();
$lePrenom = strtolower($lePrenom);
$lePrenom = ucfirst($lePrenom);
$this->setNom($leNom);
$this->setPrenom($lePrenom);
$this->setCreatedAt();
}
/**
* Génération d'un mot de passe
* $nb_caractere = nombre de caractère du mdp
* Pas de caractère qui se ressemble (L minuscule, 1 et i majuscule, Zéro et la lettre o.)
* Sécurité supplémentaire avec : caractères speciaux: - + = $ % ! # #
* Pas d'espace pour éviter les erreurs
*/
function generatePassword($nb_caractere = 7)
{
$mot_de_passe = "";
$chaine = "abcdefghjkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ023456789#!$?&";
$longeur_chaine = strlen($chaine);
for($i = 1; $i <= $nb_caractere; $i++)
{
$place_aleatoire = mt_rand(0,($longeur_chaine-1));
$mot_de_passe .= $chaine[$place_aleatoire];
}
return $mot_de_passe;
}
}
Controller :
<?php
namespace Site\PagesBundle\Controller;
use Site\PagesBundle\Entity\User;
use Symfony\Component\HttpFoundation\Request;
use Site\PagesBundle\Utils\Functions as mesFonctions;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
/**
* User controller.
*
* #Route("utilisateurs")
*/
class UserController extends Controller
{
//... Some functions
/**
* Activer le compte d'un utilisateur
*
* #Route("/activer/{id}", name="utilisateur_activation")
* #Method("GET")
*/
public function activerUtilisateur(UserPasswordEncoderInterface $encoder,Request $request, $id)
{
$em = $this->getDoctrine()->getManager(); //Récupération du manager
$nbPackages = $em->getRepository('PagesBundle:Paquet')->getNombrePackages(); //Récupération du nombre de packages (pour initialiser le nombre de téléchargements offert à l'utilisateur)
$user = $em->getRepository('PagesBundle:User')->findOneById($id); // Récupération de l'objet User en fonction de l'ID fourni en paramètre
// $lesUtilisateurs = $em->getRepository('PagesBundle:User')->findAll();
$nbHomonymes = $em->getRepository('PagesBundle:User')->getHomonymes($user->getNom(),$user->getPrenom()); //Vérification des homonymes pour attribuer un bon identifiant pour l'utilisateur activé
$user->activerCompte($nbPackages,$nbHomonymes); //Procédure d'activation du compte
$em->persist($user); //Enregistrement des informations du compte
$encoded = $encoder->encodePassword($user, $user->getPassword());
$user->setPassword($encoded);
$em->flush(); //Validation dans la bDD
mesFonctions::sendMail($user->getEmail(),"confirmation",$user->getIdentifiant()); //Envoi d'un mail à l'utilisateur sur l'adresse mail qu'il a renseigné lors de sa demande d'inscription
return $this->redirectToRoute('utilisateurs_index'); //Redirection à la liste des différents utilisateurs
}
Security.yml: I did not touch this part but I wonder if she has an interest. I do not know too much about symfony.
# To get started with security, check out the documentation:
# https://symfony.com/doc/current/security.html
security:
encoders:
PagesBundle\Entity\User: bcrypt
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
in_memory: {memory: ~}
in_database:
entity:
class: Site\PagesBundle\Entity\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: true
provider: in_database
form_login:
login_path: security_login
check_path: security_login
logout:
path: security_logout
target: informations
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
#http_basic: ~
# https://symfony.com/doc/current/security/form_login_setup.html
#form_login: ~
Thanks !
EDIT:
config :
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: services.yml }
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
locale: en
framework:
# ...
templating:
engines: ['twig']
#esi: ~
#translator: { fallbacks: ['%locale%'] }
secret: '%secret%'
router:
resource: '%kernel.project_dir%/app/config/routing.yml'
strict_requirements: ~
form: ~
csrf_protection: ~
validation: { enable_annotations: true }
#serializer: { enable_annotations: true }
default_locale: '%locale%'
trusted_hosts: ~
session:
# https://symfony.com/doc/current/reference/configuration/framework.html#handler-id
handler_id: session.handler.native_file
save_path: '%kernel.project_dir%/var/sessions/%kernel.environment%'
fragments: ~
http_method_override: true
assets: ~
php_errors:
log: true
# Twig Configuration
twig:
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
form_themes: ['bootstrap_4_layout.html.twig']
# Doctrine Configuration
doctrine:
dbal:
driver: pdo_mysql
host: '%database_host%'
port: '%database_port%'
dbname: '%database_name%'
user: '%database_user%'
password: '%database_password%'
charset: UTF8
# if using pdo_sqlite as your database driver:
# 1. add the path in parameters.yml
# e.g. database_path: '%kernel.project_dir%/var/data/data.sqlite'
# 2. Uncomment database_path in parameters.yml.dist
# 3. Uncomment next line:
#path: '%database_path%'
orm:
auto_generate_proxy_classes: '%kernel.debug%'
naming_strategy: doctrine.orm.naming_strategy.underscore
auto_mapping: true
# Swiftmailer Configuration
swiftmailer:
transport: '%mailer_transport%'
host: '%mailer_host%'
username: '%mailer_user%'
password: '%mailer_password%'
spool: { type: memory }
sensio_framework_extra:
router:
annotations: false
assetic:
debug: "%kernel.debug%"
use_controller: false
bundles: [ ]
#java: /usr/bin/java
java: C:\Program Files\Java\jdk1.8.0_65\bin\java.exe
filters:
cssrewrite: ~
cssembed:
jar: "%kernel.root_dir%/Resources/java/cssembed.jar"
yui_js:
jar: "%kernel.root_dir%/Resources/java/yuicompressor.jar"
yui_css:
jar: "%kernel.root_dir%/Resources/java/yuicompressor.jar"
lessphp:
file: "%kernel.root_dir%/../vendor/oyejorge/less.php/lessc.inc.php"
apply_to: ".less$"
assets:
jquery_js:
inputs:
- "%kernel.root_dir%/../vendor/components/jquery/jquery.min.js"
filters: [?yui_js]
output: js/jquery.min.js
bootstrap_css:
inputs:
- "%kernel.root_dir%/../vendor/twbs/bootstrap/less/bootstrap.less"
filters:
- lessphp
- cssrewrite
output: css/bootstrap.css
bootstrap_js:
inputs:
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/affix.js"
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/alert.js"
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/button.js"
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/carousel.js"
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/collapse.js"
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/dropdown.js"
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/modal.js"
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/tooltip.js"
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/popover.js"
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/scrollspy.js"
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/tab.js"
- "%kernel.root_dir%/../vendor/twbs/bootstrap/js/transition.js"
filters: [?yui_js]
output: js/bootstrap.js
fonts_glyphicons_eot:
inputs:
- "%kernel.root_dir%/../vendor/twbs/bootstrap/fonts/glyphicons-halflings-regular.eot"
output: "fonts/glyphicons-halflings-regular.eot"
fonts_glyphicons_svg:
inputs:
- "%kernel.root_dir%/../vendor/twbs/bootstrap/fonts/glyphicons-halflings-regular.svg"
output: "fonts/glyphicons-halflings-regular.svg"
fonts_glyphicons_ttf:
inputs:
- "%kernel.root_dir%/../vendor/twbs/bootstrap/fonts/glyphicons-halflings-regular.ttf"
output: "fonts/glyphicons-halflings-regular.ttf"
fonts_glyphicons_woff:
inputs:
- "%kernel.root_dir%/../vendor/twbs/bootstrap/fonts/glyphicons-halflings-regular.woff"
output: "fonts/glyphicons-halflings-regular.woff"
stof_doctrine_extensions:
orm:
default:
sluggable: true
vich_uploader:
db_driver: orm
twig: true
storage: file_system
mappings:
paquet:
uri_prefix: fichiers/packages
upload_destination: '%kernel.root_dir%/../web/fichiers/packages/'
inject_on_load: true
delete_on_update: true
delete_on_remove: true
notice:
uri_prefix: fichiers/notices
upload_destination: '%kernel.root_dir%/../web/fichiers/notices/'
inject_on_load: false
delete_on_update: true
delete_on_remove: true
fos_ck_editor:
configs:
my_config:
toolbar: [ ["Source", "-", "Save"], "/", ["Anchor"], "/", ["Maximize"] ]
uiColor: "#000000"
services.yml:
# Learn more about services, parameters and containers at
# https://symfony.com/doc/current/service_container.html
parameters:
#parameter_name: value
services:
# default configuration for services in *this* file
_defaults:
# automatically injects dependencies in your services
autowire: true
# automatically registers your services as commands, event subscribers, etc.
autoconfigure: true
# this means you cannot fetch services directly from the container via $container->get()
# if you need to do this, you can override this setting on individual services
public: false
# makes classes in src/AppBundle available to be used as services
# this creates a service per class whose id is the fully-qualified class name
AppBundle\:
resource: '../../src/AppBundle/*'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../src/AppBundle/{Entity,Repository,Tests}'
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
twig.extension.intl:
class: Twig_Extensions_Extension_Intl
tags:
- { name: twig.extension }
# add more services, or override services that need manual wiring
# AppBundle\Service\ExampleService:
# arguments:
# $someArgument: 'some_value'
Ok I get it.
You have multiple bundles based on the UserController namespace (Site\PagesBundle\Controller\UserController). But your services.yaml enable services only for your AppBundle.
you have to duplicate this configuration for all your bundles and adapt it with the right namespace :
#services.yaml file
AppBundle\:
resource: '../../src/AppBundle/*'
# you can exclude directories or files
# but if a service is unused, it's removed anyway
exclude: '../../src/AppBundle/{Entity,Repository,Tests}'
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
AppBundle\Controller\:
resource: '../../src/AppBundle/Controller'
public: true
tags: ['controller.service_arguments']
This will declare all your controllers from your bundles as services in order to enable autowiring service in controllers constructors or in controllers actions.
Edit with example for your bundle:
Site\PagesBundle\:
resource: '../../src/Site/PagesBundle/*'
exclude: '../../src/Site/PagesBundle/{Entity,Repository,Tests}'
# controllers are imported separately to make sure they're public
# and have a tag that allows actions to type-hint services
Site\PagesBundle\Controller\:
resource: '../../src/Site/PagesBundle/Controller'
public: true
tags: ['controller.service_arguments']
I'm in trouble with authentication with symfony 4 on my first REST API.
The fact is my authentication succeed, and then my redirect URL is called, but the authentication token is lost during this redirection. I've also noticed that my serialize method is never called on my User Entity.
What I want is : When my Authentication is succeeded, then my profile page is called.
But with that code, all I get is a 302 redirection from profile, means that my authentication works, but the token was lost (if it exist, never seen it)
My only hints are :
Serialize method in User never called (is this important ?) EDIT : no because i need to be stateless, so remove those methods.
My Authentication works because if I make a mistake in credential I got a correct error.
Here is the code :
My Provider
<?php
declare(strict_types = 1);
namespace App\Api\Auth\Provider;
use App\Api\User\Entity\User;
use App\Api\User\Repository\UserRepository;
use App\Domain\User\ValueObject\Email;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class AuthProvider implements UserProviderInterface
{
/**
* #var \App\Api\User\Repository\UserRepository
*/
private $userRepository;
/**
* AuthProvider constructor.
* #param \App\Api\User\Repository\UserRepository $repository
*/
public function __construct(UserRepository $repository)
{
$this->userRepository = $repository;
}
/**
* #param string $email
* #return mixed
*/
public function loadUserByUsername($email)
{
try {
$user = $this->userRepository->getUser($email);
} catch (UnsupportedUserException $e) {
throw new UsernameNotFoundException('User not found', 1001, $e);
}
return $user;
}
/**
* #param \Symfony\Component\Security\Core\User\UserInterface | User $user
* #return mixed
*/
public function refreshUser(UserInterface $user)
{
return $this->loadUserByUsername($user->getEmail());
}
/**
* Qualify the supported class for this provider
* #param string $class
* #return string
*/
public function supportsClass($class)
{
if (!$class instanceof User) {
throw new UnsupportedUserException(
sprintf('Entity given is not supported, expected User got %s', $class),
1000
);
}
return $class;
}
}
My Guard :
<?php
declare(strict_types = 1);
namespace App\Api\Auth\Guard;
use App\Api\User\Repository\UserRepository;
use App\Domain\User\Exception\InvalidCredentialsException;
use App\Domain\User\ValueObject\Credentials;
use App\Domain\User\ValueObject\Email;
use App\Domain\User\ValueObject\HashedPassword;
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\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
/**
* Allow the authentication by giving credential, when login process achieved and valid, profile page show up
* Class LoginAuthenticator
* #package App\Api\Auth\Guard
*/
final class LoginAuthenticator extends AbstractFormLoginAuthenticator
{
const LOGIN = 'login';
const SUCCESS_REDIRECT = 'profile';
/**
* #var \Symfony\Component\Routing\Generator\UrlGeneratorInterface
*/
private $router;
/**
* #var \App\Api\User\Repository\UserRepository
*/
private $repository;
public function __construct(UrlGeneratorInterface $router, UserRepository $userRepository)
{
$this->router = $router;
$this->repository = $userRepository;
}
/**
* This method will pass the returning array to getUser and getCredential methods automatically
* #param \Symfony\Component\HttpFoundation\Request $request
* #return array
*/
public function getCredentials(Request $request)
{
return [
'email' => $request->get('email'),
'password' => $request->get('password')
];
}
/**
* In the case or the Guard and the Authenticator is the same, this method is called just after getCredentials
* #param mixed $credentials
* #param \Symfony\Component\Security\Core\User\UserProviderInterface $userProvider
* #return null|\Symfony\Component\Security\Core\User\UserInterface|void
*/
public function getUser($credentials, UserProviderInterface $userProvider): UserInterface
{
try {
$email = $credentials['email'];
$mail = Email::fromString($email);
$user = $userProvider->loadUserByUsername($mail->toString());
if ($user instanceof UserInterface) {
$this->checkCredentials($credentials, $user);
}
} catch (InvalidCredentialsException $exception) {
throw new AuthenticationException();
}
return $user;
}
/**
* The ùail has been found, because a user has been identified, we take the has password we have to compare
* #param mixed $credentials
* #param \Symfony\Component\Security\Core\User\UserInterface $user
* #return bool
*/
public function checkCredentials($credentials, UserInterface $user)
{
$mail = Email::fromString($credentials['email']);
$userCredentials = new Credentials($mail, HashedPassword::fromHash($user->getPassword()));
// Plain password compared
$match = $userCredentials->password->match($credentials['password']);
if (!$match) {
throw new InvalidCredentialsException();
}
return true;
}
/**
* Called when authentication executed and was successful!
*
* This should return the Response sent back to the user, like a
* RedirectResponse to the last page they visited.
*
* If you return null, the current request will continue, and the user
* will be authenticated. This makes sense, for example, with an API.
*
* #param \Symfony\Component\HttpFoundation\Request $request
* #param \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token
* #param string $providerKey
*
* #return RedirectResponse
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return new RedirectResponse($this->router->generate(self::SUCCESS_REDIRECT));
}
protected function getLoginUrl(): string
{
return $this->router->generate(self::LOGIN);
}
/**
* Does the authenticator support the given Request?
*
* If this returns false, the authenticator will be skipped.
*
* #param Request $request
*
* #return bool
*/
public function supports(Request $request)
{
return $request->getPathInfo() === $this->router->generate(self::LOGIN) && $request->isMethod('POST');
}
}
My Security.yml
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
users:
id: 'App\Api\Auth\Provider\AuthProvider'
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
stateless: true
anonymous: true
provider: users
guard:
entry_point: 'App\User\Auth\Guard\LoginAuthenticator'
authenticators:
- 'App\Api\Auth\Guard\LoginAuthenticator'
form_login:
login_path: /sign-in
check_path: sign-in
logout:
path: /logout
target: /
api:
pattern: ^/(/user/*|/api|)
stateless: true
guard:
authenticators:
- 'App\Api\Auth\Guard\LoginAuthenticator'
# 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: ^/api, roles: USER }
- { path: ^/user/*, roles: USER }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
My User entity
<?php
declare(strict_types = 1);
namespace App\Api\User\Entity;
use App\Domain\User\Repository\Interfaces\CRUDInterface;
use App\Shared\Entity\Traits\CreatedTrait;
use App\Shared\Entity\Traits\DeletedTrait;
use App\Shared\Entity\Traits\EntityNSTrait;
use App\Shared\Entity\Traits\IdTrait;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\Encoder\EncoderAwareInterface;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Table(name="app_users")
* #ORM\Entity(repositoryClass="App\Api\User\Repository\UserRepository")
*/
class User implements UserInterface, CRUDInterface, \Serializable, EncoderAwareInterface
{
use IdTrait;
use CreatedTrait;
use DeletedTrait;
use EntityNSTrait;
/**
* #ORM\Column(type="string", length=25, unique=false, nullable=true)
*/
private $username;
/**
* #ORM\Column(type="string", length=64)
*/
private $password;
/**
* #ORM\Column(type="string", length=254, unique=true)
*/
private $email;
/**
* #return mixed
*/
public function getEmail()
{
return $this->email;
}
/**
* #param mixed $email
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
public function __construct()
{
}
public function getUsername()
{
return $this->username;
}
public function getSalt()
{
// you *may* need a real salt depending on your encoder
// see section on salt below
return null;
}
public function getPassword()
{
return $this->password;
}
public function getRoles()
{
return array('USER');
}
/**
* From UserInterface
*/
public function eraseCredentials()
{
// Never used ?‡
}
/** #see \Serializable::serialize() */
public function serialize()
{
var_dump('need it'); // never called
return serialize([
$this->id,
$this->username,
$this->email,
$this->password,
// see section on salt below
// $this->salt,
]);
}
/** #see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->email,
$this->password,
// see section on salt below
// $this->salt
) = unserialize($serialized, ['allowed_classes' => false]);
}
/**
* #param mixed $password
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Gets the name of the encoder used to encode the password.
*
* If the method returns null, the standard way to retrieve the encoder
* will be used instead.
*
* #return string
*/
public function getEncoderName()
{
return 'bcrypt';
}
}
It's my really first project on SF4, it's maybe a dumb mistake but can't find it.
EDIT : I tried to pass in security config the attribute stateless to false, my serialize method were called but then I have an access denied error on profile page.
I need to stay "stateless" but it may help you to find a solution.
A stateless firewall will never store the token in the session, so you have to pass the credentials for every request you make to the API.
Currently your guard class returns a redirect, so your authentication is lost due to symfony not storing the token for stateless firewalls. To solve this, you should return null in the method onAuthenticationSuccess instead of doing a redirect. This also means, that you should create a separate guard class for the API firewall.
You can also find a good guard example for APIs in the symfony docs: https://symfony.com/doc/current/security/guard_authentication.html#step-1-create-the-authenticator-class
Edit:
I slightly misunderstood what you are trying to achieve. So it seems that you want to have a pure REST application with symfony and authenticate the user once where you then get back a token which can be used for future requests.
Some time ago I had the same issue and I stumbled over a very good bundle called LexikJWTAuthenticationBundle. This bundle gives you the necessary feature you need out of the box.
If you install it by following the Getting started documentation, you should have the basics for this.
Your configuration should then look something like this:
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
users:
id: 'App\Api\Auth\Provider\AuthProvider'
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/sign-in
stateless: true
anonymous: true
form_login:
check_path: /api/login_check
username_parameter: email
password_parameter: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
api:
pattern: ^/(/user/*|/api|)
stateless: true
anonymous: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
# 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: ^/api, roles: USER }
- { path: ^/user/*, roles: USER }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
But don't forget to add the login_check route to your routes.yml
api_login_check:
path: /api/login_check
If everything is setup correctly, you should now be able to retrieve a new token with the following request:
curl -X POST http://localhost/api/login_check -d _username=yourUsername -d _password=yourPassword
The token you received with this call should then be used for all future request to the API. You can pass it via the Authorization header
curl -H "Authorization: Bearer $YOUR_TOKEN" http://localhost/api/some-protected-route`
If you want to pass it differently (e.g. via query param) you have to change the configuration of this bundle:
lexik_jwt_authentication:
token_extractors:
query_parameter:
enabled: true
name: auth
Now you could use https://localhost/api/some-protecte-route?auth=$YOUR_TOKEN instead.
For more information about this, take a look at the configuration reference of this bundle
I hope this helps a little bit to get you started with.
I'm using symfony 3.4 with DoctrineMongoDBBundle and LexikJWTAuthenticationBundle . I'm trying to create a user login which return JWT token. If i specify the username and password under in_memory provider, it returns the token but if i use a entity provider, it returns {"code":401,"message":"bad credentials"}.
Here is my security.yml
# To get started with security, check out the documentation:
# https://symfony.com/doc/current/security.html
security:
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
encoders:
AppBundle\Document\User:
algorithm: bcrypt
cost: 12
providers:
webprovider:
entity:
class: AppBundle\Document\User
property: username
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: ^/api/login
stateless: true
anonymous: true
provider: webprovider
form_login:
username_parameter: username
password_parameter: password
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
anonymous: ~
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
#http_basic: ~
# https://symfony.com/doc/current/security/form_login_setup.html
#form_login: ~
access_control:
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Here is my User class
<?php
// /AppBundle/Document/User.php
namespace AppBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Bundle\MongoDBBundle\Validator\Constraints\Unique as MongoDBUnique;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #MongoDB\Document(collection="users")
* #MongoDBUnique(fields="email")
*/
class User implements UserInterface
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\Field(type="string")
* #Assert\Email()
*/
protected $email;
/**
* #MongoDB\Field(type="string")
* #Assert\NotBlank()
*/
protected $username;
/**
* #MongoDB\Field(type="string")
* #Assert\NotBlank()
*/
protected $password;
/**
* #MongoDB\Field(type="boolean")
*/
private $isActive;
public function __construct()
{
var_dump("1");
$this->isActive = true;
// may not be needed, see section on salt below
// $this->salt = md5(uniqid('', true));
}
public function getId()
{
return $this->id;
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getUsername()
{
var_dump($this->username);
return $this->username;
}
public function getSalt()
{
return null;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function eraseCredentials()
{
}
}
Would really appreciate if someone could help, Thanks.
You should use FOS bundle components by extending Base model User calss in entity, better than using directly Implementing UserInterface, In your case problem could be your password is not getting encoded correct. It's better to encode using security.password_encoder. To more better understanding i am sharing an example to setup login and generating token.
Your security.yml should look like this
`# Symfony 3.4 security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
Symfony\Component\Security\Core\User\User: plaintext
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
ROLE_API: ROLE_USER, ROLE_MEDIC, ROLE_STUDENT
# https://symfony.com/doc/current/security.html#b-configuring-how-users-are-loaded
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
in_memory:
memory: ~
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
api_login:
pattern: ^/api/login
stateless: true
anonymous: true
form_login:
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
require_previous_session: false
api:
pattern: ^/(api)
stateless: true #false (not assign cookies)
anonymous: ~
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
provider: fos_userbundle
access_control: .................`
Your User class should be like this:
// /AppBundle/Document/User.php
namespace AppBundle\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use FOS\UserBundle\Model\User as BaseUser;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Bundle\MongoDBBundle\Validator\Constraints\Unique as MongoDBUnique;
/**
* #MongoDB\Document(collection="users")
* #MongoDBUnique(fields="email")
*/
class User implements BaseUser
{
/**
* #MongoDB\Id
*/
protected $id;
/**
* #MongoDB\Field(type="string")
* #Assert\Email()
*/
protected $email;
/**
* #MongoDB\Field(type="string")
* #Assert\NotBlank()
*/
protected $username;
/**
* #MongoDB\Field(type="string")
* #Assert\NotBlank()
*/
protected $password;
/**
* #MongoDB\Field(type="boolean")
*/
private $isActive;
public function __construct()
{
var_dump("1");
$this->isActive = true;
// may not be needed, see section on salt below
// $this->salt = md5(uniqid('', true));
}
public function getId()
{
return $this->id;
}
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
public function setUsername($username)
{
$this->username = $username;
}
public function getUsername()
{
var_dump($this->username);
return $this->username;
}
public function getSalt()
{
return null;
}
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function eraseCredentials()
{
}
}
Now your Api tokenController.php to generate token and validate token
namespace \your_namespace\Api;
use FOS\RestBundle\Context\Context;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\FOSRestController;
use AppBundle\Document\User;
# Below most of the components belongs to Nelmio api or Sensio or Symfony
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Encoder\EncoderFactory;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\ExpiredTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\InvalidTokenException;
use Lexik\Bundle\JWTAuthenticationBundle\Exception\JWTDecodeFailureException;
use Lexik\Bundle\JWTAuthenticationBundle\Security\Authentication\Token\PreAuthenticationJWTUserToken;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
class TokenController extends FOSRestController
{
/**
* #Rest\Post("/tokens", name="api_token_new",
* options={"method_prefix" = false},
* defaults={"_format"="json"}
* )
*
* #ApiDoc(
* section = "Security",
* description = "Get user token",
* parameters={
* {"name"="username", "dataType"="string", "required"=true, "description"="username or email"},
* {"name"="pass", "dataType"="string", "required"=true, "description"="password "},
* }
* )
*/
public function newTokenAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$username = $request->get('username');
/** #var User $user */
$user = $em->getRepository('AppBundle:User')->findOneBy(['email' => $username]);
if (!$user) {
throw $this->createNotFoundException();
}
if (!$user->isEnabled()){
throw $this->createNotFoundException($this->get('translator')->trans('security.user_is_disabled'));
}
$pass = $request->get('pass');
$isValid = $this->get('security.password_encoder')->isPasswordValid($user, $pass);
if (!$isValid) {
throw new BadCredentialsException();
}
$token = $this->get('lexik_jwt_authentication.encoder')->encode([
'username' => $user->getUsername(),
'id' => $user->getId(),
'roles' => $user->getRoles(),
'exp' => time() + (30 * 24 * 3600) // 30 days expiration -> move to parameters or config
]);
// Force login
$tokenLogin = new UsernamePasswordToken($user, $pass, "public", $user->getRoles());
$this->get("security.token_storage")->setToken($tokenLogin);
// Fire the login event
// Logging the user in above the way we do it doesn't do this automatically
$event = new InteractiveLoginEvent($request, $tokenLogin);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
$view = $this->view([
'token' => $token,
'user' => $user
]);
$context = new Context();
$context->addGroups(['Public']);
$view->setContext($context);
return $this->handleView($view);
}
/**
* #Rest\Post("/validate", name="api_token_validate",
* options={"method_prefix" = false},
* defaults={"_format"="json"}
* )
*
* #ApiDoc(
* section = "Security",
* description = "Get user by token",
* parameters={
* {"name"="token", "dataType"="textarea", "required"=true, "description"="token"},
* }
* )
*/
public function validateUserToken(Request $request)
{
$token = $request->get('token');
//get UserProviderInterface
$fos = $this->get('fos_user.user_provider.username_email');
//create PreAuthToken
$preAuthToken = new PreAuthenticationJWTUserToken($token);
try {
if (!$payload = $this->get('lexik_jwt_authentication.jwt_manager')->decode($preAuthToken)) {
throw new InvalidTokenException('Invalid JWT Token');
}
$preAuthToken->setPayload($payload);
} catch (JWTDecodeFailureException $e) {
if (JWTDecodeFailureException::EXPIRED_TOKEN === $e->getReason()) {
throw new ExpiredTokenException();
}
throw new InvalidTokenException('Invalid JWT Token', 0, $e);
}
//get user
/** #var User $user */
$user = $this->get('lexik_jwt_authentication.security.guard.jwt_token_authenticator')->getUser($preAuthToken, $fos);
$view = $this->view([
'token' => $token,
'user' => $user
]);
$context = new Context();
$context->addGroups(array_merge(['Public'],$user->getRoles()));
$view->setContext($context);
return $this->handleView($view);
}
if you face any problem then , let me know.
you need to add the provider before json_login tag this the case for me
provider: fos_userbundle
Hi everybody and I m sorry if the question is dumb. I am very new with Symfony (2.6) and my first project requires the User to authenticate from AD.
Whatever I do I keep getting: Authentication request could not be processed due to a system problem.
security.yml
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
chain_provider:
chain:
providers: [fos_userbundle, fr3d_ldapbundle]
fr3d_ldapbundle:
id: fr3d_ldap.security.user.provider
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
main:
pattern: ^/
fr3d_ldap: ~
form_login:
# check_path: fos_user_security_check
# login_path: fos_user_security_login
always_use_default_target_path: true
default_target_path: /main
provider: chain_provider
logout:
path: fos_user_security_logout
target: /login
anonymous: ~
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
config.yml
fr3d_ldap:
driver:
host: 192.168.137.200
port: 50000
username: DOMAIN\Admnistrator # Optional
password: pass_here # Optional
user:
baseDn: OU=HP,OU=MANA,DC=DOMAIN,DC=com
filter: (&(ObjectClass=Person))
attributes: # Specify ldap attributes mapping [ldap attribute, user object method]
- { ldap_attr: uid, user_method: setUsername }
- { ldap_attr: mail, user_method: setEmail }
Entity/User class
/**
* #ORM\Entity
* #ORM\Table(name="mdr_user")
*/
class User extends BaseUser implements LdapUserInterface
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", nullable=true)
*/
protected $name;
/**
* Ldap Object Distinguished Name
* #ORM\Column(type="string", length=128)
* #var string $dn
*/
private $dn;
public function __construct()
{
parent::__construct();
if (empty($this->roles)) {
$this->roles[] = 'ROLE_USER';
}
}
public function setName($name) {
$this->name = $name;
}
/**
* {#inheritDoc}
*/
public function setDn($dn)
{
$this->dn = $dn;
}
/**
* {#inheritDoc}
*/
public function getDn()
{
return $this->dn;
}
}
if I dont implement LdapUserInterface, the DB authenticates fine but always if I use anything else other than mysql entries I get that error. Can you please help me with that ?
Appreciate it.
Try to do this on your loginAction()
\Doctrine\Common\Util\Debug::dump($this->getDoctrine()->getEntityManager()->find('AppBundle:User', 1));
You might possible see an error. This works for me.
What fixed this for me was adding:
implements UserInterface, \Serializable
to the end of my entity's class declaration then adding the required methods to the entity at the bottom:
/**
* #return null
*/
public function getSalt(){
return null;
}
/**
* #return array
*/
public function getRoles(){
return array('ROLE_USER');
}
public function eraseCredentials(){
}
/**
* #return string
*/
public function serialize(){
return serialize(array($this->id, $this->username, $this->password));
}
/**
* #param string $serialized
*/
public function unserialize($serialized) {
list($this->id, $this->username,$this->password) = unserialize($serialized);
}