b) Sécurisation d'une RESTful API
La sécurisation d'API est une problématique complexe.
Nous n'allons pas aller dans les détails de cette problématique, juste offrir une solution à deux cas simples à traiter.
Protection contre les attaques XSS
Une attaque XSS, c'est quoi ?
Les attaques XSS, ou Cross-Site Scripting, sont un type d'injection de scripts malicieux dans une application web.
Imaginez le forum web d'une banque et ce scénario :

L'API de la banque permet d'enregistrer des messages qui sont associés à des forums sur lesquels leurs clients peuvent poster des messages.
Si l'API de la banque était mal sécurisée et qu'elle permettait d'enregistrer n'importe quels types d'information en tant que "messages" du forum, il serait possible à un attaquant d'injecter du JS malicieux dans l'API.
Plus tard, lors de l'affichage des messages par le forum de la banque (https://forum.my-bank.com
), le JS malicieux pourrait s'exécuter dans le browser de n'importe quel utilisateur.
Vous avez vu que via du JS, on peut envoyer de l'information n'importe où, notamment à l'aide de fetch
. Ce qui permettrait donc à un hacker, via son script malicieux, d'envoyer des cookies contenant des infos sensibles à son API malicieuse, en vue de futures attaques encore plus diaboliques, comme notamment vider le compte en banque d'utilisateurs.
La protection contre des attaques XSS se fait à différents niveaux. Ca n'est pas l'objet de ce cours de voir cela en détails, mais nous verrons un technique simple pour éviter certaines attaques.
Réaliser une attaque XSS
Nous allons maintenant réaliser une attaque XSS sur le site de la pizzeria.
Veuillez lancer le frontend jwt-fetch-hmi (nous allons le développer tout prochainement, vous pouvez simplement en faire une copie temporaire, l'installer et l'exécuter) et le backend api-auths
du site de la pizzeria (/web2/tutorials/pizzeria/api/auths
ou en cas de soucis : api-auths).
Evidemment, un hackeur, pour tenter l'attaque XSS, doit d'abord trouver un moyen d'usurper la session d'admin
. Nous ne verrons pas dans ce cours le genre d'attaques qui permettrait de le faire. Nous allons considérer que cette première attaque est réussie et nous allons prendre le rôle d'un hackeur pouvant accéder au compte admin
.
Veuillez vous loguer à l'aide du compte admin
(et le password admin
).
Veuillez vous rendre sur la page Add a pizza
.
Pour le titre de la pizza, vous pouvez ajouter n'importe quoi. Dans le contenu de la pizza, veuillez ajouter cela et soumettre :
text<img src="#" onerror="alert('You have been hacked !')">
En fait, nous avons ajouté une balise img
contenant du inline JS. onerror
est un gestionnaire d'événements qui sera d'office appelé car la source (src="#"
) donné à img
n'est pas une image.
Maintenant, tout utilisateur connecté ou pas qui se loguera sur le site va exécuter ce script "malicieux". Il verra le pop-up apparaître avec le message "You have been hacked !".
En effet, le menu des pizzas, tel qu'il est construit, est basé sur toutes les ressources de type "pizzas" renvoyées par l'API, dont une des pizzas contient le JS malicieux qui est exécuté dans une cellule de la table via ce code de la HomePage
de l'IHM :
jsfunction getAllTableLinesAsString(menu) {let pizzaTableLines = '';menu?.forEach((pizza) => {pizzaTableLines += `<tr><td>${pizza.title}</td><td>${pizza.content}</td></tr>`;});return pizzaTableLines;}
Empêcher les attaques XSS
Par rapport à l'attaque précédente, le plus simple pour protéger de l'attaque pourrait être de faire en sorte que l'application cliente n'interprète pas d'HTML / JS / CSS envoyé par l'API.
Ici, dans la HomePage
, on utilise la propriété innerHTML
d'une td
. Ainsi, l'HTML et l'inline JS associé est exécuté. Si l'on utilisais la propriété innerText
des td
, alors, ni l'HTML et le JS serait interprété par le browser, et donc pas de possibilité de script malicieux au niveau de la HomePage
.
Oui, mais si nous allions vers cette solution, cela serait problématique si nous souhaitions réellement développer plusieurs applications clientes, il faudrait toujours faire attention à cette contrainte.
Dès lors, nous allons préférer la solution où nous sécurisons l'API. Ainsi, peu importe l'application cliente développée, il devrait y avoir moins d'angles d'attaques.
Au niveau de l'API, nous allons échapper les caractères dangereux, principalement "
, '
, &
, <
, >
.
Nous allons utiliser la librairie escape-html sous Node.js échappant les string pour une utilisation des string transformées en HTML.
Pour ce nouveau tutoriel, nous allons continuer le développement de l'API api-auths et la sécuriser.
Au sein de votre repo web2
, veuillez créer le projet nommé /web2/tutorials/pizzeria/api/safe
sur base d'un copié/collé de /web2/tutorials/pizzeria/api/auths
(ou api-auths).
Pour la suite du tutoriel, nous considérons que tous les chemins absolus démarrent du répertoire
/web2/tutorials/pizzeria/api/safe
.
Veuillez installer la librairie escape-html
au sein de votre projet safe
:
bashnpm i escape-html
Nous allons supprimer, s'il existe, le fichier reprenant le code malicieux introduit précédemment : veuillez supprimer le fichier /data/pizzas.json
.
Nous allons mettre à jour le modèle de pizzas pour échapper les caractères dangereux.
Veuillez modifier /models/pizzas.js
:
jsconst escape = require('escape-html');// other bits of codefunction createOnePizza(title, content) {const pizzas = parse(jsonDbPath, defaultPizzas);const createdPizza = {id: getNextId(),title: escape(title),content: escape(content),};pizzas.push(createdPizza);serialize(jsonDbPath, pizzas);return createdPizza;}
Nous allons maintenant tester si tout est réglé au niveau de l'attaque XSS.
Dans votre frontend temporaire (jwt-fetch), veuillez vous loguer à l'aide du compte admin
(et le password admin
).
Veuillez vous rendre sur la page Add a pizza
.
Comme précédemment :
- Pour le titre de la pizza, vous pouvez ajouter n'importe quoi.
- Dans le contenu de la pizza, veuillez ajouter cela et soumettre :
text<img src="#" onerror="alert('You have been hacked !')">
Maintenant, tout utilisateur connecté verra simplement apparaître, quand il affiche la HomePage
, le menu des pizzas avec :

Il n'y a plus de code JavaScript malicieux qui peut s'exécuter côté client 🎉 !
Nous allons maintenant régler les problèmes de passwords mis en clair dans un fichier de l'API.
Sécurisation des passwords
Nous souhaitons maintenant assurer que les passwords enregistrés dans un support de données (un fichier ou une base de données) ne puissent pas être récupérés.
👍 Pour ce faire, il est recommandé d'hacher les passwords avant de les enregistrer au niveau d'une API.
Afin de se protéger contre les "hash attacks", on utilise du salt :
- un salt : c'est une donnée aléatoire qui est utilisé en entrée d'une fonction qui hache des données. Ainsi, si un hackeur utilise une base de données de passwords hachés, il devra en plus trouver le bon salt pour que son attaque puisse fonctionner.
- salt round : nombre de fois que l'opération de hachage est faite, rendant les attaques de force brute plus lente et donc difficile ; une valeur de 10 semble être généralement raisonnable et recommendée.
Pour hacher sous Node.js, nous utiliserons la librairie bcrypt.
Veuillez installer la librairie bcrypt
au sein de votre API safe
:
bashnpm i bcrypt
En résumé, nous allons utiliser :
- la fonction asynchrone
hash
debcrypt
pour hacher un password ; - la fonction asynchrone
compare
debcrypt
pour comparer un password en clair à un password haché ; si le résultat est positif, cela signifie que le password fournit pour un utilisateur correspond au password initial.
Nous préférons utiliser les librairies asynchrone afin que l'API reste disponible à gérer des requêtes clientes et ne bloque pas celles-ci jusqu'à la fin d'une opération de bcrypt !
Veuillez effacer le fichier /data/users.json
contenant les credentials d'utilisateurs où les passwords sont donnés en clair.
Nous allons maintenant mettre à jour le modèle de "users" pour utiliser bcrypt
. Veuillez modifier /models/users.js
:
js1const jwt = require('jsonwebtoken');2const bcrypt = require('bcrypt');3const path = require('node:path');4const { parse, serialize } = require('../utils/json');56const jwtSecret = 'ilovemypizza!';7const lifetimeJwt = 24 * 60 * 60 * 1000; // in ms : 24 * 60 * 60 * 1000 = 24h89const saltRounds = 10;1011const jsonDbPath = path.join(__dirname, '/../data/users.json');1213const defaultUsers = [14{15id: 1,16username: 'admin',17password: bcrypt.hashSync('admin', saltRounds),18},19];2021async function login(username, password) {22const userFound = readOneUserFromUsername(username);23if (!userFound) return undefined;2425const passwordMatch = await bcrypt.compare(password, userFound.password);26if (!passwordMatch) return undefined;2728const token = jwt.sign(29{ username }, // session data added to the payload (payload : part 2 of a JWT)30jwtSecret, // secret used for the signature (signature part 3 of a JWT)31{ expiresIn: lifetimeJwt }, // lifetime of the JWT (added to the JWT payload)32);3334const authenticatedUser = {35username,36token,37};3839return authenticatedUser;40}4142async function register(username, password) {43const userFound = readOneUserFromUsername(username);44if (userFound) return undefined;4546await createOneUser(username, password);4748const token = jwt.sign(49{ username }, // session data added to the payload (payload : part 2 of a JWT)50jwtSecret, // secret used for the signature (signature part 3 of a JWT)51{ expiresIn: lifetimeJwt }, // lifetime of the JWT (added to the JWT payload)52);5354const authenticatedUser = {55username,56token,57};5859return authenticatedUser;60}6162function readOneUserFromUsername(username) {63const users = parse(jsonDbPath, defaultUsers);64const indexOfUserFound = users.findIndex((user) => user.username === username);65if (indexOfUserFound < 0) return undefined;6667return users[indexOfUserFound];68}6970async function createOneUser(username, password) {71const users = parse(jsonDbPath, defaultUsers);7273const hashedPassword = await bcrypt.hash(password, saltRounds);7475const createdUser = {76id: getNextId(),77username,78password: hashedPassword,79};8081users.push(createdUser);8283serialize(jsonDbPath, users);8485return createdUser;86}8788function getNextId() {89const users = parse(jsonDbPath, defaultUsers);90const lastItemIndex = users?.length !== 0 ? users.length - 1 : undefined;91if (lastItemIndex === undefined) return 1;92const lastId = users[lastItemIndex]?.id;93const nextId = lastId + 1;94return nextId;95}9697module.exports = {98login,99register,100readOneUserFromUsername,101};
Voici les modifications apportées :
- pour créer un password hashé : on utilise la fonction asynchrone
hash
pour créer le password haché. Dès lors,createOneUser
devient asynchrone. De plus, commecreateOneUser
est devenue asynchrone, dansregister
, pour chaîner le traitement de création du token, on met unawait
à l'appel decreateOneUser
. Et comme unawait
est ajouté au sein deregister
, il faut mettre unasync
à la méthoderegister
. Attention, du coup, commeregister
est devenue asynchrone, il faudra aussi bien chaîner les traitements oùregister
est appelé, dans le routerauths
. - pour vérifier qu'un password en clair "match" à un password haché : on utilise la fonction asynchrone
compare
que l'on chaîne à la création du token à l'aide deawait
. Dès lors, la fonctionlogin
doit elle aussi être déclaréeasync
. Attention, du coup, commelogin
est devenue asynchrone, il faudra bien chaîner les traitements oùlogin
est appelé, dans le routerauths
. - pour créer l'utilisateur
admin
se trouvant dans lesdefaultUsers
avec des credentials par défaut : on souhaite indiquer commepassword
le password haché correspondant au password"admin"
. Pour se simplifier la vie, on appelle la fonction synchronehashSync
(voir ligne 16). Bien évidemment, dans une application plus robuste, on évitera d'hardcoder ce genre de secrets dans les sources de notre application 😉 !
Afin de traiter des deux fonctions du modèle users
qui sont devenues asynchrones, login
et register
, nous allons modifier le router /routes/auths
:
js1const express = require('express');2const { register, login } = require('../models/users');34const router = express.Router();56/* Register a user */7router.post('/register', async (req, res) => {8const username = req?.body?.username?.length !== 0 ? req.body.username : undefined;9const password = req?.body?.password?.length !== 0 ? req.body.password : undefined;1011if (!username || !password) return res.sendStatus(400); // 400 Bad Request1213const authenticatedUser = await register(username, password);1415if (!authenticatedUser) return res.sendStatus(409); // 409 Conflict1617return res.json(authenticatedUser);18});1920/* Login a user */21router.post('/login', async (req, res) => {22const username = req?.body?.username?.length !== 0 ? req.body.username : undefined;23const password = req?.body?.password?.length !== 0 ? req.body.password : undefined;2425if (!username || !password) return res.sendStatus(400); // 400 Bad Reques2627const authenticatedUser = await login(username, password);2829if (!authenticatedUser) return res.sendStatus(401); // 401 Unauthorized3031return res.json(authenticatedUser);32});3334module.exports = router;
Nous avons simplement chaîné les réponses à faire au client seulement une fois les opération register
** et login
terminées. Pour ce faire, nous avons précédé le nom de ces méthodes par await
. Nous avons donc du ajouter async
aux fonctions middleware s'occupant des routes POST /auths/register
et POST /auths/login
.
Veuillez vérifier que votre application fonctionne correctement.
Via l'IHM, veuillez faire un register d'un nouvel utilisateur.
Au niveau de l'API, allez voir le contenu du nouveau fichier /data/users.json
. Les passwords devraient maintenant être hachés, comme par exemple :
json[{"id": 1,"username": "admin","password": "$2b$10$RioLKlPFyYFEhv/46gR7dufDkke07eDpWH9tBt/A4Z9tJh0oJnnf2"},{"id": 2,"username": "manager","password": "$2b$10$NZZ1zxOPdby6gl4Dw8K0Q.v4ZRWTbh1Ta7qcYzH5G4SrO5z71H0kO"}]
Si tout fonctionne bien, faites un commit
de votre repo (web2
) avec comme message : api-safe tutorial
.
En cas de souci, vous pouvez utiliser le code du tutoriel :
- pour le frontend : jwt-fetch-hmi.
- pour l'API : api-safe.
Projet 4.2 : Sécurisation d'API
Vous devez mettre à jour l'API développée pour Projet 4.1 afin de sécuriser l'API pour échapper les caractères dangereux. Normalement, vos passwords sont déjà hachés car pour créer le Projet 4.1, vous aviez utilisé le code offert dans le boilerplate jwt-api-boilerplate.
Le code doit se trouver dans votre repository local et votre web repository (normalement appelé web2
) dans le répertoire nommé /project/4.2/api
sur base d'un copier/coller du code de Projet 4.1.
Vous devez tester, à l'aide de Rest Client, toutes les opérations que vous avez modifiées.
Quand un prototype d'api est finalisé et testé, veuillez faire un commit
de votre code avec comme message : 4.2 : api bcrypt & escape-html
.
🤝 Tips
⚡ N'oubliez pas que vos méthodes de login
et de register
deviennent asynchrones, cela impose de bien chaîner les traitements dans le router, sinon vous risquez de renvoyer des objets vides en réponses aux requêtes.
🍬 Authentification & autorisation JWT à l'aide de cookies
Pourquoi utiliser l'autorisation JWT à l'aide de cookies ?
Dans la partie sur l'Authentification et autorisation d'accès aux opérations d'une RESTful API via JWT, nous avons vu une façon de gérer des token JWT, sans gérer de cookies.
Cela impose aux client de sauvegarder les token et de les envoyer dans un authorization header
lorsqu'ils souhaitent accéder à une opération protégée.
Il est aussi possible d'intégrer les tokens au sein de cookies. Dans ce cas-là, les clients qui ont reçu un cookie de l'API renverront automatiquement ce cookie (mécanisme des browsers). Les tokens JWT voyageront automatiquement grâce au mécanisme de cookies.
💭 Faut-il mieux intégrer les token JWT dans des cookies ou pas ?
Il est difficile de donner une réponse à cette question. Chaque approche a des avantages et des inconvénients. Ce qui devrait être le point d'attention, c'est la sécurité. Et dans les deux cas, on atteint un niveau de sécurité raisonnable.
Pour ce cours, nous avons choisi la façon la plus moderne, en laissant au client le choix de sauvegarder les token dans le web storage (nous allons voir ça tout prochainement pour l'aspect frontend).
Notons que le cas le plus sûr est probablement d'avoir deux types de token, ce qui est d'une complexité qui dépasse les objectifs de ce cours. Néanmoins, pour votre info, les mécanismes d'authentification comme OAuth (authorization de MS Azure) & OpenID Connect (authentification de MS Azure) gère deux types de token :
- un token à durée de vie courte qui sera enregistré en mémoire vive au niveau du client (access token) ou dans le localStorage.
- un token à durée de vie longue qui sera enregistré par le client dans un cookie (refresh token, notamment utilisé par l'API pour créer un nouvel access token).
Un cookie, c'est quoi ?
Un cookie représente des données qu'un serveur envoie à un browser.
Le browser peut sauver ce cookie. Pour chaque requête faite au serveur sur la même origine (que l'origine où le cookie a été reçu), le cookie sera automatiquement envoyé au serveur.
Il fut un temps où les cookies étaient utilisés comme un mécanisme général de stockage de données côté client.
👍 Actuellement, si les cookies sont utilisés pour sauvegarder les données de session, il faut se protéger contre les attaques XSS et rendre les cookies inaccessibles au JavaScript : on utilise donc les cookies HttpOnly
qui sont inaccessibles à la Document.cookie API
.
Pour la suite, on va donc voir :
- comment utiliser Express pour créer des cookies au niveau d'une API ;
- comment rendre ces cookies inaccessibles aux attaques XSS en configurant
HttpOnly
.
Implémentation de l'authentification & l'autorisation JWT au sein de cookies
Intro
Nous allons donc créer une nouvelle version de l'API sauvegardant le token d'un utilisateur au sein d'un cookie, ainsi que son username, sans que ces infos soient accessible au JS côté client.
Pour ce faire nous allons utiliser la librairie cookie-session qui permet d'enregistrer des données de session dans des cookies.
Nous allons maintenant continuer le développement de l'API safe.
Au sein de votre repo web2
, veuillez créer le projet nommé /web2/tutorials/pizzeria/api/cookies
sur base d'un copié/collé de /web2/tutorials/pizzeria/api/safe
(ou api-safe).
Pour la suite du tutoriel, nous considérons que tous les chemins absolus démarrent du répertoire
/web2/tutorials/pizzeria/api/cookies
.
Veuillez installer la librairie cookie-session
au sein de votre nouveau projet cookies
:
bashnpm i cookie-session
Utilisation de la fonction middleware cookieSession
Veuillez mettre à jour /app.json
pour mettre en place la gestion de cookies :
jsconst express = require('express');const cookieParser = require('cookie-parser');const logger = require('morgan');const cookieSession = require('cookie-session');const usersRouter = require('./routes/users');const pizzaRouter = require('./routes/pizzas');const authsRouter = require('./routes/auths');const app = express();const expiryDateIn3Months = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30 * 3);const cookieSecreteKey = 'YouWouldnot!not!like!mypizza';app.use(cookieSession({name: 'user',keys: [cookieSecreteKey],httpOnly: true,expires: expiryDateIn3Months,}),);app.use(logger('dev'));app.use(express.json());app.use(express.urlencoded({ extended: false }));app.use(cookieParser());app.use('/users', usersRouter);app.use('/pizzas', pizzaRouter);app.use('/auths', authsRouter);module.exports = app;
Nous avons donc bien indiqué que le cookie est inaccessible au JS via : httpOnly: true
.
Nous avons fait en sorte que le cookie soit signé via la clé cookieSecreteKey
.
Le mécanisme de signature correspond à ce qui a été vu dans le cadre des tokens JWT.
Ainsi, si un cookie venait être modifié par un utilisateur, lors de la vérification du cookie, cela sera automatiquement détecté par la fonction middleware cookieSession
et la session ne sera pas créée.
Pour info, la fonction middleware cookieSession
va créer deux cookies :
- un cookie portant comme nom la valeur de
name
; il est encodé en base64. N'hésitez pas à vous amuser à décoder un cookie généré parcookieSession
sur base64decode. - un cookie portant comme nom la valeur de
name
+.sig
: c'est la signature qui prévient contre le "tempering" (acte intentionnel mais non autorisé qui amène à la modification d'un système ou de données).
Lecture et ajout de données de session via req.session
Pour créer des données de session, il suffit de simplement les ajouter à l'objet req.session
.
Dans le cadre de la RESTful API gérant les pizzas, cela est fait lors d'une opération de type register
ou login
.
Veuillez mettre à jour le router /routes/auths.js
:
js1const express = require('express');2const { register, login } = require('../models/users');34const router = express.Router();56/* Register a user */7router.post('/register', async (req, res) => {8const username = req?.body?.username?.length !== 0 ? req.body.username : undefined;9const password = req?.body?.password?.length !== 0 ? req.body.password : undefined;1011if (!username || !password) return res.sendStatus(400); // 400 Bad Request1213const authenticatedUser = await register(username, password);1415if (!authenticatedUser) return res.sendStatus(409); // 409 Conflict1617createCookieSessionData(req, authenticatedUser);1819return res.json({ username: authenticatedUser.username });20});2122/* Login a user */23router.post('/login', async (req, res) => {24const username = req?.body?.username?.length !== 0 ? req.body.username : undefined;25const password = req?.body?.password?.length !== 0 ? req.body.password : undefined;2627if (!username || !password) return res.sendStatus(400); // 400 Bad Reques2829const authenticatedUser = await login(username, password);3031if (!authenticatedUser) return res.sendStatus(401); // 401 Unauthorized3233createCookieSessionData(req, authenticatedUser);3435return res.json({ username: authenticatedUser.username });36});3738/* Logout a user */39router.get('/logout', (req, res) => {40req.session = null;41return res.sendStatus();42});4344function createCookieSessionData(req, authenticatedUser) {45req.session.username = authenticatedUser.username;46req.session.token = authenticatedUser.token;47}4849module.exports = router;
Dans le code ci-dessus, nous préparons les données de session qui seront écrites dans le cookie à l'aide de l'objet req.session
.
Lorsque nous renvoyons du JSON aux clients, nous ne renvoyons plus le token, mais juste le username de l'utilisateur. L'application cliente, le browser, pourra utiliser cette info pour afficher le nom de l'utilisateur. Pour rappel, le browser n'a pas accès, via le JS, à l'info se trouvant dans le cookie.
Quand nous gérons une session via des cookies, il n'est pas évident de bien clôre une session. Nous avons créé une nouvelle opération de type GET /auths/logout
qui permet d'effacer les données de session d'un utilisateur.
Il nous reste à changer le mécanisme d'autorisation.
Les tokens ne seront plus reçu via un authorization header, mais via un cookie.
Nous allons donc mettre à jour le middleware /utils/authorize
(1 seule ligne) :
js1const jwt = require('jsonwebtoken');2const { readOneUserFromUsername } = require('../models/users');34const jwtSecret = 'ilovemypizza!';56const authorize = (req, res, next) => {7const { token } = req.session;8if (!token) return res.sendStatus(401);910try {11const decoded = jwt.verify(token, jwtSecret);12console.log('decoded', decoded);13const { username } = decoded;1415const existingUser = readOneUserFromUsername(username);1617if (!existingUser) return res.sendStatus(401);1819req.user = existingUser; // request.user object is available in all other middleware functions20return next();21} catch (err) {22console.error('authorize: ', err);23return res.sendStatus(401);24}25};2627const isAdmin = (req, res, next) => {28const { username } = req.user;2930if (username !== 'admin') return res.sendStatus(403);31return next();32};3334module.exports = { authorize, isAdmin };
🍬 Test via REST Client d'une RESTful API attendant des cookies
Il nous reste à tester nos requêtes via REST Client.
Il n'y a pas de nouvelles notions à apprendre pour utiliser REST Client avec des cookies : le comportement par défaut de REST Client, lorsqu'un cookie est renvoyé dans une réponse, est d'inclure ce cookie dans chaque requête vers la même origine.
Dès lors, pour tester l'API, il suffit d'enlever tous les authorization headers et de rajouter une requête pour tester l'effacement d'une session.
Veuillez tester les requêtes à l'aide de /REST Client/pizzas.http
:
http######### NORMAL OPERATION ############## Read all pizzasGET http://localhost:3000/pizzas### Read all pizzas with File variable@baseUrl = http://localhost:3000GET {{baseUrl}}/pizzas### Read all pizzas sorted by title (ascending)GET {{baseUrl}}/pizzas/?order=+title### Read all pizzas sorted by title (descending)GET {{baseUrl}}/pizzas/?order=-title### Read pizza identified by 2GET {{baseUrl}}/pizzas/2### Create a pizza by using the admin account#### First login as the adminPOST {{baseUrl}}/auths/loginContent-Type: application/json{"username":"admin","password":"admin"}#### Create a pizza with the admin tokenPOST {{baseUrl}}/pizzasContent-Type: application/json{"title":"Magic Green","content":"Epinards, Brocolis, Olives vertes, Basilic"}### Delete pizza identified by 2 with the admin tokenDELETE {{baseUrl}}/pizzas/2### Update the pizza identified by 6 with the admin tokenPATCH {{baseUrl}}/pizzas/6Content-Type: application/json{"title":"Magic Green 2"}######### ERROR OPERATION ############## 1. Create a pizza without a tokenPOST {{baseUrl}}/pizzasContent-Type: application/json{"title":"Magic Green","content":"Epinards, Brocolis, Olives vertes, Basilic"}### 2. Create a pizza without being the admin, use manager account#### 2.1 First login as the managerPOST {{baseUrl}}/auths/loginContent-Type: application/json{"username":"manager","password":"manager"}#### 2.2 Try to create a pizza with the manager tokenPOST {{baseUrl}}/pizzasContent-Type: application/json{"title":"Magic Green","content":"Epinards, Brocolis, Olives vertes, Basilic"}### Read pizza which does not existsGET {{baseUrl}}/pizzas/100### Create a pizza which lacks a propertyPOST {{baseUrl}}/pizzasContent-Type: application/json{"content":"Epinards, Brocolis, Olives vertes, Basilic"}### Create a pizza without info for a propertyPOST {{baseUrl}}/pizzasContent-Type: application/json{"title":"","content":"Epinards, Brocolis, Olives vertes, Basilic"}### Update for a pizza which does not existPATCH {{baseUrl}}/pizzas/200Content-Type: application/json{"title":"Magic Green 2"}### Update for a pizza which does not provide any info for a propertyPATCH {{baseUrl}}/pizzas/1Content-Type: application/json{"title":"Magic Green 2","content":""}
/REST Client/auths.http
a été mis à jour pour tester GET /auths/logout
:
http### Logout any userGET {{baseUrl}}/auths/logout
Pour ajouter une pizza, il suffit juste :
- De loguer l'admin.
- De créer une nouvelle pizza ; le cookie est automatiquement envoyé.
Admettons que vous souhaitez tester l'ajout d'une pizza sans envoyer de token :
- Lancez l'opération de logout (
GET /auths/logout
) ; le cookie renvoyé ne contient pas de données de session ; - Tentez la création d'une pizza qui renverra un code
401 Unauthorized
.
Si tout fonctionne bien, faites un commit
de votre repo (web2
) avec comme message :
api-cookies tutorial
.
En cas de souci, vous pouvez utiliser le code du tutoriel api-cookies.