API Progression

Point d’entrée

Méthode URL Description Paramètres Réponse
GET / Configuration du serveur   Config

Liens :

Nom Description
inscription Inscription d’un utilisateur

Authentification

Trois méthodes permettent l’authentification pour n’importe quelle requête :

  1. Identifiant et mot de passe

    Dans l’entête Authorization, méthode Basic, les paramètres suivants séparés par des deux-points (:) et encodés en base64

    Paramètre Description Type Défaut
    identifiant Nom d’utilisateur ou courriel str  
    password Mot de passe str  
    domaine Domaine LDAP str «  »

    Exemple : Authorization: Basic Ym9iOnBhc3N3b3JkOmV4ZW1wbGUuY29t

  2. Identifiant et clé d’accès

    Dans l’entête Authorization, méthode Key, les paramètres suivants séparés par des deux-points (:) et encodés en base64

    Paramètre Description Type Défaut
    identifiant Nom d’utilisateur ou courriel str  
    key_name Identifiant de la clé Clé  
    key_secret Secret correspondant à la clé str  

    La clé nommée par key_name doit exister avec la portée «auth». Le secret peut alternativement être passé via cookie sécurisé.

  3. Token JWT

    Dans l’entête Authorization, un Token d’identification «au porteur» :

    Authorization: Bearer <token JWT>

    L’authentification par token ne permet pas de générer un token d’identification ni une clé d’authentification.

Autorisation

L’autorisation d’accéder aux ressources est donnée par un Token dans l’entête Authorization ou dans le paramètre tkres :

Paramètre Description Type Défaut
identifiant Nom d’utilisateur ou courriel str  
tkres Token d’accès Token  

Ressources

Les ressources échangées sont représentées en format JSON-API

Avancement

L’avancement d’un utilisateur pour une question.

Identifiant :

username/question_uri

Propriétés :

Nom Type Valeurs Description
état str « début » Aucune tentative envoyée
    « non_réussi » Au moins une tentative, mais aucune n’est réussie
    « réussi » Au moins une tentative dont une réussie
titre str   Le titre de la question tel qu’il était lors de la dernière tentative
niveau str   Le niveau de la question tel qu’il était lors de la dernière tentative
date_modification int   La date de la dernière tentative
date_réussie int   La date de la première tentative réussie
extra str   Infos extra à stoquer avec l’Avancement

Relations :

Nom Cardinalité Type Description
tentatives 1-n Tentatives liste de Tentatives effectuées

Liens :

Nom Description
tentative Soumettre une nouvelle tentative

Clé

Une clé donnant accès à un sous-ensemble des services ou ressources de l’API. Les clés peuvent être octroyées temporairement ou être révoquées. Une clé peut être utilisée en lieu et place d’un mot de passe au moment d’effectuer une authentification. Le token JWT retourné après une authentification réussie peut être utilisé pour accéder aux services et ressources donnés par la portée de la clé.

Identifiant :

username/nom

Propriétés :

Nom Type Valeurs Description
secret str   valeur secrète de la clé
création int   Timestamp de création
expiration int   Timestamp d’expiration, 0=n’expire jamais
portée str   Service ou ressource accessible par la clé.
    « révoquée » La clé est révoquée pour tout service ou ressource
    « authentification » La clé peut être utilisée pour authentifier l’utilisateur

Config

Configuration du serveur

Nom Type Valeurs Description
config dict   Dictionnaire de configurations
version str   Version actuelle du serveur

Commentaire

Un message sur une tentative écrit par un utilisateur.

Identifiant :

username/question_uri/date_soumission/numéro

Propriétés:

Nom Type Valeurs Description Défaut
message str      
créateur str   nom d’utilisateur du créateur Utilisateur authentifié
date int   timestamp de création Date courante
numéro_ligne int   numéro de ligne où s’applique le commentaire  

Ébauche

Une ébauche de solution fournie comme point de départ à la résolution de l’exercice.

Identifiant :

question_uri/langage

Propriétés:

Nom Type Valeurs Description
langage str   Langage de programmation de l’ébauche
code str   Code de l’ébauche

Question

Une question générique.

Identifiant :

URI

Hiérarchie

Question
▲
├ QuestionProg
├ QuestionSys
└ QuestionBD

Propriétés :

Nom Type Valeurs Description
niveau str    
titre str    
description str    
objectif str    
énoncé str ou liste de dict    
auteur str    
licence str    
sous_type str questionProg  
    questionSys  
    questionBD  

Question (sous-type QuestionProg)

Une question spécifiquement de programmation.

Relations :

Nom Cardinalité Type Description
ebauches 1-n Ébauche liste d’ébauches de Solution
tests 1-n Test liste de Tests de validation

Question (sous-type QuestionSys)

Une question spécifiquement de type système

Relations :

Nom Cardinalité Type Description
image 1 str image du conteneur
utilisateur 1 str utilisateur du conteneur
solution 1 str solution du test, s’il y a lieu
tests 1-n Test liste de Tests de validation

Résultat

Le résultat d’un test pour une solution proposée.

Identifiant :

username/question_uri/hash

Propriétés:

Nom Type Valeurs Description
sortie_observée str   Sortie standard du Test
sortie_erreur str   Sortie d’erreur du Test
résultat bool   Vrai si le Test a été validé avec un code de retour de 0 et une sortie observée égale à la sortie attendue
feedback str   Rétroaction pour ce résultat de test
temps_exécution int   temps d’exécution en ms

Sauvegarde automatique

La sauvegarde automatique du travail d’un utilisateur pour une question et dans un langage spécifique.

Identifiant :

username/question_uri/langage

Propriétés:

Nom Type Valeurs
date_sauvegarde int timestamp
code str  

Tentative

Une tentative de réponse à une question.

Identifiant :

username/question_uri/date_soumission

Hiérarchie

Tentative
▲
├ TentativeProg
├ TentativeSys
├ TentativeBD

Propriétés :

Nom Type Valeurs Description
date_soumission int   timestamp
feedback str    
réussi bool   vrai ssi la tentative a correctement répondu à la question
temps_exécution int   temps d’exécution en ms
sous-type str tentativeProg Sous-type de la ressource
    tentativeSys  
    tentativeBD  

Relations :

Nom Cardinalité Type Description
résultats 1-n Résultat les résultats de test

Tentative (sous-type tentativeProg)

Une tentative de réponse à une QuestionProg.

Propriétés:

Nom Type Valeurs Description
langage str   Langage de la tentative
code str   Code de la tentative
tests_réussis int   nb de tests réussis
publique bool   Vrai si une tentative réussie peut être partagée

Tentative (sous-type tentativeSys)

Une tentative de réponse à une QuestionSys.

Propriétés:

Nom Type Valeurs Description
conteneur str   identifiant du conteneur
réponse str   Réponse à une question à solution
tests_réussis int   nb de tests réussis

Relations :

Nom Cardinalité Type Description
résultats 1-n Résultat les résultats de test

Test

Un test de validation d’une question auquel sont soumises les solutions proposées.

Identifiant :

question_uri/numéro

Propriétés :

Nom Type Valeurs Description
nom str    
sortie_attendue str   La sortie attendue, ou null si sortie_cachée est vrai
feedback_pos str    
feedback_neg str    
caché bool   vrai si les entrées et sorties ont été caviardées

TestProg (soustype de Test)

Un test de validation d’une QuestionProg auquel sont soumises les solutions proposées.

Propriétés :

Nom Type Valeurs Description
entrée str   L’entrée du programme ou null si sortie_cachée est vrai
params str   Les paramètres du programme ou null si sortie_cachée est vrai
feedback_err str    

TestSys (soustype de Test)

Un test de validation d’une QuestionSys auquel sont soumises les solutions proposées.

Propriétés :

Nom Type Valeurs Description
validation str   Script de validation
utilisateur str   l’utilisateur du conteneur

Token

Un token JWT.

Types de tokens

Deux types de tokens peuvent être générés :

  • Token d’identification

    Permet l’authentification uniquement. Le token d’identification ne doit pas contenir de champ ressources.

  • Token ressources

    Donne accès à une ou plusieurs ressources. Contient un champ ressources.

Propriétés :

Nom Type Valeurs Description
username str   l’utilisateur propriétaire du token
création int   timestamp à la création
expiration int   timestamp d’expiration ou « +n » ou n est le nombre de secondes avant l’expiration
data any   données incluses dans le token
ressources []   ressources permises par le «token ressources»
jwt str   token jwt signé
fingerprint str   empreinte (sha256) du contexte aléatoire du token fourni via un cookie sécurisé.
version int   version de l’api ayant généré le token
  • Data

    data contient un objet peuplé de n’importes quelles données utiles au destinataire du token.

  • Expiration

    Lorsqu’on soumet un nouveau token, l’expiration peut être exprimée de façon absolue, sous forme de timestamp en secondes depuis 1/1/1970 (ex.: 1685831340) ou de façon relative en nombre de secondes après la création du token (ex.: « +300 »)

    Une date d’expiration de 0 signifie que le token n’expire jamais.

  • Ressources

    Les ressources sont définies par leur URI et par la méthode de requête HTTP, avec une expression rationnelle. Un token ressource peut décrire une ou plusieurs ressources.

    • Exemple :
      { "username" : "bob",
        "expiration" : "+300",
        "data" : []
              "ressources" : {
                      "get_bob" : {
                              "url" : "^/user/bob$",
                              "method" : "^GET$"
                      }
              },
        "fingerprint" : false
      }
      

Relations

Nom Cardinalité Type Description
user 1-1 User l’utilisateur pour lequel a été créé le token

Utilisateur

Un utilisateur du système.

Identifiant :

username

Propriétés :

Nom Type Valeurs Description
username str    
courriel str    
état str « inactif », « actif », « en_attente_de_validation »  
rôle str « normal », « admin »  
préférences str   chaîne JSON décrivant les préférences UI
prénom str    
nom str    
nom_complet str    
pseudo str    
biographie str    
occupation str « étudiant », « enseignant », « tuteur », « autre »  
avatar str    

Relations :

Nom Cardinalité Type description
avancements 1-n Avancement Liste d’avancements
cles 1-n Clé Liste de clés
tokens 1-n Token Liste de tokens en écriture seule

Exemples

Les exemples reflètent les résultats réels sur la plus récente version de l’API disponible sur https://progression.dti.crosemont.quebec/demo/api/v1/.

Obtenir la configuration du serveur

curl "https://progression.dti.crosemont.quebec/demo/api/v1//"
{
    "data": {
        "type": "config",
        "id": "serveur",
        "attributes": {
            "version": "Progression 4.1.1()",
            "config": {
                "AUTH": {
                    "LDAP": false,
                    "LOCAL": false
                }
            }
        },
        "links": {
            "self": "https://progression.dti.crosemont.quebec/demo/api/v1/",
            "inscrire": "https://progression.dti.crosemont.quebec/demo/api/v1/users"
        }
    }
}

Inscription en tant qu’utilisateur jdoe

curl "https://progression.dti.crosemont.quebec/demo/api/v1//user/jdoe" -X PUT --data '{"username": "jdoe"}' -H "Content-Type: application/json"
{
    "errors": [
        {
            "title": {
                "type": [
                    "Le champ type est manquant ou ne correspond pas à un type valide"
                ]
            },
            "status": 409
        }
    ]
}

Obtenir un token pour jdoe

DATA=$(cat <<EOF
{
        "data": {
                "type": "token",
                "attributes": {
                        "ressources": {
                                "tout": {
                                        "url": ".*",
                                        "method": ".*"
                                }
                        },
                        "expiration": "+30"
                }
        }
}
EOF
)
curl "https://progression.dti.crosemont.quebec/demo/api/v1//user/jdoe/tokens" -H 'Authorization: basic amRvZTo=' --data "$DATA" -H "Content-Type: application/vnd.api+json"
{
    "data": {
        "type": "token",
        "id": "hcHMcNB6ZKS-lE80cqU24CGhTlJ970ze38teVbw4qDE",
        "attributes": {
            "username": "jdoe",
            "création": 1743453188,
            "expiration": 1743453218,
            "data": [],
            "jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6Impkb2UiLCJjdXJyZW50IjoxNzQzNDUzMTg4LCJleHBpcmVkIjoxNzQzNDUzMjE4LCJkYXRhIjpbXSwidmVyc2lvbiI6IjQuMS4xIiwicmVzc291cmNlcyI6eyJ0b3V0Ijp7InVybCI6Ii4qIiwibWV0aG9kIjoiLioifX19.hcHMcNB6ZKS-lE80cqU24CGhTlJ970ze38teVbw4qDE",
            "version": "4.1.1",
            "ressources": {
                "tout": {
                    "url": ".*",
                    "method": ".*"
                }
            }
        },
        "links": {
            "self": "https://progression.dti.crosemont.quebec/demo/api/v1/token/hcHMcNB6ZKS-lE80cqU24CGhTlJ970ze38teVbw4qDE",
            "user": "https://progression.dti.crosemont.quebec/demo/api/v1/user/jdoe"
        }
    }
}

Obtenir le profil de l’utilisateur authentifié jdoe

curl "https://progression.dti.crosemont.quebec/demo/api/v1//user/jdoe" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Mettre à jour les préférences de jdoe

DATA=$(cat <<EOF
{
        "data": {
                "type": "user",
                "attributes": {
                           "préférences": "{\"thème\": \"sombre\"}"
                }
        }
}
EOF
)
curl "https://progression.dti.crosemont.quebec/demo/api/v1//user/jdoe" -X PATCH --data "$DATA" -H "Content-Type: application/vnd.api+json" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Obtenir la question «Les fonctions avec paramètres/Rectangle» et ses tests:

Cette question est disponible à l’URL suivant : https://progression.pages.dti.crosemont.quebec/contenu/prog_1/9bdf5f1a-489a-441f-9e6e-2c87bba58bf8/info.yml

curl "https://progression.dti.crosemont.quebec/demo/api/v1//question/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw?include=tests" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Obtenir la question «Les fonctions avec paramètres/Rectangle», ses tests et ses ébauches:

curl "https://progression.dti.crosemont.quebec/demo/api/v1//question/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw?include=tests,ebauches" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Créer l’avancement de jdoe à la question «Les fonctions avec paramètres/Rectangle»

DATA=$(cat <<EOF
{
        "data": {
                "type": "avancement",
                "id":"aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw"
        }
}
EOF
)
curl --data "$DATA" https://progression.dti.crosemont.quebec/demo/api/v1//user/jdoe/avancements -H "Content-Type: application/vnd.api+json" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Obtenir l’avancement de jdoe pour la question «Les fonctions avec paramètres/Rectangle»

curl "https://progression.dti.crosemont.quebec/demo/api/v1//avancement/jdoe/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Obtenir l’ébauche de solution en Python pour la question «Les fonctions avec paramètres/Rectangle»

curl "https://progression.dti.crosemont.quebec/demo/api/v1//ebauche/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw/python" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Soumettre une tentative de solution à la question «Les fonctions avec paramètres/Rectangle» et récupérer les résultats

DATA=$(cat <<EOF
{
        "data": {
                "type": "tentative",
                "attributes": {
                        "langage":"python",
                        "code":"# Fonction qui calcule et produit en sortie le périmètre du rectangle dont les côtés sont reçus en paramètre. À faire\ndef périmètre( une_largeur, une_longueur ):\n    # -TODO\n    # -VISIBLE\n\n\n    # +VISIBLE\n    # +TODO\n    print(42)\n\n# -TODO\n# Fonction qui calcule et produit en sortie l'aire du rectangle dont les côtés sont reçus en paramètre. À faire\n# +TODO\n\n\n\n# -TODO\n# Programme principal\n# -VISIBLE\n\n# +VISIBLE\n# Entrées\nlargeur = int( input() )\nlongueur = int( input() )\n\n# Appel des fonctions, les côtés du rectangle sont transmis en paramètre. À faire\npérimètre( largeur, longueur )\n# +TODO\n\n\n# -TODO\n# -VISIBLE\n\n\n\n\n\n\n"
                }
        }
}
EOF
)
curl --data "$DATA" https://progression.dti.crosemont.quebec/demo/api/v1//avancement/jdoe/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw/tentatives?include=resultats -H "Content-Type: application/vnd.api+json" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Obtenir une tentative de solution préalablement soumise pour la question «Les fonctions avec paramètres/Rectangle»

curl "https://progression.dti.crosemont.quebec/demo/api/v1//tentative/jdoe/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw/$TIMESTAMP" -H "Authorization: Bearer $TOKEN"
b'<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">\n<html><head>\n<title>301 Moved Permanently</title>\n</head><body>\n<h1>Moved Permanently</h1>\n<p>The document has moved <a href="http://progression.dti.crosemont.quebec/tentative/jdoe/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw">here</a>.</p>\n</body></html>\n\n'

Obtenir le test numéro 0 pour la question «Les fonctions avec paramètres/Rectangle»

curl "https://progression.dti.crosemont.quebec/demo/api/v1//test/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw/0" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Obtenir un token ressource qui donne accès à un avancement de jdoe pour la question «Les fonctions avec paramètres/Rectangle»

DATA=$(cat <<EOF
{
        "expiration" : "+300",
    "data" : [],
    "ressources" : {
      "get_avancement" : {
            "url":"^avancement/jdoe/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw$",
            "method":"^GET$"
      }
    }
}
EOF
)
curl --data "$DATA" https://progression.dti.crosemont.quebec/demo/api/v1//user/jdoe/tokens -H "Content-Type: application/json" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Créer ou mettre à jour la sauvegarde de jdoe pour la question «Les fonctions avec paramètres/Rectangle» et un code python

DATA=$(cat <<EOF
{
        "data": {
                "type": "sauvegarde",
                "attributes": {
                  "langage":"python",
                  "code": "#+TODO\nSystem.out.println('Allo le monde');\n#-TODO"
                }
        }
}
EOF
)
curl --data "$DATA" https://progression.dti.crosemont.quebec/demo/api/v1//avancement/jdoe/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw/sauvegardes -H "Content-Type: application/vnd.api+json" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Obtenir la dernière sauvegarde de jdoe pour la question «Les fonctions avec paramètres/Rectangle» effectuée avec le langage python:

curl "https://progression.dti.crosemont.quebec/demo/api/v1//sauvegarde/jdoe/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw/python" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Créer une clé d’authentification pour jdoe

DATA=$(cat <<EOF
{
        "data": {
                "type": "cle",
                "attributes": {
                  "nom":"cléAuth$(head -c 9 /dev/urandom|base64|tr '/+' '_-')",
                  "portée":1
                }
        }
}
EOF
)
curl --data "$DATA" "https://progression.dti.crosemont.quebec/demo/api/v1//user/jdoe/cles" -H "Content-Type: application/vnd.api+json" -H "Authorization: Bearer $TOKEN"
{
    "erreur": "Accès interdit."
}

Authentification par clé d’authentification pour jdoe

AUTH=$(echo -n "jdoe:$NOM_CLE:$SECRET" | base64 -w0)
DATA=$(cat <<EOF
{
        "data": {
                "type": "token",
                "attributes": {
                        "expiration" : "+300",
                        "data" : [],
                        "ressources" : {
                              "get_avancement" : {
                                          "url":"^avancement/jdoe/aHR0cHM6Ly9wcm9ncmVzc2lvbi5wYWdlcy5kdGkuY3Jvc2Vtb250LnF1ZWJlYy9jb250ZW51L3Byb2dfMS85YmRmNWYxYS00ODlhLTQ0MWYtOWU2ZS0yYzg3YmJhNThiZjgvaW5mby55bWw$",
                                          "method":"^GET$"
                                  }
          }
      }
        }
}
EOF
)
curl --data "$DATA" https://progression.dti.crosemont.quebec/demo/api/v1//user/jdoe/tokens -H "Content-Type: application/vnd.api+json" -H "Authorization: key $AUTH"
{
    "erreur": "Accès interdit."
}