npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

isecure-ts-client

v1.3.0

Published

TypeScript SDK for the ISECure WS Channel API

Downloads

1,050

Readme

ISECure TypeScript Client

TypeScript SDK for the ISECure WS Channel API.

The checked-in OpenAPI contract is wsapi_v2.json. The live source at https://isecure.fi/wsapi_v2.json currently matches this repository copy and reports API version v2.6.0.

Install

npm install isecure-ts-client
import { WSChannel } from "isecure-ts-client";

Customer Start Steps

  1. Install isecure-ts-client.
  2. Configure account, bank, endpoint, password, and RSA public key values.
  3. Create WSChannel.
  4. Call register() for first-time account setup, or login() for an existing account.
  5. Complete any returned auth state: MFA, email verification, or phone verification.
  6. Use supported operations such as listFiles, uploadFile, and uploadPgpKey.

Configuration

| Environment variable | Required | Description | | ------------------------ | -------- | --------------------------------------------------------------------------------- | | ISECURE_BASE_URL | No | API endpoint. Defaults to https://ws-api.test.isecure.fi/v2 in examples. | | ISECURE_API_KEY | No | Existing integrator API key. Use 0 or omit for initial integrator registration. | | ISECURE_COMPANY | Yes | Company name for registration. | | ISECURE_NAME | Yes | Full user name for registration. | | ISECURE_EMAIL | Yes | Account email address. | | ISECURE_PHONE | Yes | Phone number with country code, for example +358401234567. | | ISECURE_PASSWORD | Yes | Account password. | | ISECURE_PUBLIC_KEY_PEM | Yes | ISECure RSA public key in PEM format. | | ISECURE_MODE | No | admin or data. Defaults to data in examples. | | ISECURE_BANK | No | Bank identifier. Defaults to nordea in examples. |

Basic Usage

const client = new WSChannel({
  ApiKey: process.env.ISECURE_API_KEY ?? "0",
  Company: "Example Company",
  Name: "Example User",
  Password: process.env.ISECURE_PASSWORD!,
  Phone: "+358401234567",
  PublicKey: process.env.ISECURE_PUBLIC_KEY_PEM!,
  BaseUrl: "https://ws-api.test.isecure.fi/v2",
  Email: "[email protected]",
  Mode: "data",
  Bank: "nordea",
});

const state = await client.login();

if (state.status === "authenticated") {
  const files = await client.listFiles({ Status: "ALL" });
  console.log(files.FileDescriptors);
}

First Registration

const registration = await client.register();
console.log(registration.ApiKey);

let state = await client.login();

while (state.status !== "authenticated") {
  if (state.status === "needs_mfa") {
    state = await client.submitMfaCode("123456");
    continue;
  }

  if (state.status === "needs_email_verification") {
    await client.verifyEmail("123456");
    state = await client.login();
    continue;
  }

  if (state.status === "needs_phone_verification") {
    await client.verifyPhone("123456");
    state = await client.login();
    continue;
  }

  if (state.status === "failed") {
    throw new Error(state.responseText);
  }

  state = await client.login();
}

Admin login and first-time registration may require MFA, email, or phone verification. The SDK returns typed auth states instead of reading from the terminal:

const state = await client.login();

if (state.status === "needs_mfa") {
  // state.method is "sms" or "totp" (Google Authenticator) so you can label the prompt.
  await client.submitMfaCode("123456");
}

if (state.status === "needs_email_verification") {
  await client.verifyEmail("123456");
}

if (state.status === "needs_phone_verification") {
  await client.verifyPhone("123456");
}

Enrolling Google Authenticator (TOTP)

Request enrollment while completing an admin login, render the returned QR (otpauthUri) or secret, then confirm the first code. TOTP becomes the preferred factor; SMS stays enabled as a fallback.

// Complete login with the SMS code AND ask to set up TOTP in one step:
const state = await client.submitMfaCode(smsCode, { setupTotp: true });

if (state.status === "authenticated" && state.totpEnrollment) {
  const { secret, otpauthUri, accessToken } = state.totpEnrollment;
  // Render otpauthUri as a QR code (or show `secret` for manual entry).
  // accessToken is held in memory only — do not persist it.
  await client.verifyTotp(accessToken, codeFromAuthenticatorApp);
}

For CLI scripts, pass a prompt adapter. loginWithPrompt drives the whole MFA → email → phone verification machine to completion and is bounded, so you never re-implement the verify/re-login loop yourself:

const state = await client.loginWithPrompt({
  requestMfaCode: async () => "...",
  requestEmailCode: async () => "...",
  requestPhoneCode: async () => "...",
});

if (state.status === "authenticated") {
  // ready to call authenticated operations
} else if (state.status === "stalled") {
  // an accepted verification did not advance login (e.g. backend did not flip
  // confirmation). `state.step` names the stuck step instead of looping.
  console.error(`Login stalled on ${state.step} after ${state.transitions} steps`);
} else if (state.status === "failed") {
  // `state.reason` is a discriminable AuthErrorReason such as "invalid_code",
  // "expired_code", "too_many_attempts", or "missing_access_token".
  console.error(`Login failed (${state.reason}): ${state.responseText}`);
}

Verification helpers (verifyEmail, verifyPhone) and classifyVerificationResponse return the same typed failed state with an AuthErrorReason, so invalid/expired codes, rate limiting, and already-verified cases are discriminable rather than collapsed into a single error string.

Debug Logging

Logging is opt-in. By default the SDK uses a no-op logger and emits nothing. Inject a logger and keep LogLevel at debug (the default) to get redacted request/response debug lines for every call — secrets, tokens, and one-time codes are stripped before logging:

const client = new WSChannel(
  { ...props, LogLevel: "debug" },
  {
    logger: {
      debug: (message, meta) => console.debug(message, meta),
      info: () => {},
      warn: () => {},
      error: () => {},
    },
  },
);

Set LogLevel: "silent" (or omit the logger) to disable transport logging. The redaction lives in LoggingTransport, which wraps whatever transport you provide, so a custom transport is logged too.

Redaction strips known secrets and PII (tokens, passwords, codes, email, phone, name), masks token-like values regardless of field name, and removes emails from logged URLs. For low-trust log sinks, pass redaction: "strict" (via WSChannelOptions or LoggingTransportOptions) to redact everything except an allowlist of known-safe fields.

Errors and Resilience

Logical failures returned by the API as HTTP 200 with ResponseCode !== "00" surface through the typed auth/verification states described above. Transport-level failures throw a typed error hierarchy instead of raw AxiosErrors:

  • ISecureHttpError — a non-2xx response, exposing status, responseCode, responseText, requestId (quote this in support tickets), and the raw body.
  • ISecureNetworkError — a network failure or timeout (timedOut, code, cause).
  • ISecureAbortError — a request cancelled via AbortSignal.

All extend ISecureError; use isISecureError(err) to narrow.

import { isISecureError, ISecureHttpError } from "isecure-ts-client";

try {
  await client.listFiles({ Status: "ALL" });
} catch (err) {
  if (err instanceof ISecureHttpError) {
    console.error(`HTTP ${err.status} (RequestId ${err.requestId}): ${err.responseText}`);
  } else if (isISecureError(err)) {
    console.error(err.message);
  }
}

The default AxiosTransport applies production defaults — a 30s timeout and bounded exponential-backoff retries (with jitter and Retry-After support) for transient failures (network errors, 408/425/429/5xx). Tune them via AxiosTransportOptions:

const client = new WSChannel(props, {
  transport: new AxiosTransport({ timeoutMs: 10_000, retries: 3 }),
});

The transport also honors an AbortSignal on each TransportRequest (cancelling an in-flight request or a pending retry backoff), which custom transports and integrations can use directly.

Session Lifecycle

The SDK tracks the id-token expiry returned at login. Inspect it with client.isAuthenticated(), client.isSessionExpired(), and client.sessionExpiresAt. When an authenticated call is made on an expired session, the SDK invokes an optional refresh hook so you can re-establish the session in one place instead of handling 401s everywhere:

const client = new WSChannel(props, {
  expirySkewMs: 30_000, // refresh 30s early
  onSessionExpired: async (channel) => {
    await channel.loginWithPrompt(promptAdapter);
  },
});

Without a hook, an authenticated call on an expired session throws ISecureError rather than sending a request that would 401. logout() always works, even when expired.

Runtime Support

The SDK supports Node.js and modern browser bundlers. Password challenge encryption uses WebCrypto-compatible RSA-OAEP with SHA-1, so browser runtimes must provide globalThis.crypto.subtle.

Browser applications also need the ISECure WS API endpoint to allow the application origin with CORS. Avoid exposing production passwords, API keys, or id tokens in untrusted browser code; for most customer-facing web apps, run this SDK on your backend and call that backend from the browser.

The runnable terminal implementation lives in examples/full-workflow/full-workflow.ts.

Supported Operations

Generated request and response types are built from wsapi_v2.json with swagger2openapi and openapi-typescript.

Currently supported:

  • InitRegister
  • Register
  • InitPasswordReset
  • PasswordReset
  • InitLogin
  • Login
  • LoginMFA
  • VerifyEmail
  • VerifyPhone
  • ListCerts
  • ConfigCerts
  • ShareCerts
  • UnshareCerts
  • ExportCert
  • ImportCert
  • EnrollCert
  • UploadKey
  • UploadFile
  • ListFiles
  • DownloadFile
  • DeleteFile
  • ListAccounts
  • ListKeys
  • DeleteKey
  • Logout

All operations in wsapi_v2.json are now represented by WSChannel methods. The operation list is exported as SUPPORTED_OPERATIONS; UNSUPPORTED_OPERATIONS is empty.

Development

yarn install --frozen-lockfile
yarn audit
yarn format:check
yarn lint
yarn typecheck
yarn test
yarn browser:check
yarn pack:check

Important gates:

  • yarn generate:types converts Swagger 2.0 to OpenAPI 3.0 and generates TypeScript types.
  • yarn audit checks dependency advisories.
  • yarn format:check checks Prettier formatting.
  • yarn lint runs ESLint with type-aware rules.
  • yarn typecheck runs strict TypeScript for the SDK and examples.
  • yarn test runs Vitest with coverage thresholds.
  • yarn browser:check verifies the package entrypoint bundles for browser targets.
  • yarn pack:check verifies the library-only package payload.

Examples compile separately to dist-examples; they are not part of the npm package payload.

Releases

Semantic versions are prepared by Release Please from conventional commits:

  • fix: ... creates a patch release.
  • feat: ... creates a minor release.
  • feat!: ... or BREAKING CHANGE: creates a major release.

Release Please opens a version/changelog PR. Merging that PR creates the GitHub release, and the publish workflow publishes the package to npm.

Fully automated publishing requires repository secret RELEASE_PLEASE_TOKEN with a fine-grained GitHub token that can write contents, pull requests, and issues. The release workflow intentionally fails without that secret, because GitHub releases created by GITHUB_TOKEN do not trigger the npm publish workflow.

The npm package must also be configured with a trusted publisher:

  • Publisher: GitHub Actions
  • Organization or user: dforsber
  • Repository: isecure-ts-client
  • Workflow filename: publish.yml
  • Environment name: npm
  • Allowed action: npm publish