@witnium-tech/witniumchain 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -109,6 +109,16 @@ var WitniumchainClient = class {
109
109
  // tokens rotate on use (D10), so a second concurrent refresh would race the
110
110
  // first and one of them would 401.
111
111
  refreshInFlight;
112
+ // True when the current `accessToken` was minted by the session→token
113
+ // exchange (`exchangeSessionForToken`) rather than the OAuth code/refresh
114
+ // flow. Drives the 401-retry: a session-exchanged token has no refresh
115
+ // credential, so on expiry the SDK re-mints from the cookie instead of
116
+ // refreshing. Reset to false whenever an OAuth /token response lands.
117
+ accessTokenFromSession = false;
118
+ // Single-flight gate for the session→token exchange. A cookie-only client's
119
+ // first BearerJWT call (and any concurrent fan-out) awaits one in-flight
120
+ // mint rather than minting N tokens.
121
+ sessionExchangeInFlight;
112
122
  /** Subscriptions / billing helpers. See {@link Subscriptions}. */
113
123
  subscriptions;
114
124
  /** Delegated-key namespace including the one-call {@link DelegatedKeys.provision} flow. */
@@ -263,43 +273,6 @@ var WitniumchainClient = class {
263
273
  return this.req("GET", "/v1/orgs/me/users", { auth: "OrgApiKey" });
264
274
  }
265
275
  // ────────────────────────────────────────────────────────────────────────
266
- // Admin (/v1/admin/organizations/*)
267
- // ────────────────────────────────────────────────────────────────────────
268
- createOrganization(body) {
269
- return this.req("POST", "/v1/admin/organizations", {
270
- auth: "AdminToken",
271
- body
272
- });
273
- }
274
- setOrgAccountType(id, body) {
275
- return this.req(
276
- "PATCH",
277
- `/v1/admin/organizations/${encodeURIComponent(id)}/account-type`,
278
- { auth: "AdminToken", body }
279
- );
280
- }
281
- verifyOrganization(id) {
282
- return this.req(
283
- "PATCH",
284
- `/v1/admin/organizations/${encodeURIComponent(id)}/verify`,
285
- { auth: "AdminToken" }
286
- );
287
- }
288
- rotateOrgApiKey(id) {
289
- return this.req(
290
- "POST",
291
- `/v1/admin/organizations/${encodeURIComponent(id)}/rotate-key`,
292
- { auth: "AdminToken" }
293
- );
294
- }
295
- adjustOrgCredits(id, body) {
296
- return this.req(
297
- "POST",
298
- `/v1/admin/organizations/${encodeURIComponent(id)}/adjust-credits`,
299
- { auth: "AdminToken", body }
300
- );
301
- }
302
- // ────────────────────────────────────────────────────────────────────────
303
276
  // Delegated keys (/v1/users/me/delegated-keys/*)
304
277
  // ────────────────────────────────────────────────────────────────────────
305
278
  listDelegatedKeys(query) {
@@ -785,6 +758,84 @@ var WitniumchainClient = class {
785
758
  })();
786
759
  return this.refreshInFlight;
787
760
  }
761
+ /**
762
+ * Exchange the logged-in session (the `wac_session` cookie) for a short-lived
763
+ * bearer access token and cache it as the client's `accessToken`.
764
+ *
765
+ * This is the SDK side of the session→token model (accounts'
766
+ * `POST /v1/auth/access-token`): the dashboard authenticates with the
767
+ * cookie, but the operational API (delegated keys, witness writes, chain
768
+ * reads) is bearer-only — no endpoint accepts the ambient cookie, so there's
769
+ * no CSRF surface even with `SameSite=None`. The minted token is
770
+ * byte-compatible with an OAuth user token (same signing key, `iss`, `aud`,
771
+ * scope + org/role/contracts claims), so every `BearerJWT` route accepts it.
772
+ *
773
+ * Callers rarely invoke this directly — {@link applyAuth} mints transparently
774
+ * on the first `BearerJWT` call when no token is set and re-mints on a 401.
775
+ * It's exposed so a dashboard can pre-warm the token right after login.
776
+ *
777
+ * Requires a session: either `sessionCookie` on the constructor (Node) or an
778
+ * ambient `wac_session` cookie carried by the browser (`credentials:
779
+ * 'include'`). Throws {@link WitniumchainApiError} (401) when there's no
780
+ * valid session.
781
+ */
782
+ async exchangeSessionForToken() {
783
+ if (this.sessionExchangeInFlight) return this.sessionExchangeInFlight;
784
+ this.sessionExchangeInFlight = (async () => {
785
+ try {
786
+ const res = await this.req("POST", "/v1/auth/access-token", { auth: "SessionCookie" });
787
+ this.accessToken = res.accessToken;
788
+ this.accessTokenFromSession = true;
789
+ this.refreshToken = void 0;
790
+ this.hasRefreshCookie = false;
791
+ return {
792
+ accessToken: res.accessToken,
793
+ expiresAt: Math.floor(new Date(res.expiresAt).getTime() / 1e3)
794
+ };
795
+ } finally {
796
+ this.sessionExchangeInFlight = void 0;
797
+ }
798
+ })();
799
+ return this.sessionExchangeInFlight;
800
+ }
801
+ /**
802
+ * Best-effort transparent mint for the `BearerJWT` path: if no access token
803
+ * is cached, try the session exchange (single-flight). Swallows failure so
804
+ * the caller in {@link applyAuth} surfaces the actionable "no token / no
805
+ * session" guidance rather than a raw 401 from the exchange endpoint.
806
+ */
807
+ async ensureSessionAccessToken() {
808
+ if (this.accessToken) return;
809
+ try {
810
+ await this.exchangeSessionForToken();
811
+ } catch {
812
+ }
813
+ }
814
+ /**
815
+ * Obtain a fresh access token for a 401-retry, picking the right mechanism:
816
+ * an OAuth refresh when a refresh credential exists, else a session re-mint
817
+ * for a session-exchanged token. Returns true when a fresh token is in hand.
818
+ */
819
+ async reauthenticateForRetry() {
820
+ if (this.refreshToken || this.hasRefreshCookie) {
821
+ try {
822
+ await this.refreshAccessToken();
823
+ return true;
824
+ } catch {
825
+ return false;
826
+ }
827
+ }
828
+ if (this.accessTokenFromSession) {
829
+ this.accessToken = void 0;
830
+ try {
831
+ await this.exchangeSessionForToken();
832
+ return true;
833
+ } catch {
834
+ return false;
835
+ }
836
+ }
837
+ return false;
838
+ }
788
839
  /**
789
840
  * End the OAuth session.
790
841
  *
@@ -799,6 +850,7 @@ var WitniumchainClient = class {
799
850
  */
800
851
  signOut() {
801
852
  this.accessToken = void 0;
853
+ this.accessTokenFromSession = false;
802
854
  this.refreshToken = void 0;
803
855
  this.hasRefreshCookie = false;
804
856
  this.pendingLogins.clear();
@@ -896,6 +948,7 @@ var WitniumchainClient = class {
896
948
  });
897
949
  }
898
950
  this.accessToken = tokens.access_token;
951
+ this.accessTokenFromSession = false;
899
952
  if (tokens.refresh_token) {
900
953
  this.refreshToken = tokens.refresh_token;
901
954
  } else {
@@ -1026,13 +1079,9 @@ var WitniumchainClient = class {
1026
1079
  if (opts._isRetry) return false;
1027
1080
  if (opts.auth !== "BearerJWT") return false;
1028
1081
  if (res.status !== 401) return false;
1029
- if (!this.refreshToken && !this.hasRefreshCookie) return false;
1030
- try {
1031
- await this.refreshAccessToken();
1032
- return true;
1033
- } catch {
1082
+ if (!this.refreshToken && !this.hasRefreshCookie && !this.accessTokenFromSession)
1034
1083
  return false;
1035
- }
1084
+ return this.reauthenticateForRetry();
1036
1085
  }
1037
1086
  /**
1038
1087
  * Same decision as `shouldRefreshAndRetry` but for the JSON-parsed path,
@@ -1046,18 +1095,14 @@ var WitniumchainClient = class {
1046
1095
  if (opts._isRetry) return false;
1047
1096
  if (opts.auth !== "BearerJWT") return false;
1048
1097
  if (status !== 401) return false;
1049
- if (!this.refreshToken && !this.hasRefreshCookie) return false;
1098
+ if (!this.refreshToken && !this.hasRefreshCookie && !this.accessTokenFromSession)
1099
+ return false;
1050
1100
  const body = parsed;
1051
1101
  const label = typeof body?.error === "string" ? body.error : void 0;
1052
1102
  if (label !== void 0 && label !== "token_expired" && label !== "invalid_token") {
1053
1103
  return false;
1054
1104
  }
1055
- try {
1056
- await this.refreshAccessToken();
1057
- return true;
1058
- } catch {
1059
- return false;
1060
- }
1105
+ return this.reauthenticateForRetry();
1061
1106
  }
1062
1107
  /**
1063
1108
  * Resolve which base URL to call. Default is accounts (`baseUrl`). When a
@@ -1097,9 +1142,12 @@ var WitniumchainClient = class {
1097
1142
  return;
1098
1143
  }
1099
1144
  case "BearerJWT": {
1145
+ if (!this.accessToken) {
1146
+ await this.ensureSessionAccessToken();
1147
+ }
1100
1148
  if (!this.accessToken) {
1101
1149
  throw new Error(
1102
- `WitniumchainClient: ${method} ${path} requires an OAuth access token. Pass \`accessToken\` to the constructor, or call \`beginOAuthLogin\`/\`completeOAuthLogin\` to obtain one.`
1150
+ `WitniumchainClient: ${method} ${path} requires an OAuth access token. Pass \`accessToken\` to the constructor, call \`beginOAuthLogin\`/\`completeOAuthLogin\`, or \u2014 for a logged-in dashboard \u2014 provide \`sessionCookie\` (Node) or rely on the browser \`wac_session\` cookie so the SDK can mint one.`
1103
1151
  );
1104
1152
  }
1105
1153
  headers["authorization"] = `Bearer ${this.accessToken}`;
@@ -1114,15 +1162,6 @@ var WitniumchainClient = class {
1114
1162
  headers["authorization"] = `Bearer ${this.cfg.orgApiKey}`;
1115
1163
  return;
1116
1164
  }
1117
- case "AdminToken": {
1118
- if (!this.cfg.adminToken) {
1119
- throw new Error(
1120
- `WitniumchainClient: ${method} ${path} requires an admin token. Pass \`adminToken\` to the constructor.`
1121
- );
1122
- }
1123
- headers["authorization"] = `Bearer ${this.cfg.adminToken}`;
1124
- return;
1125
- }
1126
1165
  case "SignedRequest": {
1127
1166
  if (!this.cfg.signedRequest) {
1128
1167
  throw new Error(
@@ -1409,60 +1448,6 @@ async function sha256Hex(input) {
1409
1448
  return out;
1410
1449
  }
1411
1450
 
1412
- // src/admin-client.ts
1413
- var WitniumchainAdminClient = class {
1414
- inner;
1415
- constructor(config) {
1416
- if (!config.adminToken) {
1417
- throw new Error("WitniumchainAdminClient: adminToken is required");
1418
- }
1419
- this.inner = new WitniumchainClient({
1420
- baseUrl: config.baseUrl,
1421
- adminToken: config.adminToken,
1422
- timeout: config.timeout,
1423
- fetch: config.fetch
1424
- });
1425
- }
1426
- /**
1427
- * Mint a new organisation. The returned `apiKey` is shown ONCE — the server
1428
- * only retains its SHA-256 hash. Persist it before the response leaves
1429
- * scope; there is no recovery path.
1430
- *
1431
- * @param body Organisation seed: name, email, optional accountType and
1432
- * signup credit grant, optional skip-email-verification flag.
1433
- */
1434
- createOrganization(body) {
1435
- return this.inner.createOrganization(body);
1436
- }
1437
- /** Flip an org between `metered` (Stripe checkout + credit ledger) and `unlimited` (flat-fee). */
1438
- setAccountType(orgId, accountType) {
1439
- return this.inner.setOrgAccountType(orgId, { accountType });
1440
- }
1441
- /** Mark an org's email as verified. Prerequisite for the org to create users. */
1442
- verifyEmail(orgId) {
1443
- return this.inner.verifyOrganization(orgId);
1444
- }
1445
- /**
1446
- * Rotate the org's API key. The previous `wcorg_live_…` is invalidated and
1447
- * the new key is returned ONCE — same one-time-secret semantics as
1448
- * {@link createOrganization}.
1449
- */
1450
- rotateApiKey(orgId) {
1451
- return this.inner.rotateOrgApiKey(orgId);
1452
- }
1453
- /**
1454
- * Apply a signed credit delta to an org's ledger. Positive `delta` grants
1455
- * credits (goodwill, migration backfill); negative claws them back.
1456
- * Recorded as `reason: adjustment` with the supplied `note`.
1457
- *
1458
- * Use sparingly — this bypasses Stripe and should be auditable from the
1459
- * `note` alone.
1460
- */
1461
- adjustCredits(orgId, delta, note) {
1462
- return this.inner.adjustOrgCredits(orgId, { delta, note });
1463
- }
1464
- };
1465
-
1466
1451
  // src/chain-admin-client.ts
1467
1452
  var WitniumchainChainAdminClient = class {
1468
1453
  baseUrl;
@@ -1653,6 +1638,6 @@ function buildRevokeSigningKeyPayload(args) {
1653
1638
  });
1654
1639
  }
1655
1640
 
1656
- export { DelegatedKeys, MfaNamespace, MfaRecoveryCodes, MfaTotp, OWNER_OP, OauthNamespace, OauthSessions, OrgMembers, OrgUsers, SigningKeys, Subscriptions, WitniumchainAdminClient, WitniumchainApiError, WitniumchainChainAdminClient, WitniumchainClient, WitniumchainOrgClient, buildAddSigningKeyPayload, buildPausePayload, buildRevokeSigningKeyPayload, buildUnpausePayload, defaultVerifierStorage };
1641
+ export { DelegatedKeys, MfaNamespace, MfaRecoveryCodes, MfaTotp, OWNER_OP, OauthNamespace, OauthSessions, OrgMembers, OrgUsers, SigningKeys, Subscriptions, WitniumchainApiError, WitniumchainChainAdminClient, WitniumchainClient, WitniumchainOrgClient, buildAddSigningKeyPayload, buildPausePayload, buildRevokeSigningKeyPayload, buildUnpausePayload, defaultVerifierStorage };
1657
1642
  //# sourceMappingURL=index.mjs.map
1658
1643
  //# sourceMappingURL=index.mjs.map