# Le Guide API ReST par Marmicode

![Stop Smurfing APIs](/files/-LFHXld8OSHJPmMuQy6K)

## Nos objectifs <a href="#nos-objectifs" id="nos-objectifs"></a>

* Produire **rapidement** des APIs **performantes**, **robustes** et **maintenables**.
* Privilégier le **pragmatisme** et mettre l'accent sur les **bonnes pratiques**.
* Partager le fruit de nos heures de **veille**, de **recherche** et de nos **retours d'expérience**.
* Produire un **guide** **gratuit**, **à jour** et **communautaire.**
  * N'hésitez donc pas à nous envoyer vos Pull Requests sur : <https://github.com/wishtack/gitbook-guide-api-rest>

## Copyright <a href="#copyright" id="copyright"></a>

Ce guide est l'oeuvre et la propriété de Marmicode.

Il ne peut être utilisé partiellement ou intégralement comme support de prestations rémunérées sauf par l'équipe Marmicode.

En cas de doute, merci de nous contacter : <camijote@marmicode.fr​>

©Marmicode

![](/files/-Ly9z1-D0124XJUzVo8P)


# API ReST


# L'Ecosystème Moderne

* Les applications sont de moins en moins monolithiques.
* De plus en plus d’interactions entre les services afin de fournir un produit intéressant.
* Mobile Applications / Single Page Applications / Progressive Web Apps etc…
* Microservices.
* Serverless.
* Open Data.
* Obligations légales *(e.g. : PSD2)*.


# Le Besoin

Les APIs doivent donc être :

* **flexibles**, **extensibles** et **réutilisables**,
* **faciles** à utiliser et compréhensibles,
* conformes à la ***Separation of Concerns***,
* **compatibles** avec le plus de technologies possibles :
  * il faut pouvoir développer des clients et des serveurs légers,
  * et si on réutilisait nos proxy cache (Varnish, Cloudfront etc…),
* **performantes** et **sécurisées**.


# Re.S.T. : REpresentational State Transfer

## **Ce qu’une API ReST n’est pas**

* ReST n’est pas un standard mais un **style d’architecture**.

## **Roy Thomas FIELDING : Papa du ReST**

* L’origine du style ReST date des années 90. Ce style d'architecture a servi à définir les standards HTTP et URI.
* Cf. [Architectural Styles and the Design of Network-based Software Architectures by Roy Thomas FIELDING](https://www.ics.uci.edu/~fielding/pubs/dissertation/fielding_dissertation.pdf)

## Description du ReST

Le style d'architecture ReST impose 5 contraintes, définies dans le chapitre suivants, qui permettent de définir des systèmes hypermedia distribués.

> The name “Representational State Transfer” is intended to evoke an image of how a well-designed Web application behaves: a network of web pages (a virtual state-machine), where the user progresses through the application by selecting links (state transitions), resulting in the next page (representing the next state of the application) being transferred to the user and rendered for their use

## Resource-Oriented Architecture vs ReST

La plupart des APIs nommées ReST ou ReSTful le sont à tort. En effet, l'une des contraintes principales du style ReST est le "hypermedia as the engine of application state" où toutes les transitions d'états doivent être définies par relations hypermedia afin d'éviter tout couplage entre clients et serveurs.\
**En appliquant toutes les autres contraintes sauf cette dernière, nous nous écartons du style ReST et définissons alors simplement une Resource-Oriented Architecture (ROA).**

Tel que Fielding le présente dans sa thèse, le style ReST n'est pas destiné à tous les usages.\
Il est donc préférable de parler d'APIs HTTP ou APIs ROA que d'APIs ReST qui ne le sont pas.

Cf. <https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven>


# Les 5 règles et ½ de l’API ReST

ou plutôt les 5 contraintes et ½

> REST provides a set of architectural constraints that, when applied as a whole, emphasizes scalability of component interactions, generality of interfaces, independent deployment of components, and intermediary components to reduce interaction latency, enforce security, and encapsulate legacy systems.

## **Separation of Concerns&#x20;*****(Client / Server)***

* L’API ReST n’est pas concernée par l’affichage, les interactions utilisateur et la session.
* Tous ces éléments doivent être gérés par le client *(Ex. : application web frontend).*

## **Stateless**

* Une API ReST ne doit pas maintenir de session ou de contexte.

> Communication must be stateless in nature..., such that **each request from client to server must contain all of the information necessary to understand the request**, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client. This constraint induces the properties of visibility, reliability, and scalability. **Visibility** is improved because a monitoring system does not have to look beyond a single request datum in order to determine the full nature of the request. **Reliability** is improved because it eases the task of recovering from partial failures. **Scalability** is improved because not having to store state between requests allows the server component to quickly free resources, and further simplifies implementation because the server doesn’t have to manage resource usage across requests.

## **Layered**

* La présence de connecteurs intermédiaires doit être implicite pour le client et le serveur *(composant de cache / sécurité etc…)*.

## **Uniforme**

* L’interface est uniforme à tous les niveaux. Tous les éléments *(et connecteurs)* communiquent en utilisant la même interface.
* Chaque ressource est identifiée de façon unique et canonicalisée avec son URI *(URL ou URN dont voici deux exemples respectifs* [*https://www.googleapis.com/books/v1/volumes/-DNcBAAAQBAJ*](https://www.googleapis.com/books/v1/volumes/-DNcBAAAQBAJ) *et isbn:9780134051994)*.

> In order to obtain a uniform interface, multiple architectural constraints are needed to guide the behavior of components. REST is defined by four interface constraints: identification of resources; manipulation of resources through representations; selfdescriptive messages; and, hypermedia as the engine of application state.

## **Cacheable**

* Il doit être possible de mettre les ressources en cache à tous les niveaux *(front, connecteur intermédiaire, back, etc…)*.
* Il doit être possible d’utiliser les implémentations standards de cache HTTP.

## Code à la demande

Règle optionnelle ou plutôt inadaptée.

C’est le paramètre *extra* que l’on retrouve dans certaines RFC pour garder un peu de souplesse.


# Le Modèle de Maturité de Richardson

Le Modèle de Maturité de Richardson ou Web Service Maturity Heuristic

<https://www.crummy.com/writing/speaking/2008-QCon/act3.html> (2008)

![Le Modèle de Maturité de Richardson ou Web Service Maturity Heuristic](/files/-LFHY0S7Schm5-P8_f6G)

**\*P.O.X.** : Plain Old XML

## Level 0 : The Swamp of POX

XML-RPC over HTTP.

![Plain Old XML](/files/-LFHYStIKWMcNN-JDcAh)

![Tag Soup](/files/-LFHZyx8BCP7NmkN-DvI)

![ReST vs SOAP](/files/-LFH_0qRtT67j0bzpzNZ)

![](/files/-LFH_6-suTMlxY7l0qzj)

## Level 1 : Resources

L’API respecte le modèle de données et chaque ressource peut être identifiée avec une URL.

```http
POST /blogs/11111/posts
POST /posts/22222/comments
GET /comments/33333
```

![Resources](/files/-LFHa4dOVaQh_RXtd-yM)

## Level 2 : HTTP Verbs

Utilisation des méthodes HTTP autres que GET et POST pour signifier l’action souhaitée : PATCH / PUT / DELETE.

… et surtout les “status codes” HTTP pour résumer le résultat de l’opération :

**200** : OK\
**201** : Created\
**204** : No Content (delete)\
**400** : Bad Request\
**401** : Unauthorized\
**403** : Forbidden\
**404** : Not Found\
**409** : Conflict\
…

Bien sûr, les erreurs 4xx peuvent contenir un "body" avec des informations additionnelles.

{% hint style="info" %}
Utilisez le bon vocabulaire et évitez les APIs schtroumpf !

```http
SCHTROUMPF /q?data=select:*:from:carts
```

{% endhint %}

![What The Smurf!?](/files/-LFHacbEddLsuWTzByMX)

## Level 3 : Hypermedia Controls

Hypermedia est l’une des principales règles de la thèse de Fielding.

L’idée est de retrouver dans les API ReST la même logique Hypermedia qu’en HTML par exemple. Aujourd’hui, cela se résume principalement par la présence de liens dans les ressources permettant de définir la relation avec d’autres ressources.

L’API ReST devient alors ***discoverable***.


# H.A.T.E.O.A.S. & Resource Linking

Le "level 3" du modèle de maturité de Richardson *(ou Web Service Maturity Heuristic)* est souvent représenté par l’acronyme **H.A.T.E.O.A.S.** : **Hypermedia As The Engine Of Application State**.

```javascript
{
  "_links": {
    "self": {"href": "https://www.wishtack.com/posts/22222"},
    "blog": {"href": "https://www.wishtack.com/blogs/11111"},
    "comments": {"href": "https://www.wishtack.com/posts/22222/comments"}
  },
  "title": "H.A.T.E.O.A.S & Resource Linking"
  ...
}
```

### Affordances

{% embed url="<https://youtu.be/NK1Zb_5VxuM>" %}
Affordance
{% endembed %}


# Avis Subjectif sur H.A.T.E.O.A.S. et le Semantic Web

Supposons que nous disposions des objets et méthodes suivantes :

```javascript
/* GET https://v1.api.wishtack.com/users/SOME_USER_ID */
userStore.getUser({userId: 'SOME_USER_ID'});

/* GET https://v1.api.wishtack.com/users/SOME_USER_ID/wishes */
wishStore.getWishList({userId: 'SOME_USER_ID'});
```

Supposons maintenant que l’API réponde avec les données suivantes :

```javascript
{
  "href": "https://v1.api.wishtack.com/users/SOME_USER_ID",
  "wishes": {
    "href": "https://v1.api.wishtack.com/users/SOME_USER_ID/wishes"
  }
}
```

Comment faire pour réutiliser la méthode `WishStore.getWishList` ?

Faut-il ajouter une méthode `WishStore.getWishListByUrl` ?

Que faire en cas d’incohérence ?

* Les données ne sont pas proprement canonicalisées.
* Les URLs sont dupliquées et occupent une grande partie du contenu. Comment factoriser ?
* Comment récupérer l'"id" si on ne souhaite pas utiliser le "href" comme "id".
* Une URL est une information qui perd en canonicalisation. Une façon plus canonique de décrire une ressource serait la suivante :

  ```javascript
  {
    "baseUrlList": ["https://api.wishtack.com", "https://api-backup.wishtack.com"],
    "resourcePath": [
      {"id": "USER_ID", "type": "user"},
      {"id": "WISH_ID", "type": "wish"}
    ]
  }
  ```
* Comment basculer automatiquement d’une API principale à une API de backup sans “parser” et reconstruire l’URL ?
* Peut-on faire **confiance à une API au point d’utiliser naïvement les URLs qu’elle nous transmet** ?
* Il nous faudrait idéalement les informations suivantes :
  * `id` de la ressource.
  * `type` de la ressource ou encore mieux un référentiel de type. [I.A.N.A.](https://www.iana.org/) ?
  * Mapping `type` => informations sur la construction de l’URL *(Base URL  et Path)*.
  * Affordances : que puis-je faire avec la ressource ? A quoi correspond-elle ?

C’est tout simplement l’utopie du Web Sémantique, parfaitement adaptée à des ressources génériques mais à peu d'applications. **Cela nécessite des standards de canonicalisation des données et une adoption importante.**

Application-Level Profile Semantics : <http://alps.io/spec/drafts/draft-01.html>

![Alps](/files/-LFHmeKPxq9I4WfDdeJc)

![Alps](/files/-LFHmtUdNnsUuU83LMON)


# ReST over HTTP

## Les Formats d'Echange

En théorie, le format d’échange est libre.

En pratique, le format doit être **standard** et **non-linéaire** *(Hypermedia)*.

Plus concrètement, le format le plus utilisé aujourd’hui est le **JSON&#x20;*****(JavaScript Object Notation)*** car :

* l’univers JavaScript est en expansion permanente,
* contrairement aux technologies backend habituelles, le nombre de librairies et d’outils utilisés est volontairement restreint pour éviter de surcharger les clients JavaScript,
* on retrouve des outils JSON dans tous les langages,
* le JSON est facile et rapide *(au sens performance)* à manipuler,
* le JSON est *human-readable*.


# HTTP & CRUD

## Les Méthodes HTTP Associées aux Opérations C.R.U.D.

### Create

* **POST** : Création d’une ressourc&#x65;*.*

### Read

* **GET** : Récupération d’une ressource ou d’une collection.

### Update

* **PATCH** : Modification d’une ressource ou d’une collection.
* **PUT** : Remplacement d’une ressource ou d’une collection.

### Delete

* **DELETE** : Suppression d’une ressource ou d’une collection.

{% hint style="info" %}

* La méthode **POST** peut servir à modifier une ressource mais ce n’est pas recommandé.
* La méthode **PUT** peut servir à créer une ressource si on en connaît l’identifiant par avance par exemple. La seule contrainte sur la méthode **PUT** est qu’elle doit être idempotente. Le nombre d’exécution d’une même requête ne doit pas impacter le résultat.
  {% endhint %}


# ReSTful donc Stateless

## Stateless ?

Supposons le scénario d’échange suivant. Est-il *stateless* ?

{% tabs %}
{% tab title="🧐" %}

```http
GET /init
GET /select-cart?cartId=123ab
POST /add-product
POST /add-product
POST /update-product-count?productId=12345
GET /cart-summary
POST /pay
```

{% endtab %}

{% tab title="👍" %}
![No!](/files/-LFHc9byHnBum0PIt9eL)
{% endtab %}
{% endtabs %}

## Limites et Difficultés du Stateful

* L’effet "**never-click-back!**".
* Problèmes de "load balancing".
* Comment paralléliser l’ajout de deux produits dans deux paniers différents ?
* Comment mettre la ressource "/cart-summary" en cache.
* API peu intuitive et peu extensible.

## Exemple Stateless

```
POST /carts/123ab/items
POST /carts/456cb/items
PATCH /items/33333
POST /carts/123ab/payments
GET /carts/123ab
```

## Les Avantages de l'Approche Stateless

* Pas de session à maintenir et donc pas de problème de *load balancing*.
* Moins de requêtes.
* Il est possible de paralléliser les requêtes.
* *Cacheable*.
* API intuitive et extensible.
  * L’API est human readable *(pas besoin d’avoir la documentation en permanence sous les yeux)*.
  * L’API est facile à étendre *(ajout de propriétés par exemple)*.
  * L’API peut répondre facilement à des besoins qui n’ont pas été anticipé *(modification du nombre de produits dans le panier par exemple)*.

En général, on peut comparer les approches stateful et stateless à la **métaphore de la destination géographique**.

Exemple :

* Coordonnées GPS d'un lieu.\
  \
  vs.<br>
* Les indications pour s'y rendre :
  * Tournez à droite.
  * à 100m à gauche.
  * Au rond-point (s’il n’a pas changé depuis), sortez à la 3ème sortie.
  * Admirez la vue sur votre gauche
  * …


# Pragmatisme, Idéologie et ReSTafarians

Comme indiqué précédemment, les principaux objectifs des APIs sont les suivants :

* **Généricité**.
* **Facilité** d’implémentation et extensibilité.
* **Performance** et ***scalability***.

Le ReST n’est un dogme ou une idéologie et il faut donc rester **pragmatique** tout en préparant l’avenir. **Visionnaire** et non devin. **K.I.S.S.** et **Agile**. Rien ne vous oblige à implémenter une API ReSTful.

Exemple de spécification ReSTful : <https://github.com/kevinswiber/siren>.

{% hint style="warning" %}
**Ne soyez pas ReSTafarians !** Le but initial est de répondre aux besoins associés à la **User eXperience** et la **Developer eXperience**.

On ne cherche pas à être ReST ou plus ReST que les autres.
{% endhint %}


# Conventions & Bonnes Pratiques


# Nommage

## Convention de Nommage

* **kebab-case** pour les URLs.
* **under\_score** ou **camelCase** pour les paramètres en “query string” et pour les “fields” des ressources.
* **kebab-case pluriel** pour les noms des ressources dans les URLs.

## Vocabulaire

Utilisez des noms explicites respectant la métaphore *(ou ubiquitous language)* du service.

## URLs

Les URLs doivent être construites de la façon suivante :

```http
/resources
/blogs

/resources/:resourceId
/blogs/:blogId

/resources/:resourceId/subresources
/blogs/:blogId/posts
```

Evitez donc les URLs de type : &#x20;

```http
/blogs/:blogId/summary
```

Ce n’est pas du ReSTafaring. De nombreuses librairies sont conçues ainsi. Contourner ces règles vous obligera à modifier, détourner et torturer les librairies et frameworks que vous utilisez.


# Base URL

La *base URL* est l’URL de la racine de votre API.

Evitez les URLs complexes :

```http
https://www.ibm.com/index.aspx/lastCompanyWeBought/service/rest/
```

Préférez :

```http
https://api.wishtack.com
```


# Media Type

Le *Media Type* habituel défini avec le header `Content-Type` est `application/json`.

Il est courant de définir un *Media Type* spécifique pour une API ou éventuellement en fonction du *standard* utilisé. Exemple : `application/vnd.github+json`.

Les M*edia Type* de type `application/vnd*` ne sont pas standards et peuvent éventuellement poser des problèmes avec certaines librairies ou connecteurs *(Ex.: Web Application Firewall)*.

Certains s’amusent à retourner un contenu HTML *(présentation, documentation, démo etc…)* lorsque le client ne présente pas le bon Media Type dans le header `Accept`.

C’est élégant…

…mais pas pratique du tout ! Qui n’a jamais testé une URL d’API ReST sur son browser ?

{% hint style="info" %}
Nous verrons plus tard que pour des raisons sécurité, il est recommandé de rejeter les requêtes ne présentant pas le bon header `Content-Type`.
{% endhint %}


# Versioning

Etant donné que les APIs ReST sont conçues pour être utilisées par de multiples sources *(clients mobiles / web / desktop / partenaires / public…)*, elles évoluent souvent à un **rythme différent de celui des clients**.

Pour s'adapter au changement, il y a trois principales approches :

* Versioning par *Media Type*.
* Versioning par URL.
* Pas de versioning *(la solution à privilégier tant que possible)*.

<https://blog.apisyouwonthate.com/api-versioning-has-no-right-way-f3c75457c0b7>

{% embed url="<https://blog.apisyouwonthate.com/api-versioning-has-no-right-way-f3c75457c0b7>" %}

## Versioning par Media Type

Le versioning par *Media Type* consiste à utiliser le comportement standard des *headers* HTTP `Accept` et `Content-Type`.

Le client indique alors la version de l’API qu’il supporte dans le header `Accept` :\
`Accept: application/vnd.wishtack.v3+json`

L’API retourne alors les données dans la version correspondante avec le bon *Media Type* dans le header `Content-Type`.

* Séduisant mais légèrement complexe à mettre en place.
* Comment faire si la nouvelle version est implémentée dans un langage différent ou encore sur une plateforme différente ?
* Nous serions alors obligé d’utiliser un connecteur pour effectuer le *balancing*.\
  <https://github.com/Kong/kong/issues/402>

## Versioning par URL

Vu l’obstacle de *balancing* posé par le versioning par *Media Type,* pourquoi ne pas utiliser un *balancing* standard en amont… mais lequel ?

Nous pourrions utiliser le *path* de l’URL et procéder au ***balancing*****&#x20;en fonction du nom de domaine**. Cela nécessite toujours un connecteur : <https://getkong.org/docs/0.13.x/proxy/#request-path> mais c'est une approche un peu plus classique et plus facile à configurer.

{% tabs %}
{% tab title="🧐" %}
Qui dit mieux ?
{% endtab %}

{% tab title="👍" %}
Yes ! Le DNS !\
\
[`https://v1.api.wishtack.com`](https://v1.api.wishtack.com/) NodeJS hosted on Amazon.\
[`https://v2.api.wishtack.com`](https://v2.api.wishtack.com/) Python hosted on Heroku.\
[`https://v3.api.wishtack.com`](https://v3.api.wishtack.com/) Python hosted on Heroku sur un monorépo avec l’API V2.[`https://v4.api.wishtack.com`](https://v3.api.wishtack.com/) Serverless functions sur un monorépo avec l’API V2 et V3.
{% endtab %}
{% endtabs %}


# Propriété “id”

Les ressources ont un identifiant unique dans une propriété qui est conventionnellement: `id`.

La propriété `id` doit être uniforme.

Dans le cas d'une ressource immutable, chaque modification peut produire un nouvel `id` qui fait donc référence à une nouvelle ressource.


# Polymorphisme

Il peut arriver qu’une ressource de type collection contienne plusieurs ressources de types légèrement différents. Par exemple, des produits de type différents : **livres** et **films**.

* Tout d’abord, il faut harmoniser le modèle de la ressource au maximum. Par exemple, livres et films ont un prix, il faut que ce soit la même propriété. Même si tel n’est pas le cas dans votre modèle de données *(Ex. scraping)*, créez des *computed fields*. On peut imaginer naïvement un *computed field* `price` qui calcule le prix à partir de la durée du film :).
* &#x20;Il suffit alors d’ajouter un *field* `type` au modèle de votre ressource *(qu’il faudra dûment documenté).*
* Cela permet ensuite côté client de *remapper* vers les classes associées.
* **L’abus de polymorphisme nuit gravement à la santé de votre API et de ses proches.**

```javascript
{
  "objects": [
    {
      "id": "1",
      "author": {"id": "3"},
      "price": {"amount": 10, "currency": "EUR"},
      "type": "book",
    },
    {
      "duration": 5400,
      "id": "2",
      "price": {"amount": 6, "currency": "USD"},
      "type": "movie"
   }
  ]
}
```

### Inheritance and Polymorphism with Swagger

<https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/>

{% embed url="<https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/>" %}


# Datetime

Comment échanger les dates et heures avec les APIs ?

Nous n’utiliserons pas le terme *timestamp* afin éviter les conflits avec le *unix timestamp*.

Pas de débat à ce sujet, **ISO 8601** est là depuis 1997 :

* <https://www.w3.org/TR/NOTE-datetime>
* <https://www.iso.org/obp/ui#iso:std:iso:8601:-2:dis:ed-1:v1:en>
* `1997-07-16`
* `1997-07-16T19:20:01.003Z`

{% hint style="success" %}
Simplifiez la vie de vos clients en convertissant les “datetimes” en UTC.
{% endhint %}


# Ressource d'Association

Supposons la ressource `/users/123ab/friends` :

```javascript
{
  "objects": [
    {
      "id": ...,
      "firstName": ...,
      "type": "user"
    } 
  ]
}
```

{% tabs %}
{% tab title="🧐" %}
Comment représenter la *datetime* de création du lien entre les utilisateurs ?
{% endtab %}

{% tab title="👍" %}
Nous pouvons créer une ressource de type collection qui représente ces liens.

Exemple : `/friendships?userId=123ab`

```javascript
{
  "objects": [
    {
      "id": "FRIENDSHIP_ID_1",
      "creationDateTime": "2017-01-01T18:16:00.000Z",
      "friend": {
        "id": ...,
        "type": "user"
      }
    },
    ...
  ]
}
```

... puis la ressource d'instance `/friendships/FRIENDSHIP_ID_1` permettant d'accéder, modifier ou supprimer la relation.\
Exemple : `DELETE /friendships/FRIENDSHIP_ID_1`.
{% endtab %}
{% endtabs %}


# Pourquoi Appliquer ces Bonnes Pratiques

Au delà de la **généricité** et la **facilité** de **compréhension** et **d’implémentation**, l’application de ses bonnes pratiques permet d’implémenter des librairies et des **connecteurs génériques** sans aucune connaissance de l’API.

* Un cache peut facilement :
  * Anticiper la récupération de la section suivante d’une ressource paginée.
  * Récupérer une ressource depuis sa collection en cache.
  * Maintenir la synchronisation entre les données locales et celles de l’API *(Progressive Web Apps)*.
  * <https://github.com/wishtack/wishtack-steroids/tree/master/packages/rest-cache>
* Un connecteur générique peut gérer les autorisations d’accès aux ressources ou même filtrer les propriétés *readonly* ou *hidden*.


# Zalando ReSTful API Guidelines

<https://opensource.zalando.com/restful-api-guidelines/#table-of-contents>

{% embed url="<https://opensource.zalando.com/restful-api-guidelines/#table-of-contents>" %}


# Les Outils


# Swagger

Swagger est un framework qui vous permet de définir *(conformément au standard* [*OpenAPI*](https://github.com/OAI/OpenAPI-Specification)*),* documenter vos APIs ReST et générer du code.

<https://swagger.io/>

{% embed url="<https://swagger.io/>" %}

<https://swagger.io/tools/swagger-editor/>


# OpenAPI Visual Editors

## Stoplight Studio

<https://stoplight.io/studio/>

{% embed url="<https://stoplight.io/studio/>" %}

## Apibldr

Apibldr est un outil permettant de définir une spécification OpenAPI via une interface visuelle facile d'utilisation.

<https://www.apibldr.com/>

{% embed url="<https://www.apibldr.com/>" %}

## OpenAPI GUI

<https://github.com/Mermade/openapi-gui>

{% embed url="<https://github.com/Mermade/openapi-gui>" %}


# IDE Plugins

<https://plugins.jetbrains.com/plugin/8347-swagger/>

{% embed url="<https://plugins.jetbrains.com/plugin/8347-swagger/>" %}

<https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi>

{% embed url="<https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi>" %}

<https://marketplace.visualstudio.com/items?itemName=Arjun.swagger-viewer>

{% embed url="<https://marketplace.visualstudio.com/items?itemName=Arjun.swagger-viewer>" %}


# Postman

Postman est un client ReST très pratique pour analyser, expérimenter et debug vos APIs ReST.

Pensez à essayer l’extension Chrome qui permet d’analyser le trafic et rejouer les requêtes.

<https://www.getpostman.com/>

{% embed url="<https://www.getpostman.com>" %}


# Insomnia

Insomnia est une alternative à Postman, pragmatique et light.

<https://insomnia.rest/>

{% embed url="<https://insomnia.rest/>" %}


# Fake & Sandbox

## Prism

Créez une Fake API à partir d'une spécification OpenAPI !

<https://github.com/stoplightio/prism>

{% embed url="<https://github.com/stoplightio/prism>" %}

## GetSandbox

<https://getsandbox.com/>

{% embed url="<https://getsandbox.com/>" %}

## JSON Server

JSON-Server est un simple outil permettant de générer des fausses APIs à des fins de démonstration par exemple.

<https://github.com/typicode/json-server>

{% embed url="<https://github.com/typicode/json-server>" %}


# JSON Generator

JSON Generator vous permet de générer facilement des données JSON pour vos tests unitaires par exemple.

<https://www.json-generator.com/>

{% embed url="<http://www.json-generator.com/>" %}


# Pact

Consumer Driven Contract Testing avec Pact

<https://docs.pact.io/>

{% embed url="<https://docs.pact.io/>" %}


# Sécurité des APIs ReST

Les architectures modernes et distribuées dont les APIs ReST font partie nous exposent à de nouveaux risques.

Il en est de même pour certains mécanismes d’authentification modernes dont les spécifications ne sont malheureusement pas assez strictes.


# OWASP Top 10

## OWASP Top 10

<https://owasp.org/www-pdf-archive/OWASP_Top_10-2017_%28en%29.pdf.pdf>

{% embed url="<https://owasp.org/www-pdf-archive/OWASP_Top_10-2017_%28en%29.pdf.pdf>" %}

## OWASP API Top 10

<https://www.owasp.org/index.php/OWASP_API_Security_Project>

{% embed url="<https://www.owasp.org/index.php/OWASP_API_Security_Project>" %}


# Authentification et Session Management

## **De quoi avons-nous besoin ?**

### **Session management ?**

Nope ! **L’API ReST doit être Stateless !**

Si des informations liées à la session doivent être maintenues, celles-ci doivent être gérées par le client.

Les données persistées par l’API ReST sont associées à des ressources.

Rien n’interdit l’expiration d’une ressource : `GET /carts/1234 => 404 Not Found`

{% hint style="warning" %}
Ne schtroumpfez pas !`SMURF /smurf-api/sessions/current`
{% endhint %}

### **Authentification**

Idéalement, il nous faudrait un mécanisme d’authentification même si les données de l’API sont publiques.

### **Identification**

L'identification n'est implémentée que si réellement nécessaire.

**L’authentification et l’identification sont des notions distinctes.**\
Il est possible d’authentifier un utilisateur sans l’identifier.\
Il est également possible d’identifier un utilisateur sans l’authentifier mais nous n’aurions aucune garantie de l’identité.

### **Logout et révocation**

Le *logout* peut provenir d’une autre source que l’utilisateur final.

{% hint style="danger" %}
Les "tokens" d’authentification **ne doivent pas être transmis dans l’URL.**\
`GET /users/123?token=asdf....`

L’authentification "basic-auth" ne doit pas être utilisée.

Les *tokens* doivent être transmis dans le *header* `Authorization`

`Authorization: Bearer xxxxxx, Extra yyyyy`
{% endhint %}

## **Mécanismes d’authentification**

Nous parcourerons plus tard les différents mécanismes d’authentification envisageables.

Globalement *(modulo quelques étapes)*, la plupart des mécanismes d’authentification fonctionnent ainsi :

1. Le service d’authentification fournit un *token* unique au client.
2. Le client transmet ce *token* aux APIs ReST du fournisseur de service.
3. Le fournisseur de service déduit les autorisations d’accès en fonction de ce *token*.

## **Session Management côté client**

Le cas le plus complexe est celui où le client est un *browser*.

Si vous souhaitez persister des données dans le *browser* afin que l’utilisateur puisse retrouver le même contexte en changeant de fenêtre ou après un *refresh* :

* Evitez absolument l’utilisation des *cookies* ne serait-ce que pour les raisons suivantes :
  * Ce n’est pas leur rôle.
  * Vous ne voulez pas envoyer toutes ces données au backend à chaque requête.
  * Cookies are EVIL !<br>

* **`IndexedDB`** et **`localStorage`** sont là pour ça.<br>

* Problème 😱

  * L'**`IndexedDB`** et le **`localStorage`** n’ont pas de notion d’expiration sauf sur Firefox :  <https://developer.mozilla.org/en-US/docs/Web/API/IDBFactory/open>.
  * Jetez un coup d’oeil au contenu de vos storages après *logout* ou fermeture du *browser*, vous serez surpris de découvrir ce qu’on y retrouve.

* Secure Storage
  * en attendant une solution *in-the-browser*, il est recommandé de chiffrer les données stockées localement avec **une clé temporaire et unique pour chaque client transmise par l’API ReST,**
  * <https://github.com/jas-/crypt.io>,
  * ou encore mieux, en stockant la clé via l'API browser `webauthn` quand celle-ci sera globalement disponible. <https://developers.google.com/web/updates/2018/05/webauthn>&#x20;


# Autorisation et Gestion des Permissions

En fonction des *credentials* du client, les autorisations et permissions d’accès sur une ressource peuvent varier.

**Une même ressource** peut donc **retourner des informations différentes en fonction du client**. Cela ne transgresse pas les principes ReST.

Par exemple, pour la même ressource *user*, un rôle *owner* et un rôle *friend* n’auront pas accès aux mêmes opérations et propriétés :

**Avec le rôle&#x20;*****owner*****&#x20;:** `GET /users/123`

```javascript
{
    "id": "123",
    "firstName": "John",
    "lastName": "DOE",
    "address": {
        "street": "...",
        ...
    }
}
```

**Avec le rôle&#x20;*****friend*****&#x20;:** `GET /users/123`

```javascript
{
    "id": "123",
    "firstName": "John",
    "lastName": "DOE"
}
```

### Les Trois Niveaux d'Autorisation

1. **Niveau ressource :** autorisation d’accès à la ressource.
2. **Niveau verbe :** méthodes autorisées sur la ressource *(create / read / update / delete)*.
3. **Niveau propriété :** gestion de l’autorisation par propriété *(read / write / mask / restricted values per role)*.\
   Ce dernier niveau est malheureusement souvent omis par la plupart des implémentations et frameworks d’API ReST.

Par exemple, la ressource *post* d’un blog peut avoir une propriété `state` pouvant prendre les valeurs suivantes : `draft`, `private` ou `published`.

Les utilisateurs avec le rôle **administrator** peuvent **modifier cette propriété**.

En revanche, les utilisateurs avec le rôle **editor** peuvent **modifier toutes les autres propriétés sauf celle-ci**.

{% hint style="success" %}
Peu importe l’implémentation, les permissions doivent être faites à base de **whitelist**.
{% endhint %}

{% hint style="success" %}
**Séparez l'implémentation fonctionnelle et la gestion des permissions.**

Pour respecter la *separation of concerns*, améliorer la *scalability* et faciliter l’implémentation et la compréhension de l’API ReST, il est recommandé d'implémenter la gestion des permissions sur un connecteur dédié.
{% endhint %}

Pour commencer, cette implémentation peut se faire dans un middleware du framework qui plus tard pourra être migré vers un service dédié ou une solution d'API management.

* Ce connecteur est similaire aux A.C.L. *(Access Control List)* que l’on retrouve dans les filesystems ou sur les firewalls.


# Validation, Canonicalization, Escaping & Sanitization

Toutes les propriétés échangées avec l’API ReST doivent être **validées par l’API**.

La validation doit également être implémentée côté client pour éviter les aller-retours inutiles.

![La validation Clien-Side n'est pas suffisante](/files/-LFLdJPNfs8oYvvsjbZt)

## Canonicalization

L’API ReST doit convertir les données reçues vers leur forme canonique ou les rejeter.

Par exemple, les données suivantes :

```javascript
{
    "firstName": "joHn",
    "lastName": "  DoE",
    "url": "myWebsite.com"
}
```

... peuvent être converties en :

```javascript
{
    "firstName": "john",
    "lastName": "doe",
    "url": "https://mywebsite.com"
}
```

## Escaping

Ce n’est pas à l’API ReST de gérer l’escaping du contenu.

Par exemple, sur un blog, le commentaire suivant est cohérent :

```markup
<img src="not-found" onerror=alert(1)>
```

C’est au client de gérer l’escaping est d’éviter les attaques de type XSS.

## **Sanitization**

La *sanitization* est un jeu dangereux qui consiste à retirer le contenu potentiellement malicieux.

Pour l’exemple précédent, cela consisterait à retirer la partie `onerror` :

```markup
<img src="not-found">
```

Mais encore une fois, il s’agit d’une problématique client.

La difficulté est qu’il est toujours possible de trouver des techniques pour *bypass* la *sanitization*.\
Certains en ont fait leur métier 😉\
<http://n0p.net/penguicon/php_app_sec/mirror/xss.html>


# Cookies are EVIL

![Cookies are Evil!](/files/-LFJYnOTuk1Rg0sZiSVe)

Les *cookies* nous **exposent à des vulnérabilités de type C.S.R.F.** *(Cross Site Request Forgery)* que nous aborderons plus tard.

Les clients ne sont **pas toujours des "browsers"** *(Mobile, Desktop, un micro-service, un partenaire)*.

Les *cookies* seront **envoyés à chaque requête**.

Il y a un **couplage fort entre le&#x20;*****cookie*****&#x20;et la session**. En revanche, en utilisant le *header* `Authorization` nous pouvons envoyer des requêtes avec des *tokens* différents à la même API.\
\
Exemple : une session avec plusieurs comptes utilisateurs sans établir aucun lien entre les utilisateurs. C’est le cas sur les services google / twitter / facebook etc… ou encore **la fonctionnalité "voir ma page facebook en tant que X".**


# C.O.R.S.

## Définitions

Cross-Origin Resource Sharing. <https://www.w3.org/TR/cors/>

L’ "origin" *(*[*https://tools.ietf.org/html/rfc6454*](https://tools.ietf.org/html/rfc6454)*)* est composé des éléments suivants :

* Scheme : http / https / …
* FQDN : [www.attacker.com](http://www.attacker.com) / api.target.com / …
* Port : 80 / 443 / 8000

## Fonctionnement

![C.O.R.S. Flowchart](/files/-LFL_5h9d2ge4x_TiWLW)

Supposons que nous avons deux “origins” : <https://attacker.com> et <https://target.com>.

### GET ou FETCH

Par défaut, si l’application [`https://attacker.com`](https://attacker.com/) émet une requête `GET` depuis le browser *(en JavaScript)* vers l’*origin* [`https://target.com`](https://target.com/), celle-ci ne **transmettra aucun des cookies des deux&#x20;*****origins***.

Le browser analysera ensuite certains *headers* C.O.R.S. *(que nous verrons plus tard)* mais en leur absence, il ne transmettra pas la réponse à l’application.\
`Error: No 'Access-Control-Allow-Origin' header is present on the requested resource`

### Autre

S’il s’agit d’une requête **autre que `GET`** *(ou similaire `HEAD`…)* le *browser* envoie une *preflight request* de type `OPTIONS` pour vérifier si la requête est autorisée en fonction de l’ *origin* et de la méthode utilisée.

## Méfiez-vous des Fausses Solutions&#x20;

Ces mécanismes ont été mis en place pour éviter les attaques de type C.S.R.F *(Cross Site Request Forgery)*.

Malheureusement, le premier résultat sur lequel on tombe en recherchant le message d’erreur est le suivant :\
<https://stackoverflow.com/questions/20035101/why-does-my-javascript-get-a-no-access-control-allow-origin-header-is-present>

On y trouve les propositions suivantes :

* “The easy way is to just add the extension in google chrome to allow access using CORS.”\
  <https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi?hl=en-US><br>
* `chrome.exe --user-data-dir="C:/Chrome dev session" --disable-web-security`<br>
* “It’s very simple to solve if you are using PHP. Just add the following script in the beginning of your PHP page which handles the request:”\
  `<?php header('Access-Control-Allow-Origin: *'); ?>`

## Sauvés par le C.O.R.S.

Après la mise en place du *header* `Access-Control-Allow-Origin: *`, la requête émise depuis l’*origin* [`https://attacker.com`](https://attacker.com/) vers  [`https://target.com`](https://target.com/) ne contient pas de cookies.

Il faut activer l’option `withCredentials` de l’objet XHR ou de la fonction `fetch` avec le paramètre `credentials: 'include'`.

La **requête est alors envoyée avec les cookies** mais encore une fois les spécifications C.O.R.S. sont rigoureuses et **il n’est pas possible de récupérer le contenu de la réponse** si le *header* `Access-Control-Allow-Origin` vaut `*`.

Pour pouvoir transmettre des cookies et récupérer la réponse, il faut configurer le *header* **`Access-Control-Allow-Credentials`** mais encore une fois, heureusement que ce n’est pas suffisant. **Cette fonctionnalité ne peut pas être activée si `Access-Control-Allow-Origin` vaut `*`**.

Il faut donc définir une *whitelist* d’*origins* mais malgré tous ces obstacles volontaires, certains vont jusqu’au bout…\
<http://stackoverflow.com/questions/26411480/angularjs-a-wildcard-cannot-be-used-in-the-access-control-allow-origin-he>

{% hint style="danger" %}
Please don’t!!!
{% endhint %}

Après cette ultime étape, n’importe quelle application depuis n’importe quel *origin* peut communiquer librement avec votre API en utilisant les *credentials* présents dans les *cookies* de l’utilisateur actuellement authentifié.

![I Will Never Use Cookies Again](https://wishtackblog.files.wordpress.com/2017/03/i-will-never-use-cookies-again.gif?w=657)

Par précaution, même en l’absence de *cookies*, il vaut mieux éviter d’utiliser la valeur `*` pour le *header* `Access-Control-Allow-Origin` sauf dans le cas d'une API publique.

Il est préférable d’implémenter une logique de *whitelist* sur l’API qui vérifie le contenu du *header* `Origin` de la requête et le renvoie dans le *header* `Access-Control-Allow-Origin` de la réponse en cas d’autorisation réussie.

**La vérification de la&#x20;*****whitelist*****&#x20;doit être stricte !** Il ne suffit pas de vérifier le FQDN.

{% hint style="success" %}
Pensez à implémenter une règle sur votre W\.A.F. (Web Application Firewall), middlewares ou monitoring sécurité pour détecter et bloquer les réponses HTTP contenant le *header* `Access-Control-Allow-Credentials`.
{% endhint %}

{% hint style="danger" %}
**Attention ! Les certificats clients et l’authentification de type "basic auth" sont également considérés comme des&#x20;*****credentials*** et on rencontre les mêmes problèmes qu’avec les *cookies*.
{% endhint %}


# C.S.R.F.

Cross-Site Request Forgery (ou X.S.R.F.)

![Cross-Site Request Forgery](/files/-LFLd8_P-OVBXxOE_A4r)

**Cross-Site Request Forgery** est une attaque *in-the-browser* dont le scénario est le suivant :

1. Un utilisateur *(victime)* doit être authentifié sur l’application vulnérable.<br>
2. L'attaquant doit réussir à **faire visiter une application** qu’il contrôle *(entièrement ou partiellement)* par la victime.<br>
3. Lors de la visite de la victime, **l'attaquant déclenche une opération sur l’application vulnérable** en utilisant implicitement les *credentials* de la victime.\
   *On suppose qu’une requête de type GET ne peut pas déclencher d’opération sensible car autrement il suffirait de rediriger l’utilisateur vers l’URL en question.*<br>
4. Si les règles C.O.R.S. sont désactivées par l’un des moyens décrits dans le chapitre C.O.R.S., l'attaquant peut simplement déclencher une requête POST de son choix à destination de l’application vulnérable en utilisant les *credentials* de la victime.

{% hint style="warning" %}
Si la *whitelist* d'origins n’effectue pas une vérification rigoureuse, l'attaquant pourrait éventuellement contrôler le domaine **http** de l’application vulnérable en ciblant le domaine **https** de l’application vulnérable.
{% endhint %}


# C.S.R.F. & Media Type

L’une des erreurs classiques est d’accepter des *media types* autres que `application/*json` *(header `Content-Type`)*.

Sans aucune autre erreur de configuration C.O.R.S., l’acceptation du *media type*`application/x-www-form-urlencoded` permet à l’attaquant de créer un formulaire et de déclencher une simple requête POST.

```javascript
document.querySelector('form').submit()
```

Dans ce cas, la plupart des frameworks *(Ex. : expressjs)* récupèrent un objet :

```javascript
{
   email: 'pwned.by@attacker.io',
   grants: 'all'
}
```

{% hint style="danger" %}
**Il ne faut donc activer que le&#x20;*****parser*****&#x20;JSON.**
{% endhint %}

… mais supposons qu’il soit activé sur tous les "media types" et plus particulièrement `text/plain` pour simplifier la vie des développeurs "client-side".

1\. L’attaquant n’a plus qu’à adapter légèrement le formulaire précédent :

```markup
<form
    method="POST"
    action="https://app.vulnerable.com/api/products/0/admins"
    enctype="text/plain">
    <input
         name='{"email": "pwned.by@attacker.io", "grants": "all", "extra": "'
        value='"}'>
</form>
```

2\. Cela va alors envoyer le "body" suivant :

```javascript
{"email": "pwned.by@attacker.io", "grants": "all", "extra": "="}
```

3\. L’API va alors "parser" le contenu suivant :

```javascript
{
    email: 'pwned.by@attacker.io',
    grants: 'all',
    extrat: '='
}
```

{% hint style="danger" %}
**La vérification du&#x20;*****media type*****&#x20;des requêtes doit donc être rigoureuse.**
{% endhint %}


# C.S.R.F. Mitigation

En attendant l’abandon des *credentials* de type *cookie*, *basic auth* et certificat client, une solution de mitigation des attaques de type C.S.R.F. est de positionner un *cookie* *(non http-only)* contenant **un&#x20;*****token*****&#x20;aléatoire et imprévisible&#x20;*****(un nonce)***.

L’application client *(JavaScript)* doit alors envoyer la valeur de ce *token* dans le *header* `Authorization` *(Ex. : `Authorization: Bearer ..., Csrf: ...`)* à chaque requête.

L’API n’a plus qu’à **comparer les deux valeurs présentes dans le&#x20;*****cookie*****&#x20;et dans le&#x20;*****header*** pour s'assurer qu'il s'agit du bon token.

*En effet, dans le cas d'une attaque C.S.R.F., l'attaquant utilise les cookies sans y avoir accès en lecture ou écriture.*


# C.S.R.F. & "Resource Linking"

Nous avons évoqué [précédemment](/api-rest/h.a.t.e.o.a.s.-et-resource-linking) le problème de confiance lié au *resource linking* en général.

Un client *(browser ou autre)* **communique** généralement avec **plusieurs APIs**.

Une réponse malicieuse ou simplement maladroite d’une API pourrait pousser le client à forger une requête vers une autre API en envoyer le token d’authentification ou encore d’autres informations confidentielles.

```javascript
{
    "firstName": "Foo",
    "address": {
        "href": "https://api.attacker.com/"
    }
}
```

Il est possible de se protéger partiellement avec des règles C.S.P. *(Content Security Policy)* `connect-src`\
<https://w3c.github.io/webappsec-csp/>

Autrement, il est recommandé d’implémenter ou d’utiliser une librairie HTTP robuste avec des *whitelists* strictes ou le rejet d’URLs absolues bien qu’une URL relative puisse être également malicieuse.


# J.O.S.E.

JavaScript Object Signing and Encryption

JOSE est un framework destiné à fournir une méthode pour transférer de manière sécurisée des **claims** *(informations d’autorisations par exemple)* entre différentes **entités**.\
<https://datatracker.ietf.org/wg/jose/charter/>

JOSE définit principalement les 4 éléments suivant :

### [**J.W.K. : JSON Web Key**](/securite-des-apis-rest/j.o.s.e./j.w.k.)

Définit le format de la représentation JSON d’une clé cryptographique symétrique ou asymétrique.

### [**J.W.S. : JSON Web Signature**](/securite-des-apis-rest/j.o.s.e./j.w.s.)

Définit la représentation d’un contenu signé.

### [**J.W.E. : JSON Web Encryption**](/securite-des-apis-rest/j.o.s.e./j.w.e.)

Définit la représentation d’un contenu chiffré.

### [**J.W.T. : JSON Web Token**](/securite-des-apis-rest/j.w.t.)

Définit une représentation compact et URL-safe d’un **token** *(optionnellement signé ou chiffré ou signé **puis** chiffré)* ainsi que les **claims** standardisés et enregistrés auprès de l’IANA.

{% hint style="warning" %}
JOSE ne définit pas de mécanisme d’authentification ou d’autorisation.
{% endhint %}


# J.W\.K.

JSON Web Key

#### Clé symétrique destinée à du chiffrement AES256 avec validation d’un hash HMAC SHA512.

```javascript
{
    "kty": "oct", // Key type : Octet Sequence.
    "alg": "A256CBC-HS512", // Algorithm intended for this key.
    "k": "GawgguFyGrWKav7AX4VKUg" // Key.
    "kid": "0" // Key Id.
}
```

#### Clé publique asymétrique destinée à la signature avec sa chaîne de certification X509.

```javascript
{
    "kty": "RSA", // Key type: RSA.
    "alg": "RS512", // RSA SHA512.
    "use": "sig", // signature.
    "kid": "1b94c", // Key Id.
    "n": "vrjOfz9Ccdgx5nQudyhdoR17V...",
    "e": "AQAB",
    "x5c": ["MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCS...A6SdS4xSvdXK3IVfOWA=="]
}
```

#### Clé privée asymétrique destinée au chiffrement.

```javascript
{
    "kty": "RSA",
    "kid": "3j4h",
    "use": "enc",
    "n": "t6Q8PWSi1dkJj9hTP8hNYF...PFGGcG1qs2Wz-Q",
    "e": "AQAB",
    "d": "GRtbIQmhOZtyszfgKdg4...SdSgqcN96X52esAQ",
    "p": "2rnSOV4hKSN8sS4Cgc...Ngqh56HDnETTQhH3rCT5T3yJws",
    "q": "1u_RiFDP7LBYh3N4GXL...TB7LbAHRK9GqocDE5B0f808I4s",
    "dp": "KkMTWqBUefVwZ2_Dbj1...2pYhEAeYrhttWtxVqLCRViD6c",
    "dq": "AvfS0-gRxvn0bwJoMSnF...Y63TmmEAu_lRFCOJ3xDea-ots",
    "qi": "lSQi-w9CpyUReMErP1RsBL...2lNx_76aBZoOUu9HCJ-UsfSOI8"
}
```


# J.W\.S.

JSON Web Signature

#### Représentation d’un contenu signé

```javascript
{
    "payload": "eyJpc3MiOiJqb2...kjp0cnVlfQ",
    "signatures": [
        {
            "protected":"eyJhbGciOiJSUzI1NiJ9",
            "header": {"kid":"123"},
            "signature": "cC4hiUPoj9E...cN_IoypGlUPQGe77Rw"
        },
        {
            "protected":"eyJhbGciOiJFUzI1NiJ9",
            "header": {"kid":"456"},
            "signature": "DtEhU3ljbEg8L38VWA...Kg6NU1Q"
        }
    ]
}
```

{% hint style="warning" %}
Il est possible d’utiliser des clés symétriques pour authentifier un message avec HMAC.\
**Il s’agit alors d’un message authentication code et non d’une signature**.
{% endhint %}


# J.W\.E.

JSON Web Encryption

#### Représentation d’un contenu chiffré

```javascript
{
    // Integrity protected header but not encrypted!
    "protected": "eyJlbmMiOiJBMTI4Q0JDLUhTMjU2In0",
    "unprotected": {"jku":"https://server.example.com/keys.jwks"},
    "recipients":[
        {
            // Key and Alg hints.
            "header": {"alg":"RSA1_5","kid":"123"},
            // Encryption key encrypted using 123's public key.
            "encrypted_key": "UGhIOguC7IuEvf_N...XMR4gp_A"
        },
        {
            "header": {"alg":"A128KW","kid":"456"},
            "encrypted_key": "6KB707dM9YTIgHt...2IlrT1oOQ"
        }
    ],
    "iv": "AxY8DCtDaGlsbGljb3RoZQ",
    // Encrypted message.
    "ciphertext": "KDlTtXchhZTGufMYmO...4HffxPSUrfmqCHXaI9wOGY",
    // AEAD authentication tag.
    "tag": "Mz-VPPyU4RlcuYv1IwIvzw"
}
```

{% hint style="info" %}
Le chiffrement asymétrique a une taille limite de message *(modulo – padding)*. C’est pour cette raison que l’on génère une clé symétrique à la volée qui est ensuite chiffrée avec la clé publique asymétrique.
{% endhint %}


# J.W\.T.

JSON Web Token


# Description et Fonctionnement de JWT

JWT définit la structure d’un token *(**chiffré**, **signé** ou **non sécurisé**)* permettant de véhiculer des **claims** standards, publics ou privés.[ https://www.iana.org/assignments/jwt/jwt.xhtml](https://www.iana.org/assignments/jwt/jwt.xhtml)

Pour faciliter la transmission de tokens JWT, ce dernier est sérialisé dans un format compact *(qui est également applicable à JWE et JWS)*.

Chaque bloc *(header / payload / signature etc…)* est encodé en base64 URL-safe et séparé par un `.`.

Cf. <https://jwt.io/>

### Exemple

```javascript
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOmZhbHNlfQ.
FMy5mxG5mDjL4rW8defHN2fZ_U_ypDW6hUT-Oan2F6P36NzCEHq85IXWUChQc5vzCXa_SHWK9j1ZZG3vRwuEkEH-lA_FNPL2EAQjdqq_qxMhaS5SscW8RVb30rd7lw1-OvEESrKcAtqipDmkufpsv3R3YWBItF1Uev0wF1U9QGU
```

#### Header

```javascript
{"alg":"RS256","typ":"JWT"}
```

#### **Payload**

```javascript
{"sub":"1234567890","name":"John Doe","admin":false}
```

#### **Signature**

```
14ccb99b11b99838cbe2b5bc75e7c73767d9532a435ba8544ce6a7d85e8fdfa3730841eaf392175940a141ce6fcc25da48758af63d59646def470b849041e500534f2f610042376aaaac4c85a4b94ac716f1155bdf4addee5c353af1044ab29c02daa2a439a4b9fa6cbf7477616048b45d547afd3017553d4065
```


# Usages et Avantages

Les tokens JWT sont le plus souvent utilisés pour l’authentification et l’autorisation. *(Ex. : SSO, session, web sockets, email links, etc…)*.

Ils peuvent également être utilisés pour échanger ou stocker des données chiffrées ou signées.

Contrairement à des formats binaires ou des formats XML tel que SAML, les tokens JWT sont :

* relativement légers,
* faciles à manipuler *(librairies disponibles dans de nombreux environnements),*
* faciles à manipuler et à archiver *(Ex. : stockage dans une base MongoDB)*.


# Utilisation de JWT pour l’Authentification

![Utilisation de JWT pour l'Authentification](/files/-LFLzFBhd6BC0OrzSDGi)

![Exemple d'utilisation JWT](/files/-LFM2JXTAdGTC4tNBtsZ)

Vu que le token JWT contient toutes les informations nécessaires concernant l’identité du **Resource Owner** et qu’il est signé, il n’est pas nécessaire de vérifier le **token** auprès d’une base de données ou un service distant. Cela peut **augmenter considérablement les performances et la&#x20;*****scalability*** du service si aucun autre mécanisme *(de cache par exemple)* n’est mis en place.

La plupart des tokens JWT contiennent des informations liées à au **Resource Owner** et sont le plus souvent stockées sur le device de l’utilisateur *(E.g. : Local Storage)*.

{% hint style="success" %}
Il est donc recommandé de générer des tokens JWT signés puis chiffrés mais cela augmentera considérablement la taille du token qui sera envoyé à chaque requête authentifiée.
{% endhint %}


# JWT, Authentification, Sessions et Risques Sécurité

Avant d’aborder les aspects sécurité, les tokens JWT utilisés pour l’authentification ou la gestion de session sont accompagnés des problèmes suivants :

* **Taille importante** *(particulièrement en activant le chiffrement)*.<br>
* **Immutabilité des claims**.\
  Il faut générer de nouveaux de tokens JWT pour transmettre les valeurs mises à jours des **claims**.<br>
* **No key policy.**\
  \
  JWT ne définit aucune contrainte de sécurité concernant la gestion des clés *(génération des clés symétriques, rotation des clés etc…).*<br>
* **HMAC n’est pas un algorithme de signature.**\
  De nombreuses documentations et implémentations utilisent le HMAC pour authentifier les tokens JWT en parlant de signature.<br>
* **Pas d’invalidation.**\
  Bien que les tokens JWT peuvent contenir une date d’expiration, JWT ne peut définir aucun moyen pour révoquer ou invalider un token JWT.
  * Euh… comment gérer le *logout* ?
  * La seule solution possible est de stocker une information quelque part *(liste des tokens invalidés, heure de logout, etc…)*
  * Cela nécessite alors de vérifier cette information à chaque présentation d’un token. On perd alors la plus grande partie de l’intérêt de l’utilisation des tokens JWT.

## **Politique de sécurité des clés privées TLS**

Analysons la politique de sécurité généralement associée aux clés privées TLS.

Il est généralement recommandé *(et de plus en plus pratiqué)* d’utiliser une machine dédiée au chiffrement/déchiffrement des échanges TLS. Ainsi, si une application est compromise, **la clé privée en mémoire n’est pas dévoilée**.

Les clés sont **renouvelées régulièrement**.

**Les clés sont protégées par des passphrases** qui nécessitent parfois la présence de plusieurs personnes qui détiennent différentes parties de la passphrase.

Que se passe-t-il en cas d’usurpation d’une clé privée TLS ?

* L’attaquant doit combiner cette attaque avec une attaque de type Man-In-The-Middle *(ARP Poisoning, DNS Cache Poisoning…)*.<br>
* L’attaquant ne pourra impacter généralement qu’une partie géographique donnée et uniquement les utilisateurs connectés pendant la durée de l’attaque.<br>
* Dès détection, il est possible de révoquer rapidement le certificat associé et grâce à des protocoles tels que l’OCSP, les clients refuseront ce certificat.\
  <https://tools.ietf.org/html/rfc6960>

![T.L.S. Security](/files/-LFM2ipmK9O2aaNi4kkt)

## **Risque d’usurpation des clés privées JWT**

Analysons maintenant une mise en place classique d’une authentification JWT.

La clé privée du serveur d’authentification est malheureusement souvent stockée dans une variable d’environnement, une base de données ou encore un fichier *(en espérant qu’il ne finisse pas sur le Version Control System).*\
<https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server/wiki/Key-generation>\
<http://django-oidc-provider.readthedocs.io/en/v0.4.x/sections/serverkeys.html>

La clé privée peut être dévoilée de différentes façons :

* Accès au Version Control System.
* Injection SQL.
* Insecure remote file access.
* Dump des variables d’environnement en cas d’erreur.

Si l’attaquant récupère la clé privée, il peut simplement forger des tokens JWT avec des **claims** arbitraires. Il peut alors récupérer les données de tous les utilisateurs dont l’authentification repose sur JWT.

![J.W.T. Security](/files/-LFM2zgqLCMA1U3k6fit)

## **`none` alg**

Malheureusement, un token JWT peut également utiliser un algorithme **`none`** qui n’est donc ni chiffré ni signé.

L’attaquant peut donc forger des tokens JWT avec la valeur **`none`**&#x70;our la propriété **`alg`**.

Si l’implémentation de vérification du token se base sur la propriété **`alg`**, elle est alors vulnérable et peut éventuellement accepter des tokens utilisant l'**`alg`** **`none`**.

## `Alg` swapping

Certaines implémentations peuvent être vulnérables à une attaque qui consiste à utiliser la valeur **`HS256`** pour la propriété **`alg`**. **L’implémentation utilise alors la clé publique RSA comme clé symétrique pour vérifier le HMAC.**

* <https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/>


# Recommandations JWT

Il faut utiliser des **clés RSA** pour la signature.

Il faut instaurer une **rotation régulière et automatique des clés**.\
Les clés publiques doivent être publiées automatiquement également.

Etant donné que la rotation doit avoir une durée plus longue que la durée de vie des tokens, il faut réduire la durée de vie des tokens. *(E.g. : l’implémentation OpenID Connect de Google semble appliquer une rotation de 3 à 4 jours mais je recommanderais une durée encore plus courte)*

Pour réduire les risques, utilisez de nombreuses clés.

Idéalement, les clés secrètes ne devraient être manipulées que par des services dédiés hautement sécurisés avec des mécanismes de monitoring avancés type HSM (Hardware Security Module) ou KMS (Key Management Service).

### Azure / AWS / GCP KMS

{% embed url="<https://azure.microsoft.com/en-us/services/key-vault/>" %}

{% embed url="<https://aws.amazon.com/kms/>" %}

{% embed url="<https://cloud.google.com/security-key-management>" %}

### Exemple d'utilisation NodeJS / AWS

{% embed url="<https://github.com/jonathankeebler/jwt-kms>" %}

### Hashicorp's Vault Transit keys

*...et non pas en simple KV Secret Engine*

<https://www.vaultproject.io/api/secret/transit#sign-data><br>

### JWT comme mécanisme complémentaire

Les tokens JWT peuvent être utilisés comme **mécanisme complémentaire** d’un mécanisme de token classique. On peut wrapper des tokens dans un token JWT afin de vérifier rapidement leur validité et leur expiration avant de le vérifier auprès d’une base de données ou d’un tiers.


# OAuth 2

{% hint style="info" %}
Comme son nom ne l’indique pas, **OAuth 2** est un protocole **d’autorisation** et non d’authentification.
{% endhint %}

OAuth 2 est l’un des standards *(stade IETF: Proposed Standard)* d’autorisation les plus communs et répandus du Web.


# OAuth 2 Roles

OAuth 2 définit 4 rôles.

### Resource Owner

Une entité disposant de la légitimité et du pouvoir décisionnel lui permettant d’autoriser l’accès à une ou plusieurs ressources protégées.

*E.g.: L’utilisateur des services google qui souhaite autoriser une application d'agrégation d'agendas à accéder à son agenda.*

### Resource Server

Ce service détient les ressources protégées. Il est capable de répondre aux requêtes d’accès à ces ressources en fonction des *access tokens* présentés.

*E.g. : Google Calendar.*

### Client

Une application émettant des requêtes à destination du **Resource Server** au nom du **Resource Owner** et avec son autorisation.\
Le **Client** peut être entièrement frontend *(Web / Progressive Web App / Mobile Web App / Desktop etc…)* ou backend *(Serveur / Minitel etc…)*.

*E.g. : L’application d’agrégation d’agendas.*

Nous distinguerons deux types de **Clients**

#### Confidential

Capable de garder un secret.\
\
*E.g. : Backend*

#### Public

Incapable de garder un secret.

*E.g. : Frontend, Single Page App, Progressive Web App, Mobile, Desktop, Appliance…*

### **Authorization Server**

Un serveur qui fournit des *access tokens* après authentification du **Resource Owner** et obtention des autorisations.

*E.g. : Service d’authentification et d’autorisation google. Google accounts.*


# OAuth 2 Abstract Flow

![OAuth 2 Abstract Flow](/files/-LFLmta_Fzoh9RB7sCZI)

OAuth 2 propose 4 *flows* différents dont le plus commun est le [Authorization Code Flow](/securite-des-apis-rest/oauth-2/oauth-2-authorization-code-flow).


# OAuth 2 Authorization Code Flow

![OAuth 2 Authorization Code Flow](/files/-LFLnEzrsukjKk5mlO1F)

1. Le **Client** redirige le **Resource Owner** vers l'**Authorization Server** :

```javascript
https://accounts.google.com/o/oauth2/v2/auth?
response_type=code
&client_id=CLIENT_ID
&redirect_uri=https://cal-tracker.wishtack.io/oauth/callback // optional
&scope=email%20calendar:read
&state=... // state is recommended thus optional 😢
```

* **`client_id`** : ID unique du **Client**.
* **`redirect_uri`** : Une des URLs de retour parmi la liste transmise à l'**Authorization Server** à l'enregistrement. Si le paramètre est absent, l'**Authorization Server** utilisera la valeur par défaut configurée lors de l'enregistrement.
* **`scope`** : liste des autorisations demandées.
* **`state`** : Paramètre malheureusement optionnel permettant au **Client** de retrouver le contexte d'initiation de la demande. Il sert surtout à transmettre un "nonce" *(token aléatoire)* pour des raisons de sécurité.
* Le **Resource Owner** confirme ou rejette les autorisations d’accès demandées sur l’interface proposée par l'**Authorization Server**.
* Le **Client** reçoit l'**Authorization Code** par redirection *(paramètre `code`)* :

```javascript
https://cal-tracker.wishtack.io/oauth/callback?
code=AUTHORIZATION_CODE
&state=...
```

1. Le **Client** peut alors échanger l'**Authorization Code** contre un **Access Token** auprès de l’API OAuth 2 de l'**Authorization Server**.

```javascript
POST https://accounts.google.com/token

client_id=CLIENT_ID
&client_secret=CLIENT_SECRET
&grant_type=authorization_code
&code=AUTHORIZATION_CODE
&redirect_uri=https://cal-tracker.wishtack.io/oauth/callback
```

* **`client_secret`** : Secret du **Client** configuré lors de l'enregistrement.
* En cas de succès, le **Client** reçoit alors l'**Access Token** et un **Refresh Token** optionnel :

```javascript
{
    "access_token": "ACCESS_TOKEN",
    "token_type": "bearer",
    "expires_in": 2592000,
    "refresh_token": "REFRESH_TOKEN",
    "scope": "email,calendar:read",
    "some_user_info":{
        "first_name": "John",
        "last_name": "DOE",
        "email": "j.doe@ibm.com",
        "is_cool": "definitely not!"
    }
}
```

En cas d’expiration de l'**Access Token** et si le **Client** a reçu un **Refresh Token**, le **Client** peut renouveler sa demande avec le **Refresh Token** et obtenir de nouveaux **Access Token** et **Refresh Token**.

L'**Authorization Code** est un code à usage unique dont la durée de vie doit être très courte ***(moins de 10 minutes)***.


# OAuth 2 Implicit Flow

L'**Implicit Flow** est un mode dégradé de l'[**Authorization Code Flow**](/securite-des-apis-rest/oauth-2/oauth-2-authorization-code-flow).

Il est inévitable quand le **Client** est public *(non confidentiel).*

![OAuth 2 Implicit Flow](/files/-LFLsdRcN1UMrOGT2I4o)

1\. Le **Client** redirige le **Resource Owner** vers l'**Authorization Server** :<br>

```javascript
https://accounts.google.com/o/oauth2/v2/auth?
response_type=token
&client_id=CLIENT_ID
&redirect_uri=CALLBACK_URL
&scope=read
&state=...
```

2\. Le **Resource Owner** confirme ou rejette les autorisations d’accès demandées sur l’interface proposée par l'**Authorization Server**.

3\. L'**Authorization Server** redirige le **Resource Owner** vers le **Client** qui reçoit alors directement l'**Access Token** dans le fragment de l’URL.

```javascript
https://cal-tracker.wishtack.io/oauth/callback#
access_token=ACCESS_TOKEN
&token_type=bearer
&scope=...
&state=...
```

Le fragment est utilisé pour éviter de transmettre l'**Access Token** au service de "hosting" de l'application.

4\. Le **User-Agent** suit donc la redirection mais le fragment ne quitte pas le **Device**.

5\. Le **Client** exécute alors le code permettant d’extraire l'**Access Token** du fragment.

{% hint style="warning" %}
Certains “User-Agents” coquins ont tendance à perdre les fragments\
<https://bugs.webkit.org/show_bug.cgi?id=24175>
{% endhint %}

{% hint style="danger" %}
Le **Resource Owner** détient l'**Access Token** et peut donc court-circuiter le **Client** pour communiquer directement avec le **Resource Server**.

Autrement dit, en cas de **man-in-the-middle**, un attaquant peut demander des autorisations au nom du **Client** et utiliser librement l'**Access Token** pour communiquer avec le **Resource Server**.

C’est pour ces raisons entre autres qu’il est recommandé d’utiliser l'[Authorization Code Flow](/securite-des-apis-rest/oauth-2/oauth-2-authorization-code-flow).
{% endhint %}


# OAuth 2 Resource Owner Password Credentials Flow

Dans les rares cas où le lien de confiance entre le **Resource Owner** et le **Client** est très fort, le **Resource Owner** peut transmettre ses "credentials" directement au **Client**.

Le **Client** transmet alors les *credentials* à l'**Authorization Server** pour obtenir un **Access Token**.

```javascript
POST https://accounts.google.com/token?
grant_type=password
&username=...
&password=...
&client_id=CLIENT_ID // If client is confidential 
&client_secret=CLIENT_SECRET // If client is confidential
```

Ce **Flow** est rarement implémenté pour les raisons suivantes :

* Le **Resource Owner** ne peut pas valider les autorisations demandées.
* Il ne permet pas d’autres modes d’authentification que le password.
* Les **Client**s ne sont pas conçus pour véhiculer des credentials.
* Ce **Flow** est un peu le *default* d’un *switch/case* qu’on trouve souvent dans les specs afin d’augmenter les chances d’adoption.
* **OpenID Connect** n’interdit pas l’utilisation de ce **Flow** afin d’être compatible avec **OAuth 2** mais il est complètement occulté de la spec.


# OAuth 2 Client Credentials

Le **Client** peut demander un **Access Token** à l'**Authorization Server** afin d’accéder à ses propres données.

```javascript
POST https://accounts.google.com/token

grant_type=client_credentials
&client_id=CLIENT_ID
&client_secret=CLIENT_SECRET
```


# OAuth 2 Registration

Avant qu’un **Client** ne puisse communiquer avec un **Authorization Server**, il faut procéder à un enregistrement.

Cet enregistrement peut se faire de différentes manières *(hors spec OAuth 2)*.

* Par API.
* Via une interface applicative.
* Offline.
* Un mix.
* L'**Authorization Server** doit à minima obtenir les informations suivantes :
  * **Client type** : **confidential** ou **public**.
  * **Redirect URIs** : La ou les URIs vers lesquelles l'**Authorization Server** redirigera le **Resource Owner** après validation des autorisations.
* Si le **client type** est **public**, l'**Authorization Server** ne fournira pas de **Client Secret** et peut restreindre l’accès aux ressources.


# OAuth 2 Risques & Recommandations

**TLS EVERYWHERE!** OAuth 2 n’oblige malheureusement pas l’utilisation du TLS pour les "Redirect URI" mais le recommande fortement.

L'**Authorization Server** doit **vérifier** que le **Client** possède bien le **nom de domaine** associé à la **Redirect URI**. *(E.g. : Vérification de la capacité du client à ajouter une entrée DNS TXT proposée aléatoirement par l'**Authorization Server**)*

L'**Authorization Server** devrait permettre aux **Client**s de **renouveler rapidement le Client Secret.**

L'**Authorization Server** devrait obliger les **Client**s à **renouveler régulièrement le Client Secret.**

* Le **Client Secret** est souvent stocké dans une variable d’environnement sur le serveur d’application.
* Une erreur de configuration suffit pour compromettre tout le système.
* L'**Authorization Server** peut permettre de modifier le **Redirect URI** simplement en présentant le **Client ID** et le **Client Secret**.

Les **Redirect URIs** sont des absolute URIs *(scheme, fqdn, port, path)* et ne doivent pas contenir de “fragment” *(#…)*.

L'**Authorization Server** doit vérifier les **Redirect URI**s avec une **égalité stricte**.\
E.g. : [`https://www.wishtack.com/oauth?source=test`](https://www.wishtack.com/oauth?source=test) est strictement différente de [`https://www.wishtack.com/oauth`](https://www.wishtack.com/oauth).


# OAuth 2 Substitution Attack

## **Description de l’Attaque**

Cette attaque suppose que l’attaquant et la victime sont des **Resource Owners** inscrits auprès du même **Authorization Server**.

1. L’attaquant initie un **Authorization Code Flow** *(ou **Implicit Flow**)*.
2. L’attaquant interrompt le scénario à[ l’étape 3 ](/securite-des-apis-rest/oauth-2/oauth-2-authorization-code-flow)*(quand il obtient l'**Authorization Code** ou l'**Access Token**)*.
3. L’attaquant incite la victime à suivre un lien pointant vers l’URL contenant l'Authorization code ou l'**Access Token** obtenu à l’étape précédente *(social engineering ou application malicieuse)*.
4. La victime suit l’URL vers le **Client** qui interagit alors avec les ressources de l’attaquant.

## **Quelques Exemples de Scénarios**

### Banque

1. L’attaquant et la victime sont clients d’une même banque.
2. La victime se retrouve alors sur l’application de la banque avec les données de l’attaquant.
3. En pensant télécharger son propre RIB, la victime récupère le RIB de l’attaquant.

### Messagerie

1. L’attaquant usurpe l’identité de la victime en créant un faux compte sur Facebook.
2. L’attaquant ajoute des "amis" de la victime.
3. L’attaquant s’inscrit sur une application de messagerie utilisant le service OAuth 2 de Facebook.
4. La victime se retrouve sur l’application de messagerie avec le compte de l’attaquant et échange avec ses propres amis via ce compte.
5. L’attaquant se connecte à son tour sur l’application de messagerie pour récupérer les correspondances de la victime.

## **Origine de la Vulnérabilité et Solution**

Cette vulnérabilité existe car OAuth 2 n’impose aucun lien entre l’étape 1 *(**demande** de l'**Authorization Code** ou **Access Token**)* et l’étape 3 *(**récupération** de l'**Authorization Code** ou **Access Token**)*.

Heureusement, il existe un paramètre **optionnel** `state`, initialement prévu pour que le **Client** puisse retrouver son état initial après l’autorisation.

Ce paramètre est désormais détourné de son objectif initial. Il permet de lutter contre cette attaque en vérifiant que le **Resource Owner** autorisé est bien celui à l’origine de la demande.

Cela s’implémente le plus souvent de la façon suivante :

1. Le **Client** génère un **nonce** imprédictible et unique à chaque demande d’autorisation.
2. Le **Client** le positionne par exemple dans un "cookie" et dans le paramètre `state` avant de rediriger le **Resource Owner** vers l'**Authorization Server**.
3. L'**Authorization Server** redirige alors le **Resource Owner** vers le **Client** en transmettant le `state` à l’identique.
4. Le **Client** vérifie que le `state` correspond au **nonce** dans le cookie.

Malheureusement, il s’agit d’une vulnérabilité conceptuelle dans le standard OAuth 2 et qui se joue à un mot près.

Le paramètre `state` est donc “RECOMMENDED” au lieu d’être “REQUIRED” laissant ainsi le choix au **Client** de rester vulnérable à cette attaque.

Si l'**Authorization Server** rend ce paramètre obligatoire, il n’est alors plus conforme au standard.

**OpenID Connect** ajoute une notion de **nonce** plus explicite mais pour rester compatible avec OAuth 2, ce paramètre est également optionnel 😭.

## **Solution et Contournements**

La solution la plus rigoureuse est de rendre le paramètre `state` obligatoire mais bien sûr, sans vérification côté **Client**, ce paramètre est inutile. Par contre, on sort alors du standard.

L'**Authorization Server** peut réduire le périmètre d’autorisation en l’absence du paramètre `state`.

C’est l’une des raisons pour lesquelles il est nécessaire de réduire la durée de vie de l'**Authorization Code** au minimum. En revanche, si le client utilise l'**Implicit Flow**, on ne peut pas réduire la durée de vie de l'**Access Token** à quelques minutes.


# OpenID Connect

**OpenID Connect** *(OIDC)* est un surcouche d’OAuth 2 permettant d’ajouter de nouvelles fonctionnalités concernant l’authentification et l’identification. <http://openid.net/connect/>

C’est un standard de la OpenID Foundation.

**OpenID Connect** est donc **compatible** avec les implémentations **OAuth2**.

On retrouve enfin de nombreux concepts intéressants similaires à ceux de "feu" Liberty Alliance Project : <http://www.projectliberty.org/>


# Terminologie

### **OpenID Provider**

OAuth 2 Authorization Server capable d’authentifier l'**End-User** *(Resource Owner)* et transmettre des **claims** au **Relying Party** *(Client)*.

### **Relying Party**

OAuth 2 Client capable de demander des **claims** à l'**OpenID Provider**.

### **End-User**

OAuth 2 Resource Owner.


# Quoi de Neuf ?

**OpenID Connect** fournit les fonctionnalités supplémentaires suivantes :

### Authentification et Réauthentification

Le **Relying Party** peut demander à l'**OpenID Provider** **d’authentifier** ou **réauthentifier** l'**End-User**.

### Hint

Il peut **transmettre des informations supplémentaires** *(hint)* comme l’identifiant du **End-User** pour faciliter la phase d’authentification.

### J.W\.T.

OpenID Connect peut utiliser des tokens JWT mais on peut éviter de les transmettre au **End-User**.

### **Claims Distribués et Agrégés**

* Les données du **End-User** sont souvent distribuées entre plusieurs **OpenID Providers**.<br>
* Avec **OpenID Connect**, un **OpenID Provider** peut agréger les **claims** ou fournir toutes les informations nécessaires pour les récupérer chez un d’autres **OpenID Providers**.

### **Logout**

Lorsqu’un **End-User** logout de l'**OpenID Provider**, ce dernier peut notifier les **Relying Parties** par différents mécanismes.

### **Dynamic Client Registration**

Certains **OpenID Providers** peuvent activer cette fonctionnalité permettant à des “Relying Parties” de s’inscrire dynamiquement.

### **Discovery**

L'**OpenID Provider** peut fournir publiquement des informations permettant aux autres entités *(**Relying Party**, **OpenID Provider**, …)* de découvrir dynamiquement les fonctionnalités et la configuration de l'**OpenID Provider**.\
<https://accounts.google.com/.well-known/openid-configuration>

### Claims et Scopes Supplémentaires

**OpenID Connect** définit quelques **claims** supplémentaires.

**OpenID Connect** définit quelques **scopes** qui englobent plusieurs **claims**.

![OpenID Connect](https://wishtackblog.files.wordpress.com/2017/03/openid-connect-map.png?w=748)


# OpenID Connect Flows

**OpenID Connect** définit 3 flows :

### **Authorization Code Flow**

Il est identique à celui d’OAuth 2 mais on y ajoute quelques paramètres supplémentaires et l'**OpenID Provider** retourne un **`id_token`** qui est un token JWT.\
Cf. <http://openid.net/specs/openid-connect-basic-1_0.html#RequestParameters>

Dans ce flow, le token JWT est échangé directement entre l'**OpenID Provider** et le **Relying Party** sans passer par le **User-Agent**. La signature du token est simplement une sécurité supplémentaire au dessus de la sécurité du canal TLS.

### **Implicit Flow**

Il est également identique à celui d’OAuth 2 et fournit un **`id_token`**.

On retrouve les mêmes risques et inconvénients qu’avec OAuth 2 avec le risque supplémentaire lié au fait que l'**`id_token`** est un token JWT.

### **Hybrid Flow**

Ce flow permet de fusionner les deux flows précédents.

![OpenID Connect Flows Properties](/files/-LFM6o-_Izv7DaN2gMjX)


# Que Faire ?

Malheureusement, **OpenID Connect** ne définit aucune règle concerne la signature des tokens JWT, le stockage et la rotation des clés.

**OpenID Connect** est l’un des standards les plus avancés actuellement sur les aspects authentification, autorisation et gestion d’identité en général.

Il faut idéalement éviter l'**Implicit Flow**.

Il faut utiliser des **clés asymétriques**.

Il faut mettre en place **une rotation régulière des clés**.


# Autres Spécifications

Nous constatons que dès la conception d’une simple API ReST, de nombreux choix s’offrent à nous *(et on se retrouve comme des gamins chez Toys R Us)*.

Pourquoi ces choix sont-ils si importants ?

Après tout, on pourrait imaginer qu’une bonne documentation suffit. Le problème dans ce cas est que l'on risque de rencontrer rapidement **des obstacles** avec les librairies, frameworks et connecteurs qui **se basent fortement sur les conventions ReST**.

Encore mieux que les conventions, il nous faudrait un standard couvrant le maximum parmi les points suivants :

* Format des données.
* Typing des ressources.
* Linking.
* Pagination.


# JSON API

<http://jsonapi.org/>

* Créé par le co-fondateur de [http://www.tilde.io](http://www.tilde.io/), une entreprise de conseil *(en jsonapi ?).*
* C’est une spécification et non un standard.

```javascript
{
    "links": {
        "self": "http://example.com/articles",
        "next": "http://example.com/articles?page[offset]=2",
        "last": "http://example.com/articles?page[offset]=10"
    },
    "data": [{
        "type": "articles",
        "id": "1",
        "attributes": {
            "title": "JSON API paints my bikeshed!"
        },
        "relationships": {
            "author": {
                "links": {
                    "self": "http://example.com/articles/1/relationships/author",
                    "related": "http://example.com/articles/1/author"
                },
                "data": {"type": "people", "id": "9"}
            },
            "comments": {
                "links": {
                    "self": "http://example.com/articles/1/relationships/comments",
                    "related": "http://example.com/articles/1/comments"
                },
                "data": [
                    {"type": "comments", "id": "5"},
                    {"type": "comments", "id": "12"}
                ]
            }
        },
        "links": {
            "self": "http://example.com/articles/1"
        }
    }],
    "included": [{
        "type": "people",
        "id": "9",
        "attributes": {
            "first-name": "Dan",
            "last-name": "Gebhardt",
            "twitter": "dgeb"
        },
        "links": {
            "self": "http://example.com/people/9"
        }
    }, {
        "type": "comments",
        "id": "5",
        "attributes": {
            "body": "First!"
        },
        "relationships": {
            "author": {
                "data": {"type": "people", "id": "2"}
            }
        },
        "links": {
            "self": "http://example.com/comments/5"
        }
    }, {
        "type": "comments",
        "id": "12",
        "attributes": {
            "body": "I like XML better"
        },
        "relationships": {
            "author": {
                "data": {"type": "people", "id": "9"}
            }
        },
        "links": {
            "self": "http://example.com/comments/12"
        }
    }]
}
```

## Cool 👍

* Définition d’un format strict mais extensible.
* Standardisation des paramètres de *sorting*, *filtering* et de pagination *(l’implémentation reste libre pour la pagination)*.
* L’idée du *resource linking* avec des *relationships* est intéressante.

## Pas cool 👎

* Risque de collision entre les *fields* présents dans `attributes` et `relationships`.
* Pas de différence entre un lien vers une instance ou une collection.
* Les *one-to-one relationships* sont ambigües et ne respectent pas la convention :\
  `/resources/:resourceId/sub-resources/:subResourceId`
* Attention, les exemples utilisés dans la spec adoptent des conventions inhabituelles et ne sont pas imposés par la spec.
  * L’utilisation des *fields* en kebab-case n’est pas dans le standard.
  * La propriété `type` n’est pas forcément au pluriel.
* L’idée des *bulk operations* sur les *relationships* est très intéressante mais malheureusement pas appliquée aux ressources.
* De nombreuses implémentations mais la plupart ne sont plus maintenues depuis des mois voire des années.

�


# H.A.L.

* Hypertext Application Language.
* <https://tools.ietf.org/html/draft-kelly-json-hal-08>
* Créé par le fondateur de <http://stateless.co/>, une entreprise de conseil.
* Ce n’est pas un standard non plus.

```javascript
{
    "_links": {
        "self": { "href": "/orders" },
        "curies": [{
            "name": "ea",
            "href": "http://example.com/docs/rels/{rel}",
            "templated": true
        }],
        "next": { "href": "/orders?page=2" },
        "ea:find": {
            "href": "/orders{?id}",
            "templated": true
        },
        "ea:admin": [{
            "href": "/admins/2",
            "title": "Fred"
        }, {
            "href": "/admins/5",
            "title": "Kate"
        }]
    },
    "currentlyProcessing": 14,
    "shippedToday": 20,
    "_embedded": {
        "ea:order": [{
            "_links": {
                "self": { "href": "/orders/123" },
                "ea:basket": { "href": "/baskets/98712" },
                "ea:customer": { "href": "/customers/7809" }
            },
            "total": 30.00,
            "currency": "USD",
            "status": "shipped"
        }, {
            "_links": {
                "self": { "href": "/orders/124" },
                "ea:basket": { "href": "/baskets/97213" },
                "ea:customer": { "href": "/customers/12369" }
            },
            "total": 20.00,
            "currency": "USD",
            "status": "processing"
        }]
    }
}
```

## Cool 👍

* Simple, clair et facile à implémenter.
* Les "templated links" sont très prometteurs et permettent un découplage entre le code client et l’API.
* Les "curies" permettent de facilement lier les ressources à leur documentation et pourquoi pas un schéma *(mais ce n’est pas défini par H.A.L.)*.

## Pas cool 👎

* Comme son nom l’indique, H.A.L. se focalise uniquement sur le *linking*. Le périmètre est donc très limité.
* La propriété `_embedded`  manque d’intérêt et peut provoquer des conflits entre les propriétés de la ressource et les propriétés `_embedded`.
* De nombreuses implémentations mais la plupart ne sont plus maintenues depuis des mois voire des années.


# JSON LD

* A JSON-based Serialization for Linked Data.
* <https://www.w3.org/TR/json-ld/>
  * Créé par de nombreux auteurs fortement associés à l’univers du Web sémantique, [RDF](https://www.w3.org/RDF/) etc…
  * Ce n’est pas un standard mais une recommandation W3C.
* Exemple : <http://json-ld.org/playground/>

## Cool 👍

* Utilise les contextes de [shema.org](https://schema.org/).
* Possibilité de créer des contextes personnalisés.

## Pas Cool 👎

* Hérite de la culture XML / RDF.
* Quelques implémentations mais la plupart ne sont plus maintenues depuis des mois voire des années.


# Les Autres Initiatives

* Collection+json <https://github.com/collection-json/spec>
* ReSTdesc <http://restdesc.org/about/descriptions>
* Hydra <http://www.markus-lanthaler.com/hydra/>
  * Vocabulaire ReST pour JSON LD.
  * Permet d’ajouter la notion d’affordances.


# So What?

Ce qu’il faut retenir :

* JSON API, H.A.L. et JSON-LD se battent sur des terrains différents qui se croisent à certains endroits.
* JSON-LD reçoit le plus de soutien de la communauté Hypermedia.
* Tant qu’aucun standard ne s’impose, il faut essayer de prendre le meilleur de chaque monde en fonction de votre besoin.
* N’oubliez pas qu’il faut parser, sérialiser et *linker* les ressources dans les langages que vous utilisez vous et vos partenaires. Plus le format sera complexe, plus vous impacterez négativement l’adoption de votre API.


# Quelques Liens & Ressources

### Training Repository

<https://github.com/wishtack-training/wt-training-rest-api>

{% embed url="<https://github.com/wishtack-training/wt-training-rest-api>" %}

### Websheep

<https://github.com/marmicode/websheep>

{% embed url="<https://github.com/wishtack/websheep>" %}

### ReST API Checklist

<https://rest-api-checklist.marmicode.io/projects>

{% embed url="<https://rest-api-checklist.marmicode.io>" %}

## Autres ressources

### OWASP API Security Top 10

<https://github.com/OWASP/API-Security/blob/master/2019/en/dist/owasp-api-security-top-10.pdf>

{% embed url="<https://github.com/OWASP/API-Security/blob/master/2019/en/dist/owasp-api-security-top-10.pdf>" %}

### Slides et vidéos du meetup sur la sécurité des APIs ReST

<https://blog.wishtack.com/2017/07/28/slides-et-video-des-talks-au-meetup-lyonjs-47/>

{% embed url="<https://blog.wishtack.com/2017/07/28/slides-et-video-des-talks-au-meetup-lyonjs-47/>" %}

## Blog Posts & Videos

### Microservice Prerequisites by Martin Fowler

<https://martinfowler.com/bliki/MicroservicePrerequisites.html>

{% embed url="<https://martinfowler.com/bliki/MicroservicePrerequisites.html>" %}

### Mastering Chaos - A Netflix Guide to Microservices

<https://www.youtube.com/watch?v=CZ3wIuvmHeM>

{% embed url="<https://www.youtube.com/watch?v=CZ3wIuvmHeM>" %}

### From Monolith to Microservices at Zalando

<https://www.youtube.com/watch?v=gEeHZwjwehs>

{% embed url="<https://www.youtube.com/watch?v=gEeHZwjwehs>" %}

### ReST vs. GraphQL vs. gRPC Decision Tree

<https://phil.tech/2018/picking-an-api-paradigm-implementation/>

{% embed url="<https://phil.tech/2018/picking-an-api-paradigm-implementation/>" %}

### ReST APIs Must Be Hypertext-Driven by Roy T. Fielding

<https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven>

{% embed url="<https://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven>" %}

### Best Practices in API Governance

<https://swagger.io/resources/articles/best-practices-in-api-governance/>

{% embed url="<https://swagger.io/resources/articles/best-practices-in-api-governance/>" %}

### Code First vs Design First

<https://swagger.io/blog/api-design/design-first-or-code-first-api-development/>

{% embed url="<https://swagger.io/blog/api-design/design-first-or-code-first-api-development/>" %}

### ReST API & SDKs

<https://www.docusign.com/blog/if-your-rest-api-is-fine-can-you-get-away-without-having-an-sdk/>

{% embed url="<https://www.docusign.com/blog/if-your-rest-api-is-fine-can-you-get-away-without-having-an-sdk/>" %}

### OAS 3.0 Most Significant Changes

<https://blog.restcase.com/6-most-significant-changes-in-oas-3-0/>

{% embed url="<https://blog.restcase.com/6-most-significant-changes-in-oas-3-0/>" %}

### API Versioning Has No Right Way

<https://blog.apisyouwonthate.com/api-versioning-has-no-right-way-f3c75457c0b7>

{% embed url="<https://blog.apisyouwonthate.com/api-versioning-has-no-right-way-f3c75457c0b7>" %}

### Stripe API Versioning

<https://stripe.com/blog/api-versioning>

{% embed url="<https://stripe.com/blog/api-versioning>" %}

### The Difference Between API Gateways and Service Mesh

<https://konghq.com/blog/the-difference-between-api-gateways-and-service-mesh/>

{% embed url="<https://konghq.com/blog/the-difference-between-api-gateways-and-service-mesh/>" %}

### How RSA works

<https://en.wikipedia.org/wiki/RSA_(cryptosystem)>

{% embed url="<https://en.wikipedia.org/wiki/RSA_(cryptosystem)>" %}

### Problem Details for HTTP APIs RFC7807

<https://tools.ietf.org/html/rfc7807>

{% embed url="<https://tools.ietf.org/html/rfc7807>" %}


