# Exigences de sécurité

Chaque application installée sur une boutique DZBuild détient un jeton vers les commandes, les clients ou le catalogue d'un marchand. Ces règles s'appliquent à toutes les applications, en mode test comme après approbation. DZBuild peut suspendre une application qui les enfreint, et une application suspendue reçoit `403 app_suspended` à chaque appel.

## Adresses de redirection[​](#adresses-de-redirection "Lien direct vers Adresses de redirection")

Enregistrez 1 à 5 adresses de redirection dans la console développeur. Chacune doit :

* utiliser `https`,
* faire 512 caractères au plus,
* ne contenir ni nom d'utilisateur, ni mot de passe, ni `#fragment`,
* ne contenir aucun joker `*`.

DZBuild compare le `redirect_uri` de chaque requête à la liste enregistrée, caractère pour caractère. `https://app.example.com/callback` et `https://app.example.com/callback/` sont deux adresses différentes. La console n'accepte que `https` : pour tester le flux d'installation, passez par une adresse `workers.dev` ou un tunnel https et enregistrez cette adresse.

Générez un `state` aléatoire nouveau pour chaque demande d'autorisation, liez-le à la session du marchand, et refusez un retour dont le `state` ne correspond pas.

## Client secret et jetons d'accès[​](#client-secret-et-jetons-daccès "Lien direct vers Client secret et jetons d'accès")

Le client secret (`dzas_` suivi de 48 caractères hexadécimaux) s'affiche une seule fois, à la création de l'application ou au renouvellement du secret. DZBuild n'en garde qu'une empreinte. Utilisez-le uniquement sur votre serveur : jamais dans un navigateur, une application mobile, un dépôt public ou une ligne de journal.

En cas de fuite du secret, renouvelez-le dans la console développeur. L'ancien secret cesse de fonctionner immédiatement, et les jetons d'accès déjà émis continuent de fonctionner.

Les jetons d'accès (`dzpk_live_...`) donnent l'accès à l'API que le marchand a approuvé, sans expiration. Gardez-les côté serveur, chiffrés au repos, un par boutique. Les applications n'ont aucun endpoint pour révoquer un jeton. En cas de fuite d'un jeton, demandez au marchand d'installer de nouveau votre application sur cette boutique : le nouveau jeton remplace l'ancien immédiatement. Le marchand peut aussi désinstaller l'application, ce qui le révoque.

## Vérifier les signatures des webhooks[​](#vérifier-les-signatures-des-webhooks "Lien direct vers Vérifier les signatures des webhooks")

Chaque webhook que DZBuild envoie à votre application est signé avec le secret de signature de l'application, une chaîne hexadécimale de 64 caractères que vous pouvez afficher et renouveler dans la console. La requête porte `X-DZ-Timestamp`, `X-DZ-Event`, `X-DZ-Delivery` et :

```
X-DZ-Signature: t=1758880000,v1=5d41402abc4b2a76b9719d911017c592...
```

Calculez un HMAC-SHA256 avec le secret de signature sur l'horodatage, un point et le corps brut de la requête, comparez-le à `v1` en temps constant, et rejetez une requête dont l'horodatage a plus de 5 minutes. Les endpoints webhook des applications ne reçoivent jamais l'en-tête `X-DZ-Token` : la signature est la seule preuve qu'une requête vient de DZBuild. La page [webhooks](https://dzbuild.dev/fr/fr/webhooks.md) donne le schéma complet et du code dans plusieurs langages.

Un seul secret de signature couvre toutes les boutiques qui ont installé votre application. Le renouveler change la clé de toutes en une seule opération.

## Vérifier les jetons d'ouverture[​](#vérifier-les-jetons-douverture "Lien direct vers Vérifier les jetons d'ouverture")

Quand le marchand ouvre votre application depuis le tableau de bord DZBuild, DZBuild redirige le navigateur vers votre lien d'ouverture avec un paramètre de requête `dz_launch`. Le lien d'ouverture doit utiliser `https`. Le paramètre est un JWT signé en HS256 avec le secret de signature de l'application : la chaîne de 64 caractères exactement comme la console l'affiche.

| Claim        | Valeur                                                                        |
| ------------ | ----------------------------------------------------------------------------- |
| `iss`        | `dzbuild`                                                                     |
| `aud`        | Votre `client_id`.                                                            |
| `sub`        | L'identifiant de l'utilisateur DZBuild qui a ouvert l'application, en chaîne. |
| `store_id`   | La boutique depuis laquelle l'application a été ouverte.                      |
| `install_id` | Votre installation sur cette boutique.                                        |
| `is_owner`   | `true` pour le propriétaire de la boutique, `false` pour un membre d'équipe.  |
| `iat`        | Heure d'émission, en secondes Unix.                                           |
| `exp`        | `iat` plus 300 secondes.                                                      |
| `jti`        | 16 caractères hexadécimaux aléatoires.                                        |

N'acceptez le jeton que si la signature correspond, que l'en-tête indique `HS256`, que `iss` vaut `dzbuild`, que `aud` est votre identifiant client et que `exp` n'est pas dépassé. Refusez ensuite tout `jti` déjà accepté dans les 5 dernières minutes. Après avoir lu le jeton, redirigez vers une URL qui ne le contient plus, pour qu'il reste hors de l'historique du navigateur et des journaux.

Un jeton d'ouverture prouve qui a ouvert l'application et depuis quelle boutique. Il n'appelle pas l'API : utilisez le jeton d'accès enregistré pour ce `store_id` et cet `install_id`.

En PHP :

```
<?php

/** Claims of a valid dz_launch token, or null. $clientId is your app's client_id. */

function verifyLaunchToken(string $jwt, string $signingSecret, string $clientId): ?array

{

    $parts = explode('.', $jwt);

    if (count($parts) !== 3) {

        return null;

    }

    [$head, $body, $sig] = $parts;

    $b64 = static fn(string $s): string|false => base64_decode(strtr($s, '-_', '+/'), true);

    $expected = rtrim(strtr(base64_encode(hash_hmac('sha256', "$head.$body", $signingSecret, true)), '+/', '-_'), '=');

    if (!hash_equals($expected, $sig)) {

        return null;

    }

    $header = json_decode((string) $b64($head), true);

    $claims = json_decode((string) $b64($body), true);

    if (($header['alg'] ?? '') !== 'HS256' || !is_array($claims)) {

        return null;

    }

    $now = time();

    if (($claims['iss'] ?? '') !== 'dzbuild' || ($claims['aud'] ?? '') !== $clientId

        || !is_int($claims['exp'] ?? null) || $claims['exp'] < $now || ($claims['iat'] ?? 0) > $now + 60) {

        return null;

    }

    return $claims; // Then refuse a jti you have already seen in the last 5 minutes.

}
```

En Node.js :

```
const crypto = require('node:crypto');



// Claims of a valid dz_launch token, or null. clientId is your app's client_id.

function verifyLaunchToken(jwt, signingSecret, clientId) {

  const parts = String(jwt).split('.');

  if (parts.length !== 3) return null;

  const [head, body, sig] = parts;

  const expected = crypto.createHmac('sha256', signingSecret).update(`${head}.${body}`).digest();

  const given = Buffer.from(sig, 'base64url');

  if (given.length !== expected.length || !crypto.timingSafeEqual(given, expected)) return null;

  const header = JSON.parse(Buffer.from(head, 'base64url').toString('utf8'));

  const claims = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));

  const now = Math.floor(Date.now() / 1000);

  if (header.alg !== 'HS256' || claims.iss !== 'dzbuild' || claims.aud !== clientId) return null;

  if (!Number.isInteger(claims.exp) || claims.exp < now || claims.iat > now + 60) return null;

  return claims; // Then refuse a jti you have already seen in the last 5 minutes.

}
```

## Supprimer les données après la désinstallation[​](#supprimer-les-données-après-la-désinstallation "Lien direct vers Supprimer les données après la désinstallation")

Quand un marchand désinstalle votre application, DZBuild révoque le jeton de la boutique, abandonne les livraisons webhook encore en attente pour cette installation, et envoie un seul événement `app.uninstalled` à votre URL de webhook si elle est vérifiée et active :

```
{

  "id": "evt_...",

  "event": "app.uninstalled",

  "created_at": "2026-09-26T10:15:00+01:00",

  "store_id": 141,

  "data": {

    "install_id": 57,

    "client_id": "dzapp_0123456789abcdef0123",

    "store_id": 141,

    "uninstalled_at": "2026-09-26T09:15:00+00:00"

  }

}
```

Supprimez les données que vous détenez pour cette boutique dans les 30 jours qui suivent l'événement : commandes, clients, produits et toute copie du jeton d'accès. Si votre application n'a pas d'URL de webhook vérifiée, un `401` sur le jeton de la boutique est votre signal.

## Demander le moins de scopes possible[​](#demander-le-moins-de-scopes-possible "Lien direct vers Demander le moins de scopes possible")

Demandez uniquement les [scopes](https://dzbuild.dev/fr/fr/scopes.md) que vos fonctionnalités utilisent : pas de scope d'écriture sur des données que vous ne faites que lire, pas de `customers:read` pour une application qui n'affiche jamais de client. DZBuild compare les scopes demandés à ce que votre fiche dit de l'application, et le marchand voit une ligne par ressource sur l'écran de consentement avant d'approuver.

## Pas d'extraction automatisée[​](#pas-dextraction-automatisée "Lien direct vers Pas d'extraction automatisée")

N'accédez aux données de la boutique que par l'API REST et les webhooks. N'automatisez pas le tableau de bord DZBuild, ne vous connectez pas à la place du marchand, et n'extrayez pas les pages du tableau de bord ou de la boutique en ligne. Ne demandez jamais à un marchand son mot de passe DZBuild ni une clé API marchand.

## Répondre à l'e-mail de support[​](#répondre-à-le-mail-de-support "Lien direct vers Répondre à l'e-mail de support")

Enregistrez une adresse e-mail de support que vous lisez, et répondez aux marchands qui y écrivent. L'écran de consentement et la page de l'application dans le tableau de bord du marchand l'affichent. DZBuild envoie les décisions de vérification à l'e-mail de votre compte, pas à cette adresse.
