Illustration vérification des emails avec des tokens JWT en utilisant Next.js et Symfony
Illustration vérification des emails avec des tokens JWT en utilisant Next.js et Symfony

Publié le 22 novembre 2024. Mise à jour le 05 décembre 2024

Gérer Commentaires, Email et API avec Symfony/Next.js

J’ai récemment implémenté une fonctionnalité qui vérifie l’adresse e-mail d’un utilisateur lorsqu’il remplit un formulaire de commentaire. L’objectif est de fournir une validation côté client en temps réel et d’assurer une communication fluide avec le back-end, développé sous Symfony.

 

  • Les réponses (responses), qui incluent les erreurs, la confirmation de l'email, les messages d'état (chargement, succès, etc.).

 

Initialisation de l'état avec useState de React

Le composant Comments initialise un état via useState pour gérer :

 const [state, setState] = useState({
    responses: {
      confirmEmail: false,
      messageComment: '',
      isLoading: false,
    },
    form: {
      user: '',
      email: '',
      comment: '',
      posts: posts.id,
    },
  });
  • Les réponses (responses), qui incluent les erreurs, la confirmation de l'email, les messages d'état (chargement, succès, etc.).
  • Le formulaire (form), contenant les champs de saisie comme l'utilisateur, l'email, le commentaire et l'identifiant du post associé.

Vérification de l'email (onBlur) avec token JWT

Lorsqu'un utilisateur quitte le champ email (onBlur), l'email est validé via une API Symfony.

const handleBlur = async () => {
  try {
    const response = await fetch('/api/comments/verify_email', {
      method: 'POST',
      body: JSON.stringify({ email: form.email }),
      headers: { 'Content-Type': 'application/json' },
    });
    const data = await response.json();
    setResponses((prev) => ({
      ...prev,
      emailConfirmed: data.valid,
      error: data.valid ? null : 'Email invalide',
    }));
  } catch (error) {
    setResponses((prev) => ({ ...prev, error: 'Erreur lors de la vérification' }));
  }
};

Fonctionnement :

  • Une requête POST envoie l'email au serveur.
  • Si l'email est valide, l'état emailConfirmed passe à true. Sinon, un message d'erreur s’affiche.

 

Soumission du formulaire

Lors de l'envoi du formulaire, les données sont envoyées à l'API Symfony.

const handleSubmit = async (e) => {
  e.preventDefault();
  setResponses((prev) => ({ ...prev, loading: true }));

  try {
    const response = await fetch('/api/comments', {
      method: 'POST',
      body: JSON.stringify(form),
      headers: { 'Content-Type': 'application/json' },
    });
    const data = await response.json();

    if (response.ok) {
      setForm({ user: '', email: '', comment: '', postId: '' });
      setResponses({ success: true, loading: false, error: null });
    } else {
      setResponses({ success: false, loading: false, error: data.message });
    }
  } catch (error) {
    setResponses({ success: false, loading: false, error: 'Erreur réseau' });
  }
};

Fonctionnement :

  • Les données du formulaire sont envoyées via une requête POST.
  • En cas de succès, le formulaire est réinitialisé et un message de succès s’affiche.
  • En cas d’échec, un message d’erreur s’affiche.

Affichage des commentaires

Les commentaires sont rendus dynamiquement avec leurs réponses. Les dates sont formatées pour chaque commentaire.

const formattedDate = (date) =>
  new Date(date).toLocaleDateString('fr-FR', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
  });

return comments.map((comment) => (
  <div key={comment.id}>
    <p>{comment.text}</p>
    <p>{formattedDate(comment.createdAt)}</p>
    {comment.replies && comment.replies.map((reply) => (
      <div key={reply.id} style={{ marginLeft: '20px' }}>
        <p>{reply.text}</p>
        <p>{formattedDate(reply.createdAt)}</p>
      </div>
    ))}
  </div>
));

Back-Office de API Symfony avec cookies et token JWT

Le code ci-dessous vérifie l'email dans une application Symfony :

  1. Récupération de l'email : Lorsque l'utilisateur envoie une requête POST vers l'endpoint /verify_email, le code commence par extraire l'email du corps de la requête JSON à l'aide de la méthode JSON_decode($request->getContent(), true).
  2. Vérification de l'email via une API externe : Le code envoie une requête HTTP GET à l'API Mailcheck.ai pour vérifier si l'email est valide et s'il est jetable. Cette API renvoie une réponse sous forme de tableau (données JSON) qui contient une clé disposable. Si la valeur de cette clé est true, cela signifie que l'email est temporaire et non accepté. Dans ce cas, une réponse JSON est retournée pour informer l'utilisateur que l'email est jetable.
  3. Vérification de l'utilisateur existant : Si l'email n'est pas jetable, le code cherche un utilisateur dans la base de données avec cet email. Si un utilisateur est trouvé, cela signifie que l'email est associé à un compte existant.
  4. Création d'un token JWT : Si l'utilisateur existe, un token JWT est généré à l'aide du jwtManager pour authentifier l'utilisateur. Ce token est ensuite attaché à un cookie, qui est configuré pour durer un an (expiration dans un an). Ce cookie est ajouté à la réponse HTTP.
  5. Retour de la réponse : Si un utilisateur est trouvé, la réponse JSON est envoyée avec un message indiquant que l'email est valide et qu'un cookie JWT a été attaché à la session. Si l'email est jetable, une erreur est renvoyée avec un message expliquant que l'email n'est pas accepté.
  6. Réponse HTTP : La réponse JSON contient une clé message, qui est utilisée pour indiquer si l'email est valide, si un utilisateur a été trouvé ou si l'email est jetable. De plus, les en-têtes HTTP sont configurés pour inclure le cookie JWT, permettant à l'utilisateur de rester authentifié.
     
#[Route('/verify_email', name: 'verify', methods: ['POST'])]
public function verifyEmail(Request $request, HttpClientInterface $httpClient, EntityManagerInterface $entityManager): JsonResponse
{
    $email = JSON_decode($request->getContent(), true)['email'];


    try {
        $urlAPI = 'https://api.mailcheck.ai/email/' . $email;
    
        $reponse = $httpClient->request('GET', $urlAPI);
        $donnees = $reponse->toArray();
        
        $existingUser = $entityManager->getRepository(User::class)->findOneBy(['email' => $email]);
                
        if ($existingUser) {
    
            $token = $this->jwtManager->create($existingUser);
            $date = time() + (3600 * 24 * 365); 
            $cookie = new Cookie(
                'jwt',
                $token,
                $date,


                // '/',        // Le chemin du cookie (par exemple, '/')
                // '',        // Le domaine du cookie (null pour le domaine actuel)
                // true,  // Désactivez l'option Secure pour permettre les connexions HTTP
                // false,  // Désactivez l'option HttpOnly pour permettre l'accès via JavaScript
                // 'lax',
            )
    
            $response = new JsonResponse(['message' => true]);
            $response->headers->set('Content-Type', 'application/json');
            
            $response->headers->setCookie($cookie);
            
            return $response;
    
        }


        if ($donnees['disposable']) {
            return new JsonResponse(['message' => 'L\'e-mail est jetable et n\'est pas accepté.'], 400);
        }         

 

 Ajout d'un commentaire

La route /api/comments gère les requêtes POST pour sauvegarder un commentaire. Voici un exemple simplifié du contrôleur.

#[Route('/api/comments', methods: ['POST'])]
public function addComment(Request $request, EntityManagerInterface $em, PostRepository $postRepo): JsonResponse {
    $data = json_decode($request->getContent(), true);

    // Validation des données
    if (!$data['user'] || !$data['email'] || !$data['comment'] || !$data['postId']) {
        return new JsonResponse(['message' => 'Données manquantes'], 400);
    }

    $post = $postRepo->find($data['postId']);
    if (!$post) {
        return new JsonResponse(['message' => 'Post introuvable'], 404);
    }

    // Création du commentaire
    $comment = new Comment();
    $comment->setUser($data['user'])
            ->setEmail($data['email'])
            ->setText($data['comment'])
            ->setPost($post)
            ->setValidated(false);

    $em->persist($comment);
    $em->flush();

    return new JsonResponse(['message' => 'Commentaire ajouté'], 201);
}

Notification par email avec Mailer de Symfony

Une notification est envoyée à l’administrateur lorsqu’un nouveau commentaire est ajouté.

$email = (new TemplatedEmail())
    ->from('admin@site.com')
    ->to('admin@site.com')
    ->subject('Nouveau commentaire')
    ->htmlTemplate('emails/comment_notification.html.twig')
    ->context([
        'user' => $data['user'],
        'email' => $data['email'],
        'comment' => $data['comment'],
        'postTitle' => $post->getTitle(),
    ]);

$mailer->send($email);

Fonctionnement :

  • L’email est construit avec le template Twig et les données dynamiques (nom, email, commentaire, titre du post).
  • Envoyé via un service de messagerie configuré.

Postez un commentaire !