@id-wispera/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +268 -0
- package/dist/audit.d.ts +68 -0
- package/dist/audit.d.ts.map +1 -0
- package/dist/audit.js +252 -0
- package/dist/audit.js.map +1 -0
- package/dist/auth/index.d.ts +8 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/keychainProvider.d.ts +40 -0
- package/dist/auth/keychainProvider.d.ts.map +1 -0
- package/dist/auth/keychainProvider.js +98 -0
- package/dist/auth/keychainProvider.js.map +1 -0
- package/dist/auth/passphraseProvider.d.ts +80 -0
- package/dist/auth/passphraseProvider.d.ts.map +1 -0
- package/dist/auth/passphraseProvider.js +188 -0
- package/dist/auth/passphraseProvider.js.map +1 -0
- package/dist/auth/sessionTokenManager.d.ts +106 -0
- package/dist/auth/sessionTokenManager.d.ts.map +1 -0
- package/dist/auth/sessionTokenManager.js +263 -0
- package/dist/auth/sessionTokenManager.js.map +1 -0
- package/dist/delegation.d.ts +81 -0
- package/dist/delegation.d.ts.map +1 -0
- package/dist/delegation.js +299 -0
- package/dist/delegation.js.map +1 -0
- package/dist/detection.d.ts +35 -0
- package/dist/detection.d.ts.map +1 -0
- package/dist/detection.js +474 -0
- package/dist/detection.js.map +1 -0
- package/dist/exec/execManager.d.ts +60 -0
- package/dist/exec/execManager.d.ts.map +1 -0
- package/dist/exec/execManager.js +226 -0
- package/dist/exec/execManager.js.map +1 -0
- package/dist/exec/index.d.ts +6 -0
- package/dist/exec/index.d.ts.map +1 -0
- package/dist/exec/index.js +5 -0
- package/dist/exec/index.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +98 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/base.d.ts +64 -0
- package/dist/integrations/base.d.ts.map +1 -0
- package/dist/integrations/base.js +173 -0
- package/dist/integrations/base.js.map +1 -0
- package/dist/integrations/envMapping.d.ts +47 -0
- package/dist/integrations/envMapping.d.ts.map +1 -0
- package/dist/integrations/envMapping.js +174 -0
- package/dist/integrations/envMapping.js.map +1 -0
- package/dist/integrations/google-a2a.d.ts +48 -0
- package/dist/integrations/google-a2a.d.ts.map +1 -0
- package/dist/integrations/google-a2a.js +108 -0
- package/dist/integrations/google-a2a.js.map +1 -0
- package/dist/integrations/index.d.ts +14 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +14 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/langchain.d.ts +38 -0
- package/dist/integrations/langchain.d.ts.map +1 -0
- package/dist/integrations/langchain.js +45 -0
- package/dist/integrations/langchain.js.map +1 -0
- package/dist/integrations/openai-agents.d.ts +76 -0
- package/dist/integrations/openai-agents.d.ts.map +1 -0
- package/dist/integrations/openai-agents.js +95 -0
- package/dist/integrations/openai-agents.js.map +1 -0
- package/dist/integrations/slack.d.ts +59 -0
- package/dist/integrations/slack.d.ts.map +1 -0
- package/dist/integrations/slack.js +113 -0
- package/dist/integrations/slack.js.map +1 -0
- package/dist/integrations/types.d.ts +107 -0
- package/dist/integrations/types.d.ts.map +1 -0
- package/dist/integrations/types.js +6 -0
- package/dist/integrations/types.js.map +1 -0
- package/dist/locations.d.ts +157 -0
- package/dist/locations.d.ts.map +1 -0
- package/dist/locations.js +733 -0
- package/dist/locations.js.map +1 -0
- package/dist/passport.d.ts +70 -0
- package/dist/passport.d.ts.map +1 -0
- package/dist/passport.js +429 -0
- package/dist/passport.js.map +1 -0
- package/dist/policy.d.ts +80 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +392 -0
- package/dist/policy.js.map +1 -0
- package/dist/providers/openclaw.d.ts +80 -0
- package/dist/providers/openclaw.d.ts.map +1 -0
- package/dist/providers/openclaw.js +712 -0
- package/dist/providers/openclaw.js.map +1 -0
- package/dist/provisioning/adminPassport.d.ts +51 -0
- package/dist/provisioning/adminPassport.d.ts.map +1 -0
- package/dist/provisioning/adminPassport.js +101 -0
- package/dist/provisioning/adminPassport.js.map +1 -0
- package/dist/provisioning/index.d.ts +81 -0
- package/dist/provisioning/index.d.ts.map +1 -0
- package/dist/provisioning/index.js +141 -0
- package/dist/provisioning/index.js.map +1 -0
- package/dist/provisioning/provider.d.ts +59 -0
- package/dist/provisioning/provider.d.ts.map +1 -0
- package/dist/provisioning/provider.js +52 -0
- package/dist/provisioning/provider.js.map +1 -0
- package/dist/provisioning/providers/anthropic.d.ts +32 -0
- package/dist/provisioning/providers/anthropic.d.ts.map +1 -0
- package/dist/provisioning/providers/anthropic.js +116 -0
- package/dist/provisioning/providers/anthropic.js.map +1 -0
- package/dist/provisioning/providers/aws.d.ts +29 -0
- package/dist/provisioning/providers/aws.d.ts.map +1 -0
- package/dist/provisioning/providers/aws.js +455 -0
- package/dist/provisioning/providers/aws.js.map +1 -0
- package/dist/provisioning/providers/azure-entra.d.ts +32 -0
- package/dist/provisioning/providers/azure-entra.d.ts.map +1 -0
- package/dist/provisioning/providers/azure-entra.js +312 -0
- package/dist/provisioning/providers/azure-entra.js.map +1 -0
- package/dist/provisioning/providers/github.d.ts +24 -0
- package/dist/provisioning/providers/github.d.ts.map +1 -0
- package/dist/provisioning/providers/github.js +219 -0
- package/dist/provisioning/providers/github.js.map +1 -0
- package/dist/provisioning/providers/google-cloud.d.ts +34 -0
- package/dist/provisioning/providers/google-cloud.d.ts.map +1 -0
- package/dist/provisioning/providers/google-cloud.js +366 -0
- package/dist/provisioning/providers/google-cloud.js.map +1 -0
- package/dist/provisioning/providers/openai.d.ts +29 -0
- package/dist/provisioning/providers/openai.d.ts.map +1 -0
- package/dist/provisioning/providers/openai.js +263 -0
- package/dist/provisioning/providers/openai.js.map +1 -0
- package/dist/provisioning/providers/sendgrid.d.ts +27 -0
- package/dist/provisioning/providers/sendgrid.d.ts.map +1 -0
- package/dist/provisioning/providers/sendgrid.js +186 -0
- package/dist/provisioning/providers/sendgrid.js.map +1 -0
- package/dist/provisioning/providers/twilio.d.ts +27 -0
- package/dist/provisioning/providers/twilio.d.ts.map +1 -0
- package/dist/provisioning/providers/twilio.js +194 -0
- package/dist/provisioning/providers/twilio.js.map +1 -0
- package/dist/provisioning/types.d.ts +274 -0
- package/dist/provisioning/types.d.ts.map +1 -0
- package/dist/provisioning/types.js +6 -0
- package/dist/provisioning/types.js.map +1 -0
- package/dist/sharing.d.ts +60 -0
- package/dist/sharing.d.ts.map +1 -0
- package/dist/sharing.js +305 -0
- package/dist/sharing.js.map +1 -0
- package/dist/types.d.ts +396 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +88 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +45 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +110 -0
- package/dist/utils.js.map +1 -0
- package/dist/vault.d.ts +151 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/vault.js +499 -0
- package/dist/vault.js.map +1 -0
- package/package.json +117 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ID Wispera Session Token Manager
|
|
3
|
+
* Manages short-lived session tokens for headless / CI environments.
|
|
4
|
+
*
|
|
5
|
+
* Design:
|
|
6
|
+
* - Tokens live in a sidecar file (~/.id-wispera/sessions.json), NOT inside
|
|
7
|
+
* the encrypted vault (avoids the chicken-and-egg problem).
|
|
8
|
+
* - Each sidecar entry stores the vault passphrase encrypted with a key
|
|
9
|
+
* derived from the token itself (PBKDF2 + AES-256-GCM).
|
|
10
|
+
* - The token is the only runtime credential needed — set as
|
|
11
|
+
* IDW_SESSION_TOKEN env var.
|
|
12
|
+
* - If the sidecar is lost or corrupted the user still has their passphrase
|
|
13
|
+
* as the root recovery path.
|
|
14
|
+
*
|
|
15
|
+
* Token format:
|
|
16
|
+
* idw_token_ + 64 random bytes base64url-encoded (~90 chars total)
|
|
17
|
+
*
|
|
18
|
+
* Sidecar schema (cross-SDK compatible — TS, Python, Go all read/write this):
|
|
19
|
+
* {
|
|
20
|
+
* "version": 1,
|
|
21
|
+
* "entries": [
|
|
22
|
+
* {
|
|
23
|
+
* "tokenHash": "<SHA-256 hex>",
|
|
24
|
+
* "salt": "<32 bytes hex>",
|
|
25
|
+
* "iv": "<12 bytes hex>",
|
|
26
|
+
* "encryptedPassphrase": "<AES-256-GCM ciphertext, base64>",
|
|
27
|
+
* "createdAt": "<ISO 8601>",
|
|
28
|
+
* "expiresAt": "<ISO 8601 | null>",
|
|
29
|
+
* "label": "<user-provided label>"
|
|
30
|
+
* }
|
|
31
|
+
* ]
|
|
32
|
+
* }
|
|
33
|
+
*/
|
|
34
|
+
import { getRandomBytes, getCryptoSubtle, toBufferSource } from '../utils.js';
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// Constants
|
|
37
|
+
// ============================================================================
|
|
38
|
+
const TOKEN_PREFIX = 'idw_token_';
|
|
39
|
+
const TOKEN_RANDOM_BYTES = 64;
|
|
40
|
+
const SIDECAR_VERSION = 1;
|
|
41
|
+
const PBKDF2_ITERATIONS = 100_000;
|
|
42
|
+
const SALT_BYTES = 32;
|
|
43
|
+
const IV_BYTES = 12;
|
|
44
|
+
const ENV_VAR = 'IDW_SESSION_TOKEN';
|
|
45
|
+
// ============================================================================
|
|
46
|
+
// Helpers
|
|
47
|
+
// ============================================================================
|
|
48
|
+
function bytesToHex(bytes) {
|
|
49
|
+
return Array.from(bytes)
|
|
50
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
51
|
+
.join('');
|
|
52
|
+
}
|
|
53
|
+
function hexToBytes(hex) {
|
|
54
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
55
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
56
|
+
bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
|
|
57
|
+
}
|
|
58
|
+
return bytes;
|
|
59
|
+
}
|
|
60
|
+
function bytesToBase64url(bytes) {
|
|
61
|
+
// Node.js path
|
|
62
|
+
if (typeof Buffer !== 'undefined') {
|
|
63
|
+
return Buffer.from(bytes).toString('base64url');
|
|
64
|
+
}
|
|
65
|
+
// Fallback (browser / edge)
|
|
66
|
+
const base64 = btoa(String.fromCharCode(...bytes));
|
|
67
|
+
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
68
|
+
}
|
|
69
|
+
function base64urlToBytes(b64) {
|
|
70
|
+
if (typeof Buffer !== 'undefined') {
|
|
71
|
+
return new Uint8Array(Buffer.from(b64, 'base64url'));
|
|
72
|
+
}
|
|
73
|
+
const padded = b64.replace(/-/g, '+').replace(/_/g, '/');
|
|
74
|
+
const binary = atob(padded);
|
|
75
|
+
const bytes = new Uint8Array(binary.length);
|
|
76
|
+
for (let i = 0; i < binary.length; i++) {
|
|
77
|
+
bytes[i] = binary.charCodeAt(i);
|
|
78
|
+
}
|
|
79
|
+
return bytes;
|
|
80
|
+
}
|
|
81
|
+
async function sha256Hex(data) {
|
|
82
|
+
const crypto = await getCryptoSubtle();
|
|
83
|
+
const hash = await crypto.digest('SHA-256', toBufferSource(new TextEncoder().encode(data)));
|
|
84
|
+
return bytesToHex(new Uint8Array(hash));
|
|
85
|
+
}
|
|
86
|
+
async function deriveTokenKey(token, salt) {
|
|
87
|
+
const crypto = await getCryptoSubtle();
|
|
88
|
+
const keyMaterial = await crypto.importKey('raw', toBufferSource(new TextEncoder().encode(token)), 'PBKDF2', false, ['deriveBits', 'deriveKey']);
|
|
89
|
+
return crypto.deriveKey({
|
|
90
|
+
name: 'PBKDF2',
|
|
91
|
+
salt: toBufferSource(salt),
|
|
92
|
+
iterations: PBKDF2_ITERATIONS,
|
|
93
|
+
hash: 'SHA-256',
|
|
94
|
+
}, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['encrypt', 'decrypt']);
|
|
95
|
+
}
|
|
96
|
+
async function encryptPassphrase(passphrase, token, salt, iv) {
|
|
97
|
+
const crypto = await getCryptoSubtle();
|
|
98
|
+
const key = await deriveTokenKey(token, salt);
|
|
99
|
+
const ciphertext = await crypto.encrypt({ name: 'AES-GCM', iv: toBufferSource(iv) }, key, toBufferSource(new TextEncoder().encode(passphrase)));
|
|
100
|
+
return bytesToBase64url(new Uint8Array(ciphertext));
|
|
101
|
+
}
|
|
102
|
+
async function decryptPassphrase(encryptedBase64, token, salt, iv) {
|
|
103
|
+
const crypto = await getCryptoSubtle();
|
|
104
|
+
const key = await deriveTokenKey(token, salt);
|
|
105
|
+
const plaintext = await crypto.decrypt({ name: 'AES-GCM', iv: toBufferSource(iv) }, key, toBufferSource(base64urlToBytes(encryptedBase64)));
|
|
106
|
+
return new TextDecoder().decode(plaintext);
|
|
107
|
+
}
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// Session Token Manager
|
|
110
|
+
// ============================================================================
|
|
111
|
+
export class SessionTokenManager {
|
|
112
|
+
sidecarPath;
|
|
113
|
+
constructor(sidecarPath) {
|
|
114
|
+
const home = process.env.HOME ?? process.env.USERPROFILE ?? '.';
|
|
115
|
+
this.sidecarPath = sidecarPath ?? `${home}/.id-wispera/sessions.json`;
|
|
116
|
+
}
|
|
117
|
+
/** Read the sidecar file. Returns an empty structure if missing/corrupt. */
|
|
118
|
+
async readSidecar() {
|
|
119
|
+
const fs = await import('fs/promises');
|
|
120
|
+
try {
|
|
121
|
+
const content = await fs.readFile(this.sidecarPath, 'utf-8');
|
|
122
|
+
const data = JSON.parse(content);
|
|
123
|
+
if (data.version !== SIDECAR_VERSION || !Array.isArray(data.entries)) {
|
|
124
|
+
return { version: SIDECAR_VERSION, entries: [] };
|
|
125
|
+
}
|
|
126
|
+
return data;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return { version: SIDECAR_VERSION, entries: [] };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/** Write the sidecar file, creating parent directories as needed. */
|
|
133
|
+
async writeSidecar(data) {
|
|
134
|
+
const fs = await import('fs/promises');
|
|
135
|
+
const path = await import('path');
|
|
136
|
+
await fs.mkdir(path.dirname(this.sidecarPath), { recursive: true });
|
|
137
|
+
await fs.writeFile(this.sidecarPath, JSON.stringify(data, null, 2), {
|
|
138
|
+
encoding: 'utf-8',
|
|
139
|
+
mode: 0o600, // owner read/write only
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// --------------------------------------------------------------------------
|
|
143
|
+
// Public API
|
|
144
|
+
// --------------------------------------------------------------------------
|
|
145
|
+
/**
|
|
146
|
+
* Generate a new CSPRNG token string.
|
|
147
|
+
*/
|
|
148
|
+
generateToken() {
|
|
149
|
+
const randomBytes = getRandomBytes(TOKEN_RANDOM_BYTES);
|
|
150
|
+
return TOKEN_PREFIX + bytesToBase64url(randomBytes);
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Create a session token entry. Encrypts the passphrase with a key
|
|
154
|
+
* derived from the token and stores the entry in the sidecar file.
|
|
155
|
+
*
|
|
156
|
+
* @returns The plaintext token (shown to the user once — never stored).
|
|
157
|
+
*/
|
|
158
|
+
async create(passphrase, options = {}) {
|
|
159
|
+
const token = this.generateToken();
|
|
160
|
+
const tokenHash = await sha256Hex(token);
|
|
161
|
+
const salt = getRandomBytes(SALT_BYTES);
|
|
162
|
+
const iv = getRandomBytes(IV_BYTES);
|
|
163
|
+
const encrypted = await encryptPassphrase(passphrase, token, salt, iv);
|
|
164
|
+
const now = new Date();
|
|
165
|
+
const expiresAt = options.expiresInSeconds
|
|
166
|
+
? new Date(now.getTime() + options.expiresInSeconds * 1000).toISOString()
|
|
167
|
+
: null;
|
|
168
|
+
const entry = {
|
|
169
|
+
tokenHash,
|
|
170
|
+
salt: bytesToHex(salt),
|
|
171
|
+
iv: bytesToHex(iv),
|
|
172
|
+
encryptedPassphrase: encrypted,
|
|
173
|
+
createdAt: now.toISOString(),
|
|
174
|
+
expiresAt,
|
|
175
|
+
label: options.label ?? 'default',
|
|
176
|
+
};
|
|
177
|
+
const sidecar = await this.readSidecar();
|
|
178
|
+
sidecar.entries.push(entry);
|
|
179
|
+
await this.writeSidecar(sidecar);
|
|
180
|
+
return token;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Resolve a session token → vault passphrase.
|
|
184
|
+
*
|
|
185
|
+
* Looks up the sidecar entry by the token's SHA-256 hash, checks expiry,
|
|
186
|
+
* then decrypts the passphrase.
|
|
187
|
+
*
|
|
188
|
+
* @throws Error if the token is invalid, expired, or sidecar is missing.
|
|
189
|
+
*/
|
|
190
|
+
async resolve(token) {
|
|
191
|
+
if (!token.startsWith(TOKEN_PREFIX)) {
|
|
192
|
+
throw new Error('Invalid session token format.');
|
|
193
|
+
}
|
|
194
|
+
const tokenHash = await sha256Hex(token);
|
|
195
|
+
const sidecar = await this.readSidecar();
|
|
196
|
+
const entry = sidecar.entries.find((e) => e.tokenHash === tokenHash);
|
|
197
|
+
if (!entry) {
|
|
198
|
+
throw new Error('Session token not found. It may have been revoked or the sidecar file is missing. ' +
|
|
199
|
+
"Use your passphrase directly, or create a new token with 'idw auth token create'.");
|
|
200
|
+
}
|
|
201
|
+
// Check expiry
|
|
202
|
+
if (entry.expiresAt && new Date(entry.expiresAt) < new Date()) {
|
|
203
|
+
throw new Error('Session token has expired. ' +
|
|
204
|
+
"Create a new token with 'idw auth token create'.");
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
return await decryptPassphrase(entry.encryptedPassphrase, token, hexToBytes(entry.salt), hexToBytes(entry.iv));
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
throw new Error('Failed to decrypt passphrase from session token. The sidecar entry may be corrupt. ' +
|
|
211
|
+
"Use your passphrase directly, or create a new token with 'idw auth token create'.");
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Revoke a session token by its hash prefix (first 8+ hex chars).
|
|
216
|
+
* Returns `true` if an entry was removed.
|
|
217
|
+
*/
|
|
218
|
+
async revoke(hashPrefix) {
|
|
219
|
+
const sidecar = await this.readSidecar();
|
|
220
|
+
const before = sidecar.entries.length;
|
|
221
|
+
sidecar.entries = sidecar.entries.filter((e) => !e.tokenHash.startsWith(hashPrefix));
|
|
222
|
+
if (sidecar.entries.length === before)
|
|
223
|
+
return false;
|
|
224
|
+
await this.writeSidecar(sidecar);
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* List all session tokens (metadata only — never returns secrets).
|
|
229
|
+
* Expired entries are included but marked.
|
|
230
|
+
*/
|
|
231
|
+
async list() {
|
|
232
|
+
const sidecar = await this.readSidecar();
|
|
233
|
+
return sidecar.entries.map((e) => ({
|
|
234
|
+
tokenHash: e.tokenHash,
|
|
235
|
+
label: e.label,
|
|
236
|
+
createdAt: e.createdAt,
|
|
237
|
+
expiresAt: e.expiresAt,
|
|
238
|
+
}));
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Remove all expired entries from the sidecar.
|
|
242
|
+
* Returns the number of entries removed.
|
|
243
|
+
*/
|
|
244
|
+
async pruneExpired() {
|
|
245
|
+
const sidecar = await this.readSidecar();
|
|
246
|
+
const before = sidecar.entries.length;
|
|
247
|
+
const now = new Date();
|
|
248
|
+
sidecar.entries = sidecar.entries.filter((e) => !e.expiresAt || new Date(e.expiresAt) >= now);
|
|
249
|
+
const removed = before - sidecar.entries.length;
|
|
250
|
+
if (removed > 0)
|
|
251
|
+
await this.writeSidecar(sidecar);
|
|
252
|
+
return removed;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Read the token from the environment variable.
|
|
256
|
+
* Returns `null` if not set.
|
|
257
|
+
*/
|
|
258
|
+
static getTokenFromEnv() {
|
|
259
|
+
return process.env[ENV_VAR] ?? null;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
export { ENV_VAR as SESSION_TOKEN_ENV_VAR };
|
|
263
|
+
//# sourceMappingURL=sessionTokenManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionTokenManager.js","sourceRoot":"","sources":["../../src/auth/sessionTokenManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ID Wispera Delegation Chain Management
|
|
3
|
+
* Track credential provenance through agent delegation
|
|
4
|
+
*/
|
|
5
|
+
import { Passport, DelegationHop } from './types.js';
|
|
6
|
+
import { Vault } from './vault.js';
|
|
7
|
+
/**
|
|
8
|
+
* Add a delegation hop to a passport
|
|
9
|
+
*/
|
|
10
|
+
export declare function addDelegation(vault: Vault, passportId: string, delegation: Omit<DelegationHop, 'grantedAt'>, actor: string): Promise<Passport>;
|
|
11
|
+
/**
|
|
12
|
+
* Remove the last delegation hop from a passport
|
|
13
|
+
*/
|
|
14
|
+
export declare function revokeDelegation(vault: Vault, passportId: string, actor: string): Promise<Passport>;
|
|
15
|
+
/**
|
|
16
|
+
* Clear all delegations from a passport
|
|
17
|
+
*/
|
|
18
|
+
export declare function clearDelegations(vault: Vault, passportId: string, actor: string): Promise<Passport>;
|
|
19
|
+
/**
|
|
20
|
+
* Get the delegation depth of a passport
|
|
21
|
+
*/
|
|
22
|
+
export declare function getDelegationDepth(passport: Passport): number;
|
|
23
|
+
/**
|
|
24
|
+
* Get the current holder (last in delegation chain, or human owner if no chain)
|
|
25
|
+
*/
|
|
26
|
+
export declare function getCurrentHolder(passport: Passport): string;
|
|
27
|
+
/**
|
|
28
|
+
* Get the original issuer (human owner)
|
|
29
|
+
*/
|
|
30
|
+
export declare function getOriginalIssuer(passport: Passport): string;
|
|
31
|
+
/**
|
|
32
|
+
* Get all entities in the delegation chain (including owner)
|
|
33
|
+
*/
|
|
34
|
+
export declare function getAllDelegationParties(passport: Passport): string[];
|
|
35
|
+
/**
|
|
36
|
+
* Check if an entity is in the delegation chain
|
|
37
|
+
*/
|
|
38
|
+
export declare function isInDelegationChain(passport: Passport, entity: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Get the delegation path as a readable string
|
|
41
|
+
*/
|
|
42
|
+
export declare function getDelegationPath(passport: Passport): string;
|
|
43
|
+
/**
|
|
44
|
+
* Check if any delegation in the chain has expired
|
|
45
|
+
*/
|
|
46
|
+
export declare function hasExpiredDelegation(passport: Passport): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Get expired delegations
|
|
49
|
+
*/
|
|
50
|
+
export declare function getExpiredDelegations(passport: Passport): DelegationHop[];
|
|
51
|
+
/**
|
|
52
|
+
* Get delegations expiring soon (within specified days)
|
|
53
|
+
*/
|
|
54
|
+
export declare function getExpiringDelegations(passport: Passport, withinDays?: number): DelegationHop[];
|
|
55
|
+
/**
|
|
56
|
+
* Get the effective scope (intersection of all delegated scopes)
|
|
57
|
+
*/
|
|
58
|
+
export declare function getEffectiveScope(passport: Passport): string[];
|
|
59
|
+
/**
|
|
60
|
+
* Check if a specific scope is permitted through the delegation chain
|
|
61
|
+
*/
|
|
62
|
+
export declare function isScopePermitted(passport: Passport, scope: string): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Get scope restrictions applied by delegations
|
|
65
|
+
*/
|
|
66
|
+
export declare function getScopeRestrictions(passport: Passport): {
|
|
67
|
+
originalScope: string[];
|
|
68
|
+
effectiveScope: string[];
|
|
69
|
+
restrictedBy: Array<{
|
|
70
|
+
hop: DelegationHop;
|
|
71
|
+
removedScopes: string[];
|
|
72
|
+
}>;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Validate a delegation chain
|
|
76
|
+
*/
|
|
77
|
+
export declare function validateDelegationChain(passport: Passport): {
|
|
78
|
+
valid: boolean;
|
|
79
|
+
issues: string[];
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=delegation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delegation.d.ts","sourceRoot":"","sources":["../src/delegation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,QAAQ,EACR,aAAa,EAEd,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAOnC;;GAEG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC,EAC5C,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,QAAQ,CAAC,CAkCnB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,QAAQ,CAAC,CAgCnB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,QAAQ,CAAC,CA2BnB;AAMD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAE7D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAK3D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAE5D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,EAAE,CAWpE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAU/E;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAU5D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAOhE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,QAAQ,GAAG,aAAa,EAAE,CAOzE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,QAAQ,EAClB,UAAU,GAAE,MAAU,GACrB,aAAa,EAAE,CAWjB;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,EAAE,CAiB9D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAG3E;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG;IACxD,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,aAAa,CAAC;QAAC,aAAa,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACtE,CAyBA;AAMD;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,GAAG;IAC3D,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAmDA"}
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ID Wispera Delegation Chain Management
|
|
3
|
+
* Track credential provenance through agent delegation
|
|
4
|
+
*/
|
|
5
|
+
import { WisperaError, } from './types.js';
|
|
6
|
+
import { logAction } from './audit.js';
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Delegation Chain Operations
|
|
9
|
+
// ============================================================================
|
|
10
|
+
/**
|
|
11
|
+
* Add a delegation hop to a passport
|
|
12
|
+
*/
|
|
13
|
+
export async function addDelegation(vault, passportId, delegation, actor) {
|
|
14
|
+
const passport = await vault.retrievePassport(passportId);
|
|
15
|
+
if (!passport) {
|
|
16
|
+
throw new WisperaError(`Passport not found: ${passportId}`, 'PASSPORT_NOT_FOUND');
|
|
17
|
+
}
|
|
18
|
+
const hop = {
|
|
19
|
+
...delegation,
|
|
20
|
+
grantedAt: new Date().toISOString(),
|
|
21
|
+
};
|
|
22
|
+
const updatedChain = [...(passport.delegationChain ?? []), hop];
|
|
23
|
+
const updated = {
|
|
24
|
+
...passport,
|
|
25
|
+
delegationChain: updatedChain,
|
|
26
|
+
updatedAt: new Date().toISOString(),
|
|
27
|
+
};
|
|
28
|
+
await vault.storePassport(updated);
|
|
29
|
+
await logAction(vault, {
|
|
30
|
+
passportId,
|
|
31
|
+
action: 'modified',
|
|
32
|
+
actor,
|
|
33
|
+
details: `Delegated credential from "${delegation.from}" to "${delegation.to}"`,
|
|
34
|
+
metadata: {
|
|
35
|
+
delegationFrom: delegation.from,
|
|
36
|
+
delegationTo: delegation.to,
|
|
37
|
+
delegationScope: delegation.scope,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
return updated;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Remove the last delegation hop from a passport
|
|
44
|
+
*/
|
|
45
|
+
export async function revokeDelegation(vault, passportId, actor) {
|
|
46
|
+
const passport = await vault.retrievePassport(passportId);
|
|
47
|
+
if (!passport) {
|
|
48
|
+
throw new WisperaError(`Passport not found: ${passportId}`, 'PASSPORT_NOT_FOUND');
|
|
49
|
+
}
|
|
50
|
+
if (!passport.delegationChain || passport.delegationChain.length === 0) {
|
|
51
|
+
throw new WisperaError('No delegation to revoke', 'NO_DELEGATION');
|
|
52
|
+
}
|
|
53
|
+
const revokedHop = passport.delegationChain[passport.delegationChain.length - 1];
|
|
54
|
+
const updatedChain = passport.delegationChain.slice(0, -1);
|
|
55
|
+
const updated = {
|
|
56
|
+
...passport,
|
|
57
|
+
delegationChain: updatedChain.length > 0 ? updatedChain : undefined,
|
|
58
|
+
updatedAt: new Date().toISOString(),
|
|
59
|
+
};
|
|
60
|
+
await vault.storePassport(updated);
|
|
61
|
+
await logAction(vault, {
|
|
62
|
+
passportId,
|
|
63
|
+
action: 'modified',
|
|
64
|
+
actor,
|
|
65
|
+
details: `Revoked delegation from "${revokedHop?.from}" to "${revokedHop?.to}"`,
|
|
66
|
+
metadata: {
|
|
67
|
+
revokedDelegation: revokedHop,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
return updated;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Clear all delegations from a passport
|
|
74
|
+
*/
|
|
75
|
+
export async function clearDelegations(vault, passportId, actor) {
|
|
76
|
+
const passport = await vault.retrievePassport(passportId);
|
|
77
|
+
if (!passport) {
|
|
78
|
+
throw new WisperaError(`Passport not found: ${passportId}`, 'PASSPORT_NOT_FOUND');
|
|
79
|
+
}
|
|
80
|
+
const previousChain = passport.delegationChain;
|
|
81
|
+
const updated = {
|
|
82
|
+
...passport,
|
|
83
|
+
delegationChain: undefined,
|
|
84
|
+
updatedAt: new Date().toISOString(),
|
|
85
|
+
};
|
|
86
|
+
await vault.storePassport(updated);
|
|
87
|
+
await logAction(vault, {
|
|
88
|
+
passportId,
|
|
89
|
+
action: 'modified',
|
|
90
|
+
actor,
|
|
91
|
+
details: `Cleared all delegations (${previousChain?.length ?? 0} hops removed)`,
|
|
92
|
+
metadata: {
|
|
93
|
+
clearedDelegations: previousChain,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
return updated;
|
|
97
|
+
}
|
|
98
|
+
// ============================================================================
|
|
99
|
+
// Delegation Chain Analysis
|
|
100
|
+
// ============================================================================
|
|
101
|
+
/**
|
|
102
|
+
* Get the delegation depth of a passport
|
|
103
|
+
*/
|
|
104
|
+
export function getDelegationDepth(passport) {
|
|
105
|
+
return passport.delegationChain?.length ?? 0;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get the current holder (last in delegation chain, or human owner if no chain)
|
|
109
|
+
*/
|
|
110
|
+
export function getCurrentHolder(passport) {
|
|
111
|
+
if (!passport.delegationChain || passport.delegationChain.length === 0) {
|
|
112
|
+
return passport.humanOwner;
|
|
113
|
+
}
|
|
114
|
+
return passport.delegationChain[passport.delegationChain.length - 1]?.to ?? passport.humanOwner;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Get the original issuer (human owner)
|
|
118
|
+
*/
|
|
119
|
+
export function getOriginalIssuer(passport) {
|
|
120
|
+
return passport.humanOwner;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get all entities in the delegation chain (including owner)
|
|
124
|
+
*/
|
|
125
|
+
export function getAllDelegationParties(passport) {
|
|
126
|
+
const seen = new Set([passport.humanOwner]);
|
|
127
|
+
if (passport.delegationChain) {
|
|
128
|
+
for (const hop of passport.delegationChain) {
|
|
129
|
+
seen.add(hop.from);
|
|
130
|
+
seen.add(hop.to);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return Array.from(seen);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if an entity is in the delegation chain
|
|
137
|
+
*/
|
|
138
|
+
export function isInDelegationChain(passport, entity) {
|
|
139
|
+
if (passport.humanOwner === entity)
|
|
140
|
+
return true;
|
|
141
|
+
if (passport.delegationChain) {
|
|
142
|
+
for (const hop of passport.delegationChain) {
|
|
143
|
+
if (hop.from === entity || hop.to === entity)
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get the delegation path as a readable string
|
|
151
|
+
*/
|
|
152
|
+
export function getDelegationPath(passport) {
|
|
153
|
+
const parts = [passport.humanOwner];
|
|
154
|
+
if (passport.delegationChain) {
|
|
155
|
+
for (const hop of passport.delegationChain) {
|
|
156
|
+
parts.push(hop.to);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return parts.join(' → ');
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Check if any delegation in the chain has expired
|
|
163
|
+
*/
|
|
164
|
+
export function hasExpiredDelegation(passport) {
|
|
165
|
+
if (!passport.delegationChain)
|
|
166
|
+
return false;
|
|
167
|
+
const now = new Date();
|
|
168
|
+
return passport.delegationChain.some((hop) => hop.expiresAt && new Date(hop.expiresAt) < now);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get expired delegations
|
|
172
|
+
*/
|
|
173
|
+
export function getExpiredDelegations(passport) {
|
|
174
|
+
if (!passport.delegationChain)
|
|
175
|
+
return [];
|
|
176
|
+
const now = new Date();
|
|
177
|
+
return passport.delegationChain.filter((hop) => hop.expiresAt && new Date(hop.expiresAt) < now);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get delegations expiring soon (within specified days)
|
|
181
|
+
*/
|
|
182
|
+
export function getExpiringDelegations(passport, withinDays = 7) {
|
|
183
|
+
if (!passport.delegationChain)
|
|
184
|
+
return [];
|
|
185
|
+
const now = new Date();
|
|
186
|
+
const threshold = new Date(now.getTime() + withinDays * 24 * 60 * 60 * 1000);
|
|
187
|
+
return passport.delegationChain.filter((hop) => {
|
|
188
|
+
if (!hop.expiresAt)
|
|
189
|
+
return false;
|
|
190
|
+
const expiresAt = new Date(hop.expiresAt);
|
|
191
|
+
return expiresAt > now && expiresAt <= threshold;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
// ============================================================================
|
|
195
|
+
// Delegation Scope Analysis
|
|
196
|
+
// ============================================================================
|
|
197
|
+
/**
|
|
198
|
+
* Get the effective scope (intersection of all delegated scopes)
|
|
199
|
+
*/
|
|
200
|
+
export function getEffectiveScope(passport) {
|
|
201
|
+
// Start with the passport's full scope
|
|
202
|
+
let effectiveScope = new Set(passport.scope);
|
|
203
|
+
// Each delegation can only narrow, not expand the scope
|
|
204
|
+
if (passport.delegationChain) {
|
|
205
|
+
for (const hop of passport.delegationChain) {
|
|
206
|
+
if (hop.scope && hop.scope.length > 0) {
|
|
207
|
+
const delegatedScope = new Set(hop.scope);
|
|
208
|
+
for (const s of effectiveScope) {
|
|
209
|
+
if (!delegatedScope.has(s))
|
|
210
|
+
effectiveScope.delete(s);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return Array.from(effectiveScope);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Check if a specific scope is permitted through the delegation chain
|
|
219
|
+
*/
|
|
220
|
+
export function isScopePermitted(passport, scope) {
|
|
221
|
+
const effectiveScope = getEffectiveScope(passport);
|
|
222
|
+
return effectiveScope.includes(scope);
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Get scope restrictions applied by delegations
|
|
226
|
+
*/
|
|
227
|
+
export function getScopeRestrictions(passport) {
|
|
228
|
+
const originalScope = passport.scope;
|
|
229
|
+
let currentScope = new Set(originalScope);
|
|
230
|
+
const restrictedBy = [];
|
|
231
|
+
if (passport.delegationChain) {
|
|
232
|
+
for (const hop of passport.delegationChain) {
|
|
233
|
+
if (hop.scope && hop.scope.length > 0) {
|
|
234
|
+
const delegatedScope = new Set(hop.scope);
|
|
235
|
+
const removedScopes = [...currentScope].filter((s) => !delegatedScope.has(s));
|
|
236
|
+
if (removedScopes.length > 0) {
|
|
237
|
+
restrictedBy.push({ hop, removedScopes });
|
|
238
|
+
}
|
|
239
|
+
currentScope = new Set([...currentScope].filter((s) => delegatedScope.has(s)));
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
originalScope,
|
|
245
|
+
effectiveScope: Array.from(currentScope),
|
|
246
|
+
restrictedBy,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
// ============================================================================
|
|
250
|
+
// Delegation Validation
|
|
251
|
+
// ============================================================================
|
|
252
|
+
/**
|
|
253
|
+
* Validate a delegation chain
|
|
254
|
+
*/
|
|
255
|
+
export function validateDelegationChain(passport) {
|
|
256
|
+
const issues = [];
|
|
257
|
+
if (!passport.delegationChain || passport.delegationChain.length === 0) {
|
|
258
|
+
return { valid: true, issues: [] };
|
|
259
|
+
}
|
|
260
|
+
// Check for circular delegations
|
|
261
|
+
const seen = new Set([passport.humanOwner]);
|
|
262
|
+
for (const hop of passport.delegationChain) {
|
|
263
|
+
if (seen.has(hop.to)) {
|
|
264
|
+
issues.push(`Circular delegation detected: "${hop.to}" appears multiple times`);
|
|
265
|
+
}
|
|
266
|
+
seen.add(hop.to);
|
|
267
|
+
}
|
|
268
|
+
// Check for broken chain (from should match previous to)
|
|
269
|
+
let expectedFrom = passport.humanOwner;
|
|
270
|
+
for (let i = 0; i < passport.delegationChain.length; i++) {
|
|
271
|
+
const hop = passport.delegationChain[i];
|
|
272
|
+
if (hop && hop.from !== expectedFrom) {
|
|
273
|
+
issues.push(`Broken chain at hop ${i + 1}: expected from "${expectedFrom}", got "${hop.from}"`);
|
|
274
|
+
}
|
|
275
|
+
expectedFrom = hop?.to ?? expectedFrom;
|
|
276
|
+
}
|
|
277
|
+
// Check for expired delegations
|
|
278
|
+
const expired = getExpiredDelegations(passport);
|
|
279
|
+
if (expired.length > 0) {
|
|
280
|
+
issues.push(`${expired.length} delegation(s) have expired`);
|
|
281
|
+
}
|
|
282
|
+
// Check delegation timestamps are in order
|
|
283
|
+
let lastTimestamp = null;
|
|
284
|
+
for (let i = 0; i < passport.delegationChain.length; i++) {
|
|
285
|
+
const hop = passport.delegationChain[i];
|
|
286
|
+
if (hop) {
|
|
287
|
+
const hopTime = new Date(hop.grantedAt);
|
|
288
|
+
if (lastTimestamp && hopTime < lastTimestamp) {
|
|
289
|
+
issues.push(`Delegation timestamp out of order at hop ${i + 1}`);
|
|
290
|
+
}
|
|
291
|
+
lastTimestamp = hopTime;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
valid: issues.length === 0,
|
|
296
|
+
issues,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
//# sourceMappingURL=delegation.js.map
|