JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 219
  • Score
    100M100P100Q102232F
  • License MIT

TypeScript SDK for WitniumChain — identity, billing, OAuth, witness writes (v5), key management, and chain-api read surfaces (contracts, witnesses, wallets, transactions, dashboards). Single canonical SDK; supersedes @witnium-tech/accounts-sdk and @witnium-tech/chain-sdk-v5.

Package Exports

  • @witnium-tech/witniumchain

Readme

@witnium-tech/witniumchain

TypeScript SDK for WitniumChain — the canonical single client for both the accounts service (identity, billing, OAuth, delegated keys, witness writes) and the chain-api read surfaces (contract info, witness lookup, wallet balance, dashboards). The two-service backend is hidden behind one WitniumchainClient class; you give it one OAuth access token and it knows where each call belongs.

Status

v0.5 — adds OAuth 2.1 + PKCE login helpers on top of the v0.4 surface:

  • All 41 accounts routes (five auth modes: session / OAuth / org / admin / signed)
  • 9 chain-api read methods, routed to chainBaseUrl, reusing the same OAuth accessToken (accounts mints tokens with aud=https://api.witniumchain.com, so they're valid against both services unchanged)
  • Three layered clients on top: WitniumchainClient (end-user), WitniumchainOrgClient (org admin), WitniumchainAdminClient (sysadmin)
  • beginOAuthLogin / completeOAuthLogin / refreshAccessToken / signOut — browser-side Authorization-Code + PKCE login flow. Discovery-driven endpoint resolution (/.well-known/openid-configuration), PKCE verifier in sessionStorage, access token held in memory only, transparent 401-retry with single-flight refresh-token rotation. Refresh tokens are delivered as an HttpOnly wac_refresh cookie scoped to Path=/token — JavaScript never touches them. Non-browser callers (Node SSR, native apps without a cookie jar) fall back to in-band refresh-token delivery; the SDK handles both.
  • client.mfa.totp.enroll/confirm/disable + client.mfa.recoveryCodes.regenerate — TOTP MFA self-management. enroll returns the secret + otpauth URL (render as a QR code in your dashboard with any QR lib); confirm validates the first code and returns 10 single-use recovery codes (shown ONCE).

What's NOT exposed (by design): chain-api v5 writes — those are proxied by accounts' v5 surface which adds credit reservation + idempotency + scope checks. Calling chain-api writes directly would burn credits without billing them. See docs/PLAN-PHASE-SDK-UNIFIED.md for the routing rules.

Every type comes from the published OpenAPI specs of both services via openapi-typescript. npm run openapi:check regenerates and fails on diff; it runs ahead of every npm publish.

Install

npm install @witnium-tech/witniumchain

Auth model

The SDK accepts five distinct credentials. Configure whichever you need on the client; methods that require a credential you didn't supply throw at call time.

Credential Header / Cookie Used by
sessionCookie Cookie: wac_session=… /v1/auth/logout, /v1/account/*, /v1/billing/*, /v1/keys/*, /v1/contracts/{pause,unpause}, /v1/oauth/sessions*
accessToken Authorization: Bearer <JWT> /v1/users/me/delegated-keys/*, /v1/sign, all chain-api reads
orgApiKey Authorization: Bearer wcorg_live_… /v1/orgs/me/*
adminToken Authorization: Bearer <ADMIN_TOKEN> /v1/admin/*
signedRequest X-Witnium-Key/Timestamp/Signature /v1/contracts/{addr}/witnesses/{propose,sign,finalize,revoke}

Public routes need no credential (/v1/auth/{signup,verify,login,…}, /v1/contracts/provision, GET /v1/contracts/{addr}/witnesses/{id}, /health/*).

Two services, one client

Accounts and chain-api are separate services today (consolidation is a later phase). The SDK hides that:

const client = new WitniumchainClient({
  baseUrl: 'https://auth.witniumchain.com',      // accounts
  chainBaseUrl: 'https://api.witniumchain.com',  // chain-api (omit if you only need accounts)
  accessToken: oauthBearerJwt,
});

// Writes / billing / auth → accounts (baseUrl)
await client.proposeWitnessV5('0xabc', { ... });

// Reads of on-chain state → chain-api (chainBaseUrl)
const witness = await client.getWitnessV5('0xabc', witnessId);
const balance = await client.getWalletBalance('0xabc');

If you call a chain-api method without chainBaseUrl configured, the SDK throws a clear WitniumchainApiError at call time naming the missing config — no silent fallback to a wrong base URL.

Examples

End-user signup + login

import { WitniumchainClient } from '@witnium-tech/witniumchain';

const client = new WitniumchainClient({
  baseUrl: 'https://auth.witniumchain.com',
});

await client.signup({ email: 'alice@example.com', password: 'correct horse battery staple' });
// User clicks email link, lands on /verify?token=…
const { provisioningToken } = await client.verifyEmail('the-token-from-the-link');

// Then login (sets the wac_session cookie in a browser; in Node, capture
// from response headers and pass back via `sessionCookie`).
await client.login({ email: 'alice@example.com', password: '…' });

Org admin — create a user

const org = new WitniumchainClient({
  baseUrl: 'https://auth.witniumchain.com',
  orgApiKey: 'wcorg_live_…',
});

const { userId, provisioningToken } = await org.createOrgUser({
  email: 'bob@customer.example.com',
});
// Forward `provisioningToken` to Bob — he calls /v1/contracts/provision
// with locally-generated owner + signing keypairs.

Sysadmin — create an organisation

const sys = new WitniumchainClient({
  baseUrl: 'https://auth.witniumchain.com',
  adminToken: process.env.ACCOUNTS_ADMIN_TOKEN!,
});

const { organization, apiKey } = await sys.createOrganization({
  name: 'Acme Inc.',
  email: 'ops@acme.example.com',
  accountType: 'metered',
  signupGrantAmount: 100,
});
// `apiKey` is shown ONCE — store it now.

Sign in with Witnium (Authorization Code + PKCE)

For a browser SPA (e.g. a Lovable app) that wants to authenticate end-users against a Witnium org. No backend required.

const client = new WitniumchainClient({
  baseUrl: 'https://auth.witniumchain.com',
  oauthClientId: 'your-registered-client-id',
});

// 1. Kick off login. The SDK generates a PKCE pair, stashes the verifier in
//    sessionStorage, and returns the URL to send the user to.
const { authorizationUrl } = await client.beginOAuthLogin({
  redirectUri: 'https://your-app.example/callback',
});
window.location.assign(authorizationUrl);

// 2. On the callback page, complete the exchange. Returns the access token
//    (the SDK ALSO holds it in memory; subsequent BearerJWT calls Just Work).
const { accessToken, expiresAt } = await client.completeOAuthLogin(
  window.location.href,
);
window.history.replaceState({}, '', window.location.pathname); // strip ?code=…

// 3. Use the SDK normally. When the access token expires, the SDK refreshes
//    transparently on the next call. You only need to redirect to login if
//    refresh itself fails (refresh token revoked or expired).
const { keys } = await client.listDelegatedKeys();

// 4. Sign-out clears in-memory tokens.
client.signOut();

OAuth API — delegated-key + sign

const api = new WitniumchainClient({
  baseUrl: 'https://auth.witniumchain.com',
  accessToken: bearerJwt,
});

const prepared = await api.prepareDelegatedKey({ contractAddress: '0x…' });
// Sign prepared.messageToSign with your owner key locally:
const ownerSignature = await myOwnerSigner.sign(prepared.messageToSign);

const submitted = await api.submitDelegatedKey(prepared.id, { ownerSignature });
// submitted.confirmed === true once the addSigningKey tx mines (~10–15 s).

const { signature } = await api.sign(
  { delegatedKeyId: prepared.id, payload: 'deadbeef…' },
);

SDK signed-request (witness propose/sign/finalize)

const sdk = new WitniumchainClient({
  baseUrl: 'https://auth.witniumchain.com',
  signedRequest: {
    publicKeyHex: 'abcd…64-chars…',
    sign: async (canonicalMessage) => {
      // Sign with your Ed25519 key — e.g. via @noble/ed25519 or a KMS.
      // Return the 128-char hex signature.
      return await myEd25519Sign(canonicalMessage);
    },
  },
});

const intent = await sdk.proposeWitness('0x…', {
  dataId: '…64-hex…',
  requiredSigners: ['…signer-pubkey…'],
});

Errors

All non-2xx responses surface as WitniumchainApiError:

import { WitniumchainApiError } from '@witnium-tech/witniumchain';

try {
  await client.createOrgUser({ email: 'taken@example.com' });
} catch (err) {
  if (err instanceof WitniumchainApiError) {
    console.error(err.status, err.errorLabel, err.message);
    // err.body holds the raw parsed body for advanced inspection.
  }
}

License

MIT.