ZEP-17 : Elaboration de l'API des membres

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet
Cartouche
ZEP 17
Titre Elaboration de l'API des membres
Révision 5
Date de création 3 octobre 2014
Dernière révision 29 novembre 2014
Type Feature
Statut Acceptée

Contexte

Suite à des longues et constructives discussions sur la ZEP-02, qui concerne l'élaboration d'une API au sens large, il a été mentionné que son objectif, à terme, était trop ambitieux pour une seule ZEP. Afin d'alléger la ZEP

  • En terme de contenu ;
  • Pour ne pas débattre d'absolument toutes les problématiques rencontrées pour toutes les ressources proposées par Zeste de Savoir ;
  • Et donc pour permettre à cette ZEP 17 de rediriger vers d'autres ZEP pour débattre de ses problématiques spécifiques à chaque ressource.

Cette ZEP 17 voit le jour pour finir la rédaction de l'API des membres.

Objet de la proposition

Réaliser l'API des membres comme première ressource permettra de :

  • Moins de développement est nécessaire pour une V1 comparé au temps de développement nécessaire pour couvrir tous les modules en lecture seule ;
  • Il pourrait y avoir plus de possibilités et d'utilité à sa sortie jusqu'à la V2 qui couvrirait d'autres modules ;
  • Cela assure qu'un workflow complet peut être couvert ; à savoir l'authentification, la consultation, l'écriture et la déconnexion. L'approche serait "verticale" ;
  • Les contributions se feront uniquement sur le module le plus stable du projet puisque les tutoriels et les articles ont pour vocation d'évoluer à court/moyen terme.

L'objectif concret de cette ZEP sera donc de développer complètement l'API des membres de bout en bout en y faisant les choix pour les autres API futures des autres ressources.

API des membres

Informations sur la requête HTTP

Version de l'API
1
http://api.zestedesavoir.com/v0.0/...
  • La version est indiquée dans l'URL d'appel de l'API.
  • L'api est spécifié comme sous-domaine de l'URL pour ne pas la surcharger.

Pour la prod, nous pouvons imaginer une adresse plus ou moins équivalente :

1
http://api.preprod.zestedesavoir.com/v0.0/...
User-Agent

L'en-tête HTTP User-Agent doit être renseigné par le client.

Pour des informations complémentaires sur les User-Agent, rendez-vous sur son article Wikipedia.

Cross-Origin Request Sharing

Le serveur doit renvoyer les en-tête HTTP suivants afin d'éviter les soucis de CORS :

1
2
3
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: ETag, Link
Access-Control-Allow-Credentials: true

Pour des informations complémentaires sur CORS, rendez-vous sur son article Wikipedia version anglaise.

Considération générales

Gestion des droits d'accès aux ressources

La gestion des droits d'accès aux ressources n'a pas été statutée de manière définitive et restera du ressort du développeur de choisir la solution la plus adaptée en fonction du code existant dans le projet.

Ci-dessous les deux possibilités détaillées.

Possibilité n°1 : Plusieurs paths

La première solution consiste à faire plusieurs APIs (plusieurs paths REST) reposant sur un même socle côté serveur.

Avantages :

  • Plus simple de repérer les appels à l'API pour différents groupes
  • Ouvrir et fermer certaines routes directement dans un fichier de routes plutôt que dans des conditions (if/else).
  • Permet de mapper de façon plus naturelle sur le code serveur, plutôt que de router en fonction du token passé dans les headers HTTP.

Inconvénients :

  • Demande de rester cohérent entre tous les paths et de donner des responsabilités bien précises.
Possibilité n°2 : Un seul path et router sur le token

La seconde solution consiste à coller tout dans la même API et router en fonction du token passé dans les headers HTTP.

Avantages :

  • Gère un seul path par ressource. Les routes sont donc plus épurées.
  • Se rapproche sans doute plus du code actuel dans le back-end.

Inconvénients :

  • Pour un membre du groupe "staff" (par exemple), il ne peut pas "voir" des ressources comme un simple membre.
Formatage des erreurs

Lorsqu'un appel API provoque une erreur, il faut s'appuyer sur les codes HTTP standards. La réponse HTTP doit donc systématiquement utiliser un code interprétable pour le client. En plus de cela, le contenu de la réponse doit indiquer un code (par défaut le même que celui de la réponse HTTP, mais peut être différents pour donner de plus amples informations) ainsi qu'un message d'erreur compréhensible par le développeur du client de l'API.

Exemple, une réponse HTTP 404 devrait avoir le body suivant :

1
2
3
4
5
6
7
8
{
    errors: [
        {
            code:404, 
            message:"Ce membre n'existe pas"
        }
    ]
}
Format d'entrée
  • A minima, le JSON doit être géré. Les autres formats sont facultatifs.
  • Lecture du header Content-Type, seule la valeur application/json est acceptée. Les autres formats donnent lieu à un code d'erreur HTTP 406 Not Acceptable. (NB : Twitter fait la différence entre le format des données envoyées et le format dans lequel les données sont envoyées. Dans le cas présent, ils renverraient une 404. A nous de décider ce qu'on fait).
1
2
3
4
5
6
7
8
{
    errors: [
        {
            code:406, 
            message:"Le client doit envoyer des données au format application/json et positionner le header HTTP Content-Type de façon à le signaler"
        }
    ]
}
Format de sortie
  • A minima, le JSON doit être géré. Les autres formats sont facultatifs.
  • Lecture du header HTTP Accept, si la valeur application/json est absente, envoi d'un code HTTP 406 et de l'erreur suivante :
1
2
3
4
5
6
7
8
{
    errors: [
        {
            code:406, 
            message:"Le client doit accepter des réponses au format JSON et le signaler en positionnant le header HTTP Accept avec la valeur application/json"
        }
    ]
}
Format de sortie pour les fichiers de contenu

Les ressources Markdown sont renvoyées dans le corps de la requête HTTP et suivant le format indiqué dans le header de la requête avec la clé X-Data-Format.

Les formats supportés seront markdown, html et texte.

Caching

Pour obtenir un premier système de cache, l'ETag sera utilisé. L'avantage se situe principalement sur un niveau : d'alléger le back-office dans sa charge de travail. Il ne sera pas toujours nécessaire d'aller chercher l'information s'il remarque qu'elle n'a pas été modifiée entre deux appels.

Pour y parvenir, voici le fonctionnement basique d'un ETag :

  1. Lorsqu'un client fait appel à l'API, la réponse inclue un ETag avec une valeur correspondant à un hash de la donnée retournée par l'API. La valeur de cette ETag est sauvegardée pour une prochaine utilisation si nécessaire.
  2. Au prochain même appel du client sur l'API, le client peut inclure dans le header de sa requête, l'attribut If-None-Match avec le précédent ETag renvoyé par le back-office.
  3. Si l'ETag n'a pas changé, le code de la réponse sera 304 - Not Modified et aucune donnée ne sera renvoyée.
  4. Si l'ETAg a changé, le back-office fera la requête et renverra la donnée avec un nouveau ETag. Ce nouveau ETag sera sauvegardé et utilisé pour de prochains appels.
Pagination
  • Les résultats de requête doivent être paginés. Par défaut, une page de résultat doit contenir 50 résultats

  • L'utilisateur indique la page de résultats qu'il souhaite récupérer à l'aide du paramètre &page=. Dans le cas où ce paramètre est invalide (n'est pas un nombre entier ou en dehors des limites de la pagination), l'API retourne une erreur 4001 ou 4002 :

1
2
3
4
5
6
7
8
{
    errors: [
        {
            code:4001, 
            message:"le paramètre indiqué pour la pagination n'est pas un nombre entier valide"
        }
    ]
}
1
2
3
4
5
6
7
8
{
    errors: [
        {
            code:4002,
            message:"la page indiquée n'existe pas"
        }
    ]
}
  • L'API fournit des indications dans les headers de la réponse HTTP afin d'aider le client à naviguer dans les ressources paginées à l'aide du header Link. de la façon suivante :
1
2
3
4
Link: <http://zestedesavoir.com/membres/?search=toto&page=4>; rel="next",
  <http://zestedesavoir.com/membres/?search=toto&page=5>; rel="last",
  <http://zestedesavoir.com/membres/?search=toto&page=1>; rel="first",
  <http://zestedesavoir.com/membres/?search=toto&page=2>; rel="prev"

NB : Ne pas oublier de remettre tous les paramètres utilisés par le client dans l'URL (ici search=)

  • Dans de futures versions, l'utilisateur pourra indiquer le nombre de résultats souhaités en positionnant le paramètre &perPage= dans sa requête
Format de dates

Le format de dates retenu pour la communication avec le serveur est le format ISO 8601 (attention Java 7 nécessaire pour les utilisateurs de Java), pas de soucis en Python a priori.

En ce qui concerne les en-têtes HTTP, voici la liste des formats à gérer côté serveur.

Les différentes versions de l'API

Objectif

Fournir aux clients un ensemble d'outils pour consulter la liste des membres et leurs profils.

Représentation d'un membre
Exemple d'objet
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
    "id": 123456,
    "username": "Clem",
    "show_email": true,
    "email": "clem@zestedesavoir.com",
    "is_active": true,
    "site": "www.zestedesavoir.com",
    "avatar_url": "http://i.imgur.com/k6R1plZ.png",
    "biography": "Quae dum ita struuntur, indicatum est apud Tyrum indumentum regale textum occulte, incertum quo locante vel cuius usibus apparatum. ideoque rector provinciae tunc pater Apollinaris eiusdem nominis ut conscius ductus est aliique congregati sunt ex diversis civitatibus multi, qui atrocium criminum ponderibus urgebantur.",
    "sign": "",
    "email_for_answer": true,
    "last_visit": "1977-04-22T06:00:00Z",
    "date_joined": "1977-04-22T06:00:00Z"
}

J'ai délibérément omis les champs liés à la modération et spécifique au site comme le survol ou le clique pour le menu.

Colonnes
Colonne Type Description
id long Identifiant de l'utilisateur
username String Nom d'utilisateur
show_email boolean Affiche ou non l'e-mail. Si faux, le champ email ne sera pas spécifié dans l'objet
email String E-mail de l'utilisateur
is_staff boolean Est un membre du staff ou non
is_active boolean Est un compte actif ou non
site String Site web de l'utilisateur
avatar_url String Url vers l'avater de l'utilisateur
biography String Biographie de l'utilisateur
sign String Signature de l'utilisateur
email_for_answer boolean Permet l'envoie d'e-mail depuis ZdS
last_visit Date Dernière visite de l'utilisateur sur ZdS
v0.0 : Afficher un profil utilisateur
Objectif

Mise en place simple du framework et routage sur une URL

Routes
Méthode URL Paramètres et signification Description Codes d'erreur
GET /v0.0/membres/{id} Aucun Renvoie la fiche profil d'un membre 400 si l'identifiant fourni ne correspond pas au format d'identifiant attendu. 404 si le membre n'existe pas
v0.1 : Lister les membres
Objectif

Renvoyer une liste au lieu d'un objet.

Routes
Méthode URL Paramètres et signification Description Codes d'erreur
GET /v0.1/membres/ Aucun pour le moment, search pour de futures versions Renvoie la liste des membres sans restriction (pagination ou différentielle) Aucun autre que les codes standard
v0.2 : Adaptation/ponts
Objectif

Définition de notre propre Serializer : On renvoie un modèle différent du modèle stocké en base.

v0.3 : Pagination et différentiel
Objectif

Aller toucher à la requête HTTP en lecture et écriture avec la manipulation des QuerySet et calcul du ETag.

Pour cette version, l'ETag ne sera pas sauvegardée et sera donc à chaque fois recalculée mais cela permettra d'avoir une version fonctionnelle et testable de l'ETag avant de statuer sur sa persistance.

v0.4 : Gestion de l'authentification.
Objectif

Permettre une authentification OAuth2 (dont la spécification du protocole est disponible via ce lien).

Son fonctionnement est le suivant :

  • Chaque client de l'API s'inscrit en tant qu'application tierce : ça signifie qu'il faut prévoir une table de stockage des clients de l'API
  • A la fin de l'inscription, le système devra renvoyer une ZDS_AUTH_KEY et une ZDS_AUTH_SECRET au développeur
  • Lorsqu'un membre veut se logguer au site via un client externe, le client devra donc envoyer à l'API:
    • le login : login de l'utilisateur sur le site
    • le password : mot de passe de l'utilisateur sur le site
    • le ZDS_AUTH_KEY : identifiant du client de l'API
    • le ZDS_AUTH_SECRET : clé secrète du client de l'API
  • Un token doit être généré qui signifie "le membre x veut se connecter à l'API via le client y". Ce token doit avoir une date d'expiration (à définir) et devra être utilisé par le client de l'API dans les entête HTTP.
  • A partir du token notamment on saura à quoi peut accéder tel ou tel membre.

L'authentification se base sur l'utilisation d'access token et de refresh token :

1
2
3
4
5
6
{
  "access_token": "<your-access-token>", 
  "scope": "read", 
  "expires_in": 86399, 
  "refresh_token": "<your-refresh-token>"
}
  1. L'utilisateur se log pour la première fois grâce à un client à l'API
  2. Le serveur lui renvoi son access token avec son expiration et son refresh token sans expiration
  3. Durant la session, l'access token est utilisé dans les requêtes
  4. A chaque fois que l'access token expire, le client refait une authentification avec le refresh token et la clé secrète

Ce processus a plusieurs intérêts :

  • Le token d'accès est le maillon faible, s'il est intercepté, tu peux prendre la place de l'utilisateur. En limitant sa durée de vie à une "session d'utilisation" (en gros inactivité pendant quelques minutes => expiration) voire en limitant sa durée de vie tout court à quelques minutes, tu limites le danger.

  • L'application n'a pas besoin de stocker le login et le mot de passe de l'utilisateur, et ne devrait pas le faire . Et c'est bien mieux comme ça pour tout le monde. (si jamais l'application est compromise, faille de sécurité, …)

  • Si la réponse du serveur contenant l'accessToken et le refreshToken est interceptée, l'accessToken va pouvoir être utilisé (mais c'est limité dans le temps… même si c'est extrêmement dangereux) par contre pour le refreshToken il faut le client_secret. Qui n'est pas dans la réponse du serveur.

  • Permet également à un client, n'importe où, n'importe quand d'invalider des accessToken, sans avoir besoin de saisir à nouveau ses login / mot de passe sur tous ses appareils. En gros, je dis "oulà, y'a une activité suspecte sur mon compte, un de mes accessToken a été intercepté, invalide les tous"

Routes
Méthode URL Paramètres et signification Description Codes d'erreur
POST /v0.4/login/ Aucun, le login et le passwod est contenu dans la requête Authentification de l'utilisateur dans l'API 400 si les headers Authbearer sont absents. 403 si login failed.
GET /v0.4/membres/monprofil Aucun Affiche toutes les données personnelles d'un utilisateur connecté 401 : pour un utilisateur non identifié qui essaie d'accéder à sa page de profil.
v0.5 : gestion du PUT
Objectifs

Interaction en écriture avec le code côté serveur

Routes
Méthode URL Paramètres et signification Description Codes d'erreur
PUT /v0.5/membres/{id} Aucun, les paramètres se trouvent en contenu de la requêtes. Utilisateur authentifié peut modifier sa fiche profil via l'API 400 si un champ n'est pas formaté comme attendu (400x à définir : email invalide, …) s'inspirer de la gestion d'erreur déjà présente sur la page de profil. 401 si non authentifié. 403 si essai de modifier un profil autre que le sien. 404 si id du membre n'existe pas. 405 si un PUT est fait sur /membres/ puisqu'on ne peut pas modifier la liste des membres.
v0.6 : gestion du POST
Objectif

Création de compte via l'API membres.

Routes
Méthode URL Paramètres et signification Description Codes d'erreur
POST /v0.6/membres/ Aucun, les informations se trouvent en paramètre de la requête Utilisateur non identifié peut se créer un compte A définir

Futur de l'API des membres (possiblement hors scope de cette ZEP)

v1.x : Gestion des paramètres de requête.
Objectif

Etablir la liaison entre la couche API et la couche d'accès aux données. Que peut-on reprendre du code utilisé actuellement pour les vues ? Peut on factoriser du code pour faire une page de recherche parmi les membres sur le site ?

Routes
Méthode URL Paramètres et signification Description Codes d'erreur
GET /v0.7/membres/ search Possibilité de rechercher, filtrer, trier la liste des membres Aucun autre que les codes standard
v1.x : Gérer les associations dans le modèle renvoyé au client.
Objectif

Aller plus loin que "un modèle en base" <-> "un modèle renvoyé au client".

Ex : extraire un bout (l'url, le nom, …) d'un cours en bêta pour le mettre dans le contenu de la réponse envoyé lors de la consultation de la fiche d'un membre.

Édité par Andr0

+3 -0
Staff

Merci pour ce récap.

Je n'ai pas le temps de tout lire (juste le début) et voici quelques remarques/questions.

  • "L'api est spécifié comme sous-domaine de l'URL pour ne pas la surcharger" : Etant donné que ZdS se veut être un projet vraiment opensource, dans le sens ou n'importe qui pourrait l'utiliser il ne faudrait pas que la spec soit indépendante des spécificités de zds. Et donc, la spécification de l'url devrait être indépendante de l'adresse du site. Libre au site qui utilise l'application ZdS d'en faire un sous domaine ou quoi que ce soit. Tout ceci pour dure qu'il serait préférable de nommer l'api sous le format /api/v1.1/xxx, libre a chacun de pouvoir en faire un sous domaine.

  • "L'en-tête HTTP User-Agent doit être renseigné par le client" : pourquoi forcer le client à renseigner l'user agent ? J'aurai plutôt tendance à le rendre facultatif.

Pour la suite, je lirai plus tard.

  • Et donc, la spécification de l'url devrait être indépendante de l'adresse du site. Libre au site qui utilise l'application ZdS d'en faire un sous domaine ou quoi que ce soit. Tout ceci pour dure qu'il serait préférable de nommer l'api sous le format /api/v1.1/xxx, libre a chacun de pouvoir en faire un sous domaine.

On en avait discuté dans l'autre ZEP. Il avait été dit que c'est un point de détail et que, si "vX" a un vrai sens dans la description de la ressource, "je veux accéder au modèle des utilisateurs, tel qu'il était dans la version X", "api" n'a pas vraiment d'intérêt pour la description de la ressource elle-même et donc, comme Andr0 l'a souligné, surcharge l'URL avec une information pas intéressante au sens REST.

Maintenant je veux bien comprendre la problématique technique de ré-utilisabilité du code. A voir qu'est-ce-qui est plus important que l'autre. Coller à l'idée REST au plus près, ou prendre en compte cette considération. A débattre.

  • "L'en-tête HTTP User-Agent doit être renseigné par le client" : pourquoi forcer le client à renseigner l'user agent ? J'aurai plutôt tendance à le rendre facultatif.

firm1

J'ai déjà utilisé cette approche, et ça finit toujours par poser des soucis. Dans le contexte ZdS ça me paraît même essentiel.

Le jour où tu as beaucoup de clients différents pour ton API et qu'aucun n'utilise d'User-Agent (bah ouais, c'est facultatif), et que tu te retrouves avec une montagne de logs Sentry ou de logs d'accès (accès API qui provoquent des 400, des 404 en pagaille, …) tu as envie de savoir quel client est en cause, pour indiquer au développeur qu'il devrait :

  • corriger son bug côté client

  • retirer, s'il le peut, la version de son application en question

  • le remercier, si jamais son client a permis d'identifier un bug tordu côté serveur

Sans cette info, tu te retrouves coincé quand tu as pas mal de clients. A la rigueur on serait dans une boîte où tu es ton propre client, je comprendrais ta remarque (encore que), mais dans le contexte de ZdS ça me paraît vraiment primordial. L'API est à destination de n'importe qui, beaucoup de méthodes sont publiques. Et si on prend l'exemple des notifications, les clients risquent d'être nombreux.

Je ne vois vraiment pas l'intérêt de le rendre facultatif ? Peut-être que tu peux m'éclairer là-dessus.

Édité par Javier

Happiness is a warm puppy

+3 -0
Staff

On en avait discuté dans l'autre ZEP. Il avait été dit que c'est un point de détail et que, si "vX" a un vrai sens dans la description de la ressource, "je veux accéder au modèle des utilisateurs, tel qu'il était dans la version X", "api" n'a pas vraiment d'intérêt pour la description de la ressource elle-même et donc, comme Andr0 l'a souligné, surcharge l'URL avec une information pas intéressante au sens REST.

Javier

Je ne vois pas en quoi ça surcharge. Si on suppose que l'api est joignable par l'url api.zestedesavoir.com/v1.1, étant donné que c'est un sous domaine, tu dois forcément le relier à quelque chose. Et donc ça serait quoi ce quelque chose ? Mon propos est là. Plutôt que de spécifier dans la ZEP un cahier de charge qui dit que l'url doit être api.zestedesavoir.com, il est préférable de dire que l'api sera sur l'url zestedesavoir.com/api et que dans le cadre du site zestedesavoir.com , elle sera reliée au sous domaine api.zestedesavoir.com.

Au final ça ne change rien, juste la manière de spécifier la chose doit enlever tout ce qui est spécifique au site zestedesavoir.com. Si demain (au pif) developpez.com décide d'utiliser le code source de zds, ils l'hebergerons surement sur www.developpez.com, et la même api sera disponible sur www.developpez.com/api/ et ils décideront peut-être de le mettre à disposition sur www.developpez.api. Bref, c'est garder la définition de la feature opensource, et ce n'est pas incompatible avec notre approche.

Sans cette info, tu te retrouves coincé quand tu as pas mal de clients.

ça c'est très clairement parce qu'on utilise le mauvais système d'authentification. C'est bien pour ça (et pour de nombreux autres avantages) que je ne suis pas fan de l'authentification par OAuth basique. Pour moi il faut nécessairement un mécanisme d'OAuth2 avec souscription au préalable des applications qui se connectent sur le site. On a ainsi un contrôle plus direct sur ce que fait l'application, et on peut bloquer facilement une appli qui tente de faire n'importe quoi à un moment donné, bref, OAuth2 c'est la seule réponse que j'ai à donner ici.

Je ne vois pas en quoi ça surcharge.

C'est expliqué dans mon message, et dans l'autre topic (et on en a déjà débattu). J'explique une dernière fois "en quoi ça surcharge" : dans la philosophie REST, le CHEMIN d'une ressource doit décrire cette ressource et uniquement celle-ci. Si tu retrouves "api" dans le chemin d'accès à la ressource, ça la pollue. Ce n'est pas plus compliqué que ça.

D'où mon message : si l'on considère que l'idée d'une installation pour d'autres sites/domaines est plus importante que de coller à la lettre à REST, alors on part sur ta solution, sinon, on reste comme ça (sachant que c'est quand même une pratique extrêmement courante).

ça c'est très clairement parce qu'on utilise le mauvais système d'authentification. C'est bien pour ça (et pour de nombreux autres avantages) que je ne suis pas fan de l'authentification par OAuth basique. Pour moi il faut nécessairement un mécanisme d'OAuth2 avec souscription au préalable des applications qui se connectent sur le site. On a ainsi un contrôle plus direct sur ce que fait l'application, et on peut bloquer facilement une appli qui tente de faire n'importe quoi à un moment donné, bref, OAuth2 c'est la seule réponse que j'ai à donner ici.

firm1

A spécifier dans ce cas. Mais alors on a besoin de ton aide pour toute la partie login.

NB : c'est franchement dommage d'avoir discuté de cela il y a des mois déjà et de voir apparaître d'un coup d'un seul des problématiques globales sur le fonctionnement du module API dans le topic qui ne devrait traiter que de l'implémentation d'une API en particulier.

Édité par Javier

Happiness is a warm puppy

+0 -0
Staff

D'où mon message : si l'on considère que l'idée d'une installation pour d'autres sites/domaines est plus importante que de coller à la lettre à REST, alors on part sur ta solution, sinon, on reste comme ça (sachant que c'est quand même une pratique extrêmement courante).

Il y'a quelque chose que je n'ai pas compris visiblement. Pour être sur qu'on se comprend bien, peux-tu répondre à cette question : Si (comme écrit actuellement dans le post principal) on se dit que l'adresse de l'api doit être http://api.zestedesavoir.com/v0.0/..., on est bien d'accord que ça signifie que api est un sous domaine de zestedesavoir.com ? Si oui, vers quoi pointera ce sous domaine d'après toi ?

c'est franchement dommage d'avoir discuté de cela il y a des mois déjà et de voir apparaître d'un coup d'un seul des problématiques globales sur le fonctionnement du module API dans le topic qui ne devrait traiter que de l'implémentation d'une API en particulier.

Javier

J'avoue avoir perdu le fil sur l'autre topic. Mais je peux aller faire les remarques là bas si c'est mieux.

Effectivement on s'est mal compris.

Ce qui est expliqué dans la ZEP, c'est quasiment de la doc. Il n'était pas question d'implémentation technique là-dedans.

Il faut (à mon avis) que les développeurs tierce-partie utilise l'URL telle qu'elle a été décrite dans le premier message. On gros, on garantit ce fonctionnement là, parce qu'il respecte REST en effet, parce qu'on a choisit ça, parce que peut-être, un jour qui sait, on interceptera les requêtes API avant qu'elles parviennent à Django. Ca peut paraître farfelu mais j'ai moult fois mis en place ce genre de proxys "applicatifs" pour décharger des serveurs, etc.

Après, que ça tape sur zestedesavoir.com/api ou cdn.amazon.machinevirtuelledezdssousnode.com c'est effectivement une question d'implémentation, mais je pense qu'il faut documenter l'API avec une URL sur un sous-domaine (je te garantis que le cas cité plus haut est plus fréquent qu'il n'y paraît) : d'abord parce que c'est plus propre pour les clients, ensuite parce qu'une bête route Django ça pourrait être fortement amené à changer, et ça c'est notre cuisine à nous, c'est pas le problème du développeur.

Donc si ce que tu proposes c'est :

  • on communique aux développeurs tierce-partie l'URL en api.zestedesavoir

  • on implémente ça en mappant sur zestedesavoir.com/api mais c'est notre tambouille à nous (quitte à interdire l'accès public à ces URLs)

Alors OK ça relève bien d'une incompréhension.

Pour ce qui est de l'autre topic, oui ce sont des trucs "généraux" (OAuth 2 aussi d'ailleurs et c'est plus qu'essentiel !!) je pense qu'il aurait fallu en discuter là-bas lorsqu'on s'est posé la question de l'authentification notamment.

Après, on est dans un cas un peu particulier là, où certaines questions vont être soulevées au cours du développement, plus que le lieu de la discussion, c'est plus l'arrivée tardive des critiques alors qu'on s'est battu pour spécifier tout ça clairement qui me chagrine. Mais mieux vaut tard que jamais.

Édité par Javier

Happiness is a warm puppy

+0 -0

Pour moi il reste le problème assez important du login à spécifier quand même.

Soit on commence à implémenter les méthodes publiques de l'API et on reporte cette discussion lorsqu'on implémentera la partie /monProfil soit on commence à en discuter maintenant (OAuth, OAuth2 ? comment on met ça en place ? Comment les applications reçoivent un jeton ? Quel flow ?).

Peut-être qu'il faudrait quand même éclaircir ce point, même si ça n'empêche pas d'avancer sur la partie publique (+ les sérializers) en parallèle.

A vous de voir :)

Happiness is a warm puppy

+0 -0
Staff

ça me semble assez tôt pour passer en validation. Comme la précisé Javier on a encore des points à éclaircir.

Typiquement le système d'authentification. Pour de nombreuses raisons (en particulier la sécurité), il vaut mieux partir tout de suite sur l'OAuth2. La spécification du protocole est ici (oui je sais c'est long et pas souvent clair).

Le principe reste tout de même assez simple (pour ceux qui ont la flemme de lire la spec):

  • Chaque client de l'API s'inscrit en tant qu'application tierce : ça signifie qu'il faut prévoir une table de stockage des clients de l'API
  • A la fin de l'inscription, le système devra renvoyer une ZDS_AUTH_KEY et une ZDS_AUTH_SECRET à l'utilisateur
  • Lorsqu'un membre veut se logguer au site via un client externe, le client devra donc envoyer à l'API:
    • le login : login de l'utilisateur sur le site
    • le password : mot de passe de l'utilisateur sur le site
    • le ZDS_AUTH_KEY : identifiant du client de l'API
    • le ZDS_AUTH_SECRET : clé secrète du client de l'API
  • Un token doit être généré qui signifie "le membre x veut se connecter à l'API via le client y". Ce token doit avoir une date d'expiration (à définir) et devra être utilisé par le client de l'API dans les entête HTTP.
  • A partir du token notamment on saura à quoi peut acceder tel ou tel membre.

Voila en gros ce que ça doit donner.

Auteur du sujet

A chaque login, le client doit fournir ZDS_AUTH_KEY et ZDS_AUTH_SECRET ? Parce qu'entre l'inscription où ces informations semblent retournées et les futures authentifications, il peut y avoir une perte de ces données (Par exemple, sur Android, cela peut arriver très facilement).

Dans quelle mesure ces informations sont renvoyées et utilisées ?

Sinon, une autre question que je pense lié, quid du système d'authentification actuellement implémentée sur le site ? Est-ce qu'il y aura des modifications à apporter ? Qu'en est-il de l'intégration après la validation (je l'espère prochaine) de cette PR ?

+0 -0

Il manque le refreshToken. Important quand même.

Et soit j'ai pas compris comment fonctionne OAuth2, soit :

A la fin de l'inscription, le système devra renvoyer une ZDS_AUTH_KEY et une ZDS_AUTH_SECRET à l'utilisateur

à l'utilisateur ? C'est plutôt ce que tu as appelé client partout ailleurs non ? Ou j'ai pas compris ?

EDIT : @Andr0 : ces informations sont propres au client. Donc en gros, "en dur" dans l'appli mobile.

EDIT² : Vous pouvez voir ce que fait l'API Github pour vous faire une idée d'implémentation. Ca fait plusieurs fois que je cite l'API Github comme source d'inspiration, je pense qu'elle est vraiment bien foutue.

Édité par Javier

Happiness is a warm puppy

+0 -0
Staff

A chaque login, le client doit fournir ZDS_AUTH_KEY et ZDS_AUTH_SECRET ? Parce qu'entre l'inscription où ces informations semblent retournées et les futures authentifications, il peut y avoir une perte de ces données (Par exemple, sur Android, cela peut arriver très facilement).

Andr0

Tu souscris une seule fois au système et le provider te donne les KEY et SECRET propre à ton client. Ces derniers ne changent pas et ne sont connus que du client de l'API et aux développeurs de le renseigner en dur dans ton code (ou dans un fichier de config).

Pour consulter tes KEY et SECRET il faudra que le site offre une page de visualisation de ses KEY/SECRET.

Sinon, une autre question que je pense lié, quid du système d'authentification actuellement implémentée sur le site ? Est-ce qu'il y aura des modifications à apporter ?

L'authentification du site ne sera pas impactée étant donné qu'elle ne passe pas par l'API. On pourra penser à faire évoluer le système d'authetification du site une fois la base de l'API réalisée, mais ça ne rentre pas en compte dans la ZEP ci-contre.

Qu'en est-il de l'intégration après la validation (je l'espère prochaine) de cette PR ?

Andr0

Comme je l'ai dis, l'API est totalement décorrélé du site pour le moment. Ce qu'on spécifie là c'est comment les clients de l'API vont s'authentifier à l'API. Le site est à part.

Il manque le refreshToken. Important quand même.

Javier

En effet, il faudra prévoir un refreshToken

à l'utilisateur ? C'est plutôt ce que tu as appelé client partout ailleurs non ? Ou j'ai pas compris ?

Javier

Ce que j'appelle ici utilisateur, c'est celui, qui devra aller sur une page du site (à créer), renseigner le nom de son application, et cliquer sur le bouton demander un accès API et le site renverra à cet utilisateur ses KEY/SECRET correspondant à cette application. Il devra donc recopier ces éléments pour l'utiliser dans son client API

On pourrait l'appeler développeur pour éviter toute confusion.

EDIT :

  • utilisateur = utilisateur du site ou du client API (utilisateur final)
  • client = le logiciel qui utilise les APIs et qui va potentiellement se connecter en utilisant les credentials d'un utilisateur
  • développeur = la personne qui développe le client

Ça vous va comme terminologie ? Histoire qu'on s'emmêle pas les pinceaux ?

Édité par Javier

Happiness is a warm puppy

+0 -0
Staff

On pourrait l'appeler dévelopeur pour éviter toute confusion.

Javier

Faire carrément un lexique des termes utilisés, car j'avoue qu'a un moment je me suis perdu dans mon explication :)

EDIT : Pour moi il faut distinguer utilisateur du site et utilisateur du client API dans le lexique.

Édité par firm1

Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte