---
seo_title: Clé publique + JWT — Authentification API côté client | EnConvert
meta_desc: Échangez une clé publique pk_ contre un jeton JWT via POST /v1/auth/token pour l'auth côté client. Domaines autorisés, jetons d'1h, rafraîchissement auto.
keywords: authentification jwt api, clé publique côté client api, échanger une clé api contre un jwt, endpoint /v1/auth/token, cookie httponly refresh token, rafraîchir un jeton jwt javascript, liste blanche de domaines api, jeton d'accès de courte durée navigateur
---

# 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

1. **Échangez** votre clé publique (`pk_`) contre un jeton d'accès JWT en appelant `POST /v1/auth/token`.
2. **Utilisez** le jeton JWT dans l'en-tête `Authorization: Bearer <token>` sur les requêtes API.
3. **Rafraîchissez** le jeton automatiquement avant son expiration à l'aide de `POST /v1/auth/refresh`.
4. **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

```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;
}
```

<div class="alert alert-warning">
<strong>Important :</strong> Vous devez inclure <code>credentials: "include"</code> 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.
</div>

### Réponse

```json
{
  "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.

```javascript
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

```javascript
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.com` correspond uniquement à `https://example.com`.
- **Sous-domaines génériques :** `https://*.example.com` correspond à `https://app.example.com`, `https://staging.example.com`, etc.
- **Spécifique à un port :** `http://localhost:3000` correspond 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_url` pour un téléchargement immédiat. Il n'existe aucune option pour des destinations de stockage personnalisées.

<div class="alert alert-warning">
<strong>Bonnes pratiques :</strong>
<ul>
  <li>Stockez toujours les jetons d'accès uniquement en mémoire. Ne les persistez jamais dans <code>localStorage</code> ou <code>sessionStorage</code>.</li>
  <li>Mettez en place le rafraîchissement automatique des jetons pour éviter les interruptions pendant les sessions utilisateur.</li>
  <li>Gardez votre liste de domaines autorisés aussi précise que possible. Évitez les caractères génériques trop larges.</li>
  <li>Utilisez <code>credentials: "include"</code> sur toutes les requêtes fetch pour garantir l'envoi et la réception corrects des cookies.</li>
  <li>Gérez les échecs de rafraîchissement de jeton avec élégance en revenant à une ré-authentification complète avec la clé publique.</li>
</ul>
</div>

## 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.
