« JWT SPA symfony exercice pratique » : différence entre les versions

Aucun résumé des modifications
 
(14 versions intermédiaires par le même utilisateur non affichées)
Ligne 1 : Ligne 1 :
Source: ? (perdue, disparue)
Source: ? (perdue, disparue)
== Introduction ==


Chanter JWT pour l'authentification dans un SPA avec Symfony est très facile grâce à des bundles comme FOSUserBundle, LexikJWTAuthenticationBundle et JWTRefreshTokenBundle. Malheureusement, ils ne fournissent pas de sécurité contre les attaques XSS (ce qui, selon vous, n'est pas vraiment là). Dans cet article, je veux montrer comment nous pouvons améliorer une application Symfony en utilisant ces bundles pour empêcher les attaques XSS. Comme il est assez long, je vais le diviser en parties suivantes:
Chanter JWT pour l'authentification dans un SPA avec Symfony est très facile grâce à des bundles comme FOSUserBundle, LexikJWTAuthenticationBundle et JWTRefreshTokenBundle. Malheureusement, ils ne fournissent pas de sécurité contre les attaques XSS (ce qui, selon vous, n'est pas vraiment là). Dans cet article, je veux montrer comment nous pouvons améliorer une application Symfony en utilisant ces bundles pour empêcher les attaques XSS. Comme il est assez long, je vais le diviser en parties suivantes:
Ligne 62 : Ligne 64 :


==== gesdinet_jwt_refresh_token.yaml ====
==== gesdinet_jwt_refresh_token.yaml ====
<pre>
<syntaxhighlight lang="yaml" line>
gesdinet_jwt_refresh_token:
gesdinet_jwt_refresh_token:
     single_use: true
     single_use: true
</pre>
</syntaxhighlight>


Cela rend évidemment le paramètre ttl_update: true inutile.
Cela rend évidemment le paramètre ttl_update: true inutile.
Ligne 75 : Ligne 77 :


==== security.yaml ====
==== security.yaml ====
<pre>
<syntaxhighlight lang="yaml" line>
security:
security:
   firewalls:
   firewalls:
Ligne 83 : Ligne 85 :
         path: /api/logout
         path: /api/logout
         handlers: App\Service\Authentication\LogoutHandler
         handlers: App\Service\Authentication\LogoutHandler
</pre>
</syntaxhighlight>


Dans le gestionnaire, nous injectons simplement la connexion à la base de données et supprimons les jetons. Le référentiel du bundle ne prend pas en charge la suppression de tous les jetons d'actualisation pour un utilisateur, mais vous pouvez également étendre l'implémentation RefreshTokenManager, ajouter une méthode de référentiel et l'injecter ici à la place.
Dans le gestionnaire, nous injectons simplement la connexion à la base de données et supprimons les jetons. Le référentiel du bundle ne prend pas en charge la suppression de tous les jetons d'actualisation pour un utilisateur, mais vous pouvez également étendre l'implémentation RefreshTokenManager, ajouter une méthode de référentiel et l'injecter ici à la place.


==== LogoutHandler ====
==== LogoutHandler ====
<pre>
<syntaxhighlight lang="php" line>
     <?php
     <?php
     declare(strict_types=1);
     declare(strict_types=1);
Ligne 123 : Ligne 125 :
         }
         }
     }
     }
</pre>
</syntaxhighlight>


=== Ajouter un cookie à l'authentification JWT ===
=== Ajouter un cookie à l'authentification JWT ===
Ligne 136 : Ligne 138 :


===== generateSecurityCookieHash =====
===== generateSecurityCookieHash =====
<pre>
<syntaxhighlight lang="php" line>
     private function generateSecurityCookieHash(User $user, RefreshTokenInterface $refreshToken): string
     private function generateSecurityCookieHash(User $user, RefreshTokenInterface $refreshToken): string
     {
     {
Ligne 143 : Ligne 145 :
         );
         );
     }
     }
</pre>
</syntaxhighlight>


Comme nous allons avoir besoin de cela et de quelques méthodes supplémentaires, nous allons créer un service central appelé AuthenticationService.
Comme nous allons avoir besoin de cela et de quelques méthodes supplémentaires, nous allons créer un service central appelé AuthenticationService.
Ligne 150 : Ligne 152 :


===== AuthenticationService =====
===== AuthenticationService =====
<pre>
<syntaxhighlight lang="php" line>
     <?php
     <?php
     declare(strict_types=1);
     declare(strict_types=1);
Ligne 223 : Ligne 225 :
         }
         }
     }
     }
</pre>
</syntaxhighlight>


Nous utilisons la même date d'expiration pour le cookie que pour le jeton d'actualisation et le marquons comme http uniquement.
Nous utilisons la même date d'expiration pour le cookie que pour le jeton d'actualisation et le marquons comme http uniquement.
Ligne 232 : Ligne 234 :


===== security.yaml =====
===== security.yaml =====
<pre>
<syntaxhighlight lang="yaml" line>
security:
security:
   firewalls:
   firewalls:
Ligne 240 : Ligne 242 :
         check_path: /api/login
         check_path: /api/login
         success_handler: App\Service\Authentication\LoginSuccessHandler
         success_handler: App\Service\Authentication\LoginSuccessHandler
</pre>
</syntaxhighlight>


===== LoginSuccessHandler =====
===== LoginSuccessHandler =====
<pre>
<syntaxhighlight lang="php" line>
    <?php
<?php
     declare(strict_types=1);
     declare(strict_types=1);
     namespace App\Service\Authentication;
     namespace App\Service\Authentication;
Ligne 290 : Ligne 292 :
         }
         }
     }
     }
</pre>
</syntaxhighlight>


Nous allons maintenant obtenir un cookie de sécurité que nous vous enverrons à chaque demande et qui n'est pas accessible par Javascript. Mais ce n'est pas évalué pour le moment, alors faisons-le.
Nous allons maintenant obtenir un cookie de sécurité que nous vous enverrons à chaque demande et qui n'est pas accessible par Javascript. Mais ce n'est pas évalué pour le moment, alors faisons-le.
Ligne 299 : Ligne 301 :


===== security.yaml =====
===== security.yaml =====
<pre>
<syntaxhighlight lang="yaml" line>
security:
security:
   firewalls:
   firewalls:
Ligne 309 : Ligne 311 :
         authenticators:
         authenticators:
           - App\Service\Authentication\JWTAndSecurityCookieAuthenticator
           - App\Service\Authentication\JWTAndSecurityCookieAuthenticator
</pre>
</syntaxhighlight>


===== JWTAndSecurityCookieAuthenticator =====
===== JWTAndSecurityCookieAuthenticator =====
<pre>  
<syntaxhighlight lang="php" line>
    <?php
<?php
     declare(strict_types=1);
     declare(strict_types=1);
     namespace App\Service\Authentication;
     namespace App\Service\Authentication;
Ligne 361 : Ligne 363 :
         }
         }
     }
     }
</pre>
</syntaxhighlight>




Ligne 374 : Ligne 376 :


===== routes.yaml =====
===== routes.yaml =====
<pre>
<syntaxhighlight lang="yaml" line>
api_refresh_token:
api_refresh_token:
     path: '/api/token/refresh'
     path: '/api/token/refresh'
     defaults: { _controller: gesdinet.jwtrefreshtoken:refresh }
     defaults: { _controller: gesdinet.jwtrefreshtoken:refresh }
     methods: [POST]  
     methods: [POST]  
</pre>
</syntaxhighlight>


*Nous utilisons le suivant
*Nous utilisons le suivant


<pre>
<syntaxhighlight lang="yaml" line>
api_refresh_token:
api_refresh_token:
     path: '/api/token/refresh'
     path: '/api/token/refresh'
     defaults: { _controller: App\Service\Authentication\RefreshTokenSecurityCookieService:refresh }
     defaults: { _controller: App\Service\Authentication\RefreshTokenSecurityCookieService:refresh }
     methods: [POST]
     methods: [POST]
</pre>
</syntaxhighlight>


La copie complète est annotée avec les espaces où je place la validation du cookie et la création du cookie après l'ajout du nouveau jeton d'actualisation.
La copie complète est annotée avec les espaces où je place la validation du cookie et la création du cookie après l'ajout du nouveau jeton d'actualisation.


===== RefreshTokenSecurityCookieService =====
===== RefreshTokenSecurityCookieService =====
<pre>
<syntaxhighlight lang="php" line>
    <?php
<?php
     declare(strict_types=1);
     declare(strict_types=1);
     namespace App\Service\Authentication;
     namespace App\Service\Authentication;
Ligne 534 : Ligne 536 :
         }
         }
     }
     }
</pre>
</syntaxhighlight>


De cette façon, lorsqu'un nouveau JWT est demandé, le jeton d'actualisation et le cookie sont validés. Un nouveau cookie est également émis ici, car chaque fois que le JWT est demandé, un nouveau jeton d'actualisation est émis et l'ancien cookie a donc été invalidé.
De cette façon, lorsqu'un nouveau JWT est demandé, le jeton d'actualisation et le cookie sont validés. Un nouveau cookie est également émis ici, car chaque fois que le JWT est demandé, un nouveau jeton d'actualisation est émis et l'ancien cookie a donc été invalidé.
[[Category:Symfony]] [[Category:JWT]]