openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
209 lines (208 loc) • 7.84 kB
JavaScript
import { l as resolveSecretSentinel, s as looksLikeSecretSentinel } from "./sentinel-D8TyDpFw.mjs";
import crypto from "node:crypto";
//#region src/agents/execution-auth-binding.ts
const authBindingFingerprintKey = crypto.randomBytes(32);
function hashAuthBinding(value) {
return crypto.createHmac("sha256", authBindingFingerprintKey).update(JSON.stringify(value)).digest("hex");
}
function normalizeIdentity(value, lowercase = false) {
const normalized = value?.trim();
return normalized ? lowercase ? normalized.toLowerCase() : normalized : void 0;
}
/**
* Project non-secret profile ownership for runtimes that keep rotating tokens
* behind their own process boundary. An explicitly selected missing profile
* has no owner shape and must never collapse to ambient runtime authority.
*/
function fingerprintAuthProfileOwnerShape(params) {
const credential = params.credential;
if (!credential) return;
switch (credential.type) {
case "api_key": return hashAuthBinding([
"profile-owner-v1",
params.profileId,
credential.type,
credential.provider,
credential.keyRef ?? null,
normalizeIdentity(credential.email, true) ?? null,
normalizeIdentity(credential.displayName) ?? null,
credential.metadata ?? null
]);
case "token": return hashAuthBinding([
"profile-owner-v1",
params.profileId,
credential.type,
credential.provider,
credential.tokenRef ?? null,
normalizeIdentity(credential.email, true) ?? null,
normalizeIdentity(credential.displayName) ?? null
]);
case "oauth": {
const jwtIdentity = decodeJwtIdentity(credential.idToken);
return hashAuthBinding([
"profile-owner-v1",
params.profileId,
credential.type,
credential.provider,
normalizeIdentity(credential.accountId) ?? jwtIdentity.subject ?? null,
normalizeIdentity(credential.email, true) ?? jwtIdentity.email ?? null,
credential.clientId ?? null,
credential.enterpriseUrl ?? null,
credential.projectId ?? null
]);
}
}
}
/** Fingerprint the stable owner boundary of a successful opaque runtime turn. */
function fingerprintOpaqueRuntimeOwner(params) {
const runtimeArtifactFingerprint = params.runtimeArtifactFingerprint;
const authProfileId = normalizeIdentity(params.authProfileId);
if (authProfileId && !params.authProfileOwnerFingerprint) return;
if (!authProfileId && params.skipLocalCredential) return;
if ((params.kind === "cli-runtime" || params.kind === "plugin-harness") && !runtimeArtifactFingerprint) return;
return hashAuthBinding([
params.kind === "aws-sdk" ? "opaque-runtime-owner-v1" : "opaque-runtime-owner-v2",
params.kind,
params.runner,
params.provider.trim(),
params.backendId,
params.backendConfig ?? null,
authProfileId ?? null,
params.authProfileOwnerFingerprint ?? null,
params.authSource ?? null,
params.skipLocalCredential === true,
runtimeArtifactFingerprint ?? null
]);
}
/** Fingerprint only AWS SDK owners whose exact credential is observable here. */
function fingerprintAwsSdkRuntimeOwner(params) {
if (params.auth?.mode !== "aws-sdk" || params.auth.apiKey) return;
const env = params.env ?? process.env;
let owner;
if (env.AWS_BEARER_TOKEN_BEDROCK?.trim()) owner = ["bearer", hashAuthBinding(env.AWS_BEARER_TOKEN_BEDROCK.trim())];
else if (env.AWS_PROFILE?.trim()) return;
else if (env.AWS_ACCESS_KEY_ID?.trim() && env.AWS_SECRET_ACCESS_KEY?.trim()) owner = [
"access-key",
env.AWS_ACCESS_KEY_ID.trim(),
hashAuthBinding([env.AWS_SECRET_ACCESS_KEY.trim(), env.AWS_SESSION_TOKEN?.trim() ?? null])
];
else return;
return fingerprintOpaqueRuntimeOwner({
kind: "aws-sdk",
runner: "embedded",
provider: params.provider,
backendId: params.backendId,
authSource: hashAuthBinding([params.auth.source, owner])
});
}
function decodeJwtIdentity(token) {
const payload = token?.split(".")[1];
if (!payload) return {};
try {
const claims = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
return {
...typeof claims.sub === "string" && normalizeIdentity(claims.sub) ? { subject: normalizeIdentity(claims.sub) } : {},
...typeof claims.email === "string" && normalizeIdentity(claims.email, true) ? { email: normalizeIdentity(claims.email, true) } : {}
};
} catch {
return {};
}
}
/** Fingerprint the exact active credential owner used by one execution. */
function fingerprintAuthProfileCredential(params) {
const credential = params.credential;
switch (credential.type) {
case "api_key":
if (!credential.key) return;
return hashAuthBinding([
"api_key",
params.profileId,
credential.provider,
credential.key,
credential.keyRef ?? null,
credential.email ?? null,
credential.displayName ?? null,
credential.metadata ?? null
]);
case "token":
if (!credential.token) return;
return hashAuthBinding([
"token",
params.profileId,
credential.provider,
credential.token,
credential.tokenRef ?? null,
credential.email ?? null,
credential.displayName ?? null
]);
case "oauth": {
const jwtIdentity = decodeJwtIdentity(credential.idToken);
const accountId = normalizeIdentity(credential.accountId) ?? jwtIdentity.subject;
const email = normalizeIdentity(credential.email, true) ?? jwtIdentity.email;
const stableIdentity = accountId ?? email;
const opaqueIdentity = stableIdentity ? null : [
credential.access,
credential.refresh,
credential.idToken ?? null
];
if (!stableIdentity && !credential.access && !credential.refresh && !credential.idToken) return;
return hashAuthBinding([
"oauth",
params.profileId,
credential.provider,
credential.clientId ?? null,
accountId ?? null,
email ?? null,
credential.enterpriseUrl ?? null,
credential.projectId ?? null,
opaqueIdentity
]);
}
}
}
/** Fingerprint a profile after materializing its selected SecretRef value. */
function fingerprintResolvedAuthProfileCredential(params) {
const credential = params.credential;
if (credential.type === "oauth") return fingerprintAuthProfileCredential({
profileId: params.profileId,
credential
});
if (params.resolvedAuth && params.resolvedAuth.profileId !== params.profileId) return;
const inlineValue = credential.type === "api_key" ? credential.key : credential.token;
const resolvedValue = params.resolvedAuth?.apiKey ?? inlineValue;
if (!resolvedValue) return;
return fingerprintAuthProfileCredential({
profileId: params.profileId,
credential: credential.type === "api_key" ? {
...credential,
key: resolvedValue
} : {
...credential,
token: resolvedValue
}
});
}
/**
* Fingerprint an ambient/config/env credential that was actually selected.
*
* The digest covers only the credential material and its transport mode.
* Resolution-path facts (`source`, `profileId`) are deliberately excluded:
* the successful-run capture and the later owner revalidation may resolve
* the same key through different sources (env, models.json marker, profile
* fallback), and the owner gate must treat that as the same authority.
* A sentinel-sealed key is unwrapped first so both passes hash the same
* plaintext. Route and provider identity are checked separately by the
* binding, so a shared key remains one authority rather than two.
*/
function fingerprintResolvedProviderAuth(auth) {
if (!auth?.apiKey) return;
const credentialMaterial = looksLikeSecretSentinel(auth.apiKey) ? resolveSecretSentinel(auth.apiKey) : auth.apiKey;
if (!credentialMaterial) return;
return hashAuthBinding([
"resolved-v2",
auth.mode,
credentialMaterial
]);
}
//#endregion
export { fingerprintResolvedAuthProfileCredential as a, fingerprintOpaqueRuntimeOwner as i, fingerprintAuthProfileOwnerShape as n, fingerprintResolvedProviderAuth as o, fingerprintAwsSdkRuntimeOwner as r, fingerprintAuthProfileCredential as t };