Aller au contenu principal

Comptes de service

Un compte de service permet à une application d'envoyer des requêtes authentifiées à l'API Webmarketer.

Pour effectuer des requêtes à l'API Webmarketer avec un compte de service il faut :

  1. Créer un compte de service et une clé pour ce compte depuis l'interface web
    Clé privée

    La clé associée à un compte de service doit être conservée de façon sécurisée

  2. Générer dans votre application un JWT signé avec la clé privée du compte de service
  3. Échanger ce JWT contre un jeton d'accès à l'API auprès du serveur oauth de Webmarketer
  4. Envoyer le jeton d'accès récupéré à l'API lors des requêtes

Clé de compte de service

Lors de la création d'une clé pour un compte de service depuis l'interface web, celle-ci est téléchargée sur votre machine sous la forme d'un fichier JSON.

Le contenu du fichier est de la forme :

{
"clientId": <identifiant unique>,
"serviceAccountEmail": <email du compte de service>,
"privateKeyId": <identifiant de la clé>,
"privateKey": <clé privée au format PEM>
}

Ces informations sont nécessaires pour générer un JWT afin de récupérer un jeton d'accès à l'API auprès du serveur oauth de Webmarketer.

Générer un JWT

Un Json Web Token est composé de 3 parties : l'entête, le corps et la signature. L'entête et le corps du JWT sont des objets JSON.

L'entête du JWT attendu par le serveur oauth doit contenir les propriétés suivantes :

  • kid contenant l'indentifiant de la clé (privateKeyId dans le fichier de clé)
  • alg qui définit l'algorithme utilisé pour signer le JWT, le seul algorithme autorisé est RS256
  • typ contenant la valeur JWT
{
"kid": <privateKeyId>,
"alg": "RS256",
"typ": "JWT"
}

Le corps du JWT doit contenir les propriétés suivantes :

  • iss contenant l'émetteur du JWT (clientId dans le fichier clé)
  • sub contenant le sujet pour lequel le JWT est émis (serviceAccountEmail)
  • aud contenant l'audience de destination du JWT, c'est l'url de la route du serveur oauth permettant de récupérer un jeton d'accès à l'API
  • scope contenant une liste de scopes demandés pour le jeton d'accès à l'API
  • exp indiquant la date jusqu'à laquelle le JWT doit être considéré comme valide, sous la forme d'un timestamp
{
"iss": <clientId>,
"sub": <serviceAccountEmail>,
"aud": "https://oauth.webmarketer.io/oidc/token",
"scope": "full_access",
"exp": <some number>
}

Une fois l'entête est le corps du JWT générés, il faut les encodés en base64url (base64 où les caractères + et / sont remplacés par les caractères - et _ respectivements et où les caractères = ont été supprimés).

La première partie du JWT est obtenue par la concaténation de l'entête encodé, d'un point . et du corps encodé.

La dernière est partie est obtenu en signant la première partie à l'aide de RSA SHA-256 et de la clé privée contenue dans le fichier de clé (privateKey).

Une fois la signature générée, le JWT final est obtenu en concaténant la première partie, un point . et la signature encodée en base64url.

Le JWT généré est constitué de 3 parties séparées par des points :

eyJraWQiOiJwcml2YXRlS2V5SWQiLCJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJjbGllbnRJZCIsInN1YiI6InNlcnZpY2VBY2NvdW50RW1haWwiLCJhdWQiOiJodHRwczovL29hdXRoLndlYm1hcmtldGVyLmlvL29pZGMvdG9rZW4iLCJzY29wZSI6ImZ1bGxfYWNjZXNzIiwiZXhwIjoxNjI5MzA0NTY0fQ.OZBSymD_ho5OZjQUHHV_2__ctU9Gk7QxHTW-9LwiafsilR8NYwW0GR56S6zlhw-YpEj28wz75KwsiJI4shO2WYkbc3jtGSTqbc7Mw69clfA4XQ0cjxHVUrVKfVgIiX8bAwocIOSdvJhRJTcpmeN7SVjtKp79kFmfRESBcp3YTv_6QOyuFL-hABDqJYF92x0fzV14b-9AsulIb_JZggrKYEgylLPDkvauL68zI34hcV-lyVXG3A-Al5yEUo3qFzEknG-RHrjD4QmtzwttR_fLMj-1s8dlg8oSJtIruzCy8jp9eIAJjJKmLW8zK0KQEqRz-rg7rQwWMIxw_ESP7HMSaA

Exemples

import { sign } from "crypto";
import { readFile } from "fs/promises";

function base64urlEncode(buffer: Buffer): string {
return buffer.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
}

async function getKey(keyFilePath: string) {
// retrieve the key
const jsonEncodedKey = await readFile(keyFilePath, "utf-8");
const key = JSON.parse(jsonEncodedKey);
return key;
}

function generateJwt(key: Record<string, any>): string {
// generate the header
const header = {
kid: key.privateKeyId,
alg: "RS256",
typ: "JWT",
};

// generate the payload
const payload = {
iss: key.clientId,
sub: key.serviceAccountEmail,
aud: "https://oauth.webmarketer.io/oidc/token",
scope: "full_access",
exp: Date.now() / 1000 + 300, // 5 minutes
};

// first part of the jwt
let jwt = base64urlEncode(Buffer.from(JSON.stringify(header)))
+ "."
+ base64urlEncode(Buffer.from(JSON.stringify(payload)));

// generate the signature
const signature = sign("RSA-SHA256", Buffer.from(jwt), key.privateKey);

// complete jwt
jwt = jwt
+ "."
+ base64urlEncode(signature);

return jwt;
}

Obtenir un jeton d'accès à l'API

Les comptes de service négocient les jetons d'accès à l'API auprès du serveur oauth en utilisant le grant type jwt-bearer.

Il suffit d'envoyer une requête POST à la route /oidc/token avec les paramètres suivants dans le body de la requête :

  • grant_type doit avoir la valeur urn:ietf:params:oauth:grant-type:jwt-bearer
  • client_id correspond à la propriété clientId de la clé
  • assertion doit contenir le JWT généré précédemment
Content-Type

Le body de la requête doit être au format application/x-www-form-urlencoded.

Le serveur renvoie une réponse de la forme :

{
"access_token": "eyJraWQiOiJwcml2YXRlS2V5SWQiLCJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJ1bmlxdWVJZCIsInN1YiI6ImFjY291bnRJZCIsImlhdCI6MTYyOTM4ODg5MCwiZXhwIjoxNjI5MzkyNDkwLCJzY29wZSI6ImZ1bGxfYWNjZXNzIiwiaXNzIjoiaHR0cHM6Ly9vYXV0aC53ZWJtYXJrZXRlci5pbyIsImF1ZCI6ImNsaWVudElkIn0.b1BZJdE-5OKNvRKdM8uEPWVwbDHoxu74p_A6NK1KuQW2hQmKcKDLUNBRqTDpw156P9ll9DORxbBdRKrY988WdvQNMs_ehSgIHdfM-W02EHDxgSXIpyNgj5th76XibP-6Glhvhbo24-ZOFdK7V1EBVYrxcLTviunqw42JrBf59W2OsvOYIuEzDOY3jH8oQ-s20PKoCqq_g5HBcjH_9-eoFLJnwgaiTwtSbuayJiGUJjkjmki1dp_2YRORXFY0VKzsCrd6b4URgYTt6M9o-61WCsmgYiqD--1hZ55hfg4WphSIhlJgSvPl4tqn0_wITPDcgLNRHaT1MDD1m42py5_rDA",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "full_access"
}

Exemples

curl --request POST \
--url https://oauth.webmarketer.io/oidc/token \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer \
--data client_id=<clientId> \
--data assertion=<JWT>

Envoyer des requêtes à l'API

Pour envoyer une requête authentifiée à l'api, il suffit d'ajouter l'entête Authorization avec la valeur Bearer <jwt>.