Archives Wild Code School
Angular

18 - Guard

Quête 14 sur 16

title-book
Introduction

Dans cette quête, tu vas apprendre à restreindre l'accès à certaines parties de ton site. Pour y arriver, tu vas utiliser les Guards Angular.

Objectifs 🤓

✅ Être en mesure de générer et d'intégrer un guard à une application angular ✅ Comprendre les mécanismes de blocage synchrones (basique) grâce au guard ✅ Mettre en place des mécanismes de blocage asynchrones (avancé)

Avant de démarrer, assure-toi d'avoir terminé les quêtes suivantes👇

Définitions

  • Un Guard est une classe qui comporte généralement une seule fonction

  • Un Guard est attaché sur une ou plusieurs URLs de ton site application.

Le résultat retourné par la fonction (basiquement : true ou false) indiquera si le routeur permet ou non la navigation vers l'URL cible.

Pour cette quête, tu peux partir d'un projet vierge ou alors choisir de protéger certaines routes d'un projet existant.

Vérification basique

Génération de guard

Le client Angular va te simplifier une fois encore le travail. Il te suffit de taper ng g guard core/auth pour que la génération du guard commence.

Mais cette fois, le cli angular te demande quel comportement tu souhaites pour ton guard :

Which interfaces would you like to implement ?

Dans ton cas, tu vas rester sur le comportement CanActivate qui permet de valider ou non le chargement d'une page.

Ce menu permet de charger plusieurs comportements à la fois. Une fois le bon comportement choisi, tu peux terminer la génération.

✅ Voici le résultat que tu devrais obtenir dans auth.guard.ts

typescript
import { Injectable } from  '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from  '@angular/router';
import { Observable } from  'rxjs';

@Injectable({
 providedIn: 'root'
})

export class AuthGuard implements CanActivate {
 canActivate(
   next:  ActivatedRouteSnapshot,
   state:  RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
     return  true;
   }
}

Lier une page et un guard

Dans une application Angular, le routeur est l'orchestrateur de la navigation. C'est donc lui qu'il faudra informer de l'autorisation ou non de naviguer vers une page.

Pour l'informer, il faut lier notre route à notre guard. C'est grâce à la propriété canActivate de notre route que nous allons faire cela.

On lui passe un tableau de Guard (car nous pourrions mettre en place différents types de guard. Attention ! Si tu mets plusieurs guards, tous doivent être validés pour accéder à la page).

Par exemple, pour une route secured, tu aurais le code suivant :

typescript
{ path: 'secured', component: SecuredComponent, canActivate: [AuthGuard] },

A partir de maintenant, la méthode canActivate présente dans le AuthGuard sera appelée à chaque fois que l'utilisateur essaie d'accéder à la page secured. Teste cela en mettant un console.log('navigationdetected'); dans la méthode canActivate de ton guard.

👀 Bon. C'est bien gentil, tout ça, mais tu ne sais toujours pas comment empêcher l'accès à une page et donc guarder son entrée.

Empêcher l'accès - Blocage Synchrone

Tu as vu précédemment que ton guard comportait une fonction canActivate qui doit retourner un boolean.

Essaie de tester le fonctionnement du guard en lui faisant retourner false au lieu de true;

Remplace ceci 👇

typescript
canActivate(...):  ... {
 return true;
}

Par cela 👇

typescript
canActivate(...):  ... {
 return false;
}

Et tente d'accéder à la page via son url.

Info

L'accès vers la page en question n'est plus autorisé.

Plutôt que de simplement retourner false et laisser l'utilisateur bloqué sur http://localhost:4200/, tu prendras pour habitude de rediriger l'utilisateur sur une page non sécurisée.

Pour cela, tu connais déjà la méthode :

typescript
canActivate(...):  ... {
 this.router.navigate(['/notsecured']);
 return false;
}

Empêcher l'accès - Blocage Asynchrone

Pour dire si un utilisateur peut accéder à un site, il faut souvent demander l'information au serveur.

On dit qu'on est dans un contexte asynchrone (il y a un délai entre ta demande et ta réponse). Heureusement, pour gérer l'asynchronicité, les observables te sauvent la mise.

Au début de la quête, on disait que la méthode canActivate renvoyait un boolean. Mais pas uniquement : elle peut aussi te renvoyer un Observable.

L'observable pourrait être beaucoup de choses différentes. Un cas assez commun serait la récupération de l'utilisateur en passant un JWT.

Dans le cas de cette quête, tu vas simuler une réponse serveur qui mettrait à jour une propriété de ton user.service.ts

Ton fichier user.service.ts ressemble à ceci :

typescript
import { Observable, of } from  'rxjs';
import { delay } from  'rxjs/operators';

.... 

login(): Observable<boolean>  {
  return of(false).pipe( 
   delay(3000)
 );
}

Ce code effrayant n'est en réalité pas si compliqué.

  • of permet de transformer une valeur synchrone : false en valeur asynchrone

  • delay permet de rajouter un délai de 3000 millisecondes avant d'envoyer la valeur

Pour résumer, tu envoies la valeur false (authentication non ok) avec un délai de 3000 millisecondes sous forme d'observable. Ou en d'autres termes, le cas parfait pour tester ton guard ! 😎

→ Tu peux ensuite injecter ton service dans le constructor (à ajouter) de ton guard et retourner this.userService.login() dans la méthode canActivate.

Tu dois maintenant être redirigé vers localhost:4200 après 3 secondes d'attente. Change la valeur envoyée par l'observable dans la méthode login pour voir.

Challenge 💪

Gérer des rôles 🕵️‍♂️

Tu vas créer 3 pages :

  • Home page (accessible par tout le monde)
  • Dashboard page (accessible par user et admin)
  • Admin page (accessible par les admins uniquement)

2 guards :

  • isConnected
  • isAdmin

Un user service :

  • une propriété privée 'role' qui prendra comme valeur 'ANONYMOUS', 'USER' ou 'ADMIN'
  • une méthode permettant de renvoyer le ‘role’.

Tu devras réfléchir aux conditions à mettre dans tes guards pour valider les règles d'accès aux pages.

Critères de validation ✅

  • Tous les fichiers ont bien été créés
  • Les règles de navigation sont correctement implémentées pour chaque rôle