@oneidentity/safeguard 8.1.0 → 8.2.0-pre58935
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 +137 -6
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +2 -2
- package/dist/browser.js.map +1 -1
- package/dist/events.cjs.map +1 -1
- package/dist/events.d.cts +0 -1
- package/dist/events.d.cts.map +1 -1
- package/dist/events.d.ts +0 -1
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/index.cjs +66 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +33 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +66 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/events.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","names":[],"sources":["../src/events/types.ts","../src/events/listener.ts","../src/http/types.ts","../src/storage/types.ts","../src/secret.ts","../src/auth/types.ts","../src/events/persistent.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"events.d.ts","names":[],"sources":["../src/events/types.ts","../src/events/listener.ts","../src/http/types.ts","../src/storage/types.ts","../src/secret.ts","../src/auth/types.ts","../src/events/persistent.ts"],"mappings":";;;;;KAGY;;;;KAUA,gBAAgB,OAAO;;;;KAKvB,sBAAsB,OAAO;;;;UAKxB;EACf;GACC;;;;;UAMc;;EAEf;;EAEA;;;;;;;;cC5BW;;EAKC,YAAA,YAAY;;MAwBpB,SAAS;;;;EAOb,GAAG,6BAA6B,SAAS;;;;EAQnC,SAAS;;;;EAST,QAAQ;;;;;;;UCxDC;EACf;EACA;EACA,UAAU;EACV,gBAAgB;EAChB,SAAS;EACT;;UAGe;EACf;EACA,SAAS;EACT;;;;;;UA+Ce;EACf,QAAQ,SAAS,qBAAqB,QAAQ;EAC9C;;;;;;;UC9De;EACf,IAAI;EACJ,IAAI,aAAa;EACjB,OAAO;EACP;;;;;;;;;;;;cCGW;;EAGC,YAAA;;EAKZ;EAIA;EAIA;OASK,OAAO;;;;;;;UC5BG;;EAEf,aAAa;;EAEb;;EAEA;;;;;UAMe;;;;;;;EAOf,aAAa,cAAc,YAAY,YAAY,SAAS,kBAAkB,QAAQ;;;;EAKtF,cAAc,cAAc,YAAY,YAAY,SAAS,kBAAkB,QAAQ;;WAG9E;;;;;;;;;;;;cCXE;;EAoBT,YAAA,YAAY,eACZ,MAAM,MACN,cACA,YAAY,YACZ,SAAS,iBACT,UAAU,2BACV,WAAW;;MAmCT,SAAS;;;;EAOb,GAAG,6BAA6B,SAAS;;;;EAQzC,cAAc,SAAS;;;;EAQjB,SAAS;;;;EAWT,QAAQ"}
|
package/dist/events.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","names":["#connection","#handlers","#setState","#state","#connection","#auth","#host","#httpClient","#storage","#retryIntervalMs","#maxRetries","#handlers","#stateChangeHandlers","#tokenSet","#setState","#manualStop","#scheduleReconnect","#retryCount","#state","#retryTimer","#reconnect","#isTokenExpired","#refreshTokenSingleFlight","#tokenRefreshPromise"],"sources":["../src/events/listener.ts","../src/events/persistent.ts"],"sourcesContent":["import type { HubConnection } from '@microsoft/signalr';\nimport type { EventHandler, EventListenerState, SafeguardEvent } from './types.js';\n\n/**\n * One-shot event listener wrapping a SignalR HubConnection.\n * For persistent auto-reconnect behavior, use PersistentSafeguardEventListener.\n */\nexport class SafeguardEventListener {\n readonly #connection: HubConnection;\n readonly #handlers: EventHandler[] = [];\n #state: EventListenerState = 'stopped';\n\n constructor(connection: HubConnection) {\n this.#connection = connection;\n\n this.#connection.onclose(() => {\n this.#setState('disconnected');\n });\n\n this.#connection.onreconnecting(() => {\n this.#setState('reconnecting');\n });\n\n this.#connection.onreconnected(() => {\n this.#setState('connected');\n });\n\n // Register for the Safeguard event method\n this.#connection.on('NotifyEventAsync', (event: SafeguardEvent) => {\n for (const handler of this.#handlers) {\n handler(event);\n }\n });\n }\n\n /** Current connection state. */\n get state(): EventListenerState {\n return this.#state;\n }\n\n /**\n * Register an event handler for Safeguard notifications.\n */\n on(_method: 'NotifyEventAsync', handler: EventHandler): this {\n this.#handlers.push(handler);\n return this;\n }\n\n /**\n * Start the SignalR connection.\n */\n async start(): Promise<void> {\n this.#setState('starting');\n await this.#connection.start();\n this.#setState('connected');\n }\n\n /**\n * Stop the SignalR connection.\n */\n async stop(): Promise<void> {\n await this.#connection.stop();\n this.#setState('stopped');\n }\n\n #setState(state: EventListenerState): void {\n this.#state = state;\n }\n}\n","import type { HubConnection } from '@microsoft/signalr';\nimport type { Auth, TokenSet } from '../auth/types.js';\nimport type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type {\n EventHandler,\n EventListenerState,\n PersistentListenerOptions,\n SafeguardEvent,\n StateChangeHandler,\n} from './types.js';\n\nconst DEFAULT_RETRY_INTERVAL_MS = 5_000;\nconst TOKEN_REFRESH_MARGIN_MS = 60_000; // refresh 60s before expiry\n\n/**\n * Persistent event listener with automatic reconnection and token refresh.\n *\n * Wraps SignalR's built-in `withAutomaticReconnect()` AND adds:\n * - Token refresh on reconnect (re-authenticates if needed)\n * - State change event notifications\n * - Manual `stop()` that prevents further auto-reconnect\n */\nexport class PersistentSafeguardEventListener {\n readonly #connection: HubConnection;\n readonly #auth: Auth;\n readonly #host: string;\n readonly #httpClient: HttpClient;\n readonly #storage: StorageProvider;\n readonly #retryIntervalMs: number;\n readonly #maxRetries: number | undefined;\n readonly #handlers: EventHandler[] = [];\n readonly #stateChangeHandlers: StateChangeHandler[] = [];\n\n #state: EventListenerState = 'stopped';\n #tokenSet: TokenSet | undefined;\n #manualStop = false;\n #retryCount = 0;\n #retryTimer: ReturnType<typeof setTimeout> | undefined;\n /** Single-flight token refresh promise to prevent thundering herd. */\n #tokenRefreshPromise: Promise<void> | undefined;\n\n constructor(\n connection: HubConnection,\n auth: Auth,\n host: string,\n httpClient: HttpClient,\n storage: StorageProvider,\n options?: PersistentListenerOptions,\n tokenSet?: TokenSet,\n ) {\n this.#connection = connection;\n this.#auth = auth;\n this.#host = host;\n this.#httpClient = httpClient;\n this.#storage = storage;\n this.#retryIntervalMs = options?.retryIntervalMs ?? DEFAULT_RETRY_INTERVAL_MS;\n this.#maxRetries = options?.maxRetries;\n this.#tokenSet = tokenSet;\n\n this.#connection.onclose(() => {\n this.#setState('disconnected');\n if (!this.#manualStop) {\n void this.#scheduleReconnect();\n }\n });\n\n this.#connection.onreconnecting(() => {\n this.#setState('reconnecting');\n });\n\n this.#connection.onreconnected(() => {\n this.#retryCount = 0;\n this.#setState('connected');\n });\n\n this.#connection.on('NotifyEventAsync', (event: SafeguardEvent) => {\n for (const handler of this.#handlers) {\n handler(event);\n }\n });\n }\n\n /** Current connection state. */\n get state(): EventListenerState {\n return this.#state;\n }\n\n /**\n * Register an event handler for Safeguard notifications.\n */\n on(_method: 'NotifyEventAsync', handler: EventHandler): this {\n this.#handlers.push(handler);\n return this;\n }\n\n /**\n * Register a state change handler.\n */\n onStateChange(handler: StateChangeHandler): this {\n this.#stateChangeHandlers.push(handler);\n return this;\n }\n\n /**\n * Start the persistent connection.\n */\n async start(): Promise<void> {\n this.#manualStop = false;\n this.#retryCount = 0;\n this.#setState('starting');\n await this.#connection.start();\n this.#setState('connected');\n }\n\n /**\n * Stop the connection. Prevents auto-reconnect.\n */\n async stop(): Promise<void> {\n this.#manualStop = true;\n if (this.#retryTimer != null) {\n clearTimeout(this.#retryTimer);\n this.#retryTimer = undefined;\n }\n await this.#connection.stop();\n this.#setState('stopped');\n }\n\n // ─── Private ────────────────────────────────────────────────────────\n\n #setState(state: EventListenerState): void {\n this.#state = state;\n for (const handler of this.#stateChangeHandlers) {\n handler(state);\n }\n }\n\n async #scheduleReconnect(): Promise<void> {\n if (this.#manualStop) return;\n if (this.#maxRetries != null && this.#retryCount >= this.#maxRetries) {\n this.#setState('stopped');\n return;\n }\n\n this.#setState('reconnecting');\n this.#retryTimer = setTimeout(() => {\n void this.#reconnect();\n }, this.#retryIntervalMs);\n }\n\n async #reconnect(): Promise<void> {\n if (this.#manualStop) return;\n this.#retryCount++;\n\n try {\n // Only refresh the token if it's expired or near-expiry. Single-flight\n // so overlapping reconnect attempts don't double-call refreshToken or\n // trample the storage provider.\n if (this.#isTokenExpired()) {\n await this.#refreshTokenSingleFlight();\n }\n\n await this.#connection.start();\n this.#retryCount = 0;\n this.#setState('connected');\n } catch {\n // Reconnect failed — try again\n void this.#scheduleReconnect();\n }\n }\n\n async #refreshTokenSingleFlight(): Promise<void> {\n if (this.#tokenRefreshPromise) {\n await this.#tokenRefreshPromise;\n return;\n }\n this.#tokenRefreshPromise = (async (): Promise<void> => {\n if (this.#auth.refreshToken) {\n const refreshed = await this.#auth.refreshToken(this.#host, this.#httpClient, this.#storage);\n if (refreshed) {\n this.#tokenSet = refreshed;\n return;\n }\n }\n this.#tokenSet = await this.#auth.authenticate(this.#host, this.#httpClient, this.#storage);\n })();\n try {\n await this.#tokenRefreshPromise;\n } finally {\n this.#tokenRefreshPromise = undefined;\n }\n }\n\n #isTokenExpired(): boolean {\n if (!this.#tokenSet) return true; // no token at all — must authenticate\n if (!this.#tokenSet.expiresIn) return true; // can't determine lifetime — refresh to be safe\n\n const elapsed = Date.now() - this.#tokenSet.acquiredAt;\n const expiresInMs = this.#tokenSet.expiresIn * 1000;\n return elapsed + TOKEN_REFRESH_MARGIN_MS >= expiresInMs;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"events.js","names":[],"sources":["../src/events/listener.ts","../src/events/persistent.ts"],"sourcesContent":["import type { HubConnection } from '@microsoft/signalr';\nimport type { EventHandler, EventListenerState, SafeguardEvent } from './types.js';\n\n/**\n * One-shot event listener wrapping a SignalR HubConnection.\n * For persistent auto-reconnect behavior, use PersistentSafeguardEventListener.\n */\nexport class SafeguardEventListener {\n readonly #connection: HubConnection;\n readonly #handlers: EventHandler[] = [];\n #state: EventListenerState = 'stopped';\n\n constructor(connection: HubConnection) {\n this.#connection = connection;\n\n this.#connection.onclose(() => {\n this.#setState('disconnected');\n });\n\n this.#connection.onreconnecting(() => {\n this.#setState('reconnecting');\n });\n\n this.#connection.onreconnected(() => {\n this.#setState('connected');\n });\n\n // Register for the Safeguard event method\n this.#connection.on('NotifyEventAsync', (event: SafeguardEvent) => {\n for (const handler of this.#handlers) {\n handler(event);\n }\n });\n }\n\n /** Current connection state. */\n get state(): EventListenerState {\n return this.#state;\n }\n\n /**\n * Register an event handler for Safeguard notifications.\n */\n on(_method: 'NotifyEventAsync', handler: EventHandler): this {\n this.#handlers.push(handler);\n return this;\n }\n\n /**\n * Start the SignalR connection.\n */\n async start(): Promise<void> {\n this.#setState('starting');\n await this.#connection.start();\n this.#setState('connected');\n }\n\n /**\n * Stop the SignalR connection.\n */\n async stop(): Promise<void> {\n await this.#connection.stop();\n this.#setState('stopped');\n }\n\n #setState(state: EventListenerState): void {\n this.#state = state;\n }\n}\n","import type { HubConnection } from '@microsoft/signalr';\nimport type { Auth, TokenSet } from '../auth/types.js';\nimport type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type {\n EventHandler,\n EventListenerState,\n PersistentListenerOptions,\n SafeguardEvent,\n StateChangeHandler,\n} from './types.js';\n\nconst DEFAULT_RETRY_INTERVAL_MS = 5_000;\nconst TOKEN_REFRESH_MARGIN_MS = 60_000; // refresh 60s before expiry\n\n/**\n * Persistent event listener with automatic reconnection and token refresh.\n *\n * Wraps SignalR's built-in `withAutomaticReconnect()` AND adds:\n * - Token refresh on reconnect (re-authenticates if needed)\n * - State change event notifications\n * - Manual `stop()` that prevents further auto-reconnect\n */\nexport class PersistentSafeguardEventListener {\n readonly #connection: HubConnection;\n readonly #auth: Auth;\n readonly #host: string;\n readonly #httpClient: HttpClient;\n readonly #storage: StorageProvider;\n readonly #retryIntervalMs: number;\n readonly #maxRetries: number | undefined;\n readonly #handlers: EventHandler[] = [];\n readonly #stateChangeHandlers: StateChangeHandler[] = [];\n\n #state: EventListenerState = 'stopped';\n #tokenSet: TokenSet | undefined;\n #manualStop = false;\n #retryCount = 0;\n #retryTimer: ReturnType<typeof setTimeout> | undefined;\n /** Single-flight token refresh promise to prevent thundering herd. */\n #tokenRefreshPromise: Promise<void> | undefined;\n\n constructor(\n connection: HubConnection,\n auth: Auth,\n host: string,\n httpClient: HttpClient,\n storage: StorageProvider,\n options?: PersistentListenerOptions,\n tokenSet?: TokenSet,\n ) {\n this.#connection = connection;\n this.#auth = auth;\n this.#host = host;\n this.#httpClient = httpClient;\n this.#storage = storage;\n this.#retryIntervalMs = options?.retryIntervalMs ?? DEFAULT_RETRY_INTERVAL_MS;\n this.#maxRetries = options?.maxRetries;\n this.#tokenSet = tokenSet;\n\n this.#connection.onclose(() => {\n this.#setState('disconnected');\n if (!this.#manualStop) {\n void this.#scheduleReconnect();\n }\n });\n\n this.#connection.onreconnecting(() => {\n this.#setState('reconnecting');\n });\n\n this.#connection.onreconnected(() => {\n this.#retryCount = 0;\n this.#setState('connected');\n });\n\n this.#connection.on('NotifyEventAsync', (event: SafeguardEvent) => {\n for (const handler of this.#handlers) {\n handler(event);\n }\n });\n }\n\n /** Current connection state. */\n get state(): EventListenerState {\n return this.#state;\n }\n\n /**\n * Register an event handler for Safeguard notifications.\n */\n on(_method: 'NotifyEventAsync', handler: EventHandler): this {\n this.#handlers.push(handler);\n return this;\n }\n\n /**\n * Register a state change handler.\n */\n onStateChange(handler: StateChangeHandler): this {\n this.#stateChangeHandlers.push(handler);\n return this;\n }\n\n /**\n * Start the persistent connection.\n */\n async start(): Promise<void> {\n this.#manualStop = false;\n this.#retryCount = 0;\n this.#setState('starting');\n await this.#connection.start();\n this.#setState('connected');\n }\n\n /**\n * Stop the connection. Prevents auto-reconnect.\n */\n async stop(): Promise<void> {\n this.#manualStop = true;\n if (this.#retryTimer != null) {\n clearTimeout(this.#retryTimer);\n this.#retryTimer = undefined;\n }\n await this.#connection.stop();\n this.#setState('stopped');\n }\n\n // ─── Private ────────────────────────────────────────────────────────\n\n #setState(state: EventListenerState): void {\n this.#state = state;\n for (const handler of this.#stateChangeHandlers) {\n handler(state);\n }\n }\n\n async #scheduleReconnect(): Promise<void> {\n if (this.#manualStop) return;\n if (this.#maxRetries != null && this.#retryCount >= this.#maxRetries) {\n this.#setState('stopped');\n return;\n }\n\n this.#setState('reconnecting');\n this.#retryTimer = setTimeout(() => {\n void this.#reconnect();\n }, this.#retryIntervalMs);\n }\n\n async #reconnect(): Promise<void> {\n if (this.#manualStop) return;\n this.#retryCount++;\n\n try {\n // Only refresh the token if it's expired or near-expiry. Single-flight\n // so overlapping reconnect attempts don't double-call refreshToken or\n // trample the storage provider.\n if (this.#isTokenExpired()) {\n await this.#refreshTokenSingleFlight();\n }\n\n await this.#connection.start();\n this.#retryCount = 0;\n this.#setState('connected');\n } catch {\n // Reconnect failed — try again\n void this.#scheduleReconnect();\n }\n }\n\n async #refreshTokenSingleFlight(): Promise<void> {\n if (this.#tokenRefreshPromise) {\n await this.#tokenRefreshPromise;\n return;\n }\n this.#tokenRefreshPromise = (async (): Promise<void> => {\n if (this.#auth.refreshToken) {\n const refreshed = await this.#auth.refreshToken(this.#host, this.#httpClient, this.#storage);\n if (refreshed) {\n this.#tokenSet = refreshed;\n return;\n }\n }\n this.#tokenSet = await this.#auth.authenticate(this.#host, this.#httpClient, this.#storage);\n })();\n try {\n await this.#tokenRefreshPromise;\n } finally {\n this.#tokenRefreshPromise = undefined;\n }\n }\n\n #isTokenExpired(): boolean {\n if (!this.#tokenSet) return true; // no token at all — must authenticate\n if (!this.#tokenSet.expiresIn) return true; // can't determine lifetime — refresh to be safe\n\n const elapsed = Date.now() - this.#tokenSet.acquiredAt;\n const expiresInMs = this.#tokenSet.expiresIn * 1000;\n return elapsed + TOKEN_REFRESH_MARGIN_MS >= expiresInMs;\n }\n}\n"],"mappings":"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"}
|
package/dist/index.cjs
CHANGED
|
@@ -218,7 +218,7 @@ function base64UrlEncode(bytes) {
|
|
|
218
218
|
* Returns a 43-character base64url string (32 random bytes).
|
|
219
219
|
*/
|
|
220
220
|
function generateCodeVerifier() {
|
|
221
|
-
const bytes = new Uint8Array(32);
|
|
221
|
+
const bytes = /* @__PURE__ */ new Uint8Array(32);
|
|
222
222
|
crypto.getRandomValues(bytes);
|
|
223
223
|
return base64UrlEncode(bytes);
|
|
224
224
|
}
|
|
@@ -235,7 +235,7 @@ async function generateCodeChallenge(verifier) {
|
|
|
235
235
|
* Generates a random state parameter for OAuth flows.
|
|
236
236
|
*/
|
|
237
237
|
function generateState() {
|
|
238
|
-
const bytes = new Uint8Array(16);
|
|
238
|
+
const bytes = /* @__PURE__ */ new Uint8Array(16);
|
|
239
239
|
crypto.getRandomValues(bytes);
|
|
240
240
|
return base64UrlEncode(bytes);
|
|
241
241
|
}
|
|
@@ -308,19 +308,46 @@ const StorageKeys = {
|
|
|
308
308
|
//#endregion
|
|
309
309
|
//#region src/http/node.ts
|
|
310
310
|
/**
|
|
311
|
+
* Resolve the undici Agent `connect` (TLS) options from {@link TlsOptions}.
|
|
312
|
+
*
|
|
313
|
+
* Applies the opt-in `minVersion`/`maxVersion` pins. When a client certificate
|
|
314
|
+
* is present and the caller has pinned neither bound, the connection is capped
|
|
315
|
+
* at TLS 1.2: Node has no client-side TLS 1.3 post-handshake authentication
|
|
316
|
+
* (see https://github.com/nodejs/node/issues/46120), so on TLS 1.3 the client
|
|
317
|
+
* never presents its certificate and cert/A2A auth fails (error 60094) on the
|
|
318
|
+
* appliance's Standard binding. Capping at TLS 1.2 keeps the cert request
|
|
319
|
+
* in-handshake so it works by default. Setting either bound explicitly (for
|
|
320
|
+
* example `minVersion: 'TLSv1.3'` when targeting a Cert SNI hostname) disables
|
|
321
|
+
* the auto-cap. Password/token connections carry no certificate and keep
|
|
322
|
+
* negotiating up to TLS 1.3.
|
|
323
|
+
*/
|
|
324
|
+
function resolveTlsConnectOptions(tlsOptions) {
|
|
325
|
+
const connect = {
|
|
326
|
+
ca: tlsOptions?.ca,
|
|
327
|
+
cert: tlsOptions?.cert,
|
|
328
|
+
key: tlsOptions?.key,
|
|
329
|
+
pfx: tlsOptions?.pfx,
|
|
330
|
+
passphrase: tlsOptions?.passphrase,
|
|
331
|
+
rejectUnauthorized: tlsOptions?.rejectUnauthorized ?? true
|
|
332
|
+
};
|
|
333
|
+
const hasClientCert = Boolean(tlsOptions?.cert ?? tlsOptions?.key ?? tlsOptions?.pfx);
|
|
334
|
+
if (tlsOptions?.minVersion) connect.minVersion = tlsOptions.minVersion;
|
|
335
|
+
if (tlsOptions?.maxVersion) connect.maxVersion = tlsOptions.maxVersion;
|
|
336
|
+
else if (hasClientCert && !tlsOptions?.minVersion) connect.maxVersion = "TLSv1.2";
|
|
337
|
+
return connect;
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
311
340
|
* Node.js HTTP client using undici for full TLS control.
|
|
312
341
|
* Creates a per-instance Agent — no global state mutation.
|
|
313
342
|
*/
|
|
314
343
|
var NodeHttpClient = class {
|
|
315
344
|
#agent;
|
|
316
345
|
constructor(tlsOptions) {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
rejectUnauthorized: tlsOptions?.rejectUnauthorized ?? true
|
|
323
|
-
} });
|
|
346
|
+
const hasClientCert = Boolean(tlsOptions?.cert ?? tlsOptions?.key ?? tlsOptions?.pfx);
|
|
347
|
+
this.#agent = new undici.Agent({
|
|
348
|
+
connect: resolveTlsConnectOptions(tlsOptions),
|
|
349
|
+
...hasClientCert ? { allowH2: false } : {}
|
|
350
|
+
});
|
|
324
351
|
}
|
|
325
352
|
async request(options) {
|
|
326
353
|
const { url, method, headers, body, signal, timeout } = options;
|
|
@@ -1256,7 +1283,8 @@ var A2AClient = class {
|
|
|
1256
1283
|
* @param apiKey - The API key for the retrievable account
|
|
1257
1284
|
*/
|
|
1258
1285
|
async retrievePassword(apiKey) {
|
|
1259
|
-
|
|
1286
|
+
const body = await this.#a2aRequest(apiKey, "GET", "Credentials", { type: "Password" });
|
|
1287
|
+
return new SecretValue(JSON.parse(body));
|
|
1260
1288
|
}
|
|
1261
1289
|
/**
|
|
1262
1290
|
* Retrieve an SSH private key credential via A2A.
|
|
@@ -1265,10 +1293,11 @@ var A2AClient = class {
|
|
|
1265
1293
|
*/
|
|
1266
1294
|
async retrievePrivateKey(apiKey, format) {
|
|
1267
1295
|
const keyType = format ?? "OpenSsh";
|
|
1268
|
-
|
|
1269
|
-
type: "
|
|
1296
|
+
const body = await this.#a2aRequest(apiKey, "GET", "Credentials", {
|
|
1297
|
+
type: "PrivateKey",
|
|
1270
1298
|
keyFormat: keyType
|
|
1271
|
-
})
|
|
1299
|
+
});
|
|
1300
|
+
return new SecretValue(JSON.parse(body));
|
|
1272
1301
|
}
|
|
1273
1302
|
/**
|
|
1274
1303
|
* Retrieve an API key secret credential via A2A.
|
|
@@ -1279,16 +1308,32 @@ var A2AClient = class {
|
|
|
1279
1308
|
}
|
|
1280
1309
|
/**
|
|
1281
1310
|
* Get the list of retrievable accounts available to this certificate.
|
|
1311
|
+
*
|
|
1312
|
+
* Retrievable accounts live under the Core service and are authorized by the
|
|
1313
|
+
* client certificate alone (no per-account API key). Enumerate every A2A
|
|
1314
|
+
* registration bound to the certificate, then collect each registration's
|
|
1315
|
+
* retrievable accounts.
|
|
1282
1316
|
*/
|
|
1283
1317
|
async getRetrievableAccounts() {
|
|
1284
1318
|
this.#ensureHttpClient();
|
|
1285
|
-
const
|
|
1286
|
-
url: `https://${this.#host}/service/
|
|
1319
|
+
const regResponse = await this.#httpClient.request({
|
|
1320
|
+
url: `https://${this.#host}/service/core/${this.#apiVersion}/A2ARegistrations`,
|
|
1287
1321
|
method: "GET",
|
|
1288
1322
|
headers: { Accept: "application/json" }
|
|
1289
1323
|
});
|
|
1290
|
-
if (
|
|
1291
|
-
|
|
1324
|
+
if (regResponse.status !== 200) throw ApiError.fromResponse(regResponse.status, regResponse.body);
|
|
1325
|
+
const registrations = JSON.parse(regResponse.body);
|
|
1326
|
+
const accounts = [];
|
|
1327
|
+
for (const reg of registrations) {
|
|
1328
|
+
const acctResponse = await this.#httpClient.request({
|
|
1329
|
+
url: `https://${this.#host}/service/core/${this.#apiVersion}/A2ARegistrations/${reg.Id}/RetrievableAccounts`,
|
|
1330
|
+
method: "GET",
|
|
1331
|
+
headers: { Accept: "application/json" }
|
|
1332
|
+
});
|
|
1333
|
+
if (acctResponse.status !== 200) throw ApiError.fromResponse(acctResponse.status, acctResponse.body);
|
|
1334
|
+
accounts.push(...JSON.parse(acctResponse.body));
|
|
1335
|
+
}
|
|
1336
|
+
return accounts;
|
|
1292
1337
|
}
|
|
1293
1338
|
/**
|
|
1294
1339
|
* Set a password on a managed account via A2A.
|
|
@@ -1298,7 +1343,7 @@ var A2AClient = class {
|
|
|
1298
1343
|
async setPassword(apiKey, password) {
|
|
1299
1344
|
this.#ensureHttpClient();
|
|
1300
1345
|
const response = await this.#httpClient.request({
|
|
1301
|
-
url: `https://${this.#host}/service/a2a/${this.#apiVersion}/Credentials
|
|
1346
|
+
url: `https://${this.#host}/service/a2a/${this.#apiVersion}/Credentials/Password`,
|
|
1302
1347
|
method: "PUT",
|
|
1303
1348
|
headers: {
|
|
1304
1349
|
Authorization: `A2A ${apiKey}`,
|
|
@@ -1318,13 +1363,11 @@ var A2AClient = class {
|
|
|
1318
1363
|
*/
|
|
1319
1364
|
async setPrivateKey(apiKey, key, passphrase, format) {
|
|
1320
1365
|
this.#ensureHttpClient();
|
|
1321
|
-
const
|
|
1322
|
-
|
|
1323
|
-
KeyFormat: format ?? "OpenSsh"
|
|
1324
|
-
};
|
|
1366
|
+
const keyFormat = format ?? "OpenSsh";
|
|
1367
|
+
const payload = { PrivateKey: key };
|
|
1325
1368
|
if (passphrase) payload["Passphrase"] = passphrase;
|
|
1326
1369
|
const response = await this.#httpClient.request({
|
|
1327
|
-
url: `https://${this.#host}/service/a2a/${this.#apiVersion}/Credentials?
|
|
1370
|
+
url: `https://${this.#host}/service/a2a/${this.#apiVersion}/Credentials/SshKey?keyFormat=${keyFormat}`,
|
|
1328
1371
|
method: "PUT",
|
|
1329
1372
|
headers: {
|
|
1330
1373
|
Authorization: `A2A ${apiKey}`,
|