Scrydon

Modes d'authentification

Comment les fournisseurs s'authentifient auprès de leur service sous-jacent — déclarez un type de credential une seule fois, la plateforme gère le stockage des connexions, le renouvellement des tokens et l'injection au moment de l'exécution

Un fournisseur déclare quel type de credentials il nécessite en listant une ou plusieurs entrées sous auth.credentials. La plateforme lit cette déclaration pour :

  1. Afficher la bonne interface « Connecter » lorsqu'un administrateur d'organisation ajoute l'intégration (une redirection de type Google, un formulaire de credentials standard, ou simplement le champ de clé API).
  2. Stocker l'enregistrement de connexion chiffré, limité à l'organisation (ou à l'espace de travail, pour les credentials en mode système).
  3. Injecter un payload ctx.auth utilisable dans chaque exécution d'outil, afin que le code du bundle ne manipule jamais les secrets bruts.

Cette page répertorie les sept types de credentials pris en charge, quand choisir l'un ou l'autre, et ce que le bundle reçoit au moment de l'exécution.

Vue d'ensemble

TypeFluxSpécificationCe que le bundle reçoit dans ctx.authUtilisation typique
oauthRedirection code d'autorisation (PKCE optionnel + refresh token)RFC 6749 §4.1 (+ RFC 7636 pour PKCE, RFC 6749 §6 pour le refresh){ kind: "oauth", accessToken, refreshToken?, tokenType: "Bearer", expiresAt? }OAuth orienté utilisateur — Google, Slack, Microsoft, GitHub pour les utilisateurs
oauthAppInstallation d'application OAuth (consentement admin)RFC 6749 §4.1 (pattern d'installation d'application propre au fournisseur par-dessus){ kind: "oauth", accessToken, expiresAt? }GitHub Apps, applications Slack, applications Atlassian — installées une fois par espace de travail
oauthClientCredentialsOctroi client-credentials (sans redirection)RFC 6749 §4.4 (+ §2.3.1 pour useBasicAuth){ kind: "oauth", accessToken, tokenType: "Bearer", expiresAt }SAP S/4HANA XSUAA, Auth0 M2M, applications de service Okta, Microsoft Graph en mode système
apiKeyClé statique unique dans un en-tête ou un paramètre de requêtePas de RFC formelle (convention ; RFC 6750 quand headerPrefix: "Bearer"){ kind: "apiKey", apiKey: "<key>" }Stripe, OpenAI, Anthropic, la plupart des API REST
basicAuthNom d'utilisateur + mot de passe (sans redirection)RFC 7617Livré par compte : { kind: "oauth", accessToken: "<base64(user:pass)>", tokenType: "Basic" } · Paire brute livrée par connexion : { kind: "basicAuth", username, password }API on-prem legacy, utilisateurs de communication SAP, Jira Server, Confluence Server
botTokenToken bot/service statiquePas de RFC formelle (convention fournisseur ; RFC 6750 quand headerPrefix: "Bearer"){ kind: "botToken", botToken: "<token>" }Tokens bot Slack, tokens bot Discord, bots Telegram
noneSans authentification{ kind: "none" }Fournisseurs locaux (Ollama, vLLM), API publiques

ctx.auth est une union discriminée par kind — faites toujours un narrowing sur ctx.auth.kind avant de lire les champs de credential. Utilisez les helpers de narrowing de @scrydon/sdk-authoring/integrations/context (requireOAuthToken, requireApiKey, requireBasicAuth, requireBotToken) pour obtenir la valeur du credential avec une erreur claire en cas de mismatch de kind. La plateforme pré-résout le flux déclaré par le credential — les bundles n'implémentent jamais eux-mêmes les redirections OAuth, la logique de refresh, ou l'encodage basic-auth.

Choisir un type

Parcourez ces questions dans l'ordre :

  1. L'utilisateur se connecte-t-il de manière interactive chez le fournisseur ?oauth.
  2. Le fournisseur souhaite-t-il un enregistrement unique par espace de travail via une installation d'application (sans redirection par utilisateur) ?oauthApp.
  3. Le fournisseur expose-t-il un OAuth machine-à-machine (clientId + clientSecret → token, sans utilisateur) ?oauthClientCredentials.
  4. Le fournisseur veut-il une seule clé API dans un en-tête ou un paramètre de requête ?apiKey.
  5. Le fournisseur accepte-t-il l'authentification HTTP Basic avec un nom d'utilisateur + mot de passe ?basicAuth.
  6. Le fournisseur émet-il un token bot/service statique (style Slack xoxb-...) ?botToken.
  7. N'y a-t-il aucune authentification (ex. Ollama auto-hébergé) ?none.

Un fournisseur peut en déclarer plusieurs. Exemples :

  • Un SaaS qui propose à la fois OAuth interactif et un token bot : déclarez les deux, l'utilisateur choisit au moment de la connexion.
  • Un fournisseur qui fonctionne soit dans le Cloud (OAuth client-credentials), soit on-prem (HTTP Basic) : déclarez les deux, conditionnez la visibilité sur un deploymentType configField.

Exemples de configuration

oauth — Flux code d'autorisation

Pour les connexions utilisateur interactives. La plateforme gère la redirection, PKCE, la rotation du refresh token et le stockage.

auth: {
  credentials: {
    oauth: {
      type: "oauth",
      label: "Google Account",
      authorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
      tokenUrl: "https://oauth2.googleapis.com/token",
      userInfoUrl: "https://www.googleapis.com/oauth2/v3/userinfo",
      scopes: ["openid", "email", "https://www.googleapis.com/auth/drive.readonly"],
      pkce: true,
      accessType: "offline",
      prompt: "consent",
      supportsRefreshTokenRotation: true,
    },
  },
  default: "oauth",
},

Les URL limitées au tenant sont basées sur des modèles utilisant les vendor.configFields :

auth: {
  credentials: {
    oauth: {
      type: "oauth",
      label: "Microsoft Account",
      authorizationUrl: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize",
      tokenUrl: "https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token",
      requiredConfig: ["tenantId"],
      // ...
    },
  },
},
configFields: [
  { key: "tenantId", label: "Azure AD Tenant ID", required: true, type: "text" },
],

oauthApp — Installation d'application OAuth

Pour les flux d'installation d'application (un enregistrement par espace de travail, pas par utilisateur). Même forme que oauth, mais sans redirection utilisateur — l'application est installée une fois par un administrateur.

auth: {
  credentials: {
    app: {
      type: "oauthApp",
      label: "GitHub App",
      authorizationUrl: "https://github.com/apps/scrydon-bot/installations/new",
      tokenUrl: "https://api.github.com/app/installations/{installationId}/access_tokens",
      scopes: [],
      pkce: false,
    },
  },
  default: "app",
},

oauthClientCredentials — Machine-à-machine

RFC 6749 §4.4. L'utilisateur fournit clientId + clientSecret une seule fois lors de la connexion, la plateforme les échange contre un token d'accès à chaque fois que nécessaire et le met en cache jusqu'à expiration. Pas de redirection utilisateur.

auth: {
  credentials: {
    oauth: {
      type: "oauthClientCredentials",
      label: "Auth0 Service Account",
      tokenUrl: "https://example.auth0.com/oauth/token",
      scopes: ["read:users", "create:users"],
      audience: "https://api.example.com",
      useBasicAuth: false,
    },
  },
  default: "oauth",
},

Pour les endpoints de token par tenant (le pattern SAP S/4HANA XSUAA), utilisez un modèle pour tokenUrl en vous appuyant sur les configFields :

auth: {
  credentials: {
    oauth: {
      type: "oauthClientCredentials",
      label: "SAP S/4HANA Service Account",
      tokenUrl: "https://{subdomain}.authentication.{region}.hana.ondemand.com/oauth/token",
      requiredConfig: ["subdomain", "region"],
      useBasicAuth: true,
    },
  },
  default: "oauth",
},
configFields: [
  { key: "subdomain", label: "BTP Subdomain", required: true, type: "text" },
  { key: "region", label: "BTP Region", required: true, type: "text" },
],
ChampRôle
tokenUrlEndpoint de token. Peut contenir des espaces réservés {configKey}.
requiredConfigClés de configField substituées dans tokenUrl au moment de la récupération. Chacune doit figurer dans vendor.configFields.
scopesScopes séparés par des espaces demandés dans l'appel token.
useBasicAuthRFC 6749 §2.3.1 — quand true (défaut), clientId:clientSecret sont envoyés via Authorization: Basic <b64> dans la requête token ; quand false, dans le corps du formulaire. Certains IdP rejettent l'une ou l'autre forme.
audienceRequis par Auth0, Okta et plusieurs IdP personnalisés.
additionalBodyParamsExtras propres au fournisseur postés en complément de grant_type=client_credentials.

apiKey — Clé statique unique

auth: {
  credentials: {
    apiKey: {
      type: "apiKey",
      label: "API Key",
      headerName: "Authorization",
      headerPrefix: "Bearer",
    },
  },
  default: "apiKey",
},

headerName, headerPrefix et queryParam sont tous optionnels — omettez le préfixe pour les API à token brut, ou utilisez queryParam: "api_key" pour les API qui n'acceptent que les paramètres de requête.

À l'exécution, les outils reçoivent { kind: "apiKey", apiKey: "<key>" }. Lisez-le avec requireApiKey(ctx.auth) ou en faisant un narrowing sur kind :

import { requireApiKey } from "@scrydon/sdk-authoring/integrations/context";

async execute(input, ctx) {
  const apiKey = requireApiKey(ctx.auth); // throws CredentialKindError on mismatch
  const response = await fetch("https://api.example.com/data", {
    headers: { Authorization: `Bearer ${apiKey}` },
  });
  // ...
}

Blocs de canevas autonomes

Les intégrations à clé API fonctionnent comme des blocs de canevas autonomes sans sélecteur de credential par bloc. La plateforme résout automatiquement la connexion configurée de l'organisation pour le fournisseur propre à l'outil en cours d'exécution — aucune action de l'utilisateur n'est nécessaire au moment du placement du bloc. La résolution est limitée à l'environnement : la connexion correspondant à l'environnement de l'espace de travail actuel l'emporte, avec un fallback générique. Elle est également structurellement limitée : un outil ne peut recevoir que le credential de sa propre intégration, jamais celui d'un autre fournisseur. ctx.secrets reste le canal pour les secrets configurés hors authentification (extras déclarés dans vendor.secrets[] comme un identifiant de moteur de recherche) et n'est jamais utilisé pour le credential principal.

basicAuth — Nom d'utilisateur + mot de passe

auth: {
  credentials: {
    basic: {
      type: "basicAuth",
      label: "Communication User",
      description: "SAP communication user with password",
    },
  },
  default: "basic",
},

À l'exécution, ctx.auth arrive sous l'une de deux formes selon la manière dont la connexion a été livrée :

  • Livré par compte (la plateforme pré-encode la paire) : { kind: "oauth", accessToken: "<base64(user:pass)>", tokenType: "Basic" }. Passez accessToken et tokenType directement dans l'en-tête Authorization — l'encodage est déjà effectué.
  • Paire brute livrée par connexion : { kind: "basicAuth", username, password }. Utilisez requireBasicAuth(ctx.auth) (ou ctx.auth.kind === "basicAuth") et encodez l'en-tête vous-même.

Les outils qui gèrent des fournisseurs basicAuth doivent faire un narrowing sur kind pour accepter les deux formes :

import { requireBasicAuth } from "@scrydon/sdk-authoring/integrations/context";

async execute(input, ctx) {
  let authHeader: string;

  if (ctx.auth.kind === "oauth" && ctx.auth.tokenType === "Basic") {
    // Account-delivered: platform already encoded the pair
    authHeader = `Basic ${ctx.auth.accessToken}`;
  } else {
    // Connection-delivered raw pair
    const { username, password } = requireBasicAuth(ctx.auth);
    authHeader = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
  }

  const response = await fetch("https://api.example.com/data", {
    headers: { Authorization: authHeader },
  });
  // ...
}

Pour les en-têtes non standard (quelques API utilisent X-Auth-Token ou un schéma non-Basic), remplacez le nom et le préfixe de l'en-tête dans la déclaration de credential :

{
  type: "basicAuth",
  label: "Custom Header",
  headerName: "X-Auth-Token",
  headerPrefix: "Token",
},

botToken — Token bot/service statique

auth: {
  credentials: {
    bot: {
      type: "botToken",
      label: "Slack Bot Token",
      headerName: "Authorization",
      headerPrefix: "Bearer",
    },
  },
  default: "bot",
},

À l'exécution, les outils reçoivent { kind: "botToken", botToken: "<token>" }. Utilisez requireBotToken(ctx.auth) ou faites un narrowing sur kind :

import { requireBotToken } from "@scrydon/sdk-authoring/integrations/context";

async execute(input, ctx) {
  const token = requireBotToken(ctx.auth);
  const response = await fetch("https://slack.com/api/conversations.list", {
    headers: { Authorization: `Bearer ${token}` },
  });
  // ...
}

none — Sans authentification

auth: {
  credentials: {
    none: { type: "none" },
  },
  default: "none",
},

À l'exécution, les outils reçoivent { kind: "none" }. Protégez-vous contre les lectures accidentelles de credential en faisant un narrowing :

async execute(input, ctx) {
  // No credential — proceed without Authorization header
  if (ctx.auth.kind !== "none") {
    throw new Error("Expected no-auth credential");
  }
  const response = await fetch("https://api.example.com/public/data");
  // ...
}

Pattern de code bundle

ctx.auth est une union discriminée par kind. Importez les helpers de narrowing depuis @scrydon/sdk-authoring/integrations/context et faites un narrowing sur kind avant de lire les champs de credential :

import {
  requireOAuthToken,
  requireApiKey,
  requireBasicAuth,
  requireBotToken,
} from "@scrydon/sdk-authoring/integrations/context";

async execute(input, ctx) {
  const headers: Record<string, string> = { Accept: "application/json" };

  switch (ctx.auth.kind) {
    case "oauth":
      // Covers oauth, oauthApp, oauthClientCredentials, and account-delivered basicAuth.
      // ctx.auth.tokenType is "Bearer" for OAuth flows and "Basic" for basicAuth accounts.
      headers.Authorization = ctx.auth.tokenType
        ? `${ctx.auth.tokenType} ${ctx.auth.accessToken}`
        : ctx.auth.accessToken;
      break;

    case "apiKey":
      // The key is now in ctx.auth.apiKey — not accessToken.
      headers.Authorization = `Bearer ${ctx.auth.apiKey}`;
      break;

    case "basicAuth": {
      // Connection-delivered raw pair — encode it yourself.
      const { username, password } = ctx.auth;
      headers.Authorization = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
      break;
    }

    case "botToken":
      headers.Authorization = `Bearer ${ctx.auth.botToken}`;
      break;

    case "none":
      // No credential — do not set an Authorization header.
      break;
  }

  const response = await fetch(`${ctx.config.baseUrl}/items`, { headers });
  // ...
}

Pour les outils à type unique, les helpers require* sont plus concis et lancent une CredentialKindError avec un message explicite en cas de mismatch de kind :

import { requireApiKey } from "@scrydon/sdk-authoring/integrations/context";

async execute(input, ctx) {
  const apiKey = requireApiKey(ctx.auth);
  // ...
}

Pour les flux OAuth, la plateforme renouvelle les tokens expirés avant d'invoquer l'acteur — les bundles ne reçoivent jamais un accessToken expiré. Pour oauthClientCredentials, la plateforme ré-échange clientId:clientSecret lorsque le token en cache arrive à moins de 60 secondes de l'expiration.

Migration depuis la v2ctx.auth est maintenant une union discriminée par kind, et non plus un objet plat.

  • Les outils à clé API doivent passer de ctx.auth.accessToken à ctx.auth.apiKey (ou requireApiKey(ctx.auth)). La lecture de accessToken pour un credential de type clé API produira undefined à l'exécution.
  • Les outils OAuth conservent accessToken sous la variante de kind "oauth" et continuent de fonctionner, mais devraient désormais faire un narrowing sur ctx.auth.kind === "oauth" pour bénéficier de la sécurité du typage.
  • Les outils bot token doivent passer de ctx.auth.accessToken à ctx.auth.botToken (ou requireBotToken(ctx.auth)).
  • Les outils none recevaient auparavant { accessToken: "" } — ils reçoivent maintenant { kind: "none" } sans champ accessToken.

authMode au niveau du bloc

Chaque bloc sous un produit déclare le credential qu'il utilise via authMode. La chaîne doit correspondre à une clé dans auth.credentials du fournisseur (ou l'une des chaînes de mode canoniques — api_key, oauth, oauth_app, oauth_client_credentials, basic_auth, bot_token, none).

const myBlock = defineBlock({
  type: "my_block",
  authMode: "oauth",       // matches auth.credentials.oauth above
  // ...
});

Lorsqu'un fournisseur déclare plusieurs types de credentials, chaque bloc peut choisir le sien — l'éditeur de workflow invite l'utilisateur à connecter le bon.

Voir aussi

  • Démarrage — rédigez votre premier fournisseur
  • Référence SDK — API complète defineVendor / defineProduct
  • Capacités — LLM, STT, TTS, embedding et autres capacités d'exécution
Sur cette page

Sur cette page