Authentification avec le SecurityBundle de Symfony¶
Nous allons ici présenter comment faire une authentification en utilisant le SecurityBundle de Symfony . Il permet la connexion et la navigation de manière sécurisée.
Initialisation du projet Symfony¶
Tout d’abord création d’un nouveau projet Symfony (code pour la v5 de Symfony)
symfony new --full my_project
cd my_project
Installation de SecurityBundle¶
composer require symfony/security-bundle
Création de l’entité utilisateur¶
php bin/console make:user

Cette entité est directement liée au SecurityBundle.
<?php
namespace App\Entity;
use App\Repository\UtilisateurRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* @ORM\Entity(repositoryClass=UtilisateurRepository::class)
*/
class Utilisateur implements UserInterface
{
/**
* @ORM\Id()
* @ORM\GeneratedValue()
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=180, unique=true)
*/
private $username;
/**
* @ORM\Column(type="json")
*/
private $roles = [];
/**
* @var string The hashed password
* @ORM\Column(type="string")
*/
private $password;
public function getId(): ?int
{
return $this->id;
}
/**
* 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;
}
/**
* @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;
}
}
Un fichier config/packages/security.yaml est créé. Il contient tous les paramètres pour une gestion sécurisée des utilisateurs.
security:
encoders:
App\Entity\Utilisateur:
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\Utilisateur
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
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 }
Faites ensuite la migration :
php bin/console make:migration
php bin/console doctrine:migrations:migrate
Nous pouvons maintenant générer des formulaires spécialement prévu pour de l’authentification :
php bin/console make:auth

- Cette commande :
modifie security.yaml
ajoute un controller
ajoute une classe héritiaire de AbstractFormLoginAuthenticator
ajoute un template
Nous avons dès lors un formulaire de login sécurisé ainsi qu’une route pour se déconnecter. Cependant le bundle ne prévoit pas la gestion des utilisateurs. Nous allons donc utiliser le maker bundle pour faire un CRUD.
CRUD utilisateur¶
php bin/console make:crud
Comme pour l’authentication à la main, nous allons devoir apporter quelques modifications.
Le controller :¶
Ajout de use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
Ceci afin de pour crypter le mot de passe saisi lors de l’inscription.
<?php
/**
* @Route("/new", name="utilisateur_new", methods={"GET","POST"})
*/
public function new(Request $request, UserPasswordEncoderInterface $passwordEncoder): Response
{
$utilisateur = new Utilisateur();
$form = $this->createForm(UtilisateurType::class, $utilisateur);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
//encodage du mot de passe
$utilisateur->setPassword(
$passwordEncoder->encodePassword($utilisateur, $utilisateur->getPassword()));
$entityManager->persist($utilisateur);
$entityManager->flush();
return $this->redirectToRoute('utilisateur_index');
}
return $this->render('utilisateur/new.html.twig', [
'utilisateur' => $utilisateur,
'form' => $form->createView(),
]);
}
Le formulaire :¶
<?php
namespace App\Form;
use App\Entity\Utilisateur;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
//ajout du use pour utiliser le type input password de Symfony
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
class UtilisateurType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username')
// suppression du role qui sera défini par défaut
->add('password', PasswordType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Utilisateur::class,
]);
}
}
Pour avoir le champ password caché.
Nous modifions maintenant le template du new afin d’ajouter l’input de vérification du mot de passe :
{% extends 'base.html.twig' %}
{% block title %}New Utilisateur{% endblock %}
{% block body %}
<h1>Create new Utilisateur</h1>
{{ form_start(form, {'attr': {'id': 'new_edit_utilisateur'}}) }}
{# utilisation de classes bootstrap pour la mise en forme #}
<div class="row">
<div class="col-12">
{{ form_label(form.username) }}
{{ form_widget(form.username) }}
</div>
<div class="col-12">
{{ form_label(form.password) }}
{{ form_widget(form.password) }}
</div>
<div class="col-12">
<label for="verifpass">Saisir une seconde fois le mot de passe</label>
<input type="password" id="verifpass" required>
</div>
</div>
<button class="btn btn-success">{{ button_label|default('Save') }}</button>
{{ form_end(form) }}
<a href="{{ path('utilisateur_index') }}">back to list</a>
{% endblock %}
Création d’un script JS avec jQuery afin de tester si les deux mot de passe sont identiques. Créez tout d’abord un dossier js dans public puis dans ce dossier, un fichier script.js.
Script de vérification :¶
$("#new_edit_utilisateur").on('submit', function(){
if($("#utilisateur_password").val() != $("#verifpass").val()) {
//implémntez votre code
alert("Les deux mots de passe saisies sont différents");
alert("Merci de renouveler l'opération");
return false;
}
})
Il ne faut pas oublier d’intégrer le lien vers jQuery et notre script.js dans le base template :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}
<link rel="stylesheet" href="https://bootswatch.com/4/yeti/bootstrap.min.css">
{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}
<script
src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
crossorigin="anonymous">
</script>
<script src="/js/script.js"></script>
{% endblock %}
</body>
</html>
LoginFormAuthenticator :¶
Dans le dossier Security nous allons ajouter une route lorsque la connexion a été réalisée avec succès :
<?php
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
//on renvoie à la liste des utilisateurs
return new RedirectResponse($this->urlGenerator->generate('utilisateur_index'));
}
Nous sommes maintenant capable de créer un utilisateur et de se connecter de manière sécurisée. Nous allons maintenant faire un peu de mise en forme. Dans le base template, ajout d’une barre de menu qui sera notre interface de connexion.
Base template :¶
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}
<link rel="stylesheet" href="https://bootswatch.com/4/yeti/bootstrap.min.css">
{% endblock %}
</head>
<body>
<nav class="navbar navbar-light bg-light">
<a class="navbar-brand">Navbar</a>
{% if app.user %}
<div>
Bonjour {{ app.user.username }} <a class="btn btn-sm btn-danger" href="{{ path('app_logout') }}">Déconnexion</a>
</div>
{% else %}
<div>
<a class="btn btn-sm btn-primary" href="{{ path('utilisateur_new') }}">S'inscrire</a>
<a class="btn btn-sm btn-success" href="{{ path('app_login') }}">Se connecter</a>
</div>
{% endif %}
</nav>
<div class="container">
{% if message is defined %}
<div class="alert alert-danger">
{{ message }}
</div>
{% endif %}
{% block body %}
{% endblock %}
</div>
{% block javascripts %}
<script
src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
crossorigin="anonymous">
</script>
<script src="/js/script.js"></script>
{% endblock %}
</body>
</html>
Nous proposons à nos utilisateurs d’acceder aux pages de connexion ou d’inscription s’ils ne sont pas identifiés. Dans le cas contraire nous leur proposons de se déconnecter.
Modification d’un utilisateur¶
Nous avons besoin lorsque nous allons modifier un utilisateur d’avoir la même double vérification pour le mot de passe.
Dans le template :
{% extends 'base.html.twig' %}
{% block title %}Edit Utilisateur{% endblock %}
{% block body %}
<h1>Edit Utilisateur</h1>
{{ form_start(form, {'attr': {'id': 'new_edit_utilisateur'}}) }}
{# utilisation de classes bootstrap pour la mise en forme #}
<div class="row">
<div class="col-12">
{{ form_label(form.username) }}
{{ form_widget(form.username) }}
</div>
<div class="col-12">
{{ form_label(form.password) }}
{{ form_widget(form.password) }}
</div>
<div class="col-12">
<label for="verifpass">Saisir une seconde fois le mot de passe</label>
<input type="password" id="verifpass" required>
</div>
</div>
<button class="btn btn-success">{{ button_label|default('Update') }}</button>
{{ form_end(form) }}
<a href="{{ path('utilisateur_index') }}">back to list</a>
{{ include('utilisateur/_delete_form.html.twig') }}
{% endblock %}
Dans le controller :
<?php
/**
* @Route("/{id}/edit", name="utilisateur_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Utilisateur $utilisateur, UserPasswordEncoderInterface $passwordEncoder): Response
{
$form = $this->createForm(UtilisateurType::class, $utilisateur);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$utilisateur->setPassword($passwordEncoder->encodePassword($utilisateur, $utilisateur->getPassword()));
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('utilisateur_index');
}
return $this->render('utilisateur/edit.html.twig', [
'utilisateur' => $utilisateur,
'form' => $form->createView(),
]);
}
Nous pouvons maintenant modifier le mot de passe de façon sécurisée.
Exemple final¶
Maintenant je vais vous présenter la solution que j’ai retenu pour mon exemple. En fait il s’agit d’un mix de toutes les méthodes vues précédemment.
Fichier de config¶
security.yaml¶
security:
encoders:
App\Entity\Utilisateur:
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\Utilisateur
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
provider: app_user_provider
guard:
authenticators:
- App\Security\LogInFormAuthenticator
logout:
path: app_logout
# where to redirect after logout
target: home
# 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: ^/logout, roles: ROLE_USER }
# permet de rendre la route /new accessible pour les utilisateurs anonymes (non connecté)
- { path: ^/utilisateur/new, roles: IS_AUTHENTICATED_ANONYMOUSLY }
# bloque toutes les routes commençant par /utilisateur sauf la ligne du dessus
- { path: ^/utilisateur, roles: ROLE_USER }
- { path: ^/membre, roles: IS_AUTHENTICATED_FULLY }
Je ne l’utilise que pour les routes nécessitant d’être connecté (sans distinction de droits).
Note
ROLE_USER == IS_AUTHENTICATED_FULLY
Controllers¶
UtilisateurController¶
<?php
namespace App\Controller;
use App\Entity\Utilisateur;
use App\Form\UtilisateurType;
use App\Repository\UtilisateurRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\HttpFoundation\Session\Session;
/**
* @Route("/utilisateur")
*/
class UtilisateurController extends AbstractController
{
/**
* @Route("/", name="utilisateur_index", methods={"GET"})
*/
public function index(UtilisateurRepository $utilisateurRepository, Session $session): Response
{
//besoin de droits admin
$utilisateur = $this->getUser();
if(!$utilisateur)
{
$session->set("message", "Merci de vous connecter");
return $this->redirectToRoute('app_login');
}
else if(in_array('ROLE_ADMIN', $utilisateur->getRoles())){
return $this->render('utilisateur/index.html.twig', [
'utilisateurs' => $utilisateurRepository->findAll(),
]);
}
return $this->redirectToRoute('home');
}
/**
* @Route("/new", name="utilisateur_new", methods={"GET","POST"})
*/
public function new(Request $request, UserPasswordEncoderInterface $passwordEncoder, Session $session): Response
{
//test de sécurité, un utilisateur connecté ne peut pas s'inscrire
$utilisateur = $this->getUser();
if($utilisateur)
{
$session->set("message", "Vous ne pouvez pas créer un compte lorsque vous êtes connecté");
return $this->redirectToRoute('membre');
}
$utilisateur = new Utilisateur();
$form = $this->createForm(UtilisateurType::class, $utilisateur);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$utilisateur->setPassword($passwordEncoder->encodePassword($utilisateur, $utilisateur->getPassword()));
/* uniquement pour créer un admin
$role = ['ROLE_ADMIN'];
$utilisateur->setRoles($role); */
$entityManager->persist($utilisateur);
$entityManager->flush();
return $this->redirectToRoute('utilisateur_index');
}
return $this->render('utilisateur/new.html.twig', [
'utilisateur' => $utilisateur,
'form' => $form->createView(),
]);
}
/**
* @Route("/{id}", name="utilisateur_show", methods={"GET"})
*/
public function show(Utilisateur $utilisateur): Response
{
//accès géré dans le security.yaml
return $this->render('utilisateur/show.html.twig', [
'utilisateur' => $utilisateur,
]);
}
/**
* @Route("/{id}/edit", name="utilisateur_edit", methods={"GET","POST"})
*/
public function edit(Request $request, Utilisateur $utilisateur, UserPasswordEncoderInterface $passwordEncoder, Session $session, $id): Response
{
$utilisateur = $this->getUser();
if($utilisateur->getId() != $id )
{
// un utilisateur ne peut pas en modifier un autre
$session->set("message", "Vous ne pouvez pas modifier cet utilisateur");
return $this->redirectToRoute('membre');
}
$form = $this->createForm(UtilisateurType::class, $utilisateur);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$utilisateur->setPassword($passwordEncoder->encodePassword($utilisateur, $utilisateur->getPassword()));
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('utilisateur_index');
}
return $this->render('utilisateur/edit.html.twig', [
'utilisateur' => $utilisateur,
'form' => $form->createView(),
]);
}
/**
* @Route("/{id}", name="utilisateur_delete", methods={"DELETE"})
*/
public function delete(Request $request, Utilisateur $utilisateur, Session $session, $id): Response
{
$utilisateur = $this->getUser();
if($utilisateur->getId() != $id )
{
// un utilisateur ne peut pas en supprimer un autre
$session->set("message", "Vous ne pouvez pas supprimer cet utilisateur");
return $this->redirectToRoute('membre');
}
if ($this->isCsrfTokenValid('delete'.$utilisateur->getId(), $request->request->get('_token')))
{
$entityManager = $this->getDoctrine()->getManager();
$entityManager->remove($utilisateur);
$entityManager->flush();
// permet de fermer la session utilisateur et d'éviter que l'EntityProvider ne trouve pas la session
$session = new Session();
$session->invalidate();
}
return $this->redirectToRoute('home');
}
}
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;
use Symfony\Component\HttpFoundation\Session\Session;
class SecurityController extends AbstractController
{
/**
* @Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils, Session $session): 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 = ['last_username' => $lastUsername, 'error' => $error];
if($session->has('message'))
{
$message = $session->get('message');
$session->remove('message'); //on vide la variable message dans la session
$return['message'] = $message; //on ajoute à l'array de paramètres notre message
}
return $this->render('security/login.html.twig', $return);
}
/**
* @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.');
}
}
Templates¶
base.html.twig¶
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>{% block title %}Welcome!{% endblock %}</title>
{% block stylesheets %}
<link rel="stylesheet" href="https://bootswatch.com/4/yeti/bootstrap.min.css">
<link rel="stylesheet" href="/css/style.css">
{% endblock %}
</head>
<body>
<nav class="navbar navbar-light bg-light">
<a class="navbar-brand" href="{{ path('home') }}">Accueil</a>
{% if app.user %}
<a class="btn btn-outline-info" href="{{ path('membre') }}">Page membre</a>
{% if is_granted('ROLE_ADMIN') %}
<a class="btn btn-outline-warning" href="{{ path('admin') }}">Page admin</a>
{% endif %}
<div>
Bonjour {{ app.user.username }} <a class="btn btn-sm btn-danger" href="{{ path('app_logout') }}">Déconnexion</a>
</div>
{% else %}
<div>
<a class="btn btn-sm btn-primary" href="{{ path('utilisateur_new') }}">S'inscrire</a>
<a class="btn btn-sm btn-success" href="{{ path('app_login') }}">Se connecter</a>
</div>
{% endif %}
</nav>
<div class="container">
{% if message is defined %}
<div class="alert alert-danger">
{{ message }}
</div>
{% endif %}
{% block body %}
{% endblock %}
</div>
<footer class="navbar-light bg-light">
<div class="footer-copyright text-center py-3">© 2020 Copyright:
<a href="https://www.univ-orleans.fr/iut-orleans/informatique/intra/tuto/php/"> Gérard Rozsavolgyi & Sylvain Austruy</a>
</div>
</footer>
{% block javascripts %}
<script
src="https://code.jquery.com/jquery-3.5.1.min.js"
integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
crossorigin="anonymous">
</script>
<script src="/js/script.js"></script>
{% endblock %}
</body>
</html>
new.html.twig¶
{% extends 'base.html.twig' %}
{% block title %}Inscription{% endblock %}
{% block body %}
<h1>Inscription</h1>
<fieldset>
<legend>Inscrivez vous</legend>
{{ form_start(form, {'attr': {'id': 'new_edit_utilisateur'}}) }}
{# utilisation de classes bootstrap pour la mise en forme #}
<div class="row">
<div class="col-12">
{{ form_label(form.username) }}
{{ form_widget(form.username) }}
{{ form_errors(form.username) }}
</div>
<div class="col-12">
{{ form_label(form.password) }}
{{ form_widget(form.password) }}
</div>
<div class="col-12">
<label for="verifpass">Saisir une seconde fois le mot de passe</label>
<input type="password" id="verifpass" required>
</div>
</div>
<button class="btn btn-success">{{ button_label|default('S\'enregistrer') }}</button>
{{ form_end(form) }}
</fieldset>
<a class="btn btn-info" href="{{ path('home') }}">Page d'accueil</a>
{% endblock %}
Ajout de {{ form_errors(form.username) }} pour afficher une erreur quand le pseudo est déjà utilisé (la vérification se fait automatiquement, car nous avons choisi l’attribut username comme unique) et un peu de css.
edit.html.twig¶
{% extends 'base.html.twig' %}
{% block title %}Modification compte{% endblock %}
{% block body %}
<h1>Modifier votre compte</h1>
<fieldset>
<legend>Modification</legend>
{{ form_start(form, {'attr': {'id': 'new_edit_utilisateur'}}) }}
{# utilisation de classes bootstrap pour la mise en forme #}
<div class="row">
<div class="col-12">
{{ form_label(form.username) }}
{{ form_widget(form.username) }}
</div>
<div class="col-12">
{{ form_label(form.password) }}
{{ form_widget(form.password) }}
</div>
<div class="col-12">
<label for="verifpass">Saisir une seconde fois le mot de passe</label>
<input type="password" id="verifpass" required>
</div>
</div>
<button class="btn btn-success">{{ button_label|default('Mettre à jour') }}</button>
{{ form_end(form) }}
</fieldset>
<a class="btn btn-outline-primary" href="{{ path('membre') }}">Page membre</a>
{{ include('utilisateur/_delete_form.html.twig') }}
{% endblock %}
show.html.twig¶
{% extends 'base.html.twig' %}
{% block title %}Utilisateur{% endblock %}
{% block body %}
<h1>Utilisateur</h1>
<table class="table">
<tbody>
<tr>
<th>Id</th>
<td>{{ utilisateur.id }}</td>
</tr>
<tr>
<th>Username</th>
<td>{{ utilisateur.username }}</td>
</tr>
<tr>
<th>Roles</th>
<td>{{ utilisateur.roles ? utilisateur.roles|json_encode : '' }}</td>
</tr>
<tr>
<th>Password</th>
<td>{{ utilisateur.password }}</td>
</tr>
</tbody>
</table>
<a class="btn btn-outline-primary mt-2" href="{{ path('membre') }}">Page membre</a>
<a class="btn btn-warning mt-2" href="{{ path('utilisateur_edit', {'id': utilisateur.id}) }}">Modifier</a>
{{ include('utilisateur/_delete_form.html.twig') }}
{% endblock %}
login.html.twig¶
{% extends 'base.html.twig' %}
{% block title %}Connexion{% endblock %}
{% block body %}
<form method="post" class="mt-5">
<fieldset>
<legend>Connectez vous</legend>
{% if error %}
<div class="alert alert-danger">{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
{% if app.user %}
<div class="mb-3">
Bonjour {{ app.user.username }} <a class="btn btn-sm btn-danger" href="{{ path('app_logout') }}">Déconnexion</a>
</div>
{% else %}
<div class="row">
<div class="col-12 mt-3">
<input type="text" value="{{ last_username }}" name="username" id="inputUsername" class="form-control" required autofocus placeholder="pseudo">
</div>
<div class="col-12 mt-3 mb-3">
<input type="password" name="password" id="inputPassword" class="form-control" required placeholder="mot de passe">
</div>
</div>
<button class="btn btn-success" type="submit">
Connexion
</button>
{% endif %}
<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>
#}
</fieldset>
</form>
{% endblock %}
CSS¶
public/css/style.css¶
fieldset {
margin: 0 auto;
width: 50%;
padding: 1em;
border: 1px solid #CCC;
border-radius: 1em;
}
legend {
width: 50%;
}
body {
margin: 0;
min-height: 100vh;
display: grid;
grid-template-rows: auto 1fr auto;
}
Note
Ce tuto est terminé, il ne vous reste qu’à l’adapter à votre projet. Ainsi que de réaliser un beau CSS avec Bootstrap ou autre.