Skip to content

Stockez et récupérez des identifiants sensibles de manière sécurisée en utilisant les API natives de stockage d'identifiants du système d'exploitation.

typescript
import { secrets } from "bun";

let githubToken: string | null = await secrets.get({
  service: "my-cli-tool",
  name: "github-token",
});

if (!githubToken) {
  githubToken = prompt("Veuillez entrer votre token GitHub");

  await secrets.set({
    service: "my-cli-tool",
    name: "github-token",
    value: githubToken,
  });

  console.log("Token GitHub stocké");
}

const response = await fetch("https://api.github.com/user", {
  headers: { Authorization: `token ${githubToken}` },
});

console.log(`Connecté en tant que ${(await response.json()).login}`);

Aperçu

Bun.secrets fournit une API multiplateforme pour gérer des identifiants sensibles que les outils CLI et les applications de développement stockent généralement dans des fichiers en clair comme ~/.npmrc, ~/.aws/credentials, ou des fichiers .env. Il utilise :

  • macOS : Keychain Services
  • Linux : libsecret (GNOME Keyring, KWallet, etc.)
  • Windows : Windows Credential Manager

Toutes les opérations sont asynchrones et non bloquantes, s'exécutant sur le pool de threads de Bun.

NOTE

À l'avenir, nous pourrions ajouter une option `provider` supplémentaire pour rendre cela plus adapté aux secrets de déploiement en production, mais aujourd'hui cette API est surtout utile pour les outils de développement local.

API

Bun.secrets.get(options)

Récupère un identifiant stocké.

typescript
import { secrets } from "bun";

const password = await Bun.secrets.get({
  service: "my-app",
  name: "alice@example.com",
});
// Retourne : string | null

// Ou si vous préférez sans objet
const password = await Bun.secrets.get("my-app", "alice@example.com");

Paramètres :

  • options.service (string, requis) - Le nom du service ou de l'application
  • options.name (string, requis) - Le nom d'utilisateur ou l'identifiant du compte

Retourne :

  • Promise<string | null> - Le mot de passe stocké, ou null s'il n'est pas trouvé

Bun.secrets.set(options, value)

Stocke ou met à jour un identifiant.

typescript
import { secrets } from "bun";

await secrets.set({
  service: "my-app",
  name: "alice@example.com",
  value: "super-secret-password",
});

Paramètres :

  • options.service (string, requis) - Le nom du service ou de l'application
  • options.name (string, requis) - Le nom d'utilisateur ou l'identifiant du compte
  • value (string, requis) - Le mot de passe ou secret à stocker

Notes :

  • Si un identifiant existe déjà pour la combinaison service/nom donnée, il sera remplacé
  • La valeur stockée est chiffrée par le système d'exploitation

Bun.secrets.delete(options)

Supprime un identifiant stocké.

typescript
const deleted = await Bun.secrets.delete({
  service: "my-app",
  name: "alice@example.com",
  value: "super-secret-password",
});
// Retourne : boolean

Paramètres :

  • options.service (string, requis) - Le nom du service ou de l'application
  • options.name (string, requis) - Le nom d'utilisateur ou l'identifiant du compte

Retourne :

  • Promise<boolean> - true si un identifiant a été supprimé, false s'il n'a pas été trouvé

Exemples

Stockage d'identifiants d'outils CLI

javascript
// Stocker le token GitHub CLI (au lieu de ~/.config/gh/hosts.yml)
await Bun.secrets.set({
  service: "my-app.com",
  name: "github-token",
  value: "ghp_xxxxxxxxxxxxxxxxxxxx",
});

// Ou si vous préférez sans objet
await Bun.secrets.set("my-app.com", "github-token", "ghp_xxxxxxxxxxxxxxxxxxxx");

// Stocker le token du registre npm (au lieu de ~/.npmrc)
await Bun.secrets.set({
  service: "npm-registry",
  name: "https://registry.npmjs.org",
  value: "npm_xxxxxxxxxxxxxxxxxxxx",
});

// Récupérer pour les appels API
const token = await Bun.secrets.get({
  service: "gh-cli",
  name: "github.com",
});

if (token) {
  const response = await fetch("https://api.github.com/name", {
    headers: {
      Authorization: `token ${token}`,
    },
  });
}

Migration depuis des fichiers de configuration en clair

javascript
// Au lieu de stocker dans ~/.aws/credentials
await Bun.secrets.set({
  service: "aws-cli",
  name: "AWS_SECRET_ACCESS_KEY",
  value: process.env.AWS_SECRET_ACCESS_KEY,
});

// Au lieu de fichiers .env avec des données sensibles
await Bun.secrets.set({
  service: "my-app",
  name: "api-key",
  value: "sk_live_xxxxxxxxxxxxxxxxxxxx",
});

// Charger au moment de l'exécution
const apiKey =
  (await Bun.secrets.get({
    service: "my-app",
    name: "api-key",
  })) || process.env.API_KEY; // Repli pour CI/production

Gestion des erreurs

javascript
try {
  await Bun.secrets.set({
    service: "my-app",
    name: "alice",
    value: "password123",
  });
} catch (error) {
  console.error("Échec du stockage de l'identifiant :", error.message);
}

// Vérifier si un identifiant existe
const password = await Bun.secrets.get({
  service: "my-app",
  name: "alice",
});

if (password === null) {
  console.log("Aucun identifiant trouvé");
}

Mise à jour d'identifiants

javascript
// Mot de passe initial
await Bun.secrets.set({
  service: "email-server",
  name: "admin@example.com",
  value: "old-password",
});

// Mettre à jour avec un nouveau mot de passe
await Bun.secrets.set({
  service: "email-server",
  name: "admin@example.com",
  value: "new-password",
});

// L'ancien mot de passe est remplacé

Comportement par plateforme

macOS (Keychain)

  • Les identifiants sont stockés dans le trousseau de connexion de l'utilisateur
  • Le trousseau peut demander une autorisation d'accès lors de la première utilisation
  • Les identifiants persistent après les redémarrages du système
  • Accessibles par l'utilisateur qui les a stockés

Linux (libsecret)

  • Nécessite un daemon de service secret (GNOME Keyring, KWallet, etc.)
  • Les identifiants sont stockés dans la collection par défaut
  • Peut demander un déverrouillage si le trousseau est verrouillé
  • Le service secret doit être en cours d'exécution

Windows (Credential Manager)

  • Les identifiants sont stockés dans Windows Credential Manager
  • Visibles dans Panneau de configuration → Credential Manager → Windows Credentials
  • Persistent avec le drapeau CRED_PERSIST_ENTERPRISE donc ils sont limités par utilisateur
  • Chiffrés en utilisant Windows Data Protection API

Considérations de sécurité

  1. Chiffrement : Les identifiants sont chiffrés par le gestionnaire d'identifiants du système d'exploitation
  2. Contrôle d'accès : Seul l'utilisateur qui a stocké l'identifiant peut le récupérer
  3. Pas de texte clair : Les mots de passe ne sont jamais stockés en texte clair
  4. Sécurité mémoire : Bun efface la mémoire des mots de passe après utilisation
  5. Isolation des processus : Les identifiants sont isolés par compte utilisateur

Limitations

  • La longueur maximale du mot de passe varie selon la plateforme (généralement 2048-4096 octets)
  • Les noms de service et d'utilisateur doivent avoir des longueurs raisonnables (< 256 caractères)
  • Certains caractères spéciaux peuvent nécessiter un échappement selon la plateforme
  • Nécessite des services système appropriés :
    • Linux : Le daemon de service secret doit être en cours d'exécution
    • macOS : Keychain Access doit être disponible
    • Windows : Le service Credential Manager doit être activé

Comparaison avec les variables d'environnement

Contrairement aux variables d'environnement, Bun.secrets :

  • ✅ Chiffre les identifiants au repos (grâce au système d'exploitation)
  • ✅ Évite d'exposer les secrets dans les vidages de mémoire de processus (la mémoire est effacée après qu'elle n'est plus nécessaire)
  • ✅ Survit aux redémarrages d'applications
  • ✅ Peut être mis à jour sans redémarrer l'application
  • ✅ Fournit un contrôle d'accès au niveau de l'utilisateur
  • ❌ Nécessite un service d'identifiants du système d'exploitation
  • ❌ Pas très utile pour les secrets de déploiement (utilisez des variables d'environnement en production)

Bonnes pratiques

  1. Utilisez des noms de service descriptifs : Faites correspondre le nom de l'outil ou de l'application Si vous construisez un CLI pour une utilisation externe, vous devriez probablement utiliser un UTI (Uniform Type Identifier) pour le nom du service.

    javascript
    // Bon - correspond à l'outil réel
    { service: "com.docker.hub", name: "username" }
    { service: "com.vercel.cli", name: "team-name" }
    
    // À éviter - trop générique
    { service: "api", name: "key" }
  2. Uniquement les identifiants : Ne stockez pas la configuration de l'application dans cette API Cette API est lente, vous aurez probablement toujours besoin d'utiliser un fichier de configuration pour certaines choses.

  3. Utilisez pour les outils de développement local :

    • ✅ Outils CLI (gh, npm, docker, kubectl)
    • ✅ Serveurs de développement local
    • ✅ Clés API personnelles pour les tests
    • ❌ Serveurs de production (utilisez une gestion appropriée des secrets)

TypeScript

typescript
namespace Bun {
  interface SecretsOptions {
    service: string;
    name: string;
  }

  interface Secrets {
    get(options: SecretsOptions): Promise<string | null>;
    set(options: SecretsOptions, value: string): Promise<void>;
    delete(options: SecretsOptions): Promise<boolean>;
  }

  const secrets: Secrets;
}

Bun édité par www.bunjs.com.cn