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 :
- 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).
- Stocker l'enregistrement de connexion chiffré, limité à l'organisation (ou à l'espace de travail, pour les credentials en mode système).
- Injecter un payload
ctx.authutilisable 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
| Type | Flux | Spécification | Ce que le bundle reçoit dans ctx.auth | Utilisation typique |
|---|---|---|---|---|
oauth | Redirection 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 |
oauthApp | Installation 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 |
oauthClientCredentials | Octroi 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 |
apiKey | Clé statique unique dans un en-tête ou un paramètre de requête | Pas de RFC formelle (convention ; RFC 6750 quand headerPrefix: "Bearer") | { kind: "apiKey", apiKey: "<key>" } | Stripe, OpenAI, Anthropic, la plupart des API REST |
basicAuth | Nom d'utilisateur + mot de passe (sans redirection) | RFC 7617 | Livré 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 |
botToken | Token bot/service statique | Pas de RFC formelle (convention fournisseur ; RFC 6750 quand headerPrefix: "Bearer") | { kind: "botToken", botToken: "<token>" } | Tokens bot Slack, tokens bot Discord, bots Telegram |
none | Sans 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 :
- L'utilisateur se connecte-t-il de manière interactive chez le fournisseur ? →
oauth. - Le fournisseur souhaite-t-il un enregistrement unique par espace de travail via une installation d'application (sans redirection par utilisateur) ? →
oauthApp. - Le fournisseur expose-t-il un OAuth machine-à-machine (clientId + clientSecret → token, sans utilisateur) ? →
oauthClientCredentials. - Le fournisseur veut-il une seule clé API dans un en-tête ou un paramètre de requête ? →
apiKey. - Le fournisseur accepte-t-il l'authentification HTTP Basic avec un nom d'utilisateur + mot de passe ? →
basicAuth. - Le fournisseur émet-il un token bot/service statique (style Slack
xoxb-...) ? →botToken. - 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
deploymentTypeconfigField.
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" },
],| Champ | Rôle |
|---|---|
tokenUrl | Endpoint de token. Peut contenir des espaces réservés {configKey}. |
requiredConfig | Clés de configField substituées dans tokenUrl au moment de la récupération. Chacune doit figurer dans vendor.configFields. |
scopes | Scopes séparés par des espaces demandés dans l'appel token. |
useBasicAuth | RFC 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. |
audience | Requis par Auth0, Okta et plusieurs IdP personnalisés. |
additionalBodyParams | Extras 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" }. PassezaccessTokenettokenTypedirectement dans l'en-têteAuthorization— l'encodage est déjà effectué. - Paire brute livrée par connexion :
{ kind: "basicAuth", username, password }. UtilisezrequireBasicAuth(ctx.auth)(ouctx.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 v2 — ctx.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(ourequireApiKey(ctx.auth)). La lecture deaccessTokenpour un credential de type clé API produiraundefinedà l'exécution. - Les outils OAuth conservent
accessTokensous la variante de kind"oauth"et continuent de fonctionner, mais devraient désormais faire un narrowing surctx.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(ourequireBotToken(ctx.auth)). - Les outils
nonerecevaient auparavant{ accessToken: "" }— ils reçoivent maintenant{ kind: "none" }sans champaccessToken.
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