Authentification

Nette fournit un moyen de programmer l'authentification sur nos pages, mais ne nous impose rien. L'implémentation dépend entièrement de nous. Nette contient l'interface Nette\Security\Authenticator, qui ne nécessite qu'une seule méthode authenticate, qui vérifie l'utilisateur de la manière que nous souhaitons.

Il existe de nombreuses possibilités pour vérifier un utilisateur. La méthode d'authentification la plus courante est par mot de passe (l'utilisateur fournit son nom ou son e-mail et son mot de passe), mais il existe d'autres moyens. Vous connaissez peut-être les boutons “Se connecter avec Facebook”, ou la connexion via Google/Twitter/GitHub sur certains sites. Avec Nette, nous pouvons avoir n'importe quelle méthode de connexion, ou nous pouvons même les combiner. C'est à nous de décider.

Normalement, nous écririons notre propre authentificateur, mais pour ce petit blog simple, nous utiliserons l'authentificateur intégré, qui se connecte sur la base du mot de passe et du nom d'utilisateur stockés dans le fichier de configuration. Il est utile à des fins de test. Ajoutons donc la section security suivante au fichier de configuration config/common.neon:

security:
	users:
		admin: secret  # utilisateur 'admin', mot de passe 'secret'

Nette créera automatiquement un service dans le conteneur DI.

Formulaire de connexion

Maintenant que l'authentification est prête, nous devons préparer l'interface utilisateur pour la connexion. Créons donc un nouveau presenter nommé SignPresenter, qui :

  • affichera le formulaire de connexion (avec nom d'utilisateur et mot de passe)
  • après l'envoi du formulaire, vérifiera l'utilisateur
  • fournira la possibilité de se déconnecter

Commençons par le formulaire de connexion. Nous savons déjà comment fonctionnent les formulaires dans les presenters. Créons donc le presenter SignPresenter et écrivons la méthode createComponentSignInForm. Il devrait ressembler à quelque chose comme ça :

<?php
namespace App\Presentation\Sign;

use Nette;
use Nette\Application\UI\Form;

final class SignPresenter extends Nette\Application\UI\Presenter
{
	protected function createComponentSignInForm(): Form
	{
		$form = new Form;
		$form->addText('username', 'Nom d\'utilisateur :')
			->setRequired('Veuillez entrer votre nom d\'utilisateur.');

		$form->addPassword('password', 'Mot de passe :')
			->setRequired('Veuillez entrer votre mot de passe.');

		$form->addSubmit('send', 'Se connecter');

		$form->onSuccess[] = $this->signInFormSucceeded(...);
		return $form;
	}
}

Il y a des champs pour le nom d'utilisateur et le mot de passe.

Template

Le formulaire sera rendu dans le template in.latte:

{block content}
<h1 n:block=title>Connexion</h1>

{control signInForm}

Callback de connexion

Ensuite, ajoutons le callback pour la connexion de l'utilisateur, qui sera appelé juste après l'envoi réussi du formulaire.

Le callback récupère simplement le nom d'utilisateur et le mot de passe que l'utilisateur a remplis et les transmet à l'authentificateur. Après la connexion, nous redirigeons vers la page d'accueil.

private function signInFormSucceeded(Form $form, \stdClass $data): void
{
	try {
		$this->getUser()->login($data->username, $data->password);
		$this->redirect('Home:');

	} catch (Nette\Security\AuthenticationException $e) {
		$form->addError('Nom d\'utilisateur ou mot de passe incorrect.');
	}
}

La méthode User::login() lèvera une exception si le nom d'utilisateur et le mot de passe ne correspondent pas aux informations du fichier de configuration. Comme nous le savons déjà, cela peut entraîner une page d'erreur rouge, ou en mode production, un message informant d'une erreur serveur. Nous ne voulons pas cela. C'est pourquoi nous attrapons cette exception et transmettons un message d'erreur agréable et convivial au formulaire.

Dès qu'une erreur se produit dans le formulaire, la page avec le formulaire est redessinée et un message agréable s'affiche au-dessus du formulaire informant l'utilisateur qu'il a saisi un nom d'utilisateur ou un mot de passe incorrect.

Sécurisation des presenters

Sécurisons le formulaire pour ajouter et modifier des articles. Il est défini dans le presenter EditPresenter. L'objectif est d'empêcher l'accès à la page aux utilisateurs qui ne sont pas connectés.

Nous créerons une méthode startup(), qui s'exécute immédiatement au début du cycle de vie du presenter. Elle redirigera les utilisateurs non connectés vers le formulaire de connexion.

public function startup(): void
{
	parent::startup();

	if (!$this->getUser()->isLoggedIn()) {
		$this->redirect('Sign:in');
	}
}

Masquer les liens

Un utilisateur non autorisé ne peut plus voir les pages create ou edit, mais il peut toujours voir les liens vers elles. Nous devrions également les masquer. Un tel lien se trouve dans le template app/Presentation/Home/default.latte et ne devrait être visible que par les utilisateurs connectés.

Nous pouvons le masquer en utilisant un n:attribut nommé n:if. Si cette condition est false, toute la balise <a>, y compris son contenu, restera cachée.

<a n:href="Edit:create" n:if="$user->isLoggedIn()">Créer un article</a>

ce qui est un raccourci pour la notation suivante (à ne pas confondre avec tag-if) :

{if $user->isLoggedIn()}<a n:href="Edit:create">Créer un article</a>{/if}

De la même manière, nous masquerons également le lien dans le template app/Presentation/Post/show.latte.

Lien de connexion

Comment accédons-nous réellement à la page de connexion ? Il n'y a aucun lien qui y mène. Ajoutons-le donc au template @layout.latte. Essayez de trouver un endroit approprié – cela peut être presque n'importe où.

...
<ul class="navig">
	<li><a n:href="Home:">Articles</a></li>
	{if $user->isLoggedIn()}
		<li><a n:href="Sign:out">Se déconnecter</a></li>
	{else}
		<li><a n:href="Sign:in">Se connecter</a></li>
	{/if}
</ul>
...

Si l'utilisateur n'est pas connecté, le lien “Se connecter” s'affiche. Sinon, le lien “Se déconnecter” s'affiche. Ajoutons également cette action au SignPresenter.

Comme nous redirigeons immédiatement l'utilisateur après la déconnexion, aucun template n'est nécessaire. La déconnexion ressemble à ceci :

public function actionOut(): void
{
	$this->getUser()->logout();
	$this->flashMessage('Déconnexion réussie.');
	$this->redirect('Home:');
}

Seule la méthode logout() est appelée, puis un message agréable confirmant la déconnexion réussie s'affiche.

Résumé

Nous avons un lien pour la connexion et aussi pour la déconnexion de l'utilisateur. Pour l'authentification, nous avons utilisé l'authentificateur intégré et les informations de connexion se trouvent dans le fichier de configuration, car il s'agit d'une simple application de test. Nous avons également sécurisé les formulaires d'édition, de sorte que seuls les utilisateurs connectés peuvent ajouter et modifier des articles.

Ici, vous pouvez en lire plus sur la connexion des utilisateurs et la Vérification des permissions.