Authentification
Deux headers sur toutes les requêtes, deux 401 typés, et un impératif souvent oublié : la borne est une application navigateur, votre API doit répondre aux preflights CORS.
Les deux headers
X-Kiosk-Auth-Key: <clé partagée> ← « Clé d'autorisation compte » (admin ApiBorne, page Connectivité)
X-Kiosk-Device-Id: <id du device> ← « Id de l'appareil » (admin ApiBorne, page Bornes)La clé identifie le compte (elle est commune à toutes les bornes du compte), le device id identifie quelle borne appelle — c'est lui qui permet de révoquer une borne isolément.
Où trouver la clé et les identifiants
- La clé partagée est la « Clé d'autorisation compte » affichée dans l'admin ApiBorne, page Connectivité (champ copiable). C'est cette valeur que chaque borne envoie dans
X-Kiosk-Auth-Key— vous la stockez chez vous telle quelle, à l'installation de l'intégration. C'est aussi elle qui sert de headerAuthorizationpour vos appels sortants vers le serveur ApiBorne. - Les identifiants de devices sont les « Id de l'appareil » de la page Bornes de l'admin ApiBorne (ex.
HfDXSQ0dv). La flotte est gérée là-bas ; chaque borne transmet son id tel quel dansX-Kiosk-Device-Id.
Implémenter la vérification, pas à pas
L'ordre est contractuel — à chaque requête entrante :
- requête
OPTIONS? → répondre au preflight CORS (voir plus bas), sans exiger les headers d'auth ; X-Kiosk-Device-Idabsent ou inconnu →401avec le codeUNKNOWN_DEVICE;X-Kiosk-Auth-Keyabsente ou différente de la clé attendue →401avec le codeINVALID_AUTH_KEY;- sinon → traiter l'opération.
// src/server/contract/auth.ts (implémentation de référence) — extrait
export function requireKioskAuth(request: NextRequest): NextResponse | null {
// 1. Le device D'ABORD (ordre contractuel)
const deviceId = request.headers.get("x-kiosk-device-id");
if (!deviceId) {
return contractError("UNKNOWN_DEVICE", "Missing X-Kiosk-Device-Id header");
}
if (!isKnownDevice(deviceId)) {
return contractError("UNKNOWN_DEVICE", `Unknown kiosk device '${deviceId}'`);
}
// 2. Puis la clé partagée
return requireAuthKey(request);
}
export function requireAuthKey(request: NextRequest): NextResponse | null {
const authKey = request.headers.get("x-kiosk-auth-key");
if (!authKey || authKey !== expectedKey()) {
return contractError("INVALID_AUTH_KEY", "Invalid authorization key for this device");
}
return null; // authentifié
}Et dans chaque handler, la garde s'appelle en première ligne :
// Dans CHAQUE handler du contrat : la garde en première ligne
export const POST = withErrorBoundary(async (request: NextRequest) => {
const authError = requireKioskAuth(request);
if (authError) return authError; // 401 typé, corps normalisé
// … logique métier de l'opération
});Voir le fichier complet sur GitHub → (avec la politique tolérante et le header X-Kiosk-Office-Id commentés).
Tester vos 401 avec curl
Sans headers — le device est vérifié en premier :
curl -sS -i "$BASE/patients/identify" -H 'Content-Type: application/json' -d '{"criteria":{}}'
# HTTP/1.1 401 Unauthorized
# { "error": { "code": "UNKNOWN_DEVICE", "message": "Missing X-Kiosk-Device-Id header", "details": null } }Device connu mais mauvaise clé :
curl -sS -i "$BASE/patients/identify" \
-H 'X-Kiosk-Device-Id: KIOSK-042' -H 'X-Kiosk-Auth-Key: mauvaise-cle' \
-H 'Content-Type: application/json' -d '{"criteria":{}}'
# HTTP/1.1 401 Unauthorized
# { "error": { "code": "INVALID_AUTH_KEY", "message": "Invalid authorization key for this device", "details": null } }Appel authentifié :
curl -sS "$BASE/patients/identify" \
-H 'X-Kiosk-Device-Id: KIOSK-042' -H 'X-Kiosk-Auth-Key: s3cr3t-key' \
-H 'Content-Type: application/json' -d '{"criteria":{"lastName":"DURAND","firstName":"MARIE","birthDate":"1980-05-12"}}'
# HTTP/1.1 200 OK — { "patients": [ … ] }{ "error": { code, message, details } }.CORS obligatoire
La borne appelle votre serveur depuis un navigateur. Vous devez répondre aux preflights OPTIONS avec :
Access-Control-Allow-Origin: <origine de la borne>
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Kiosk-Auth-Key, X-Kiosk-Device-IdModule de référence : src/server/contract/cors.ts.
Multi-sites : X-Kiosk-Office-Id
ApiBorne envoie aussi X-Kiosk-Office-Id sur chaque appel : un éditeur multi-établissements sélectionne ainsi le bon site sans avoir à le résoudre depuis sa table de devices. Sa valeur est l'identifiant libre saisi dans la configuration d'intégration ApiBorne — vous choisissez son format à l'installation. Un éditeur mono-site peut simplement l'ignorer.
Exceptions : appels du serveur ApiBorne
Deux opérations sont appelées par le serveur ApiBorne (Cockpit), pas par une borne : POST /staff/sign-in et PUT /appointments/{id}/status. Elles ne portent que X-Kiosk-Auth-Key — pas de device id : votre garde doit prévoir les deux niveaux (requireKioskAuth complet vs requireAuthKey seul).
