Authentification par clé publique + JWT#
L'authentification par clé publique et JWT permet aux applications côté client (navigateur) d'appeler l'API Enconvert en toute sécurité : vous échangez votre clé publique (pk_) contre un jeton d'accès JWT de courte durée via POST /v1/auth/token, puis vous envoyez ce jeton dans l'en-tête Authorization: Bearer <token> sur les requêtes API. Comme une clé publique est visible par les utilisateurs finaux, elle ne peut pas appeler l'API directement — un JWT valide est toujours requis, et les jetons ne sont délivrés qu'aux domaines autorisés. Les jetons d'accès expirent au bout d'1 heure et sont rafraîchis via POST /v1/auth/refresh à l'aide d'un cookie de jeton de rafraîchissement HttpOnly.
Fonctionnement#
- Échangez votre clé publique (
pk_) contre un jeton d'accès JWT en appelantPOST /v1/auth/token. - Utilisez le jeton JWT dans l'en-tête
Authorization: Bearer <token>sur les requêtes API. - Rafraîchissez le jeton automatiquement avant son expiration à l'aide de
POST /v1/auth/refresh. - La liste blanche de domaines garantit que seules les requêtes provenant de vos domaines autorisés sont acceptées.
Étape 1 : échanger la clé publique contre un JWT#
Envoyez votre clé publique et l'origine parente pour obtenir un jeton d'accès.
Endpoint#
POST /v1/auth/token
En-têtes de la requête#
| En-tête | Valeur | Description |
|---|---|---|
X-API-Key |
pk_your_public_key |
Votre clé API publique |
Exemple JavaScript#
async function getToken() {
const response = await fetch("https://api.enconvert.com/v1/auth/token", {
method: "POST",
headers: {
"X-API-Key": "pk_your_public_key",
},
credentials: "include",
});
if (!response.ok) {
throw new Error(`Token exchange failed: ${response.status}`);
}
const data = await response.json();
return data.token;
}
credentials: "include" dans les options du fetch. Cela garantit que le cookie du jeton de rafraîchissement est stocké par le navigateur, ce qui est nécessaire pour le rafraîchissement automatique du jeton.
Réponse#
{
"token": "eyJhbGciOiJIUzI1NiIs..."
}
La réponse définit également un cookie HttpOnly contenant le jeton de rafraîchissement. Ce cookie est géré automatiquement par le navigateur et utilisé lors du rafraîchissement du jeton d'accès.
Étape 2 : utiliser le jeton JWT#
Incluez le jeton JWT dans l'en-tête Authorization en tant que jeton Bearer sur toutes les requêtes API suivantes.
async function convertUrlToPdf(token, url) {
const response = await fetch("https://api.enconvert.com/v1/convert/url-to-pdf", {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ url }),
});
return await response.json();
}
// Usage
const token = await getToken();
const result = await convertUrlToPdf(token, "https://example.com");
console.log(result.presigned_url);
Étape 3 : rafraîchissement automatique du jeton#
Les jetons d'accès expirent après une courte période. Utilisez l'endpoint de rafraîchissement pour obtenir un nouveau jeton d'accès sans obliger l'utilisateur à se ré-authentifier.
Endpoint#
POST /v1/auth/refresh
Le jeton de rafraîchissement est envoyé automatiquement via le cookie HttpOnly défini lors de l'échange de jeton initial. Aucun corps de requête ni en-tête supplémentaire n'est nécessaire.
Exemple JavaScript de rafraîchissement automatique#
class EnconvertClient {
constructor(publicKey) {
this.publicKey = publicKey;
this.token = null;
this.tokenExpiry = null;
}
async getToken() {
const response = await fetch("https://api.enconvert.com/v1/auth/token", {
method: "POST",
headers: {
"X-API-Key": this.publicKey,
"X-Parent-Origin": window.location.origin,
},
credentials: "include",
});
const data = await response.json();
this.token = data.token;
// Set expiry to 55 minutes (refresh before the 1-hour expiry)
this.tokenExpiry = Date.now() + 55 * 60 * 1000;
return this.token;
}
async refreshToken() {
const response = await fetch("https://api.enconvert.com/v1/auth/refresh", {
method: "POST",
credentials: "include",
});
if (!response.ok) {
// Refresh token expired, re-authenticate
return await this.getToken();
}
const data = await response.json();
this.token = data.token;
this.tokenExpiry = Date.now() + 55 * 60 * 1000;
return this.token;
}
async getValidToken() {
if (!this.token || Date.now() >= this.tokenExpiry) {
if (this.token) {
return await this.refreshToken();
}
return await this.getToken();
}
return this.token;
}
async convert(endpoint, body) {
const token = await this.getValidToken();
const response = await fetch(`https://api.enconvert.com${endpoint}`, {
method: "POST",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
return await response.json();
}
}
// Usage
const client = new EnconvertClient("pk_your_public_key");
const result = await client.convert("/v1/convert/url-to-pdf", {
url: "https://example.com",
});
Durées de vie des jetons#
| Jeton | Durée de vie | Stockage |
|---|---|---|
| Jeton d'accès | 1 heure | Renvoyé dans le corps de la réponse JSON ; à stocker en mémoire |
| Jeton de rafraîchissement | 7 jours | Défini en tant que cookie HttpOnly ; géré par le navigateur |
Liste blanche de domaines#
Les clés publiques sont restreintes à des domaines spécifiques configurés dans votre tableau de bord.
Règles de correspondance#
- Correspondance exacte :
https://example.comcorrespond uniquement àhttps://example.com. - Sous-domaines génériques :
https://*.example.comcorrespond àhttps://app.example.com,https://staging.example.com, etc. - Spécifique à un port :
http://localhost:3000correspond uniquement à cette origine exacte, port inclus.
Exemples#
| Domaine autorisé | Correspond à | Ne correspond pas à |
|---|---|---|
https://example.com |
https://example.com |
https://www.example.com |
https://*.example.com |
https://app.example.com, https://dev.example.com |
https://example.com |
http://localhost:3000 |
http://localhost:3000 |
http://localhost:8080 |
Fonctionnalités de sécurité#
- Jetons de courte durée : Les jetons d'accès expirent au bout d'1 heure, ce qui limite la fenêtre d'exposition en cas de compromission d'un jeton.
- Cookies de rafraîchissement HttpOnly : Les jetons de rafraîchissement sont stockés dans des cookies
HttpOnly, ce qui les rend inaccessibles au JavaScript et résistants aux attaques XSS. - Restrictions de domaine : Les jetons ne sont délivrés que lorsque la requête provient d'un domaine autorisé.
- Pas d'accès direct à l'API : Les clés publiques seules ne peuvent pas appeler les endpoints de conversion. Un JWT valide est toujours requis.
Restrictions de la clé publique#
L'authentification par clé publique présente les limitations suivantes par rapport aux clés privées :
- Synchrone uniquement : Seuls les endpoints de conversion synchrones sont disponibles. Les opérations asynchrones, l'interrogation de tâches et les webhooks ne sont pas pris en charge.
- Une seule URL par requête : Le traitement par lots n'est pas disponible. Chaque requête ne peut convertir qu'une seule URL ou un seul fichier.
- Téléchargement direct : Les réponses fournissent une
presigned_urlpour un téléchargement immédiat. Il n'existe aucune option pour des destinations de stockage personnalisées.
- Stockez toujours les jetons d'accès uniquement en mémoire. Ne les persistez jamais dans
localStorageousessionStorage. - Mettez en place le rafraîchissement automatique des jetons pour éviter les interruptions pendant les sessions utilisateur.
- Gardez votre liste de domaines autorisés aussi précise que possible. Évitez les caractères génériques trop larges.
- Utilisez
credentials: "include"sur toutes les requêtes fetch pour garantir l'envoi et la réception corrects des cookies. - Gérez les échecs de rafraîchissement de jeton avec élégance en revenant à une ré-authentification complète avec la clé publique.
Questions fréquentes#
Comment échanger une clé API contre un jeton JWT ?#
Appelez POST /v1/auth/token avec votre clé publique dans l'en-tête X-API-Key et credentials: "include" dans les options du fetch. La réponse renvoie un token dans le corps JSON et définit un cookie HttpOnly contenant le jeton de rafraîchissement.
Combien de temps durent les jetons d'accès et les jetons de rafraîchissement ?#
Les jetons d'accès expirent au bout d'1 heure et doivent être stockés uniquement en mémoire. Les jetons de rafraîchissement durent 7 jours et sont définis en tant que cookie HttpOnly géré par le navigateur.
Pourquoi le rafraîchissement de mon jeton échoue-t-il sans credentials: "include" ?#
Le jeton de rafraîchissement est stocké dans un cookie HttpOnly défini lors de l'échange de jeton initial, et POST /v1/auth/refresh repose sur l'envoi automatique de ce cookie par le navigateur. Si vous omettez credentials: "include" de vos requêtes fetch, le cookie n'est ni enregistré ni envoyé — en cas d'échec du rafraîchissement, revenez à une ré-authentification complète avec votre clé publique.
Puis-je utiliser des sous-domaines génériques dans la liste blanche de domaines ?#
Oui. https://*.example.com correspond à https://app.example.com et https://staging.example.com, mais pas à https://example.com lui-même. Les correspondances exactes et les origines spécifiques à un port comme http://localhost:3000 sont également prises en charge.
Puis-je utiliser une clé publique pour des conversions asynchrones ou par lots ?#
Non. L'authentification par clé publique ne prend en charge que les endpoints de conversion synchrones, avec une seule URL ou un seul fichier par requête ; les opérations asynchrones, l'interrogation de tâches, les webhooks et le traitement par lots nécessitent une clé privée. Les réponses fournissent une presigned_url pour un téléchargement immédiat.