Vous êtes sur la page 1sur 56

Programmation back-end &

front-end
ReactJS/NodeJS : authentification et sécurité

Alexandre TELLE // Master MIAGE SITN // Novembre 2023


Plan du cours
ReactJS/NodeJS : connexion à la base de données

1. Correction de l’exercice Notes App


2. Sécurité
1. Introduction
2. Attaque CSRF
3. Injection XSS

3. Authentification
1. Les différentes méthodes d’authentification
2. Se connecter avec NextJS
3. Exemple

4. Récapitulatif
5. Présentation du projet

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 2


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Nous avons des notes dans notre base de données, créées lors de nos tests avec Postman. Essayons de les afficher en premier lieu. Dans
notre composant List.js, nous allons supprimer les données en dur et rajouter l’import suivant :
import { useState, useEffect } from 'react'

Puis avant notre fonction return() nous allons rajouter :

const [notes, setNotes] = useState([])

useEffect(() => {
fetch('http://localhost:3001/notes', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then((res) => res.json())
.then((data) => {
setNotes(data)
})
}, [])

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 3


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

La méthode useEffect() va appeler la route à chaque fois que l’on va accéder au composant, et récupérer avec fetch() les
éléments depuis l’API.

Juste en rafraîchissant la page, nous pouvons voir le résultat :

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 4


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

La date n’apparaît plus : c’est normal, le nom du champ est différent dans la base de données. Dans le fichier Note.js nous allons
remplacer la balise time existante par celle-ci :

<time dateTime={note.createdAt} className="flex-none text-xs text-gray-500">


{note.createdAt.substring(0, 10)}
</time>

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 5


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Maintenant procédons à la création d’une note. Cette fois-ci nous n’utiliserons pas useEffect(). Pourquoi ? Car nous voulons juste déclencher la
route lors du click sur le bouton. Dans AddForm.js, Nous allons ajouter ces imports :
import { useState } from ‘react'
import { useRouter } from "next/navigation";

Et une nouvelle fois, ajouter les états qui nous intéressent : le but ici est de sauvegarder les inputs de l’utilisateur, tant qu’il n’a pas soumis son
formulaire, et de gérer plus facilement les changements. Nous allons également initialiser le routeur qui nous servira pour la suite.

const router = useRouter();


const [title, setTitle] = useState('')
const [note, setNote] = useState('')
const [tag, setTag] = useState('')

Maintenant nous allons modifier notre JSX et mettre à jour les lignes suivantes :
<form onSubmit={submitForm}>

Ainsi que pour tous les inputs définis, les mettre à jour avec une valeur (value) et une fonction onChange() (voir slides suivantes).

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 6


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Input title :

<input
type="text"
name="title"
id="title"
className="XXX"
onChange={(e) => setTitle(e.target.value)}
value={title}
/>

Input tag :

<input
type="text"
name="tag"
id="tag"
className="XXX"
onChange={(e) => setTag(e.target.value)}
value={tag}
/>

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 7


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Textarea note :

<textarea
id="note"
name="note"
rows={3}
className="XXX"
onChange={(e) => setNote(e.target.value)}
value={note}
/>

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 8


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Maintenant, nous allons créer la fonction submitForm() qui fera l’appel vers l’API une fois que le bouton aura été appuyé :

const submitForm = async(e) => {


e.preventDefault();

const response = await fetch('http://localhost:3001/notes', {


method: 'POST',
body: JSON.stringify({
title: title,
note: note,
tag: tag
}),
headers: {
'Content-Type': 'application/json'
}
});

const data = await response.json()


.then(() => {
alert("Note ajoutée !");
router.refresh();
})
.catch((err) => { alert("Erreur lors de l'ajout.") });

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 9


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Si on teste maintenant, notre note est bien ajoutée à notre liste :

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 10


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Nous allons maintenant rajouter de quoi modifier et supprimer une note. En dessous de notre note, nous allons rajouter des actions qui
vont nous permettre de le faire. Cela devrait ressembler à quelque chose comme ça :

Pour ceux qui utilisent Tailwind, rajoutez le code juste en dessous du paragraphe:
<div className="mt-2">
<span className="text-sm cursor-pointer">Modifier</span>
<span className="text-sm ml-3 text-red-500 cursor-pointer">Supprimer</span>
</div>

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 11


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Nous allons commencer par implémenter la suppression. Toujours dans Note.js, nous allons ajouter les imports suivants :
import { useRouter } from « next/navigation";

Et initialiser le routeur comme d’habitude : const router = useRouter();

Sur notre span « Supprimer » nous allons ajouter une fonction onClick() :
<span onClick={() => deleteNote(note.id)} className="XXX">Supprimer</span>

Maintenant définissons cette fameuse fonction deleteNote() :

const deleteNote = async(noteId) => {


const response = await fetch('http://localhost:3001/notes/' + noteId, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
}
}).then((response) => {
if(response.ok){
alert("Note supprimée !");
router.refresh();
}
}).catch((error) => {
alert("Erreur lors de la suppression.")
});
}

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 12


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Pour la modification, nous allons procéder autrement. La solution facile serait de créer un deuxième panel EditForm.js et de l’appeler lorsque l’on clique
sur le bouton. La solution optimale serait de réutiliser AddForm.js (et potentiellement le renommer Form.js) et de gérer les états de façon plus granulaire
depuis index.js. C’est ce que nous allons faire.

Dans index.js nous allons rajouter deux états qui nous permettront de traquer si le formulaire est appelé pour ajouter/modifier et aussi centraliser la note à
ajouter/modifier :

const [isUpdate, setIsUpdate] = useState(false)


const [currentNote, setCurrentNote] = useState({
id: '',
title: '',
note: '',
tag: '',
});

Et nous allons mettre à jour l’appel de nos composant Panel, List et ListHeader :

<ListHeader setOpen={setOpen} setIsUpdate={setIsUpdate} setCurrentNote={setCurrentNote} />

<List setOpen={setOpen} setIsUpdate={setIsUpdate} setCurrentNote={setCurrentNote} />

<Panel
open={open}
setOpen={setOpen}
isUpdate={isUpdate}
currentNote={currentNote}
setCurrentNote={setCurrentNote}
/>
Alexandre TELLE // Master MIAGE SITN // Novembre 2023 13
1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Et leurs appels respectifs dans les fichiers Panel.js, ListHeader.js et List.js :


export default function ListHeader({ setOpen, setIsUpdate, setCurrentNote }) {

export default function List({ setOpen, setIsUpdate, setCurrentNote }) {

export default function Panel({ open, setOpen, isUpdate, currentNote, setCurrentNote }) {

Dans ListHeader.js nous allons modifier la méthode onClick() :

onClick={() => {
setOpen((open) => !open);
setIsUpdate(false);
setCurrentNote({
id: '',
title: '',
note: '',
tag: '',
});
}}

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 14


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Dans List.js nous allons modifier l’appel du composant Note :

<Note
key={note.id}
note={note}
open={open}
setOpen={setOpen}
setIsUpdate={setIsUpdate}
setCurrentNote={setCurrentNote}
/>

Pour lequel il ne faudra pas oublier de mettre à jour la définition :


export default function Note({note, setOpen, setIsUpdate, setCurrentNote }) {

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 15


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Et modifier le span « Modifier » :

<span
className="text-sm cursor-pointer"
onClick={() => {
setOpen((open) => !open);
setIsUpdate(true);
setCurrentNote({
id: note.id,
title: note.title,
note: note.note,
tag: note.tag,
});
}}
>
Modifier
</span>

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 16


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Maintenant dernière étape : AddForm.js. On y supprime les états définis auparavant et nous adaptons la définition du composant :

import { useRouter } from "next/navigation";

export default function Form({ currentNote, setCurrentNote, isUpdate }) {


const router = useRouter();

Ensuite nous adaptons la méthode submitForm().

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 17


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

const submitForm = async(e) => {


e.preventDefault();
let fetchMethod = 'POST';
let url = 'http://localhost:3001/notes';

if(isUpdate){
fetchMethod = 'PUT';
url = 'http://localhost:3001/notes/' + currentNote.id;
}

const response = await fetch(url, {


method: fetchMethod,
body: JSON.stringify({
title: currentNote.title,
note: currentNote.note,
tag: currentNote.tag
}),
headers: {
'Content-Type': 'application/json',
}
});

const data = await response.json()


.then(() => {
if(isUpdate){
alert("Note modifiée !");
}
else {
alert("Note ajoutée !");
}
router.refresh();
})
.catch((err) => { alert("Erreur lors de l'ajout/modification. Essayez de vous réauthentifier.") });

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 18


1. Correction de l’exercice Notes app
Connecter l’API à son frontend

Nous adaptons les onChange() et value de nos inputs. Ci-dessous l’exemple avec title.

onChange={(e) => {
setCurrentNote({
...currentNote,
title: e.target.value,
});
}}
value={currentNote.title}

Et enfin dans Panel.js, nous modifions l’appel du composant AddForm :


<AddForm
currentNote={currentNote}
setCurrentNote={setCurrentNote}
isUpdate={isUpdate}
/>

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 19


2. Sécurité
Introduction

La sécurité des applications web désigne l’ensemble des pratiques consistant à protéger les sites web, les applications et les API contre les
attaques. Il s'agit d'une vaste discipline, mais ses objectifs ultimes sont de maintenir le bon fonctionnement des applications web et de
protéger les entreprises du cyber vandalisme, du vol de données, de la concurrence déloyale et d'autres conséquences négatives.

Les applications web peuvent être confrontées à plusieurs types d'attaques en fonction des objectifs de l'attaquant, de la nature du travail
de l'organisation ciblée et des lacunes de sécurité particulières de l'application. Les types d'attaques les plus courants sont les suivants :

• Vulnérabilités de type "zero day" : Il s'agit de vulnérabilités inconnues des fabricants d'une application, et qui n'ont donc pas de
correctif disponible. Les hackers cherchent à les exploiter le plus rapidement possible.
• Injection XSS
• Injection SQL
• Attaque DDoS
• Attaque CSRF
• Page scraping : Il s’agit d’utiliser des robots pour "aspirer" le contenu de pages web à grande échelle et ensuite utiliser ce contenu
pour obtenir un avantage tarifaire sur un concurrent, imiter le site à des fins malveillantes, ou pour d'autres raisons.
• Abus d’API : Les attaquants cherchent à envoyer un code malveillant dans l'une des applications ou d'intercepter des données sensibles
lorsqu'elles passent d'une application à l’autre via l’API.
• Credential stuf ng : Les attaquants peuvent utiliser des robots pour saisir rapidement un grand nombre de combinaisons de noms
d'utilisateur et de mots de passe volés dans le portail de connexion d'une application Web.
Alexandre TELLE // Master MIAGE SITN // Novembre 2023 20
fi
2. Sécurité
Attaque CSRF

CSRF ou Cross-Site Request Forgery (falsification de requêtes entre sites). Cette attaque a pour objectif de piéger une victime a n de
l'amener à effectuer une requête utilisant ses identi ants ou ses autorisations. L'exploitation des privilèges de compte d'un utilisateur
permet à un acteur malveillant d'envoyer une requête en se faisant passer pour ce dernier. Une fois le compte d'un utilisateur compromis,
le pirate peut exfiltrer, détruire ou modifier les informations importantes qu'il recèle. Les comptes aux privilèges les plus élevés, comme
ceux des administrateurs ou des cadres, sont fréquemment pris pour cible.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 21


fi
fi
2. Sécurité
Attaque CSRF

Afin de se prémunir au mieux de ce genre d’attaques, voici quelques mesures qui peuvent être prises :
• Utiliser des tokens CSRF : Un jeton CSRF est une valeur unique, secrète et imprévisible générée par l'application côté serveur et
partagée avec le client. Lorsqu'il tente d'effectuer une action sensible, telle que l'envoi d'un formulaire, le client doit inclure le jeton
CSRF correct dans sa demande. Il est ainsi très difficile pour un pirate de construire une requête valide au nom de la victime.

• Utilisation de cookies SameSite : SameSite est un mécanisme de sécurité du navigateur qui détermine quand les cookies d'un site
web sont inclus dans des requêtes provenant d'autres sites web. Comme les demandes d'exécution d'actions sensibles nécessitent
généralement un cookie de session authentifié, les restrictions SameSite appropriées peuvent empêcher un attaquant de déclencher
ces actions intersites. Depuis 2021, Chrome applique les restrictions SameSite par défaut.

• Validation basée sur le header Referer : Certaines applications utilisent l'en-tête HTTP Referer pour tenter de se défendre contre les
attaques CSRF, normalement en vérifiant que la demande provient du propre domaine de l'application. Cette méthode est
généralement moins efficace que la validation du jeton CSRF. Pour rappel, l'en-tête de requête Referer contient l'adresse de la page
web précédente à partir de laquelle un lien a été suivi pour demander la page courante. L’en-tête Referer permet aux serveurs
d'identifier la provenance des visiteurs d'une page et cette information peut être utilisée à des fins d'analyse, de journalisation ou pour
améliorer la politique de cache par exemple.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 22


2. Sécurité
Attaque CSRF

Sans jeton CSRF :


• Supposons que vous soyez actuellement connecté à votre banque en ligne à l'adresse www.mybank.com.
• Supposons qu'un transfert d'argent à partir de mybank.com se traduise par une demande de la forme http://www.mybank.com/
transfer?to=<Certains numéros de compte>;montant=<Certains montants>. (Votre numéro de compte n'est pas
nécessaire, car il est implicite dans votre login).
• Vous visitez www.cute-cat-pictures.org, sans savoir qu'il s'agit d'un site malveillant.
• Si le propriétaire de ce site connaît la forme de la demande ci-dessus (facile) et devine correctement que vous êtes connecté à
mybank.com (il faut un peu de chance là), il peut inclure sur sa page une demande du type http://www.mybank.com/transfer?
to=123456;amount=10000 (où 123456 est le numéro de son compte aux îles Caïmans et 10000 la somme à transférer).
• Vous avez récupéré cette page www.cute-cat-pictures.org, et votre navigateur va donc faire cette demande.
• Votre banque ne peut pas reconnaître l'origine de la demande : Votre navigateur web enverra la demande avec votre cookie
www.mybank.com et cela semblera parfaitement légitime. Votre argent a disparu !

Avec jeton CSRF :


• La demande de transfert est complétée par un troisième argument : http://www.mybank.com/transfer?
to=123456;amount=10000;token=31415926535897932384626433832795028841971.
• Ce jeton est un nombre aléatoire énorme, impossible à deviner, que mybank.com inclura sur sa propre page web lorsqu'elle vous la
servira. Il est différent à chaque fois que mybank.com transmet une page à quelqu'un.
• L'attaquant n'est pas en mesure de deviner le jeton, ni de convaincre votre navigateur web de le rendre, et donc ne sera pas en mesure
de créer une requête valide, car les requêtes avec le mauvais jeton (ou sans jeton) seront refusées par www.mybank.com.
Alexandre TELLE // Master MIAGE SITN // Novembre 2023 23
2. Sécurité
Injection XSS

XSS ou Cross-Site Scripting : cette faille de sécurité permet à un pirate d'injecter des scripts au sein d'une page web, côté client, dans
le but d'accéder directement à des informations importantes, d'usurper l'identité des utilisateurs ou de piéger ces derniers afin de les
amener à révéler des informations importantes.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 24


2. Sécurité
Injection XSS

Les attaques de script intersites en JavaScript sont populaires parce que JavaScript a accès à certaines données sensibles qui peuvent
être utilisées pour usurper l'identité et à d'autres fins malveillantes. Par exemple, JavaScript a accès à des cookies, et un pirate pourrait
utiliser une attaque XSS pour voler les cookies d'un utilisateur et se faire passer pour lui en ligne. JavaScript peut également créer des
requêtes HTTP, qui peuvent être utilisées pour renvoyer des données (telles que des cookies volés) au pirate. En outre, le JavaScript côté
client peut également aider un pirate à accéder à des API qui contiennent des coordonnées de géolocalisation, des données de webcam
et d'autres informations sensibles.

Pour éviter le Cross-Site Scripting, il n’y a pas de stratégie unique, mais un ensemble de mesures de protection :
• Éviter le HTML en input : empêcher les utilisateurs de poster du HTML dans les données des formulaires.
• Validation des inputs : La validation consiste à mettre en œuvre des règles qui empêchent un utilisateur de poster des données dans
une forme qui ne répond pas à certains critères.
• Assainissement des données : examiner et supprimer les données indésirables, telles que les balises HTML jugées dangereuses.
Conservez les données sûres et supprimez les caractères dangereux. L'assainissement des données est similaire à la validation, mais il a
lieu après que les données ont déjà été publiées sur le serveur web, mais avant qu'elles ne soient affichées à un autre utilisateur.
• Sécurité des cookies : Les applications web peuvent également définir des règles spéciales pour la gestion des cookies, ce qui peut
limiter le vol de cookies par des attaques de Cross-Site Scripting.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 25


2. Sécurité
Authenti cation et autorisation

La sécurité passe aussi par le fait de pouvoir reconnaître les personnes qui utilisent les services que l’on fournit. Pour pouvoir le faire, il y a
deux concepts essentiels :

• L’authenti cation (authentication) : L'authentification est le processus de confirmation de l'identité des utilisateurs. Ce processus
empêche l'accès illégal/non autorisé à un fichier sécurisé et prévient également le vol de données.

• L’autorisation (authorization) : L'autorisation est une procédure de sécurité qui contrôle/limite l'accès d'un utilisateur à une entité
sécurisée particulière. Ce concept de sécurité concerne le nombre de privilèges d'accès dont dispose un utilisateur pour pouvoir
accéder à un fichier sécurisé.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 26


fi
fi
2. Sécurité
Authenti cation et autorisation

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 27


fi
3. Authentification
Les différentes méthodes

Lorsque l’on parle d’authentification, on peut penser à plusieurs types de méthodes, qui varient en fonction des systèmes utilisés :
• Token authentication
• Password authentication
• Session authentication
• Biometric authentication
• Multi-factor authentication
• Certificate-based authentication
• Identification Authentication methods
• API authentication methods
• User authentication methods
• Vault authentication methods
• Wireless authentication methods
• Email authentication methods
• Database authentication methods
• Et bien d’autres…

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 28


3. Authentification
Les différentes méthodes : Session-based (cookies)

Les sessions sont généralement utilisés pour gérer l'authentification des utilisateurs dans les applications web. Voici un diagramme qui
illustre ce fonctionnement :

Comme vous pouvez le voir ici, le navigateur du client envoie


au serveur la demande POST d'informations d'identification.

Le serveur vérifie alors les informations d'identification qui lui


ont été envoyées avec le code d'état HTTP 200 OK.

Il crée un identifiant de session stocké sur le serveur et le


renvoie au client via Set-Cookie : session=.....

Lors des demandes suivantes, l'identifiant de session du


cookie est vérifié dans le serveur et la demande
correspondante est traitée.

Lorsque vous vous déconnectez de l'application, votre


identifiant de session est effacé du client et du serveur.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 29


3. Authentification
Les différentes méthodes : Session-based (cookies)

L’utilisation de cookies peut représenter quelques inconvénients. Par défaut, l'authentification basée sur les cookies ne dispose pas d'une
protection solide contre les attaques, et elle est principalement vulnérable aux attaques de type cross-site scripting (XSS) et cross-site
request forgery (CSRF).

Mais il est possible de modifier explicitement les en-têtes des cookies pour les protéger contre ces attaques. Par exemple, les cookies
peuvent être facilement protégés contre les attaques XSS en utilisant l'attribut HttpOnly lors de la définition des en-têtes de cookies.

De plus, les cookies ne sont pas forcément la meilleure façon pour gérer l’authentification pour une API (sauf si le client est un web
browser, du coup une application React/Vue/Angular/etc.) ou une application mobile.

Par ailleurs, vu que le cookie est émis par le serveur, si le nombre d’utilisateurs augmente, il faudra sauvegarder les sessions pour chacun
de ces utilisateurs. Il y aura une sujet en terme de scalabilité à gérer.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 30


3. Authentification
Les différentes méthodes : Token-based

L'authentification par jeton a été introduite pour remédier à plusieurs lacunes de l'approche basée sur les cookies. Contrairement aux
cookies, l'approche basée sur les jetons nécessite une mise en œuvre manuelle et les jetons sont sauvegardés du côté du client.

Lorsque vous vous connectez à une application web, le serveur vérifie vos informations d'identification et envoie un jeton crypté au
navigateur. Le navigateur stocke ensuite ce jeton, qui peut être ajouté à l'en-tête d'autorisation des futures demandes.

Lorsque les informations d'identification sont reçues du


navigateur du client, le serveur les valide et génère également
un JWT signé contenant toutes les informations relatives à
l'utilisateur.

Le jeton est sans état, il n'est donc jamais stocké sur le


serveur. Au cours des requêtes suivantes, le jeton est transmis
au serveur, puis décodé afin d'être vérifié sur le serveur.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 31


3. Authentification
Les différentes méthodes : Token-based

En termes d’authentification basée sur les jetons, les JSON Web Token (JWT) sont les plus utilisés. Un jeton JWT est composé de 3
parties : l’en-tête (qui contient le type de chiffrement par exemple), le payload (les informations à transmettre) et la signature (en base64
qui aide à valider le token et le décrypter)

Bien que les jetons tentent de résoudre les problèmes de sécurité liés aux cookies, ils ne sont pas totalement à l'épreuve des balles.

Les jetons enregistrés dans le navigateur peuvent être vulnérables aux attaques XSS si votre application permet à des scripts JavaScript
externes d'être intégrés dans votre application.

En outre, comme le jeton est sans état (stateless), s'il est exposé à l’extérieur de votre application, il n'y a aucun moyen de le révoquer
avant qu'il n'expire. Il est donc essentiel que le jeton soit le plus petit possible. Pas mal de services d'identité utiliser 5 minutes par défaut
pour les jetons JWT, ou des jetons de refresh.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 32


3. Authentification
Les différentes méthodes : 3rd Party-based (OAuth)

OAuth est un protocole ouvert qui permet des méthodes d'authentification sécurisées à partir d'applications web, mobiles et de bureau.
Ce protocole permet de s'authentifier auprès du serveur en tant qu’utilisateur.

Typiquement, le Social Login se repose sur OAuth. Lorsque vous vous connectez sur un site avec votre compte Facebook, Google,
LinkedIn, Reddit ou Apple, vous utilisez OAuth sous le capot.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 33


3. Authentification
Se connecter avec NextJS

Nous avons vu la théorie autour de l’authentification et la sécurité d’une application web. L’authenti cation est quelque chose de
compliqué à mettre en place : il y a pas mal de paramètres et de comportements à prendre en compte, et même si on peut la mettre en
place de façon simple en comparant juste un nom d’utilisateur et un mot de passe depuis une base de données, c’est le moyen le plus
facile de rendre son application vulnérable.

Heureusement pour nous, il existe des librairies/middlewares qui nous facilitent la vie et implémentent pas mai de comportements
nécessaires à un bon processus d’authentification :
• Avec NextJS nous avons NextAuth
• Il y a également Passport.js qui est une librairie très connue (certainement la plus populaire dans le monde du JS)
• Il y en a d’autres encore, notamment des entreprises privées telles que Auth0, Clerk, Firebase etc.

La majorité de ses solutions permettent de gérer les authentification via Token, Cookie ou encore Social Media. Pour ce cours, nous allons
utiliser Passport.js, ce qui vous permettra de pouvoir le réutiliser avec d’autres frameworks.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 34

fi
3. Authentification
Se connecter avec NextJS

Passport.js offre plusieurs stratégies. Si nous nous rendons sur le site officiel, nous pouvons voir une liste exhaustive de 539 stratégies
d’authentification :

Nous allons utiliser la stratégie passport-jwt.


Alexandre TELLE // Master MIAGE SITN // Novembre 2023 35
3. Authentification
Exemple

Reprenons notre application de notes. Allons dans notre API Express.

Dans le dossier routes, nous allons créer un nouveau fichier nommé auth.route.js. Dans celui-ci nous allons ajouter ce code :

import express from 'express';

// Initialize the router


const routerAuth = express.Router();

// Routes
routerAuth.post('/login', login);
routerAuth.post(‘/signup', signup);

export default routerAuth;

// Functions
function login(req, res) {
res.json({ "message" : "Route en construction" });
}

function signup(req, res) {


res.json({ "message" : "Route en construction" });
}

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 36


3. Authentification
Exemple

Dans notre fichier index.js, nous allons rajouter les deux lignes suivantes afin de faire appel à notre nouveau routeur :

import routerAuth from './src/routes/auth.route.js';

et

app.use('/auth', routerAuth);

Rajoutez les à côté des lignes déjà créées pour le routeur des notes.

Maintenant nous allons installer Passport.js :

npm install passport


npm install passport-jwt
npm install jsonwebtoken

Ici nous installons le package et également la stratégie JWT qui est la stratégie permettant de s’authentifier avec un token.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 37


3. Authentification
Exemple

Maintenant créons notre modèle User. Dans le dossier models, ajoutons un fichier user.model.js :

import sequelize from "../../db.js";


import { DataTypes } from "sequelize";

const User = sequelize.define('User', {


username: {
type: DataTypes.STRING
},
password: {
type: DataTypes.STRING
}
});

export default User;

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 38


3. Authentification
Exemple

Maintenant il est heure de créer notre stratégie d’authentification avec Passport. Dans notre fichier index.js, nous allons ajouter les
imports suivants :

import passport from 'passport';


import { Strategy as JwtStrategy, ExtractJwt } from ‘passport-jwt';

import User from ‘./src/models/user.model.js’;

On en profite pour retirer l’option { force : true } du code de synchronisation des modèles :

// Synchronize all models


*/
sequelize.sync({ force: true }).then(() => {
console.log('Database & tables created.');
});

Cela permettra de ne plus supprimer le contenu de la base à chaque fois que l’API est lancée.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 39


3. Authentification
Exemple

Ensuite, au niveau des appels middlewares (là où se trouvent les app.use() ), nous allons en rajouter un nouveau (juste après
app.use(cors()); par exemple) pour notre session/authentification :

// Initialize Passport JWT Strategy for authentication


passport.use(
new JwtStrategy(
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'Mi@GE2023',
},
(payload, done) => {
User.findByPk(payload.id)
.then((user) => {
if(user) {
return done(null, user);
}
return done(null, false);
})
.catch((err) => {
console.log('Error in finding user by ID in JWT.');
});
}
)
);

app.use(passport.initialize());
Alexandre TELLE // Master MIAGE SITN // Novembre 2023 40
3. Authentification
Exemple

Globalement, le bout de code précédent indique qu’à chaque fois que notre API recevra un JWT, on vérifiera le token afin de voir si un
utilisateur y existe vraiment.

Dans un premier temps on définit nos options (la méthode d’extraction du JWT et le secret pour la signature).

Ensuite on analyse le payload afin d’y extraire l’user s’il existe, et renvoyer les erreurs adéquates dans le cas contraire.

La fonction done() est un callback. Si on regarde sa définition, on peut comprendre un peu mieux comment elle fonctionne :

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 41


3. Authentification
Exemple

Parfait ! Maintenant il est l’heure d’intégrer notre stratégie dans nos routes. Dans notre fichier auth.route.js, nous allons rajouter les
imports suivants :
import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';

import User from '../models/user.model.js';

Bcrypt est une librairie de cryptographie qui va nous permettre de hasher nos mots de passe. Installez-la avec la commande suivante :
npm install bcryptjs

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 42


3. Authentification
Exemple

Nous allons modifier la fonction login() :


function login(req, res) {
const { username, password } = req.body;
if (username && password) {
User.findOne({ where: { username: username } })
.then(async (user) => {
if(!user){
res.status(401).json({ message: 'Incorrect username or password.' });
return;
}

if (await bcrypt.compare(password, user.password)) {


let payload = { id: user.id };
let token = jwt.sign(payload, 'Mi@GE2023');
res.json({ message: 'ok', token: token });
return;
}
else {
res.status(401).json({ message: 'Incorrect username or password.' });
return;
}
})
.catch(() => res.status(401).json({ message: 'Incorrect username or password.' }));
}
}

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 43


3. Authentification
Exemple

Ensuite la fonction signup() :

function signup(req, res) {


// Hash the password and create user in DB
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(req.body.password, salt, function(err, hash) {
User.create({
username: req.body.username,
password: hash
}).then(note => res.status(201).json(note));
});
});
}

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 44


3. Authentification
Exemple

Nous pouvons essayer nos nouvelles routes sur Postman. D’abord, enregistrons un nouvel utilisateur :

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 45


3. Authentification
Exemple

Essayons de nous logger avec celui-ci :

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 46


3. Authentification
Exemple

Maintenant avec un utilisateur qui n’existe pas :

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 47


3. Authentification
Exemple

Maintenant que l’authentification fonctionne… Comment s’assurer que nos routes soient protégées par un JWT ?

Rien de plus simple. De retour dans index.js, nous allons modifier l’appel du routeur routerNote :

app.use('/notes', passport.authenticate('jwt', { session: false }), routerNote);

Maintenant à chaque fois que nous allons appeler une route /notes, il faudra y renseigner un token pour accéder au résultat. Dans
Postman, on peut le faire depuis l’onglet Authorization :

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 48


3. Authentification
Exemple

Maintenant nous allons repasser du côté de notre application NextJS, afin d’y rajouter une page de login. Dans le dossier pages, créer
un nouveau fichier nommé login.js. Nous allons y mettre le code suivant :

export default function Login() {


return (
<>
<h1>Se connecter</h1>
<form onSubmit={submitForm}>
<section>
<label for="username">Username</label>
<input id="username" name="username" type="text" required />
</section>
<section>
<label for="password">Password</label>
<input id="password" name="password" type="password" required />
</section>
<button type="submit">Se connecter</button>
</form>
</>
)
}

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 49


3. Authentification
Exemple

Nous allons installer un package afin de stocker notre token dans un cookie côté client :
npm install cookies-next

Nous allons maintenant lier notre formulaire à notre API. Toujours dans notre fichier login.js, nous allons rajouter les imports suivants :

import { useState } from 'react'


import { useRouter } from "next/navigation"
import { setCookie } from 'cookies-next'

Ensuite juste avant notre fonction return(), nous allons initialiser les variables que l’on utilisera. Nous allons stocker l’username et le
mot de passe dans un state :

const router = useRouter();


const [username, setUsername] = useState('')
const [password, setPassword] = useState('')

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 50


3. Authentification
Exemple

Ensuite, sur le même principe que la soumission du formulaire pour la création d’une note, nous allons soumettre un formulaire pour la connexion :

const submitForm = async(e) => {


e.preventDefault();

const response = await fetch('http://localhost:3001/auth/login', {


method: 'POST',
body: JSON.stringify({
username: username,
password: password
}),
headers: {
'Content-Type': 'application/json'
}
}).then((response) => {
if(response.ok){
response.json().then((data) => {
setCookie('authToken', data.token);
router.push("/");
});
}
else {
alert("Username ou mot de passe incorrect.");
}
}).catch((error) => {
alert(error);
});
}
Alexandre TELLE // Master MIAGE SITN // Novembre 2023 51
3. Authentification
Exemple

Nous allons maintenant modifier notre formulaire en conséquence :

return (
<>
<h1>Se connecter</h1>
<form onSubmit={submitForm}>
<section>
<label for="username">Username</label>
<input
id="username"
name="username"
type="text"
required
onChange={(e) => {
setUsername(e.target.value);
}}
value={username}
/>
</section>
<section>
<label for="password">Password</label>
<input
id="password"
name="password"
type="password"
required
onChange={(e) => {
setPassword(e.target.value);
}}
value={password}
/>
</section>
<button type="submit">Se connecter</button>
</form>
</>
)
Alexandre TELLE // Master MIAGE SITN // Novembre 2023 52
3. Authentification
Exemple

Nous avions créé précédemment les routes permettant de modifier et supprimer des notes… Mais maintenant elles sont protégées par
un JWT. Comment fait-on maintenant pour les appeler ?

Nous allons réutiliser le cookie qui a été défini lors du login. Ainsi, à chaque fois que l’ont appelle fetch(), nous allons rajouter dans le
header notre token :

headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${getCookie('authToken')}`
}

Bien évidemment, ne pas oublier d’importer getCookie() :

import { getCookie } from 'cookies-next'

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 53


3. Récapitulatif
Les points importants du cours

Lors de ce cours nous avons vu plusieurs notions, exemples et technologies. Ce qu’il faut retenir, pour maintenant et pour la suite :

• Il y a plusieurs frameworks que ce soit pour le frontend ou le backend : essayez les le plus possible afin de vous faire un avis
• Faire du style c’est compliqué, heureusement il y a des frameworks comme Tailwind et Bootstrap qui facilitent la vie. Revoyez les
bases du CSS, ça rendra leur utilisation plus facile
• Les frameworks frontend tournent autour du concept de composants
• Les nouveautés du JavaScript après ES6 sont essentielles dans la compréhension et l’utilisation des nouvelles technologies web
• Le développement d’une API est intimement lié à son modèle de données : de préférence réfléchir à ce dernier avant de se lancer
dans la conception de l’API
• Utiliser un ORM pour gérer sa base de données est une pratique répandue et permet d’accélérer le développement de son application
• La sécurité est un volet important : essayer de tout faire tout seul peut-être dangereux et inutile. Utilisez les outils/méthodologies/
approches existants
• Les promises sont très utilisées pour le développement web : elles permettent de faire les appels asynchrones de votre API depuis
votre frontend
• Apprenez Typescript
• Et enfin… Lisez les documentations of cielles. Constamment (et de préférence en anglais). Il y a énormément de notions à voir,
d’exemples à explorer. Prendre le temps de bien lire les docs c’est faire plus de la moitié du travail. Il n’y a JAMAIS une seule façon de
faire.

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 54


fi
4. Présentation du projet
Faire une todo-list

Il s’agit d’un des projets les plus communs à réaliser en développement web ! Nous allons réaliser une application qui permet de créer et
gérer des todo-lists.

Cette application sera collaborative :


• Un utilisateur crée un compte et se connecte pour avoir accès à ses todo et également pour pouvoir en créer/modifier/supprimer
• Un utilisateur peut créer un groupe et inviter d’autres utilisateurs : ils auront en commun des todos qui sont possibles de faire. Ainsi, un
utilisateur a sa liste de todo privée ainsi qu’une ou plusieurs listes de todo collaboratives
• Une todo est assignée à un utilisateur
• Lorsqu’une deadline est passée, si la todo n’est toujours pas complétée ou abandonnée, il faut rajouter un signe distinctif sur la todo
indiquant qu’elle est en retard (un texte, une pastille, un changement de couleur, ce que vous voulez)

De façon plus générale une Todo comporte :


• Une tâche
• Une catégorie
• Une deadline
• Un statut (à faire, en cours, complétée, abandonnée)
• Un utilisateur assigné

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 55


4. Présentation du projet
Faire une todo-list

D’un point de vue technique il faudra en effet :


- Prévoir tout ce qui est authentification
- Créer une base de données (SQLite ou autre, le choix est libre)
- Utiliser un ORM (Sequelize ou autre le choix est libre)
- Un frontend NextJS (l’organisation des pages est libre également)
- Une API Express (avec modèles et routes)
- Ajouter quelques contrôles : ne pas pouvoir soumettre un champ de formulaire vide, tester les données qui arrivent dans l’API, tester si
un utilisateur existe déjà etc.

Niveau template, quartier libre également. Tailwind, Bootstrap, pur CSS, le choix et libre.

Il y aura une soutenance de projet. Votre présentation devra contenir vos choix des techniques et les explications associées. Aussi,
détaillez-y le rôle de chaque membre du projet, et rajoutez-y tout élément que vous estimez important : comme toujours, mieux vous
expliquez, plus vous serez récompensés !

A vous de jouer !

Alexandre TELLE // Master MIAGE SITN // Novembre 2023 56

Vous aimerez peut-être aussi