@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 CHANGED
@@ -241,14 +241,18 @@ await client.delete(Service.CORE, `Users/${newUser.Id}`);
241
241
  ## A2A (Application to Application)
242
242
 
243
243
  ```typescript
244
- import { A2AClient, CertificateAuth } from '@oneidentity/safeguard';
244
+ import { A2AClient, CertificateAuth, NodeHttpClient } from '@oneidentity/safeguard';
245
245
 
246
- const a2a = new A2AClient('safeguard.sample.corp', {
247
- auth: new CertificateAuth({
248
- certFile: 'client.pem',
249
- keyFile: 'client.key',
250
- }),
246
+ const auth = new CertificateAuth({
247
+ certFile: 'client.pem',
248
+ keyFile: 'client.key',
251
249
  });
250
+ const a2a = new A2AClient('safeguard.sample.corp', { auth });
251
+
252
+ // A2A requires a client-cert HttpClient — it has no default and throws without one.
253
+ // On SPP 9.0 this also auto-caps the connection at TLS 1.2 so certificate auth
254
+ // works out of the box (see "TLS 1.3 and SPP 9.0").
255
+ a2a.setHttpClient(new NodeHttpClient(auth.getTlsOptions()));
252
256
 
253
257
  // Retrieve a password
254
258
  const password = await a2a.retrievePassword(apiKey);
@@ -433,6 +437,133 @@ would be surprising. But you should be aware that:
433
437
  If you find `NODE_TLS_REJECT_UNAUTHORIZED=0` in a deployment environment,
434
438
  treat it as a finding to remediate, not as a working configuration.
435
439
 
440
+ ### TLS 1.3 and SPP 9.0
441
+
442
+ Starting with **SPP 9.0**, the appliance enables **TLS 1.3**. For
443
+ password/token authentication there is **nothing to change** — connections
444
+ negotiate TLS 1.3 automatically. Certificate-based auth needs a little care on
445
+ Node, described below. safeguard.js handles the common case for you by default.
446
+
447
+ #### The Node limitation: no client-side post-handshake auth
448
+
449
+ TLS 1.3 moves client-certificate authentication to a **post-handshake**
450
+ exchange (RFC 8446 §4.6.2): the server sends a `CertificateRequest` *after* the
451
+ handshake and the client must answer it. **Node.js does not implement
452
+ client-side post-handshake authentication** — the upstream request
453
+ ([nodejs/node#46120](https://github.com/nodejs/node/issues/46120)) was closed
454
+ `NOT_PLANNED`, and `tls.connect` exposes no toggle for it. Over a plain TLS 1.3
455
+ connection the client therefore never presents its certificate, and certificate
456
+ / A2A auth fails against SPP 9.0 with `60094 Authorization is denied`.
457
+
458
+ > This differs from PySafeguard, where Python's `ssl` module *can* answer the
459
+ > post-handshake request (`post_handshake_auth = True`). No equivalent exists in
460
+ > Node, so safeguard.js uses TLS-version control instead.
461
+
462
+ #### What safeguard.js does by default
463
+
464
+ `NodeHttpClient` accepts opt-in `minVersion` / `maxVersion` TLS pins. When you
465
+ build a client for certificate/A2A auth (i.e. `TlsOptions` carries a
466
+ `cert`/`key` or `pfx`) and you have **not** pinned a version, the connection is
467
+ **automatically capped at TLS 1.2**. That keeps the certificate request
468
+ *in-handshake*, so certificate and A2A auth work out of the box on SPP 9.0's
469
+ Standard binding:
470
+
471
+ ```typescript
472
+ import { SafeguardClient, CertificateAuth, NodeHttpClient } from '@oneidentity/safeguard';
473
+
474
+ const auth = new CertificateAuth({ certFile: './client.pem', keyFile: './client.key' });
475
+ const client = new SafeguardClient('safeguard.corp.example', { auth });
476
+
477
+ // cert present + no version pin ⇒ NodeHttpClient caps at TLS 1.2 for you
478
+ client.setHttpClient(new NodeHttpClient(auth.getTlsOptions()));
479
+ await client.connect();
480
+ ```
481
+
482
+ Password/token connections carry no client certificate and continue to
483
+ negotiate up to TLS 1.3.
484
+
485
+ #### Reaching TLS 1.3 for certificate auth (Cert SNI)
486
+
487
+ The only way to do **certificate auth over TLS 1.3** from Node is the
488
+ appliance's **Cert SNI** hostname, which requests the client certificate
489
+ *during* the handshake (no post-handshake step). Target that hostname and pin
490
+ `minVersion: 'TLSv1.3'`; pinning a bound explicitly disables the auto-cap:
491
+
492
+ ```typescript
493
+ const client = new SafeguardClient('cert-sni.safeguard.corp.example', { auth });
494
+ client.setHttpClient(
495
+ new NodeHttpClient({ ...auth.getTlsOptions(), minVersion: 'TLSv1.3' }),
496
+ );
497
+ await client.connect();
498
+ ```
499
+
500
+ #### A2A on SPP 9.0
501
+
502
+ A2A retrieval and write-back authenticate with a **client certificate only** —
503
+ there is no password/token alternative — so on SPP 9.0's Standard binding A2A is
504
+ **always** subject to the same TLS 1.2 auto-cap as certificate auth. `A2AClient`
505
+ has no default HTTP client, so wire a cert-configured `NodeHttpClient` through
506
+ `setHttpClient` (the auto-cap and HTTP/1.1 pin both apply through it):
507
+
508
+ ```typescript
509
+ import { A2AClient, CertificateAuth, NodeHttpClient } from '@oneidentity/safeguard';
510
+
511
+ const auth = new CertificateAuth({ certFile: './client.pem', keyFile: './client.key' });
512
+ const a2a = new A2AClient('safeguard.corp.example', { auth });
513
+
514
+ // cert present + no version pin ⇒ NodeHttpClient caps at TLS 1.2 for you
515
+ a2a.setHttpClient(new NodeHttpClient(auth.getTlsOptions()));
516
+ const password = await a2a.retrievePassword(apiKey);
517
+ ```
518
+
519
+ To run A2A over **TLS 1.3**, use the appliance's Cert SNI hostname and pin
520
+ `minVersion: 'TLSv1.3'` (which disables the auto-cap), exactly as for
521
+ certificate auth:
522
+
523
+ ```typescript
524
+ const a2a = new A2AClient('cert-sni.safeguard.corp.example', { auth });
525
+ a2a.setHttpClient(
526
+ new NodeHttpClient({ ...auth.getTlsOptions(), minVersion: 'TLSv1.3' }),
527
+ );
528
+ const password = await a2a.retrievePassword(apiKey);
529
+ ```
530
+
531
+ #### Pinning the TLS version (opt-in)
532
+
533
+ `NodeHttpClient` accepts `minVersion` and `maxVersion` (`TlsVersion` =
534
+ `'TLSv1.3' | 'TLSv1.2' | 'TLSv1.1' | 'TLSv1'`, default unset = negotiate
535
+ normally):
536
+
537
+ ```typescript
538
+ // Require TLS 1.3 (e.g. to enforce it against SPP 9.0)
539
+ new NodeHttpClient({ minVersion: 'TLSv1.3' });
540
+
541
+ // Interim: cap the connection at TLS 1.2
542
+ new NodeHttpClient({ maxVersion: 'TLSv1.2' });
543
+ ```
544
+
545
+ The pins govern the client's request transport (all API, token, and A2A
546
+ traffic). Leaving them unset lets Node negotiate the highest mutually supported
547
+ version — the recommended default for password/token auth.
548
+
549
+ #### Node/JS gotchas
550
+
551
+ - **No client post-handshake auth.** There is no way to present a client
552
+ certificate on a TLS 1.3 connection whose cert request is post-handshake. Use
553
+ the TLS 1.2 default or the Cert SNI hostname — those are the only two options.
554
+ - **Keep HTTP/1.1 for certificate auth — do not enable HTTP/2.** The
555
+ post-handshake `CertificateRequest` is disallowed under HTTP/2, and the
556
+ appliance's Standard binding rejects certificate auth over HTTP/2 with
557
+ `HTTP_1_1_REQUIRED`. undici offers HTTP/2 via ALPN by default, so
558
+ `NodeHttpClient` automatically pins HTTP/1.1 (`allowH2: false`) whenever a
559
+ client certificate is present; password/token connections keep HTTP/2. Do not
560
+ force `allowH2` on a certificate connection.
561
+ - **The auto-cap only triggers when a client cert is present and no bound is
562
+ pinned.** Setting `minVersion` *or* `maxVersion` yourself puts you fully in
563
+ control and disables the TLS 1.2 default.
564
+ - **Use the `TlsVersion` strings**, e.g. `'TLSv1.3'`, not Node's numeric
565
+ constants.
566
+
436
567
  ### Host Validation
437
568
 
438
569
  The `SafeguardClient` constructor validates the `host` parameter to prevent
@@ -1 +1 @@
1
- {"version":3,"file":"browser.d.ts","names":[],"sources":["../src/http/types.ts","../src/storage/types.ts","../src/secret.ts","../src/auth/types.ts","../src/types.ts","../src/errors.ts","../src/utils.ts","../src/storage/memory.ts","../src/storage/session.ts","../src/http/browser.ts","../src/auth/password.ts","../src/auth/pkce.ts","../src/auth/device-code.ts","../src/auth/pkce-callback.ts","../src/auth/token.ts","../src/auth/anonymous.ts","../src/client.ts"],"mappings":"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"}
1
+ {"version":3,"file":"browser.d.ts","names":[],"sources":["../src/http/types.ts","../src/storage/types.ts","../src/secret.ts","../src/auth/types.ts","../src/types.ts","../src/errors.ts","../src/utils.ts","../src/storage/memory.ts","../src/storage/session.ts","../src/http/browser.ts","../src/auth/password.ts","../src/auth/pkce.ts","../src/auth/device-code.ts","../src/auth/pkce-callback.ts","../src/auth/token.ts","../src/auth/anonymous.ts","../src/client.ts"],"mappings":"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"}
package/dist/browser.js CHANGED
@@ -216,7 +216,7 @@ function base64UrlEncode(bytes) {
216
216
  * Returns a 43-character base64url string (32 random bytes).
217
217
  */
218
218
  function generateCodeVerifier() {
219
- const bytes = new Uint8Array(32);
219
+ const bytes = /* @__PURE__ */ new Uint8Array(32);
220
220
  crypto.getRandomValues(bytes);
221
221
  return base64UrlEncode(bytes);
222
222
  }
@@ -233,7 +233,7 @@ async function generateCodeChallenge(verifier) {
233
233
  * Generates a random state parameter for OAuth flows.
234
234
  */
235
235
  function generateState() {
236
- const bytes = new Uint8Array(16);
236
+ const bytes = /* @__PURE__ */ new Uint8Array(16);
237
237
  crypto.getRandomValues(bytes);
238
238
  return base64UrlEncode(bytes);
239
239
  }
@@ -1 +1 @@
1
- {"version":3,"file":"browser.js","names":["#value","#store","#credentials","#doFetch","#username","#password","#provider","#getRstsToken","#exchangeForUserToken","#redirectUri","#provider","#exchangeCode","#initiateRedirect","#onDeviceCode","#provider","#clientId","#pollingIntervalSeconds","#signal","#requestDeviceCode","#poll","#exchangeForUserToken","#accessToken","#host","#auth","#apiVersion","#timeout","#autoRefresh","#ca","#verify","#storage","#connected","#httpClient","#tokenSet","#ensureValidToken","#tokenRefreshPromise"],"sources":["../src/types.ts","../src/errors.ts","../src/secret.ts","../src/utils.ts","../src/storage/memory.ts","../src/storage/session.ts","../src/storage/types.ts","../src/http/browser.ts","../src/auth/password.ts","../src/auth/pkce.ts","../src/auth/device-code.ts","../src/auth/pkce-callback.ts","../src/auth/token.ts","../src/auth/anonymous.ts","../src/client.ts"],"sourcesContent":["/**\n * Safeguard appliance service endpoints.\n */\nexport enum Service {\n CORE = 'core',\n APPLIANCE = 'appliance',\n NOTIFICATION = 'notification',\n A2A = 'a2a',\n EVENT = 'event',\n RSTS = 'RSTS',\n}\n\n/**\n * HTTP methods supported by the Safeguard API.\n */\nexport enum HttpMethod {\n GET = 'GET',\n POST = 'POST',\n PUT = 'PUT',\n DELETE = 'DELETE',\n PATCH = 'PATCH',\n}\n\n/**\n * Full response from a Safeguard API call (returned when fullResponse: true).\n */\nexport interface SafeguardResponse<T = unknown> {\n data: T;\n status: number;\n headers: Record<string, string>;\n}\n\n/**\n * Options for individual API requests.\n */\nexport interface RequestOptions {\n /** Request body as JSON-serializable object. */\n json?: unknown;\n /** Raw request body. */\n body?: BodyInit | null;\n /** Additional HTTP headers. */\n headers?: Record<string, string>;\n /** URL query parameters. */\n query?: Record<string, string | number | boolean>;\n /** AbortSignal for request cancellation. */\n signal?: AbortSignal;\n /** Request timeout in milliseconds (overrides client default). */\n timeout?: number;\n /** If true, returns full response with status and headers. */\n fullResponse?: boolean;\n}\n\n/**\n * Options for constructing a SafeguardClient.\n */\nexport interface SafeguardClientOptions {\n /** Authentication strategy. */\n auth: import('./auth/types.js').Auth;\n /** Custom CA certificate (PEM string or Buffer). Node.js only. */\n ca?: string | Buffer;\n /** Whether to verify TLS certificates. Default: true. */\n verify?: boolean;\n /** Safeguard API version prefix. Default: 'v4'. */\n apiVersion?: string;\n /** Request timeout in milliseconds. Default: 300_000 (5 min). */\n timeout?: number;\n /** Auto-refresh tokens before expiry. Default: true. */\n autoRefresh?: boolean;\n}\n","/**\n * Error hierarchy for the Safeguard SDK.\n *\n * SafeguardError\n * ├── ApiError (HTTP errors from Safeguard API)\n * │ ├── AuthenticationError (401)\n * │ ├── AuthorizationError (403)\n * │ └── NotFoundError (404)\n * ├── TransportError (Network/connection failures)\n * └── ConfigurationError (Invalid client options)\n */\n\nexport class SafeguardError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'SafeguardError';\n }\n}\n\nexport interface ApiErrorBody {\n Code?: string;\n Message?: string;\n InnerError?: { Message?: string };\n [key: string]: unknown;\n}\n\nexport class ApiError extends SafeguardError {\n readonly status: number;\n readonly body: ApiErrorBody | null;\n\n constructor(message: string, status: number, body: ApiErrorBody | null = null, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ApiError';\n this.status = status;\n this.body = body;\n }\n\n /**\n * Creates an appropriate ApiError subclass based on HTTP status code.\n */\n static fromResponse(status: number, body: string | ApiErrorBody | null): ApiError {\n const parsed = ApiError.parseBody(body);\n const message = ApiError.buildMessage(status, parsed);\n\n switch (status) {\n case 401:\n return new AuthenticationError(message, parsed);\n case 403:\n return new AuthorizationError(message, parsed);\n case 404:\n return new NotFoundError(message, parsed);\n default:\n return new ApiError(message, status, parsed);\n }\n }\n\n private static parseBody(body: string | ApiErrorBody | null): ApiErrorBody | null {\n if (body === null || body === undefined) return null;\n if (typeof body === 'object') return body;\n try {\n return JSON.parse(body) as ApiErrorBody;\n } catch {\n return { Message: body };\n }\n }\n\n private static buildMessage(status: number, body: ApiErrorBody | null): string {\n const apiMessage = body?.Message ?? body?.InnerError?.Message;\n if (apiMessage) {\n return `HTTP ${String(status)}: ${apiMessage}`;\n }\n return `HTTP ${String(status)}`;\n }\n}\n\nexport class AuthenticationError extends ApiError {\n constructor(message: string, body: ApiErrorBody | null = null, options?: ErrorOptions) {\n super(message, 401, body, options);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class AuthorizationError extends ApiError {\n constructor(message: string, body: ApiErrorBody | null = null, options?: ErrorOptions) {\n super(message, 403, body, options);\n this.name = 'AuthorizationError';\n }\n}\n\nexport class NotFoundError extends ApiError {\n constructor(message: string, body: ApiErrorBody | null = null, options?: ErrorOptions) {\n super(message, 404, body, options);\n this.name = 'NotFoundError';\n }\n}\n\nexport class TransportError extends SafeguardError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'TransportError';\n }\n}\n\nexport class ConfigurationError extends SafeguardError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ConfigurationError';\n }\n}\n","const REDACTED = '[REDACTED]';\n\n/**\n * A wrapper that prevents accidental logging of sensitive values.\n *\n * - `toString()` returns \"[REDACTED]\"\n * - `toJSON()` returns \"[REDACTED]\"\n * - `console.log(secretValue)` prints \"[REDACTED]\"\n * - `.expose()` returns the raw value when intentionally needed\n */\nexport class SecretValue {\n readonly #value: string;\n\n constructor(value: string) {\n this.#value = value;\n }\n\n /** Returns the raw secret. Use only when you intentionally need the plaintext. */\n expose(): string {\n return this.#value;\n }\n\n toString(): string {\n return REDACTED;\n }\n\n toJSON(): string {\n return REDACTED;\n }\n\n /** Node.js custom inspect (prevents leaking in console.log / util.inspect). */\n [Symbol.for('nodejs.util.inspect.custom')](): string {\n return REDACTED;\n }\n\n get [Symbol.toStringTag](): string {\n return 'SecretValue';\n }\n}\n","import { Service } from './types.js';\nimport { ConfigurationError } from './errors.js';\n\n/**\n * Validates that a host string is a bare hostname or IP address.\n * Rejects URLs, paths, query strings, userinfo, and embedded ports.\n *\n * @throws {ConfigurationError} if the host contains disallowed characters\n */\nexport function validateHost(host: string): string {\n if (!host) throw new ConfigurationError('Host must not be empty');\n\n const trimmed = host.trim();\n if (trimmed !== host) {\n throw new ConfigurationError('Host must not contain leading or trailing whitespace');\n }\n\n // Reject protocol prefixes\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(host)) {\n throw new ConfigurationError(\n `Host must be a bare hostname or IP, not a URL. Remove the protocol prefix (e.g., use \"${host.replace(/^[a-z][a-z0-9+.-]*:\\/\\//i, '')}\")`,\n );\n }\n\n // Reject path, query, fragment, userinfo characters\n if (/[/?#@]/.test(host)) {\n throw new ConfigurationError(\n 'Host must be a bare hostname or IP address — must not contain /, ?, #, or @',\n );\n }\n\n // Reject port suffix (e.g. \"host:8443\") — SDK always uses HTTPS/443\n if (/:\\d+$/.test(host) && !host.startsWith('[')) {\n throw new ConfigurationError(\n 'Host must not include a port number. The SDK always connects over HTTPS (port 443).',\n );\n }\n\n return host;\n}\n\n/**\n * Builds the base URL for a Safeguard service endpoint.\n *\n * @example\n * buildServiceUrl('appliance.example.com', Service.CORE)\n * // => 'https://appliance.example.com/service/core/v4'\n */\nexport function buildServiceUrl(host: string, service: Service, apiVersion: string = 'v4'): string {\n if (service === Service.RSTS) {\n return `https://${host}/RSTS`;\n }\n return `https://${host}/service/${service}/${apiVersion}`;\n}\n\n/**\n * Builds a full API URL including path and optional query parameters.\n *\n * @example\n * buildRequestUrl('appliance.example.com', Service.CORE, 'Users', { filter: 'Name eq \"admin\"' })\n * // => 'https://appliance.example.com/service/core/v4/Users?filter=Name+eq+%22admin%22'\n */\nexport function buildRequestUrl(\n host: string,\n service: Service,\n relativeUrl: string,\n query?: Record<string, string | number | boolean>,\n apiVersion: string = 'v4',\n): string {\n const base = buildServiceUrl(host, service, apiVersion);\n const path = relativeUrl.startsWith('/') ? relativeUrl.slice(1) : relativeUrl;\n const url = new URL(`${base}/${path}`);\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n url.searchParams.set(key, String(value));\n }\n }\n\n return url.toString();\n}\n\n/**\n * Encodes bytes to a URL-safe base64 string (no padding).\n */\nexport function base64UrlEncode(bytes: Uint8Array): string {\n const binary = String.fromCharCode(...bytes);\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/**\n * Generates a cryptographically random code verifier for PKCE.\n * Returns a 43-character base64url string (32 random bytes).\n */\nexport function generateCodeVerifier(): string {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n\n/**\n * Computes the S256 code challenge from a code verifier.\n * Uses Web Crypto API (available in Node 20+ and all modern browsers).\n */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const digest = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n\n/**\n * Generates a random state parameter for OAuth flows.\n */\nexport function generateState(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n\n/**\n * Extracts the `exp` claim from a JWT and returns expiresIn (seconds from now).\n * Returns undefined if the token is not a valid JWT or has no `exp` claim.\n */\nexport function getTokenExpiresIn(jwt: string): number | undefined {\n try {\n const parts = jwt.split('.');\n if (parts.length !== 3) return undefined;\n // Base64url decode the payload\n const payload = parts[1]!.replace(/-/g, '+').replace(/_/g, '/');\n const json = atob(payload);\n const claims = JSON.parse(json) as { exp?: number };\n if (typeof claims.exp !== 'number') return undefined;\n const now = Math.floor(Date.now() / 1000);\n return Math.max(0, claims.exp - now);\n } catch {\n return undefined;\n }\n}\n","import type { StorageProvider } from './types.js';\n\n/**\n * In-memory storage provider. Default for Node.js environments.\n * Data does not persist across process restarts.\n */\nexport class MemoryStorage implements StorageProvider {\n readonly #store = new Map<string, string>();\n\n get(key: string): string | null {\n return this.#store.get(key) ?? null;\n }\n\n set(key: string, value: string): void {\n this.#store.set(key, value);\n }\n\n remove(key: string): void {\n this.#store.delete(key);\n }\n\n clear(): void {\n this.#store.clear();\n }\n}\n","import type { StorageProvider } from './types.js';\n\n/**\n * Browser sessionStorage adapter. Data persists for the browser tab lifetime.\n * Requires a DOM environment (browser or jsdom).\n */\nexport class BrowserSessionStorage implements StorageProvider {\n get(key: string): string | null {\n return sessionStorage.getItem(key);\n }\n\n set(key: string, value: string): void {\n sessionStorage.setItem(key, value);\n }\n\n remove(key: string): void {\n sessionStorage.removeItem(key);\n }\n\n clear(): void {\n sessionStorage.clear();\n }\n}\n","/**\n * Interface for token/state storage backends.\n */\nexport interface StorageProvider {\n get(key: string): string | null;\n set(key: string, value: string): void;\n remove(key: string): void;\n clear(): void;\n}\n\n/** Well-known storage keys used by the SDK. */\nexport const StorageKeys = {\n STATE: 'safeguard_state',\n CODE_VERIFIER: 'safeguard_code_verifier',\n} as const;\n","import type { HttpClient, HttpRequestOptions, HttpResponse } from './types.js';\n\n/**\n * Browser HTTP client using native fetch API.\n * No TLS configuration needed — browser handles certificates.\n */\nexport class BrowserHttpClient implements HttpClient {\n readonly #credentials: RequestCredentials;\n\n constructor(options?: { credentials?: RequestCredentials }) {\n this.#credentials = options?.credentials ?? 'include';\n }\n\n async request(options: HttpRequestOptions): Promise<HttpResponse> {\n const { url, method, headers, body, signal, timeout } = options;\n\n const requestInit: RequestInit = {\n method,\n credentials: this.#credentials,\n };\n\n if (headers) requestInit.headers = headers;\n if (body != null) requestInit.body = body as BodyInit;\n if (signal || timeout) {\n const controller = new AbortController();\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if (timeout) {\n timeoutId = setTimeout(() => controller.abort(new Error('Request timed out')), timeout);\n }\n\n requestInit.signal = signal\n ? AbortSignal.any([signal, controller.signal])\n : controller.signal;\n\n try {\n return await this.#doFetch(url, requestInit);\n } finally {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n }\n }\n\n return this.#doFetch(url, requestInit);\n }\n\n async #doFetch(url: string, init: RequestInit): Promise<HttpResponse> {\n const response = await fetch(url, init);\n\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n const responseBody = await response.text();\n\n return {\n status: response.status,\n headers: responseHeaders,\n body: responseBody,\n };\n }\n}\n","import type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type { Auth, TokenSet } from './types.js';\nimport { ConfigurationError } from '../errors.js';\nimport { SecretValue } from '../secret.js';\nimport { getTokenExpiresIn } from '../utils.js';\n\nexport interface PasswordAuthOptions {\n username: string;\n password: string;\n /** Identity provider name. Default: 'local'. */\n provider?: string;\n}\n\n/**\n * Authenticates using username/password via RSTS Resource Owner grant.\n */\nexport class PasswordAuth implements Auth {\n readonly #username: string;\n readonly #password: SecretValue;\n readonly #provider: string;\n\n constructor(options: PasswordAuthOptions) {\n if (!options.username) throw new ConfigurationError('PasswordAuth requires username');\n if (!options.password) throw new ConfigurationError('PasswordAuth requires password');\n if (!options.provider) throw new ConfigurationError('PasswordAuth requires provider');\n this.#username = options.username;\n this.#password = new SecretValue(options.password);\n this.#provider = options.provider;\n }\n\n get description(): string {\n return `PasswordAuth(${this.#username}@${this.#provider})`;\n }\n\n async authenticate(host: string, httpClient: HttpClient, _storage: StorageProvider): Promise<TokenSet> {\n // Step 1: Get RSTS token\n const rstsToken = await this.#getRstsToken(host, httpClient);\n\n // Step 2: Exchange RSTS token for Safeguard user token\n const userToken = await this.#exchangeForUserToken(host, httpClient, rstsToken);\n\n return userToken;\n }\n\n async refreshToken(host: string, httpClient: HttpClient, storage: StorageProvider): Promise<TokenSet | null> {\n // Password auth can always re-authenticate\n return this.authenticate(host, httpClient, storage);\n }\n\n async #getRstsToken(host: string, httpClient: HttpClient): Promise<string> {\n const body = JSON.stringify({\n grant_type: 'password',\n username: this.#username,\n password: this.#password.expose(),\n scope: 'rsts:sts:primaryproviderid:' + this.#provider.toLowerCase(),\n });\n\n const response = await httpClient.request({\n url: `https://${host}/RSTS/oauth2/token`,\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n });\n\n if (response.status !== 200) {\n const { ApiError } = await import('../errors.js');\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n const data = JSON.parse(response.body) as { access_token: string };\n return data.access_token;\n }\n\n async #exchangeForUserToken(host: string, httpClient: HttpClient, rstsToken: string): Promise<TokenSet> {\n const response = await httpClient.request({\n url: `https://${host}/service/core/v4/Token/LoginResponse`,\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${rstsToken}`,\n },\n body: JSON.stringify({ StsAccessToken: rstsToken }),\n });\n\n if (response.status !== 200) {\n const { ApiError } = await import('../errors.js');\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n const data = JSON.parse(response.body) as { UserToken: string; ExpiresIn?: number };\n const tokenSet: TokenSet = {\n accessToken: new SecretValue(data.UserToken),\n acquiredAt: Date.now(),\n };\n if (data.ExpiresIn != null) {\n tokenSet.expiresIn = data.ExpiresIn;\n } else {\n const expiresIn = getTokenExpiresIn(data.UserToken);\n if (expiresIn != null) tokenSet.expiresIn = expiresIn;\n }\n return tokenSet;\n }\n}\n","import type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type { Auth, TokenSet } from './types.js';\nimport { ConfigurationError } from '../errors.js';\nimport { SecretValue } from '../secret.js';\nimport { StorageKeys } from '../storage/types.js';\nimport { generateCodeVerifier, generateCodeChallenge, generateState } from '../utils.js';\n\nexport interface PkceAuthOptions {\n /** OAuth redirect URI (must match RSTS configuration). */\n redirectUri: string;\n /** Identity provider name. Default: 'local'. */\n provider?: string;\n}\n\n/**\n * Authenticates using PKCE (Proof Key for Code Exchange) with browser redirect.\n * For browser environments where the user is redirected to RSTS login.\n */\nexport class PkceAuth implements Auth {\n readonly #redirectUri: string;\n readonly #provider: string;\n\n constructor(options: PkceAuthOptions) {\n if (!options.redirectUri) throw new ConfigurationError('PkceAuth requires redirectUri');\n if (!options.provider) throw new ConfigurationError('PkceAuth requires provider');\n this.#redirectUri = options.redirectUri;\n this.#provider = options.provider;\n }\n\n get description(): string {\n return `PKCE(redirect=${this.#redirectUri})`;\n }\n\n /**\n * Initiates the PKCE flow. If a stored authorization code exists (from callback),\n * exchanges it for a token. Otherwise, redirects to RSTS login.\n */\n async authenticate(host: string, httpClient: HttpClient, storage: StorageProvider): Promise<TokenSet> {\n // Check for authorization code (set by handlePkceCallback)\n const code = storage.get('safeguard_auth_code');\n if (code) {\n const verifier = storage.get(StorageKeys.CODE_VERIFIER);\n if (!verifier) {\n throw new ConfigurationError('PKCE code verifier not found in storage');\n }\n storage.remove('safeguard_auth_code');\n storage.remove(StorageKeys.CODE_VERIFIER);\n storage.remove(StorageKeys.STATE);\n return this.#exchangeCode(host, httpClient, code, verifier);\n }\n\n // No code — initiate redirect\n await this.#initiateRedirect(host, storage);\n\n // This point is never reached in a browser (redirect happens).\n // For testability, throw rather than hang.\n throw new ConfigurationError(\n 'PKCE redirect initiated. Call handlePkceCallback() on the callback page.',\n );\n }\n\n async #initiateRedirect(host: string, storage: StorageProvider): Promise<void> {\n const verifier = generateCodeVerifier();\n const challenge = await generateCodeChallenge(verifier);\n const state = generateState();\n\n storage.set(StorageKeys.CODE_VERIFIER, verifier);\n storage.set(StorageKeys.STATE, state);\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: 'safeguard.js',\n redirect_uri: this.#redirectUri,\n scope: 'rsts:sts:primaryproviderid:' + this.#provider,\n code_challenge: challenge,\n code_challenge_method: 'S256',\n state,\n });\n\n const authUrl = `https://${host}/RSTS/Login?${params.toString()}`;\n\n // In browser environment, redirect\n if (typeof window !== 'undefined' && window.location) {\n window.location.href = authUrl;\n }\n }\n\n async #exchangeCode(\n host: string,\n httpClient: HttpClient,\n code: string,\n verifier: string,\n ): Promise<TokenSet> {\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: this.#redirectUri,\n client_id: 'safeguard.js',\n code_verifier: verifier,\n });\n\n const response = await httpClient.request({\n url: `https://${host}/RSTS/oauth2/token`,\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (response.status !== 200) {\n const { ApiError } = await import('../errors.js');\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n const data = JSON.parse(response.body) as { access_token: string; expires_in?: number };\n\n // Exchange RSTS token for Safeguard user token\n const userTokenResponse = await httpClient.request({\n url: `https://${host}/service/core/v4/Token/LoginResponse`,\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${data.access_token}`,\n },\n body: JSON.stringify({ StsAccessToken: data.access_token }),\n });\n\n if (userTokenResponse.status !== 200) {\n const { ApiError } = await import('../errors.js');\n throw ApiError.fromResponse(userTokenResponse.status, userTokenResponse.body);\n }\n\n const userData = JSON.parse(userTokenResponse.body) as { UserToken: string; ExpiresIn?: number };\n const tokenSet: TokenSet = {\n accessToken: new SecretValue(userData.UserToken),\n acquiredAt: Date.now(),\n };\n if (userData.ExpiresIn != null) tokenSet.expiresIn = userData.ExpiresIn;\n\n // Token stays in-memory only (SafeguardClient.#tokenSet). Not persisted to\n // sessionStorage to avoid XSS exfiltration of high-privilege PAM tokens.\n return tokenSet;\n }\n}\n","import type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type { Auth, TokenSet } from './types.js';\nimport { ApiError, ConfigurationError, SafeguardError } from '../errors.js';\nimport { SecretValue } from '../secret.js';\n\n/**\n * Public device-code details handed to the caller for display.\n *\n * The raw `device_code` is intentionally NOT exposed — the library owns\n * polling and the caller only needs to render the verification URL and code.\n */\nexport interface DeviceCodeInfo {\n /** URL the user opens to authenticate (e.g. shown as a QR target or link). */\n verificationUri: string;\n /** Short code the user enters at the verification URL. */\n userCode: string;\n /** Verification URL with the user code pre-filled (RFC 8628 §3.3.1). */\n verificationUriComplete: string;\n /** Seconds until the device code expires. */\n expiresIn: number;\n /** Suggested minimum seconds between poll attempts. */\n interval: number;\n}\n\n/**\n * Options for {@link DeviceCodeAuth}.\n */\nexport interface DeviceCodeAuthOptions {\n /**\n * REQUIRED callback that receives the {@link DeviceCodeInfo} for display.\n * The library performs no display I/O; the caller renders the URL + code\n * (console, DOM, log). May be async; it is awaited before polling begins.\n */\n onDeviceCode: (info: DeviceCodeInfo) => void | Promise<void>;\n /** Identity provider name. Default: 'local'. */\n provider?: string;\n /** OAuth client id. Default: '' (matches other Safeguard SDKs). */\n clientId?: string;\n /** Starting poll interval in seconds. Default: 5. Auto-bumps on `slow_down`. */\n pollingIntervalSeconds?: number;\n /** Cancellation signal applied to HTTP requests and inter-poll delays. */\n signal?: AbortSignal;\n}\n\n/** Number of seconds to add to the poll interval on each `slow_down`. */\nconst SLOW_DOWN_INCREMENT_SECONDS = 5;\n\ninterface DeviceLoginResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n expires_in: number;\n interval: number;\n}\n\ninterface TokenPollResponse {\n access_token?: string;\n error?: string;\n}\n\n/** Error whose `name` is `AbortError`, matching standard web cancellation. */\nclass AbortError extends Error {\n constructor(message = 'The operation was aborted') {\n super(message);\n this.name = 'AbortError';\n }\n}\n\n/** Reject promptly if the signal is already aborted. */\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) throw new AbortError();\n}\n\n/** Abortable sleep that rejects with an `AbortError` if the signal fires. */\nfunction delay(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(new AbortError());\n return;\n }\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(new AbortError());\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/**\n * OAuth 2.0 Device Authorization Grant (RFC 8628) for Safeguard.\n *\n * Headless display-and-poll only: it requests a device code, hands the public\n * verification details to the required `onDeviceCode` callback, then polls the\n * token endpoint until the user authenticates in their own browser. It never\n * spawns or opens a browser and performs no display I/O. Platform-agnostic, so\n * it is exported from both the Node and browser entry points.\n */\nexport class DeviceCodeAuth implements Auth {\n readonly #onDeviceCode: (info: DeviceCodeInfo) => void | Promise<void>;\n readonly #provider: string;\n readonly #clientId: string;\n readonly #pollingIntervalSeconds: number;\n readonly #signal: AbortSignal | undefined;\n\n constructor(options: DeviceCodeAuthOptions) {\n if (typeof options?.onDeviceCode !== 'function') {\n throw new ConfigurationError('DeviceCodeAuth requires an onDeviceCode callback');\n }\n if (options.pollingIntervalSeconds !== undefined && options.pollingIntervalSeconds <= 0) {\n throw new ConfigurationError('pollingIntervalSeconds must be greater than 0');\n }\n this.#onDeviceCode = options.onDeviceCode;\n this.#provider = options.provider ?? 'local';\n this.#clientId = options.clientId ?? '';\n this.#pollingIntervalSeconds = options.pollingIntervalSeconds ?? 5;\n this.#signal = options.signal;\n }\n\n get description(): string {\n return 'DeviceCodeAuth';\n }\n\n async authenticate(\n host: string,\n httpClient: HttpClient,\n _storage: StorageProvider,\n ): Promise<TokenSet> {\n const signal = this.#signal;\n throwIfAborted(signal);\n\n const deviceCode = await this.#requestDeviceCode(host, httpClient, signal);\n\n const info: DeviceCodeInfo = {\n verificationUri: deviceCode.verification_uri,\n userCode: deviceCode.user_code,\n verificationUriComplete: deviceCode.verification_uri_complete,\n expiresIn: deviceCode.expires_in,\n interval: deviceCode.interval,\n };\n await this.#onDeviceCode(info);\n\n const accessToken = await this.#poll(host, httpClient, deviceCode, signal);\n return this.#exchangeForUserToken(host, httpClient, accessToken, signal);\n }\n\n async #requestDeviceCode(\n host: string,\n httpClient: HttpClient,\n signal: AbortSignal | undefined,\n ): Promise<DeviceLoginResponse> {\n const requestOptions = {\n url: `https://${host}/RSTS/oauth2/DeviceLogin`,\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n client_id: this.#clientId,\n scope: `rsts:sts:primaryproviderid:${this.#provider}`,\n }),\n ...(signal ? { signal } : {}),\n };\n const response = await httpClient.request(requestOptions);\n\n if (response.status !== 200) {\n // The disabled-grant response is HTML, not JSON — substring match only,\n // never JSON.parse this path.\n if (response.body.toLowerCase().includes('device code grant type is not allowed')) {\n throw new ConfigurationError(\n 'Device Code grant is not enabled on this appliance. Enable it under ' +\n 'Settings -> OAuth 2.0 Grant Types (Allowed OAuth2 Grant Types must include DeviceCode).',\n );\n }\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n return JSON.parse(response.body) as DeviceLoginResponse;\n }\n\n async #poll(\n host: string,\n httpClient: HttpClient,\n deviceCode: DeviceLoginResponse,\n signal: AbortSignal | undefined,\n ): Promise<string> {\n const deadline = Date.now() + deviceCode.expires_in * 1000;\n let intervalSeconds = this.#pollingIntervalSeconds;\n\n for (;;) {\n throwIfAborted(signal);\n if (Date.now() >= deadline) {\n throw new SafeguardError('Device code authorization timed out before approval');\n }\n\n await delay(intervalSeconds * 1000, signal);\n throwIfAborted(signal);\n\n const response = await httpClient.request({\n url: `https://${host}/RSTS/oauth2/token`,\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode.device_code,\n client_id: this.#clientId,\n }),\n ...(signal ? { signal } : {}),\n });\n\n const data = JSON.parse(response.body) as TokenPollResponse;\n\n if (response.status === 200 && data.access_token) {\n return data.access_token;\n }\n\n switch (data.error) {\n case 'authorization_pending':\n continue;\n case 'slow_down':\n intervalSeconds += SLOW_DOWN_INCREMENT_SECONDS;\n continue;\n case 'access_denied':\n throw new SafeguardError('Device code authorization was denied by the user');\n case 'expired_token':\n throw new SafeguardError('Device code expired before authorization was completed');\n default:\n throw ApiError.fromResponse(response.status, response.body);\n }\n }\n }\n\n async #exchangeForUserToken(\n host: string,\n httpClient: HttpClient,\n stsAccessToken: string,\n signal: AbortSignal | undefined,\n ): Promise<TokenSet> {\n const response = await httpClient.request({\n url: `https://${host}/service/core/v4/Token/LoginResponse`,\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${stsAccessToken}`,\n },\n body: JSON.stringify({ StsAccessToken: stsAccessToken }),\n ...(signal ? { signal } : {}),\n });\n\n if (response.status !== 200) {\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n const userData = JSON.parse(response.body) as { UserToken: string; ExpiresIn?: number };\n const tokenSet: TokenSet = {\n accessToken: new SecretValue(userData.UserToken),\n acquiredAt: Date.now(),\n };\n if (userData.ExpiresIn != null) tokenSet.expiresIn = userData.ExpiresIn;\n return tokenSet;\n }\n}\n","import { StorageKeys } from '../storage/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport { ConfigurationError } from '../errors.js';\n\nexport interface PkceCallbackResult {\n code: string;\n state: string;\n}\n\n/**\n * Parses the PKCE callback URL and stores the authorization code.\n * Call this on the callback/redirect page to complete the PKCE flow.\n *\n * This is the explicit replacement for the legacy `safeguardJsAccessTokenChecker.js`\n * script that ran as a side-effect on import.\n *\n * @param storage - StorageProvider to persist the code\n * @param url - The callback URL to parse (defaults to window.location.href)\n * @returns The parsed code and state, or null if not a valid callback\n */\nexport function handlePkceCallback(\n storage: StorageProvider,\n url?: string,\n): PkceCallbackResult | null {\n const callbackUrl = url ?? (typeof window !== 'undefined' ? window.location.href : '');\n if (!callbackUrl) return null;\n\n const parsed = new URL(callbackUrl);\n const code = parsed.searchParams.get('code');\n const state = parsed.searchParams.get('state');\n\n if (!code || !state) return null;\n\n // Validate state matches what we stored\n const storedState = storage.get(StorageKeys.STATE);\n if (storedState && storedState !== state) {\n throw new ConfigurationError('PKCE state mismatch — possible CSRF attack');\n }\n\n // Store the code for the PkceAuth strategy to pick up\n storage.set('safeguard_auth_code', code);\n\n return { code, state };\n}\n","import type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type { Auth, TokenSet } from './types.js';\nimport { ConfigurationError } from '../errors.js';\nimport { SecretValue } from '../secret.js';\n\nexport interface TokenAuthOptions {\n /** Pre-existing Safeguard access token. */\n accessToken: string;\n}\n\n/**\n * Uses a pre-existing access token. No authentication flow — the token is assumed valid.\n * Useful when tokens are obtained externally or for service-to-service scenarios.\n */\nexport class TokenAuth implements Auth {\n readonly #accessToken: SecretValue;\n\n constructor(options: TokenAuthOptions) {\n if (!options.accessToken) throw new ConfigurationError('TokenAuth requires accessToken');\n this.#accessToken = new SecretValue(options.accessToken);\n }\n\n get description(): string {\n return 'TokenAuth(pre-existing)';\n }\n\n async authenticate(_host: string, _httpClient: HttpClient, _storage: StorageProvider): Promise<TokenSet> {\n return {\n accessToken: this.#accessToken,\n acquiredAt: Date.now(),\n };\n }\n\n // No refresh possible with a pre-existing token\n}\n","import type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type { Auth, TokenSet } from './types.js';\nimport { SecretValue } from '../secret.js';\n\n/**\n * Anonymous authentication — no credentials.\n * Used for accessing public Safeguard endpoints (e.g., appliance status).\n */\nexport class AnonymousAuth implements Auth {\n get description(): string {\n return 'AnonymousAuth';\n }\n\n async authenticate(_host: string, _httpClient: HttpClient, _storage: StorageProvider): Promise<TokenSet> {\n return {\n accessToken: new SecretValue(''),\n acquiredAt: Date.now(),\n };\n }\n}\n","import { Service, HttpMethod } from './types.js';\nimport type { RequestOptions, SafeguardClientOptions } from './types.js';\nimport type { Auth, TokenSet } from './auth/types.js';\nimport type { HttpClient } from './http/types.js';\nimport type { StorageProvider } from './storage/types.js';\nimport { MemoryStorage } from './storage/memory.js';\nimport { buildRequestUrl, validateHost } from './utils.js';\nimport { AuthenticationError, ConfigurationError, ApiError } from './errors.js';\n\nconst DEFAULT_TIMEOUT = 300_000;\nconst DEFAULT_API_VERSION = 'v4';\nconst TOKEN_REFRESH_MARGIN_MS = 60_000; // refresh 60s before expiry\n\n/**\n * Main entry point for the Safeguard SDK.\n * Instance-based — no global state. Multiple clients to different appliances are supported.\n */\nexport class SafeguardClient {\n readonly #host: string;\n readonly #auth: Auth;\n readonly #apiVersion: string;\n readonly #timeout: number;\n readonly #autoRefresh: boolean;\n readonly #ca: string | Buffer | undefined;\n readonly #verify: boolean;\n\n #httpClient: HttpClient | undefined;\n #storage: StorageProvider;\n #tokenSet: TokenSet | undefined;\n #connected = false;\n /** Single-flight refresh promise to prevent thundering herd on token expiry. */\n #tokenRefreshPromise: Promise<void> | undefined;\n\n constructor(host: string, options: SafeguardClientOptions) {\n if (!host) throw new ConfigurationError('SafeguardClient requires a host');\n if (!options.auth) throw new ConfigurationError('SafeguardClient requires an auth strategy');\n this.#host = validateHost(host);\n this.#auth = options.auth;\n this.#apiVersion = options.apiVersion ?? DEFAULT_API_VERSION;\n this.#timeout = options.timeout ?? DEFAULT_TIMEOUT;\n this.#autoRefresh = options.autoRefresh !== false;\n this.#ca = options.ca;\n this.#verify = options.verify !== false;\n this.#storage = new MemoryStorage();\n }\n\n /** The appliance hostname this client connects to. */\n get host(): string {\n return this.#host;\n }\n\n /** The API version prefix (e.g. 'v4'). */\n get apiVersion(): string {\n return this.#apiVersion;\n }\n\n /** Custom CA certificate, if provided. */\n get ca(): string | Buffer | undefined {\n return this.#ca;\n }\n\n /** Whether TLS verification is enabled. */\n get verify(): boolean {\n return this.#verify;\n }\n\n /** Whether the client has an active authenticated session. */\n get isConnected(): boolean {\n return this.#connected;\n }\n\n /**\n * Sets a custom StorageProvider (e.g., BrowserSessionStorage).\n * Must be called before connect().\n */\n setStorage(storage: StorageProvider): this {\n this.#storage = storage;\n return this;\n }\n\n /**\n * Sets the HttpClient implementation. If not called, must be set before connect().\n * The factory pattern allows platform-specific client injection:\n * - Node: `new NodeHttpClient({ ca, rejectUnauthorized })`\n * - Browser: `new BrowserHttpClient()`\n */\n setHttpClient(httpClient: HttpClient): this {\n this.#httpClient = httpClient;\n return this;\n }\n\n /**\n * Authenticate and establish a session.\n * @throws {AuthenticationError} if authentication fails\n * @throws {ConfigurationError} if httpClient not set\n */\n async connect(): Promise<void> {\n if (!this.#httpClient) {\n throw new ConfigurationError(\n 'HttpClient not set. Call setHttpClient() before connect(), or use SafeguardClient.createNode() / SafeguardClient.createBrowser().',\n );\n }\n\n this.#tokenSet = await this.#auth.authenticate(this.#host, this.#httpClient, this.#storage);\n if (!this.#tokenSet.accessToken.expose()) {\n throw new AuthenticationError('Authentication returned empty access token');\n }\n this.#connected = true;\n }\n\n /**\n * Disconnect — dispose HTTP client and clear token.\n */\n async disconnect(): Promise<void> {\n this.#tokenSet = undefined;\n this.#connected = false;\n this.#httpClient?.dispose?.();\n }\n\n /**\n * Returns seconds until the current access token expires, or -1 if unknown.\n */\n getAccessTokenLifetimeRemaining(): number {\n if (!this.#tokenSet?.expiresIn) return -1;\n const elapsed = (Date.now() - this.#tokenSet.acquiredAt) / 1000;\n return Math.max(0, this.#tokenSet.expiresIn - elapsed);\n }\n\n // ─── Convenience HTTP verbs ─────────────────────────────────────────\n\n async get<T = unknown>(service: Service, relativeUrl: string, options?: RequestOptions): Promise<T> {\n return this.invoke<T>(service, HttpMethod.GET, relativeUrl, options);\n }\n\n async post<T = unknown>(service: Service, relativeUrl: string, options?: RequestOptions): Promise<T> {\n return this.invoke<T>(service, HttpMethod.POST, relativeUrl, options);\n }\n\n async put<T = unknown>(service: Service, relativeUrl: string, options?: RequestOptions): Promise<T> {\n return this.invoke<T>(service, HttpMethod.PUT, relativeUrl, options);\n }\n\n async delete<T = unknown>(service: Service, relativeUrl: string, options?: RequestOptions): Promise<T> {\n return this.invoke<T>(service, HttpMethod.DELETE, relativeUrl, options);\n }\n\n async patch<T = unknown>(service: Service, relativeUrl: string, options?: RequestOptions): Promise<T> {\n return this.invoke<T>(service, HttpMethod.PATCH, relativeUrl, options);\n }\n\n // ─── Core invoke ────────────────────────────────────────────────────\n\n /**\n * Low-level request method. Convenience verbs delegate here.\n *\n * When `options.fullResponse` is true, returns SafeguardResponse<T> with status + headers.\n * Otherwise returns just the parsed response body as T.\n */\n async invoke<T = unknown>(\n service: Service,\n method: HttpMethod,\n relativeUrl: string,\n options?: RequestOptions,\n ): Promise<T> {\n if (!this.#connected || !this.#httpClient) {\n throw new ConfigurationError('Client not connected. Call connect() first.');\n }\n\n // Auto-refresh token if needed\n if (this.#autoRefresh) {\n await this.#ensureValidToken();\n }\n\n const url = buildRequestUrl(this.#host, service, relativeUrl, options?.query, this.#apiVersion);\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.#tokenSet!.accessToken.expose()}`,\n Accept: 'application/json',\n ...options?.headers,\n };\n\n let body: string | null = null;\n if (options?.json != null) {\n body = JSON.stringify(options.json);\n headers['Content-Type'] = 'application/json';\n } else if (options?.body != null) {\n body = options.body as string;\n }\n\n const requestOpts: import('./http/types.js').HttpRequestOptions = {\n url,\n method,\n headers,\n body,\n timeout: options?.timeout ?? this.#timeout,\n };\n if (options?.signal) requestOpts.signal = options.signal;\n\n const response = await this.#httpClient.request(requestOpts);\n\n if (response.status >= 400) {\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n const data = response.body ? (JSON.parse(response.body) as T) : (undefined as T);\n\n if (options?.fullResponse) {\n return { data, status: response.status, headers: response.headers } as unknown as T;\n }\n\n return data;\n }\n\n // ─── Private helpers ────────────────────────────────────────────────\n\n async #ensureValidToken(): Promise<void> {\n if (!this.#tokenSet) return;\n if (!this.#tokenSet.expiresIn) return; // can't determine expiry\n\n const elapsed = Date.now() - this.#tokenSet.acquiredAt;\n const expiresInMs = this.#tokenSet.expiresIn * 1000;\n\n if (elapsed + TOKEN_REFRESH_MARGIN_MS < expiresInMs) {\n return; // token still fresh\n }\n\n // Single-flight: if a refresh is already running, all concurrent callers\n // await the same promise so the auth server only sees one exchange and no\n // two callers race the storage provider.\n if (this.#tokenRefreshPromise) {\n await this.#tokenRefreshPromise;\n return;\n }\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\n try {\n await this.#tokenRefreshPromise;\n } finally {\n // Always clear the slot — failures must not be cached, otherwise\n // every subsequent invoke would inherit the same rejection forever.\n this.#tokenRefreshPromise = undefined;\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"browser.js","names":[],"sources":["../src/types.ts","../src/errors.ts","../src/secret.ts","../src/utils.ts","../src/storage/memory.ts","../src/storage/session.ts","../src/storage/types.ts","../src/http/browser.ts","../src/auth/password.ts","../src/auth/pkce.ts","../src/auth/device-code.ts","../src/auth/pkce-callback.ts","../src/auth/token.ts","../src/auth/anonymous.ts","../src/client.ts"],"sourcesContent":["/**\n * Safeguard appliance service endpoints.\n */\nexport enum Service {\n CORE = 'core',\n APPLIANCE = 'appliance',\n NOTIFICATION = 'notification',\n A2A = 'a2a',\n EVENT = 'event',\n RSTS = 'RSTS',\n}\n\n/**\n * HTTP methods supported by the Safeguard API.\n */\nexport enum HttpMethod {\n GET = 'GET',\n POST = 'POST',\n PUT = 'PUT',\n DELETE = 'DELETE',\n PATCH = 'PATCH',\n}\n\n/**\n * Full response from a Safeguard API call (returned when fullResponse: true).\n */\nexport interface SafeguardResponse<T = unknown> {\n data: T;\n status: number;\n headers: Record<string, string>;\n}\n\n/**\n * Options for individual API requests.\n */\nexport interface RequestOptions {\n /** Request body as JSON-serializable object. */\n json?: unknown;\n /** Raw request body. */\n body?: BodyInit | null;\n /** Additional HTTP headers. */\n headers?: Record<string, string>;\n /** URL query parameters. */\n query?: Record<string, string | number | boolean>;\n /** AbortSignal for request cancellation. */\n signal?: AbortSignal;\n /** Request timeout in milliseconds (overrides client default). */\n timeout?: number;\n /** If true, returns full response with status and headers. */\n fullResponse?: boolean;\n}\n\n/**\n * Options for constructing a SafeguardClient.\n */\nexport interface SafeguardClientOptions {\n /** Authentication strategy. */\n auth: import('./auth/types.js').Auth;\n /** Custom CA certificate (PEM string or Buffer). Node.js only. */\n ca?: string | Buffer;\n /** Whether to verify TLS certificates. Default: true. */\n verify?: boolean;\n /** Safeguard API version prefix. Default: 'v4'. */\n apiVersion?: string;\n /** Request timeout in milliseconds. Default: 300_000 (5 min). */\n timeout?: number;\n /** Auto-refresh tokens before expiry. Default: true. */\n autoRefresh?: boolean;\n}\n","/**\n * Error hierarchy for the Safeguard SDK.\n *\n * SafeguardError\n * ├── ApiError (HTTP errors from Safeguard API)\n * │ ├── AuthenticationError (401)\n * │ ├── AuthorizationError (403)\n * │ └── NotFoundError (404)\n * ├── TransportError (Network/connection failures)\n * └── ConfigurationError (Invalid client options)\n */\n\nexport class SafeguardError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'SafeguardError';\n }\n}\n\nexport interface ApiErrorBody {\n Code?: string;\n Message?: string;\n InnerError?: { Message?: string };\n [key: string]: unknown;\n}\n\nexport class ApiError extends SafeguardError {\n readonly status: number;\n readonly body: ApiErrorBody | null;\n\n constructor(message: string, status: number, body: ApiErrorBody | null = null, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ApiError';\n this.status = status;\n this.body = body;\n }\n\n /**\n * Creates an appropriate ApiError subclass based on HTTP status code.\n */\n static fromResponse(status: number, body: string | ApiErrorBody | null): ApiError {\n const parsed = ApiError.parseBody(body);\n const message = ApiError.buildMessage(status, parsed);\n\n switch (status) {\n case 401:\n return new AuthenticationError(message, parsed);\n case 403:\n return new AuthorizationError(message, parsed);\n case 404:\n return new NotFoundError(message, parsed);\n default:\n return new ApiError(message, status, parsed);\n }\n }\n\n private static parseBody(body: string | ApiErrorBody | null): ApiErrorBody | null {\n if (body === null || body === undefined) return null;\n if (typeof body === 'object') return body;\n try {\n return JSON.parse(body) as ApiErrorBody;\n } catch {\n return { Message: body };\n }\n }\n\n private static buildMessage(status: number, body: ApiErrorBody | null): string {\n const apiMessage = body?.Message ?? body?.InnerError?.Message;\n if (apiMessage) {\n return `HTTP ${String(status)}: ${apiMessage}`;\n }\n return `HTTP ${String(status)}`;\n }\n}\n\nexport class AuthenticationError extends ApiError {\n constructor(message: string, body: ApiErrorBody | null = null, options?: ErrorOptions) {\n super(message, 401, body, options);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class AuthorizationError extends ApiError {\n constructor(message: string, body: ApiErrorBody | null = null, options?: ErrorOptions) {\n super(message, 403, body, options);\n this.name = 'AuthorizationError';\n }\n}\n\nexport class NotFoundError extends ApiError {\n constructor(message: string, body: ApiErrorBody | null = null, options?: ErrorOptions) {\n super(message, 404, body, options);\n this.name = 'NotFoundError';\n }\n}\n\nexport class TransportError extends SafeguardError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'TransportError';\n }\n}\n\nexport class ConfigurationError extends SafeguardError {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = 'ConfigurationError';\n }\n}\n","const REDACTED = '[REDACTED]';\n\n/**\n * A wrapper that prevents accidental logging of sensitive values.\n *\n * - `toString()` returns \"[REDACTED]\"\n * - `toJSON()` returns \"[REDACTED]\"\n * - `console.log(secretValue)` prints \"[REDACTED]\"\n * - `.expose()` returns the raw value when intentionally needed\n */\nexport class SecretValue {\n readonly #value: string;\n\n constructor(value: string) {\n this.#value = value;\n }\n\n /** Returns the raw secret. Use only when you intentionally need the plaintext. */\n expose(): string {\n return this.#value;\n }\n\n toString(): string {\n return REDACTED;\n }\n\n toJSON(): string {\n return REDACTED;\n }\n\n /** Node.js custom inspect (prevents leaking in console.log / util.inspect). */\n [Symbol.for('nodejs.util.inspect.custom')](): string {\n return REDACTED;\n }\n\n get [Symbol.toStringTag](): string {\n return 'SecretValue';\n }\n}\n","import { Service } from './types.js';\nimport { ConfigurationError } from './errors.js';\n\n/**\n * Validates that a host string is a bare hostname or IP address.\n * Rejects URLs, paths, query strings, userinfo, and embedded ports.\n *\n * @throws {ConfigurationError} if the host contains disallowed characters\n */\nexport function validateHost(host: string): string {\n if (!host) throw new ConfigurationError('Host must not be empty');\n\n const trimmed = host.trim();\n if (trimmed !== host) {\n throw new ConfigurationError('Host must not contain leading or trailing whitespace');\n }\n\n // Reject protocol prefixes\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(host)) {\n throw new ConfigurationError(\n `Host must be a bare hostname or IP, not a URL. Remove the protocol prefix (e.g., use \"${host.replace(/^[a-z][a-z0-9+.-]*:\\/\\//i, '')}\")`,\n );\n }\n\n // Reject path, query, fragment, userinfo characters\n if (/[/?#@]/.test(host)) {\n throw new ConfigurationError(\n 'Host must be a bare hostname or IP address — must not contain /, ?, #, or @',\n );\n }\n\n // Reject port suffix (e.g. \"host:8443\") — SDK always uses HTTPS/443\n if (/:\\d+$/.test(host) && !host.startsWith('[')) {\n throw new ConfigurationError(\n 'Host must not include a port number. The SDK always connects over HTTPS (port 443).',\n );\n }\n\n return host;\n}\n\n/**\n * Builds the base URL for a Safeguard service endpoint.\n *\n * @example\n * buildServiceUrl('appliance.example.com', Service.CORE)\n * // => 'https://appliance.example.com/service/core/v4'\n */\nexport function buildServiceUrl(host: string, service: Service, apiVersion: string = 'v4'): string {\n if (service === Service.RSTS) {\n return `https://${host}/RSTS`;\n }\n return `https://${host}/service/${service}/${apiVersion}`;\n}\n\n/**\n * Builds a full API URL including path and optional query parameters.\n *\n * @example\n * buildRequestUrl('appliance.example.com', Service.CORE, 'Users', { filter: 'Name eq \"admin\"' })\n * // => 'https://appliance.example.com/service/core/v4/Users?filter=Name+eq+%22admin%22'\n */\nexport function buildRequestUrl(\n host: string,\n service: Service,\n relativeUrl: string,\n query?: Record<string, string | number | boolean>,\n apiVersion: string = 'v4',\n): string {\n const base = buildServiceUrl(host, service, apiVersion);\n const path = relativeUrl.startsWith('/') ? relativeUrl.slice(1) : relativeUrl;\n const url = new URL(`${base}/${path}`);\n\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n url.searchParams.set(key, String(value));\n }\n }\n\n return url.toString();\n}\n\n/**\n * Encodes bytes to a URL-safe base64 string (no padding).\n */\nexport function base64UrlEncode(bytes: Uint8Array): string {\n const binary = String.fromCharCode(...bytes);\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/**\n * Generates a cryptographically random code verifier for PKCE.\n * Returns a 43-character base64url string (32 random bytes).\n */\nexport function generateCodeVerifier(): string {\n const bytes = new Uint8Array(32);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n\n/**\n * Computes the S256 code challenge from a code verifier.\n * Uses Web Crypto API (available in Node 20+ and all modern browsers).\n */\nexport async function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const digest = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n\n/**\n * Generates a random state parameter for OAuth flows.\n */\nexport function generateState(): string {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n return base64UrlEncode(bytes);\n}\n\n/**\n * Extracts the `exp` claim from a JWT and returns expiresIn (seconds from now).\n * Returns undefined if the token is not a valid JWT or has no `exp` claim.\n */\nexport function getTokenExpiresIn(jwt: string): number | undefined {\n try {\n const parts = jwt.split('.');\n if (parts.length !== 3) return undefined;\n // Base64url decode the payload\n const payload = parts[1]!.replace(/-/g, '+').replace(/_/g, '/');\n const json = atob(payload);\n const claims = JSON.parse(json) as { exp?: number };\n if (typeof claims.exp !== 'number') return undefined;\n const now = Math.floor(Date.now() / 1000);\n return Math.max(0, claims.exp - now);\n } catch {\n return undefined;\n }\n}\n","import type { StorageProvider } from './types.js';\n\n/**\n * In-memory storage provider. Default for Node.js environments.\n * Data does not persist across process restarts.\n */\nexport class MemoryStorage implements StorageProvider {\n readonly #store = new Map<string, string>();\n\n get(key: string): string | null {\n return this.#store.get(key) ?? null;\n }\n\n set(key: string, value: string): void {\n this.#store.set(key, value);\n }\n\n remove(key: string): void {\n this.#store.delete(key);\n }\n\n clear(): void {\n this.#store.clear();\n }\n}\n","import type { StorageProvider } from './types.js';\n\n/**\n * Browser sessionStorage adapter. Data persists for the browser tab lifetime.\n * Requires a DOM environment (browser or jsdom).\n */\nexport class BrowserSessionStorage implements StorageProvider {\n get(key: string): string | null {\n return sessionStorage.getItem(key);\n }\n\n set(key: string, value: string): void {\n sessionStorage.setItem(key, value);\n }\n\n remove(key: string): void {\n sessionStorage.removeItem(key);\n }\n\n clear(): void {\n sessionStorage.clear();\n }\n}\n","/**\n * Interface for token/state storage backends.\n */\nexport interface StorageProvider {\n get(key: string): string | null;\n set(key: string, value: string): void;\n remove(key: string): void;\n clear(): void;\n}\n\n/** Well-known storage keys used by the SDK. */\nexport const StorageKeys = {\n STATE: 'safeguard_state',\n CODE_VERIFIER: 'safeguard_code_verifier',\n} as const;\n","import type { HttpClient, HttpRequestOptions, HttpResponse } from './types.js';\n\n/**\n * Browser HTTP client using native fetch API.\n * No TLS configuration needed — browser handles certificates.\n */\nexport class BrowserHttpClient implements HttpClient {\n readonly #credentials: RequestCredentials;\n\n constructor(options?: { credentials?: RequestCredentials }) {\n this.#credentials = options?.credentials ?? 'include';\n }\n\n async request(options: HttpRequestOptions): Promise<HttpResponse> {\n const { url, method, headers, body, signal, timeout } = options;\n\n const requestInit: RequestInit = {\n method,\n credentials: this.#credentials,\n };\n\n if (headers) requestInit.headers = headers;\n if (body != null) requestInit.body = body as BodyInit;\n if (signal || timeout) {\n const controller = new AbortController();\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n if (timeout) {\n timeoutId = setTimeout(() => controller.abort(new Error('Request timed out')), timeout);\n }\n\n requestInit.signal = signal\n ? AbortSignal.any([signal, controller.signal])\n : controller.signal;\n\n try {\n return await this.#doFetch(url, requestInit);\n } finally {\n if (timeoutId !== undefined) clearTimeout(timeoutId);\n }\n }\n\n return this.#doFetch(url, requestInit);\n }\n\n async #doFetch(url: string, init: RequestInit): Promise<HttpResponse> {\n const response = await fetch(url, init);\n\n const responseHeaders: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n const responseBody = await response.text();\n\n return {\n status: response.status,\n headers: responseHeaders,\n body: responseBody,\n };\n }\n}\n","import type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type { Auth, TokenSet } from './types.js';\nimport { ConfigurationError } from '../errors.js';\nimport { SecretValue } from '../secret.js';\nimport { getTokenExpiresIn } from '../utils.js';\n\nexport interface PasswordAuthOptions {\n username: string;\n password: string;\n /** Identity provider name. Default: 'local'. */\n provider?: string;\n}\n\n/**\n * Authenticates using username/password via RSTS Resource Owner grant.\n */\nexport class PasswordAuth implements Auth {\n readonly #username: string;\n readonly #password: SecretValue;\n readonly #provider: string;\n\n constructor(options: PasswordAuthOptions) {\n if (!options.username) throw new ConfigurationError('PasswordAuth requires username');\n if (!options.password) throw new ConfigurationError('PasswordAuth requires password');\n if (!options.provider) throw new ConfigurationError('PasswordAuth requires provider');\n this.#username = options.username;\n this.#password = new SecretValue(options.password);\n this.#provider = options.provider;\n }\n\n get description(): string {\n return `PasswordAuth(${this.#username}@${this.#provider})`;\n }\n\n async authenticate(host: string, httpClient: HttpClient, _storage: StorageProvider): Promise<TokenSet> {\n // Step 1: Get RSTS token\n const rstsToken = await this.#getRstsToken(host, httpClient);\n\n // Step 2: Exchange RSTS token for Safeguard user token\n const userToken = await this.#exchangeForUserToken(host, httpClient, rstsToken);\n\n return userToken;\n }\n\n async refreshToken(host: string, httpClient: HttpClient, storage: StorageProvider): Promise<TokenSet | null> {\n // Password auth can always re-authenticate\n return this.authenticate(host, httpClient, storage);\n }\n\n async #getRstsToken(host: string, httpClient: HttpClient): Promise<string> {\n const body = JSON.stringify({\n grant_type: 'password',\n username: this.#username,\n password: this.#password.expose(),\n scope: 'rsts:sts:primaryproviderid:' + this.#provider.toLowerCase(),\n });\n\n const response = await httpClient.request({\n url: `https://${host}/RSTS/oauth2/token`,\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body,\n });\n\n if (response.status !== 200) {\n const { ApiError } = await import('../errors.js');\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n const data = JSON.parse(response.body) as { access_token: string };\n return data.access_token;\n }\n\n async #exchangeForUserToken(host: string, httpClient: HttpClient, rstsToken: string): Promise<TokenSet> {\n const response = await httpClient.request({\n url: `https://${host}/service/core/v4/Token/LoginResponse`,\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${rstsToken}`,\n },\n body: JSON.stringify({ StsAccessToken: rstsToken }),\n });\n\n if (response.status !== 200) {\n const { ApiError } = await import('../errors.js');\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n const data = JSON.parse(response.body) as { UserToken: string; ExpiresIn?: number };\n const tokenSet: TokenSet = {\n accessToken: new SecretValue(data.UserToken),\n acquiredAt: Date.now(),\n };\n if (data.ExpiresIn != null) {\n tokenSet.expiresIn = data.ExpiresIn;\n } else {\n const expiresIn = getTokenExpiresIn(data.UserToken);\n if (expiresIn != null) tokenSet.expiresIn = expiresIn;\n }\n return tokenSet;\n }\n}\n","import type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type { Auth, TokenSet } from './types.js';\nimport { ConfigurationError } from '../errors.js';\nimport { SecretValue } from '../secret.js';\nimport { StorageKeys } from '../storage/types.js';\nimport { generateCodeVerifier, generateCodeChallenge, generateState } from '../utils.js';\n\nexport interface PkceAuthOptions {\n /** OAuth redirect URI (must match RSTS configuration). */\n redirectUri: string;\n /** Identity provider name. Default: 'local'. */\n provider?: string;\n}\n\n/**\n * Authenticates using PKCE (Proof Key for Code Exchange) with browser redirect.\n * For browser environments where the user is redirected to RSTS login.\n */\nexport class PkceAuth implements Auth {\n readonly #redirectUri: string;\n readonly #provider: string;\n\n constructor(options: PkceAuthOptions) {\n if (!options.redirectUri) throw new ConfigurationError('PkceAuth requires redirectUri');\n if (!options.provider) throw new ConfigurationError('PkceAuth requires provider');\n this.#redirectUri = options.redirectUri;\n this.#provider = options.provider;\n }\n\n get description(): string {\n return `PKCE(redirect=${this.#redirectUri})`;\n }\n\n /**\n * Initiates the PKCE flow. If a stored authorization code exists (from callback),\n * exchanges it for a token. Otherwise, redirects to RSTS login.\n */\n async authenticate(host: string, httpClient: HttpClient, storage: StorageProvider): Promise<TokenSet> {\n // Check for authorization code (set by handlePkceCallback)\n const code = storage.get('safeguard_auth_code');\n if (code) {\n const verifier = storage.get(StorageKeys.CODE_VERIFIER);\n if (!verifier) {\n throw new ConfigurationError('PKCE code verifier not found in storage');\n }\n storage.remove('safeguard_auth_code');\n storage.remove(StorageKeys.CODE_VERIFIER);\n storage.remove(StorageKeys.STATE);\n return this.#exchangeCode(host, httpClient, code, verifier);\n }\n\n // No code — initiate redirect\n await this.#initiateRedirect(host, storage);\n\n // This point is never reached in a browser (redirect happens).\n // For testability, throw rather than hang.\n throw new ConfigurationError(\n 'PKCE redirect initiated. Call handlePkceCallback() on the callback page.',\n );\n }\n\n async #initiateRedirect(host: string, storage: StorageProvider): Promise<void> {\n const verifier = generateCodeVerifier();\n const challenge = await generateCodeChallenge(verifier);\n const state = generateState();\n\n storage.set(StorageKeys.CODE_VERIFIER, verifier);\n storage.set(StorageKeys.STATE, state);\n\n const params = new URLSearchParams({\n response_type: 'code',\n client_id: 'safeguard.js',\n redirect_uri: this.#redirectUri,\n scope: 'rsts:sts:primaryproviderid:' + this.#provider,\n code_challenge: challenge,\n code_challenge_method: 'S256',\n state,\n });\n\n const authUrl = `https://${host}/RSTS/Login?${params.toString()}`;\n\n // In browser environment, redirect\n if (typeof window !== 'undefined' && window.location) {\n window.location.href = authUrl;\n }\n }\n\n async #exchangeCode(\n host: string,\n httpClient: HttpClient,\n code: string,\n verifier: string,\n ): Promise<TokenSet> {\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: this.#redirectUri,\n client_id: 'safeguard.js',\n code_verifier: verifier,\n });\n\n const response = await httpClient.request({\n url: `https://${host}/RSTS/oauth2/token`,\n method: 'POST',\n headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n\n if (response.status !== 200) {\n const { ApiError } = await import('../errors.js');\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n const data = JSON.parse(response.body) as { access_token: string; expires_in?: number };\n\n // Exchange RSTS token for Safeguard user token\n const userTokenResponse = await httpClient.request({\n url: `https://${host}/service/core/v4/Token/LoginResponse`,\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${data.access_token}`,\n },\n body: JSON.stringify({ StsAccessToken: data.access_token }),\n });\n\n if (userTokenResponse.status !== 200) {\n const { ApiError } = await import('../errors.js');\n throw ApiError.fromResponse(userTokenResponse.status, userTokenResponse.body);\n }\n\n const userData = JSON.parse(userTokenResponse.body) as { UserToken: string; ExpiresIn?: number };\n const tokenSet: TokenSet = {\n accessToken: new SecretValue(userData.UserToken),\n acquiredAt: Date.now(),\n };\n if (userData.ExpiresIn != null) tokenSet.expiresIn = userData.ExpiresIn;\n\n // Token stays in-memory only (SafeguardClient.#tokenSet). Not persisted to\n // sessionStorage to avoid XSS exfiltration of high-privilege PAM tokens.\n return tokenSet;\n }\n}\n","import type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type { Auth, TokenSet } from './types.js';\nimport { ApiError, ConfigurationError, SafeguardError } from '../errors.js';\nimport { SecretValue } from '../secret.js';\n\n/**\n * Public device-code details handed to the caller for display.\n *\n * The raw `device_code` is intentionally NOT exposed — the library owns\n * polling and the caller only needs to render the verification URL and code.\n */\nexport interface DeviceCodeInfo {\n /** URL the user opens to authenticate (e.g. shown as a QR target or link). */\n verificationUri: string;\n /** Short code the user enters at the verification URL. */\n userCode: string;\n /** Verification URL with the user code pre-filled (RFC 8628 §3.3.1). */\n verificationUriComplete: string;\n /** Seconds until the device code expires. */\n expiresIn: number;\n /** Suggested minimum seconds between poll attempts. */\n interval: number;\n}\n\n/**\n * Options for {@link DeviceCodeAuth}.\n */\nexport interface DeviceCodeAuthOptions {\n /**\n * REQUIRED callback that receives the {@link DeviceCodeInfo} for display.\n * The library performs no display I/O; the caller renders the URL + code\n * (console, DOM, log). May be async; it is awaited before polling begins.\n */\n onDeviceCode: (info: DeviceCodeInfo) => void | Promise<void>;\n /** Identity provider name. Default: 'local'. */\n provider?: string;\n /** OAuth client id. Default: '' (matches other Safeguard SDKs). */\n clientId?: string;\n /** Starting poll interval in seconds. Default: 5. Auto-bumps on `slow_down`. */\n pollingIntervalSeconds?: number;\n /** Cancellation signal applied to HTTP requests and inter-poll delays. */\n signal?: AbortSignal;\n}\n\n/** Number of seconds to add to the poll interval on each `slow_down`. */\nconst SLOW_DOWN_INCREMENT_SECONDS = 5;\n\ninterface DeviceLoginResponse {\n device_code: string;\n user_code: string;\n verification_uri: string;\n verification_uri_complete: string;\n expires_in: number;\n interval: number;\n}\n\ninterface TokenPollResponse {\n access_token?: string;\n error?: string;\n}\n\n/** Error whose `name` is `AbortError`, matching standard web cancellation. */\nclass AbortError extends Error {\n constructor(message = 'The operation was aborted') {\n super(message);\n this.name = 'AbortError';\n }\n}\n\n/** Reject promptly if the signal is already aborted. */\nfunction throwIfAborted(signal?: AbortSignal): void {\n if (signal?.aborted) throw new AbortError();\n}\n\n/** Abortable sleep that rejects with an `AbortError` if the signal fires. */\nfunction delay(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(new AbortError());\n return;\n }\n const onAbort = (): void => {\n clearTimeout(timer);\n reject(new AbortError());\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/**\n * OAuth 2.0 Device Authorization Grant (RFC 8628) for Safeguard.\n *\n * Headless display-and-poll only: it requests a device code, hands the public\n * verification details to the required `onDeviceCode` callback, then polls the\n * token endpoint until the user authenticates in their own browser. It never\n * spawns or opens a browser and performs no display I/O. Platform-agnostic, so\n * it is exported from both the Node and browser entry points.\n */\nexport class DeviceCodeAuth implements Auth {\n readonly #onDeviceCode: (info: DeviceCodeInfo) => void | Promise<void>;\n readonly #provider: string;\n readonly #clientId: string;\n readonly #pollingIntervalSeconds: number;\n readonly #signal: AbortSignal | undefined;\n\n constructor(options: DeviceCodeAuthOptions) {\n if (typeof options?.onDeviceCode !== 'function') {\n throw new ConfigurationError('DeviceCodeAuth requires an onDeviceCode callback');\n }\n if (options.pollingIntervalSeconds !== undefined && options.pollingIntervalSeconds <= 0) {\n throw new ConfigurationError('pollingIntervalSeconds must be greater than 0');\n }\n this.#onDeviceCode = options.onDeviceCode;\n this.#provider = options.provider ?? 'local';\n this.#clientId = options.clientId ?? '';\n this.#pollingIntervalSeconds = options.pollingIntervalSeconds ?? 5;\n this.#signal = options.signal;\n }\n\n get description(): string {\n return 'DeviceCodeAuth';\n }\n\n async authenticate(\n host: string,\n httpClient: HttpClient,\n _storage: StorageProvider,\n ): Promise<TokenSet> {\n const signal = this.#signal;\n throwIfAborted(signal);\n\n const deviceCode = await this.#requestDeviceCode(host, httpClient, signal);\n\n const info: DeviceCodeInfo = {\n verificationUri: deviceCode.verification_uri,\n userCode: deviceCode.user_code,\n verificationUriComplete: deviceCode.verification_uri_complete,\n expiresIn: deviceCode.expires_in,\n interval: deviceCode.interval,\n };\n await this.#onDeviceCode(info);\n\n const accessToken = await this.#poll(host, httpClient, deviceCode, signal);\n return this.#exchangeForUserToken(host, httpClient, accessToken, signal);\n }\n\n async #requestDeviceCode(\n host: string,\n httpClient: HttpClient,\n signal: AbortSignal | undefined,\n ): Promise<DeviceLoginResponse> {\n const requestOptions = {\n url: `https://${host}/RSTS/oauth2/DeviceLogin`,\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n client_id: this.#clientId,\n scope: `rsts:sts:primaryproviderid:${this.#provider}`,\n }),\n ...(signal ? { signal } : {}),\n };\n const response = await httpClient.request(requestOptions);\n\n if (response.status !== 200) {\n // The disabled-grant response is HTML, not JSON — substring match only,\n // never JSON.parse this path.\n if (response.body.toLowerCase().includes('device code grant type is not allowed')) {\n throw new ConfigurationError(\n 'Device Code grant is not enabled on this appliance. Enable it under ' +\n 'Settings -> OAuth 2.0 Grant Types (Allowed OAuth2 Grant Types must include DeviceCode).',\n );\n }\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n return JSON.parse(response.body) as DeviceLoginResponse;\n }\n\n async #poll(\n host: string,\n httpClient: HttpClient,\n deviceCode: DeviceLoginResponse,\n signal: AbortSignal | undefined,\n ): Promise<string> {\n const deadline = Date.now() + deviceCode.expires_in * 1000;\n let intervalSeconds = this.#pollingIntervalSeconds;\n\n for (;;) {\n throwIfAborted(signal);\n if (Date.now() >= deadline) {\n throw new SafeguardError('Device code authorization timed out before approval');\n }\n\n await delay(intervalSeconds * 1000, signal);\n throwIfAborted(signal);\n\n const response = await httpClient.request({\n url: `https://${host}/RSTS/oauth2/token`,\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode.device_code,\n client_id: this.#clientId,\n }),\n ...(signal ? { signal } : {}),\n });\n\n const data = JSON.parse(response.body) as TokenPollResponse;\n\n if (response.status === 200 && data.access_token) {\n return data.access_token;\n }\n\n switch (data.error) {\n case 'authorization_pending':\n continue;\n case 'slow_down':\n intervalSeconds += SLOW_DOWN_INCREMENT_SECONDS;\n continue;\n case 'access_denied':\n throw new SafeguardError('Device code authorization was denied by the user');\n case 'expired_token':\n throw new SafeguardError('Device code expired before authorization was completed');\n default:\n throw ApiError.fromResponse(response.status, response.body);\n }\n }\n }\n\n async #exchangeForUserToken(\n host: string,\n httpClient: HttpClient,\n stsAccessToken: string,\n signal: AbortSignal | undefined,\n ): Promise<TokenSet> {\n const response = await httpClient.request({\n url: `https://${host}/service/core/v4/Token/LoginResponse`,\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${stsAccessToken}`,\n },\n body: JSON.stringify({ StsAccessToken: stsAccessToken }),\n ...(signal ? { signal } : {}),\n });\n\n if (response.status !== 200) {\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n const userData = JSON.parse(response.body) as { UserToken: string; ExpiresIn?: number };\n const tokenSet: TokenSet = {\n accessToken: new SecretValue(userData.UserToken),\n acquiredAt: Date.now(),\n };\n if (userData.ExpiresIn != null) tokenSet.expiresIn = userData.ExpiresIn;\n return tokenSet;\n }\n}\n","import { StorageKeys } from '../storage/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport { ConfigurationError } from '../errors.js';\n\nexport interface PkceCallbackResult {\n code: string;\n state: string;\n}\n\n/**\n * Parses the PKCE callback URL and stores the authorization code.\n * Call this on the callback/redirect page to complete the PKCE flow.\n *\n * This is the explicit replacement for the legacy `safeguardJsAccessTokenChecker.js`\n * script that ran as a side-effect on import.\n *\n * @param storage - StorageProvider to persist the code\n * @param url - The callback URL to parse (defaults to window.location.href)\n * @returns The parsed code and state, or null if not a valid callback\n */\nexport function handlePkceCallback(\n storage: StorageProvider,\n url?: string,\n): PkceCallbackResult | null {\n const callbackUrl = url ?? (typeof window !== 'undefined' ? window.location.href : '');\n if (!callbackUrl) return null;\n\n const parsed = new URL(callbackUrl);\n const code = parsed.searchParams.get('code');\n const state = parsed.searchParams.get('state');\n\n if (!code || !state) return null;\n\n // Validate state matches what we stored\n const storedState = storage.get(StorageKeys.STATE);\n if (storedState && storedState !== state) {\n throw new ConfigurationError('PKCE state mismatch — possible CSRF attack');\n }\n\n // Store the code for the PkceAuth strategy to pick up\n storage.set('safeguard_auth_code', code);\n\n return { code, state };\n}\n","import type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type { Auth, TokenSet } from './types.js';\nimport { ConfigurationError } from '../errors.js';\nimport { SecretValue } from '../secret.js';\n\nexport interface TokenAuthOptions {\n /** Pre-existing Safeguard access token. */\n accessToken: string;\n}\n\n/**\n * Uses a pre-existing access token. No authentication flow — the token is assumed valid.\n * Useful when tokens are obtained externally or for service-to-service scenarios.\n */\nexport class TokenAuth implements Auth {\n readonly #accessToken: SecretValue;\n\n constructor(options: TokenAuthOptions) {\n if (!options.accessToken) throw new ConfigurationError('TokenAuth requires accessToken');\n this.#accessToken = new SecretValue(options.accessToken);\n }\n\n get description(): string {\n return 'TokenAuth(pre-existing)';\n }\n\n async authenticate(_host: string, _httpClient: HttpClient, _storage: StorageProvider): Promise<TokenSet> {\n return {\n accessToken: this.#accessToken,\n acquiredAt: Date.now(),\n };\n }\n\n // No refresh possible with a pre-existing token\n}\n","import type { HttpClient } from '../http/types.js';\nimport type { StorageProvider } from '../storage/types.js';\nimport type { Auth, TokenSet } from './types.js';\nimport { SecretValue } from '../secret.js';\n\n/**\n * Anonymous authentication — no credentials.\n * Used for accessing public Safeguard endpoints (e.g., appliance status).\n */\nexport class AnonymousAuth implements Auth {\n get description(): string {\n return 'AnonymousAuth';\n }\n\n async authenticate(_host: string, _httpClient: HttpClient, _storage: StorageProvider): Promise<TokenSet> {\n return {\n accessToken: new SecretValue(''),\n acquiredAt: Date.now(),\n };\n }\n}\n","import { Service, HttpMethod } from './types.js';\nimport type { RequestOptions, SafeguardClientOptions } from './types.js';\nimport type { Auth, TokenSet } from './auth/types.js';\nimport type { HttpClient } from './http/types.js';\nimport type { StorageProvider } from './storage/types.js';\nimport { MemoryStorage } from './storage/memory.js';\nimport { buildRequestUrl, validateHost } from './utils.js';\nimport { AuthenticationError, ConfigurationError, ApiError } from './errors.js';\n\nconst DEFAULT_TIMEOUT = 300_000;\nconst DEFAULT_API_VERSION = 'v4';\nconst TOKEN_REFRESH_MARGIN_MS = 60_000; // refresh 60s before expiry\n\n/**\n * Main entry point for the Safeguard SDK.\n * Instance-based — no global state. Multiple clients to different appliances are supported.\n */\nexport class SafeguardClient {\n readonly #host: string;\n readonly #auth: Auth;\n readonly #apiVersion: string;\n readonly #timeout: number;\n readonly #autoRefresh: boolean;\n readonly #ca: string | Buffer | undefined;\n readonly #verify: boolean;\n\n #httpClient: HttpClient | undefined;\n #storage: StorageProvider;\n #tokenSet: TokenSet | undefined;\n #connected = false;\n /** Single-flight refresh promise to prevent thundering herd on token expiry. */\n #tokenRefreshPromise: Promise<void> | undefined;\n\n constructor(host: string, options: SafeguardClientOptions) {\n if (!host) throw new ConfigurationError('SafeguardClient requires a host');\n if (!options.auth) throw new ConfigurationError('SafeguardClient requires an auth strategy');\n this.#host = validateHost(host);\n this.#auth = options.auth;\n this.#apiVersion = options.apiVersion ?? DEFAULT_API_VERSION;\n this.#timeout = options.timeout ?? DEFAULT_TIMEOUT;\n this.#autoRefresh = options.autoRefresh !== false;\n this.#ca = options.ca;\n this.#verify = options.verify !== false;\n this.#storage = new MemoryStorage();\n }\n\n /** The appliance hostname this client connects to. */\n get host(): string {\n return this.#host;\n }\n\n /** The API version prefix (e.g. 'v4'). */\n get apiVersion(): string {\n return this.#apiVersion;\n }\n\n /** Custom CA certificate, if provided. */\n get ca(): string | Buffer | undefined {\n return this.#ca;\n }\n\n /** Whether TLS verification is enabled. */\n get verify(): boolean {\n return this.#verify;\n }\n\n /** Whether the client has an active authenticated session. */\n get isConnected(): boolean {\n return this.#connected;\n }\n\n /**\n * Sets a custom StorageProvider (e.g., BrowserSessionStorage).\n * Must be called before connect().\n */\n setStorage(storage: StorageProvider): this {\n this.#storage = storage;\n return this;\n }\n\n /**\n * Sets the HttpClient implementation. If not called, must be set before connect().\n * The factory pattern allows platform-specific client injection:\n * - Node: `new NodeHttpClient({ ca, rejectUnauthorized })`\n * - Browser: `new BrowserHttpClient()`\n */\n setHttpClient(httpClient: HttpClient): this {\n this.#httpClient = httpClient;\n return this;\n }\n\n /**\n * Authenticate and establish a session.\n * @throws {AuthenticationError} if authentication fails\n * @throws {ConfigurationError} if httpClient not set\n */\n async connect(): Promise<void> {\n if (!this.#httpClient) {\n throw new ConfigurationError(\n 'HttpClient not set. Call setHttpClient() before connect(), or use SafeguardClient.createNode() / SafeguardClient.createBrowser().',\n );\n }\n\n this.#tokenSet = await this.#auth.authenticate(this.#host, this.#httpClient, this.#storage);\n if (!this.#tokenSet.accessToken.expose()) {\n throw new AuthenticationError('Authentication returned empty access token');\n }\n this.#connected = true;\n }\n\n /**\n * Disconnect — dispose HTTP client and clear token.\n */\n async disconnect(): Promise<void> {\n this.#tokenSet = undefined;\n this.#connected = false;\n this.#httpClient?.dispose?.();\n }\n\n /**\n * Returns seconds until the current access token expires, or -1 if unknown.\n */\n getAccessTokenLifetimeRemaining(): number {\n if (!this.#tokenSet?.expiresIn) return -1;\n const elapsed = (Date.now() - this.#tokenSet.acquiredAt) / 1000;\n return Math.max(0, this.#tokenSet.expiresIn - elapsed);\n }\n\n // ─── Convenience HTTP verbs ─────────────────────────────────────────\n\n async get<T = unknown>(service: Service, relativeUrl: string, options?: RequestOptions): Promise<T> {\n return this.invoke<T>(service, HttpMethod.GET, relativeUrl, options);\n }\n\n async post<T = unknown>(service: Service, relativeUrl: string, options?: RequestOptions): Promise<T> {\n return this.invoke<T>(service, HttpMethod.POST, relativeUrl, options);\n }\n\n async put<T = unknown>(service: Service, relativeUrl: string, options?: RequestOptions): Promise<T> {\n return this.invoke<T>(service, HttpMethod.PUT, relativeUrl, options);\n }\n\n async delete<T = unknown>(service: Service, relativeUrl: string, options?: RequestOptions): Promise<T> {\n return this.invoke<T>(service, HttpMethod.DELETE, relativeUrl, options);\n }\n\n async patch<T = unknown>(service: Service, relativeUrl: string, options?: RequestOptions): Promise<T> {\n return this.invoke<T>(service, HttpMethod.PATCH, relativeUrl, options);\n }\n\n // ─── Core invoke ────────────────────────────────────────────────────\n\n /**\n * Low-level request method. Convenience verbs delegate here.\n *\n * When `options.fullResponse` is true, returns SafeguardResponse<T> with status + headers.\n * Otherwise returns just the parsed response body as T.\n */\n async invoke<T = unknown>(\n service: Service,\n method: HttpMethod,\n relativeUrl: string,\n options?: RequestOptions,\n ): Promise<T> {\n if (!this.#connected || !this.#httpClient) {\n throw new ConfigurationError('Client not connected. Call connect() first.');\n }\n\n // Auto-refresh token if needed\n if (this.#autoRefresh) {\n await this.#ensureValidToken();\n }\n\n const url = buildRequestUrl(this.#host, service, relativeUrl, options?.query, this.#apiVersion);\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.#tokenSet!.accessToken.expose()}`,\n Accept: 'application/json',\n ...options?.headers,\n };\n\n let body: string | null = null;\n if (options?.json != null) {\n body = JSON.stringify(options.json);\n headers['Content-Type'] = 'application/json';\n } else if (options?.body != null) {\n body = options.body as string;\n }\n\n const requestOpts: import('./http/types.js').HttpRequestOptions = {\n url,\n method,\n headers,\n body,\n timeout: options?.timeout ?? this.#timeout,\n };\n if (options?.signal) requestOpts.signal = options.signal;\n\n const response = await this.#httpClient.request(requestOpts);\n\n if (response.status >= 400) {\n throw ApiError.fromResponse(response.status, response.body);\n }\n\n const data = response.body ? (JSON.parse(response.body) as T) : (undefined as T);\n\n if (options?.fullResponse) {\n return { data, status: response.status, headers: response.headers } as unknown as T;\n }\n\n return data;\n }\n\n // ─── Private helpers ────────────────────────────────────────────────\n\n async #ensureValidToken(): Promise<void> {\n if (!this.#tokenSet) return;\n if (!this.#tokenSet.expiresIn) return; // can't determine expiry\n\n const elapsed = Date.now() - this.#tokenSet.acquiredAt;\n const expiresInMs = this.#tokenSet.expiresIn * 1000;\n\n if (elapsed + TOKEN_REFRESH_MARGIN_MS < expiresInMs) {\n return; // token still fresh\n }\n\n // Single-flight: if a refresh is already running, all concurrent callers\n // await the same promise so the auth server only sees one exchange and no\n // two callers race the storage provider.\n if (this.#tokenRefreshPromise) {\n await this.#tokenRefreshPromise;\n return;\n }\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\n try {\n await this.#tokenRefreshPromise;\n } finally {\n // Always clear the slot — failures must not be cached, otherwise\n // every subsequent invoke would inherit the same rejection forever.\n this.#tokenRefreshPromise = undefined;\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"events.cjs","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.cjs","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/events.d.cts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { HubConnection } from "@microsoft/signalr";
2
2
  import { SecretValue } from './index.d.cts';
3
-
4
3
  //#region src/events/types.d.ts
5
4
  /**
6
5
  * Event listener connection states.
@@ -1 +1 @@
1
- {"version":3,"file":"events.d.cts","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,kBAAA;;;;KAUA,YAAA,IAAgB,KAAqB,EAAd,cAAc;AAAjD;;;AAAA,KAKY,kBAAA,IAAsB,KAAyB,EAAlB,kBAAkB;AALV;AAKjD;;AALiD,UAUhC,cAAA;EACf,SAAA;EAAA,CACC,GAAW;AAAA;;;;UAMG,yBAAA;EAAA;EAEf,eAAA;;EAEA,UAAU;AAAA;;;;AAhCZ;;;cCIa,sBAAA;EAAA;cAKC,UAAA,EAAY,aAAA;EDCF;EAAA,ICuBlB,KAAA,IAAS,kBAAA;EDvBoB;AAAc;AAKjD;ECyBE,EAAA,CAAG,OAAA,sBAA6B,OAAA,EAAS,YAAA;;;ADzBgB;ECiCnD,KAAA,IAAS,OAAA;ED5Bc;;;ECqCvB,IAAA,IAAQ,OAAA;AAAA;;;;;;UCxDC,kBAAA;EACf,GAAA;EACA,MAAA;EACA,OAAA,GAAU,MAAA;EACV,IAAA,YAAgB,MAAA;EAChB,MAAA,GAAS,WAAA;EACT,OAAA;AAAA;AAAA,UAGe,YAAA;EACf,MAAA;EACA,OAAA,EAAS,MAAM;EACf,IAAA;AAAA;;;;ADTF;UC6BiB,UAAA;EACf,OAAA,CAAQ,OAAA,EAAS,kBAAA,GAAqB,OAAA,CAAQ,YAAA;EAC9C,OAAA;AAAA;;;;;;UCnCe,eAAA;EACf,GAAA,CAAI,GAAA;EACJ,GAAA,CAAI,GAAA,UAAa,KAAA;EACjB,MAAA,CAAO,GAAA;EACP,KAAA;AAAA;;;;;;AHJF;;;;AAA8B;cIOjB,WAAA;EAAA;cAGC,KAAA;EJAqB;EIKjC,MAAA;EAIA,QAAA;EAIA,MAAA;EAAA,KASK,MAAA,CAAO,WAAW;AAAA;;;AJhCzB;;;AAAA,UKIiB,QAAA;ELJa;EKM5B,WAAA,EAAa,WAAW;ELIF;EKFtB,SAAA;ELEiC;EKAjC,UAAA;AAAA;;;;UAMe,IAAA;ELIA;;;;AAEH;AAMd;EKLE,YAAA,CAAa,IAAA,UAAc,UAAA,EAAY,UAAA,EAAY,OAAA,EAAS,eAAA,GAAkB,OAAA,CAAQ,QAAA;;;ALS5E;EKJV,YAAA,EAAc,IAAA,UAAc,UAAA,EAAY,UAAA,EAAY,OAAA,EAAS,eAAA,GAAkB,OAAA,CAAQ,QAAA;;WAG9E,WAAA;AAAA;;;;;AL/BmB;AAU9B;;;;AAAiD;cMUpC,gCAAA;EAAA;cAoBT,UAAA,EAAY,aAAA,EACZ,IAAA,EAAM,IAAA,EACN,IAAA,UACA,UAAA,EAAY,UAAA,EACZ,OAAA,EAAS,eAAA,EACT,OAAA,GAAU,yBAAA,EACV,QAAA,GAAW,QAAA;EN/B0B;EAAA,IMkEnC,KAAA,IAAS,kBAAA;EN7DE;;;EMoEf,EAAA,CAAG,OAAA,sBAA6B,OAAA,EAAS,YAAA;ENlE7B;AAMd;;EMoEE,aAAA,CAAc,OAAA,EAAS,kBAAA;ENlEvB;AAEU;;EMwEJ,KAAA,IAAS,OAAA;;ALpGjB;;EK+GQ,IAAA,IAAQ,OAAA;AAAA"}
1
+ {"version":3,"file":"events.d.cts","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.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { HubConnection } from "@microsoft/signalr";
2
2
  import { SecretValue } from './index.js';
3
-
4
3
  //#region src/events/types.d.ts
5
4
  /**
6
5
  * Event listener connection states.