@witnium-tech/witniumchain 0.12.0 → 0.12.2
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.d.mts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +72 -40
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +72 -40
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -12044,12 +12044,12 @@ declare class WitniumchainClient {
|
|
|
12044
12044
|
private refreshToken;
|
|
12045
12045
|
private hasRefreshCookie;
|
|
12046
12046
|
private readonly oauthClientId;
|
|
12047
|
-
private pendingLogins;
|
|
12048
12047
|
private readonly verifierStorage;
|
|
12049
12048
|
private discoveryCache;
|
|
12050
12049
|
private refreshInFlight;
|
|
12051
12050
|
private accessTokenFromSession;
|
|
12052
12051
|
private sessionExchangeInFlight;
|
|
12052
|
+
private credentialGeneration;
|
|
12053
12053
|
/** Subscriptions / billing helpers. See {@link Subscriptions}. */
|
|
12054
12054
|
readonly subscriptions: Subscriptions;
|
|
12055
12055
|
/** Delegated-key namespace including the one-call {@link DelegatedKeys.provision} flow. */
|
|
@@ -12161,8 +12161,9 @@ declare class WitniumchainClient {
|
|
|
12161
12161
|
enrollTotp(): Promise<TotpEnrollResponse>;
|
|
12162
12162
|
/**
|
|
12163
12163
|
* Confirming bumps the user's security epoch server-side, which ends every
|
|
12164
|
-
* session-minted access token
|
|
12165
|
-
* call mints a fresh
|
|
12164
|
+
* session-minted access token and OAuth artifact of the user. Cached and
|
|
12165
|
+
* in-flight tokens are dropped so the next bearer call mints a fresh one
|
|
12166
|
+
* from the (surviving) session.
|
|
12166
12167
|
*/
|
|
12167
12168
|
confirmTotp(body: TotpConfirmRequest): Promise<TotpConfirmResponse>;
|
|
12168
12169
|
/** Step-up: 403 `mfa_step_up_failed` on a wrong proof, 429 `mfa_rate_limited`. */
|
|
@@ -12280,6 +12281,12 @@ declare class WitniumchainClient {
|
|
|
12280
12281
|
* valid session.
|
|
12281
12282
|
*/
|
|
12282
12283
|
exchangeSessionForToken(): Promise<OAuthTokenSnapshot>;
|
|
12284
|
+
private assertCredentialGeneration;
|
|
12285
|
+
/**
|
|
12286
|
+
* Forget every cached credential and orphan in-flight mints/refreshes, so
|
|
12287
|
+
* none of them can store a token afterwards.
|
|
12288
|
+
*/
|
|
12289
|
+
private invalidateCredentials;
|
|
12283
12290
|
/**
|
|
12284
12291
|
* Best-effort transparent mint for the `BearerJWT` path: if no access token
|
|
12285
12292
|
* is cached, try the session exchange (single-flight). Swallows failure so
|
package/dist/index.d.ts
CHANGED
|
@@ -12044,12 +12044,12 @@ declare class WitniumchainClient {
|
|
|
12044
12044
|
private refreshToken;
|
|
12045
12045
|
private hasRefreshCookie;
|
|
12046
12046
|
private readonly oauthClientId;
|
|
12047
|
-
private pendingLogins;
|
|
12048
12047
|
private readonly verifierStorage;
|
|
12049
12048
|
private discoveryCache;
|
|
12050
12049
|
private refreshInFlight;
|
|
12051
12050
|
private accessTokenFromSession;
|
|
12052
12051
|
private sessionExchangeInFlight;
|
|
12052
|
+
private credentialGeneration;
|
|
12053
12053
|
/** Subscriptions / billing helpers. See {@link Subscriptions}. */
|
|
12054
12054
|
readonly subscriptions: Subscriptions;
|
|
12055
12055
|
/** Delegated-key namespace including the one-call {@link DelegatedKeys.provision} flow. */
|
|
@@ -12161,8 +12161,9 @@ declare class WitniumchainClient {
|
|
|
12161
12161
|
enrollTotp(): Promise<TotpEnrollResponse>;
|
|
12162
12162
|
/**
|
|
12163
12163
|
* Confirming bumps the user's security epoch server-side, which ends every
|
|
12164
|
-
* session-minted access token
|
|
12165
|
-
* call mints a fresh
|
|
12164
|
+
* session-minted access token and OAuth artifact of the user. Cached and
|
|
12165
|
+
* in-flight tokens are dropped so the next bearer call mints a fresh one
|
|
12166
|
+
* from the (surviving) session.
|
|
12166
12167
|
*/
|
|
12167
12168
|
confirmTotp(body: TotpConfirmRequest): Promise<TotpConfirmResponse>;
|
|
12168
12169
|
/** Step-up: 403 `mfa_step_up_failed` on a wrong proof, 429 `mfa_rate_limited`. */
|
|
@@ -12280,6 +12281,12 @@ declare class WitniumchainClient {
|
|
|
12280
12281
|
* valid session.
|
|
12281
12282
|
*/
|
|
12282
12283
|
exchangeSessionForToken(): Promise<OAuthTokenSnapshot>;
|
|
12284
|
+
private assertCredentialGeneration;
|
|
12285
|
+
/**
|
|
12286
|
+
* Forget every cached credential and orphan in-flight mints/refreshes, so
|
|
12287
|
+
* none of them can store a token afterwards.
|
|
12288
|
+
*/
|
|
12289
|
+
private invalidateCredentials;
|
|
12283
12290
|
/**
|
|
12284
12291
|
* Best-effort transparent mint for the `BearerJWT` path: if no access token
|
|
12285
12292
|
* is cached, try the session exchange (single-flight). Swallows failure so
|
package/dist/index.js
CHANGED
|
@@ -94,11 +94,6 @@ var WitniumchainClient = class {
|
|
|
94
94
|
// refresh might work even when the in-memory refresh token is undefined.
|
|
95
95
|
hasRefreshCookie = false;
|
|
96
96
|
oauthClientId;
|
|
97
|
-
// The redirect URI the most recent `beginOAuthLogin` issued, alongside the
|
|
98
|
-
// PKCE verifier. `completeOAuthLogin` reads it back so the token-exchange
|
|
99
|
-
// request matches the original /auth request (RFC 6749 §4.1.3 requires
|
|
100
|
-
// redirect_uri at /token to equal the one at /auth). Keyed by state.
|
|
101
|
-
pendingLogins = /* @__PURE__ */ new Map();
|
|
102
97
|
verifierStorage;
|
|
103
98
|
// Cache of the parsed OIDC discovery document keyed by issuer URL. Saves a
|
|
104
99
|
// round trip on every OAuth call after the first. oidc-provider's discovery
|
|
@@ -121,6 +116,11 @@ var WitniumchainClient = class {
|
|
|
121
116
|
// first BearerJWT call (and any concurrent fan-out) awaits one in-flight
|
|
122
117
|
// mint rather than minting N tokens.
|
|
123
118
|
sessionExchangeInFlight;
|
|
119
|
+
// Bumped whenever the cached credentials stop being valid (sign-out, the
|
|
120
|
+
// TOTP-confirm epoch bump). A mint or refresh that started under an older
|
|
121
|
+
// generation must not store its result: it could be a previous user's
|
|
122
|
+
// token, or one the server has already ended.
|
|
123
|
+
credentialGeneration = 0;
|
|
124
124
|
/** Subscriptions / billing helpers. See {@link Subscriptions}. */
|
|
125
125
|
subscriptions;
|
|
126
126
|
/** Delegated-key namespace including the one-call {@link DelegatedKeys.provision} flow. */
|
|
@@ -502,18 +502,16 @@ var WitniumchainClient = class {
|
|
|
502
502
|
}
|
|
503
503
|
/**
|
|
504
504
|
* Confirming bumps the user's security epoch server-side, which ends every
|
|
505
|
-
* session-minted access token
|
|
506
|
-
* call mints a fresh
|
|
505
|
+
* session-minted access token and OAuth artifact of the user. Cached and
|
|
506
|
+
* in-flight tokens are dropped so the next bearer call mints a fresh one
|
|
507
|
+
* from the (surviving) session.
|
|
507
508
|
*/
|
|
508
509
|
async confirmTotp(body) {
|
|
509
510
|
const res = await this.req("POST", "/v1/account/mfa/totp/confirm", {
|
|
510
511
|
auth: "SessionCookie",
|
|
511
512
|
body
|
|
512
513
|
});
|
|
513
|
-
|
|
514
|
-
this.accessToken = void 0;
|
|
515
|
-
this.accessTokenFromSession = false;
|
|
516
|
-
}
|
|
514
|
+
this.invalidateCredentials();
|
|
517
515
|
return res;
|
|
518
516
|
}
|
|
519
517
|
/** Step-up: 403 `mfa_step_up_failed` on a wrong proof, 429 `mfa_rate_limited`. */
|
|
@@ -563,7 +561,9 @@ var WitniumchainClient = class {
|
|
|
563
561
|
// Routing: every method here passes `service: 'chain'` to `req()`. Calls
|
|
564
562
|
// throw at call time if `chainBaseUrl` wasn't configured.
|
|
565
563
|
//
|
|
566
|
-
// Auth:
|
|
564
|
+
// Auth: the contract, witness, transaction and wallet reads are public on
|
|
565
|
+
// chain-api (anyone can check a witness), so they send no credential. The
|
|
566
|
+
// dashboard reads are BearerJWT: the accounts-issued token already carries
|
|
567
567
|
// `aud=https://api.witniumchain.com`, so the same `accessToken` works
|
|
568
568
|
// unchanged against both services.
|
|
569
569
|
//
|
|
@@ -577,49 +577,49 @@ var WitniumchainClient = class {
|
|
|
577
577
|
return this.req(
|
|
578
578
|
"GET",
|
|
579
579
|
`/v5/contracts/${encodeURIComponent(contractAddress)}/info`,
|
|
580
|
-
{ auth: "
|
|
580
|
+
{ auth: "Public", service: "chain" }
|
|
581
581
|
);
|
|
582
582
|
}
|
|
583
583
|
getContractVerification(contractAddress) {
|
|
584
584
|
return this.req(
|
|
585
585
|
"GET",
|
|
586
586
|
`/v5/contracts/${encodeURIComponent(contractAddress)}/verify`,
|
|
587
|
-
{ auth: "
|
|
587
|
+
{ auth: "Public", service: "chain" }
|
|
588
588
|
);
|
|
589
589
|
}
|
|
590
590
|
listWitnessesV5(contractAddress, params) {
|
|
591
591
|
return this.req(
|
|
592
592
|
"GET",
|
|
593
593
|
`/v5/contracts/${encodeURIComponent(contractAddress)}/witnesses`,
|
|
594
|
-
{ auth: "
|
|
594
|
+
{ auth: "Public", service: "chain", query: params }
|
|
595
595
|
);
|
|
596
596
|
}
|
|
597
597
|
getWitnessV5(contractAddress, witnessId) {
|
|
598
598
|
return this.req(
|
|
599
599
|
"GET",
|
|
600
600
|
`/v5/contracts/${encodeURIComponent(contractAddress)}/witnesses/${encodeURIComponent(witnessId)}`,
|
|
601
|
-
{ auth: "
|
|
601
|
+
{ auth: "Public", service: "chain" }
|
|
602
602
|
);
|
|
603
603
|
}
|
|
604
604
|
getContractTransaction(contractAddress, txHash) {
|
|
605
605
|
return this.req(
|
|
606
606
|
"GET",
|
|
607
607
|
`/v5/contracts/${encodeURIComponent(contractAddress)}/transactions/${encodeURIComponent(txHash)}`,
|
|
608
|
-
{ auth: "
|
|
608
|
+
{ auth: "Public", service: "chain" }
|
|
609
609
|
);
|
|
610
610
|
}
|
|
611
611
|
getTransaction(txHash) {
|
|
612
612
|
return this.req(
|
|
613
613
|
"GET",
|
|
614
614
|
`/v5/transactions/${encodeURIComponent(txHash)}`,
|
|
615
|
-
{ auth: "
|
|
615
|
+
{ auth: "Public", service: "chain" }
|
|
616
616
|
);
|
|
617
617
|
}
|
|
618
618
|
getWalletBalance(address) {
|
|
619
619
|
return this.req(
|
|
620
620
|
"GET",
|
|
621
621
|
`/v5/wallets/${encodeURIComponent(address)}/balance`,
|
|
622
|
-
{ auth: "
|
|
622
|
+
{ auth: "Public", service: "chain" }
|
|
623
623
|
);
|
|
624
624
|
}
|
|
625
625
|
getDashboardContract() {
|
|
@@ -699,8 +699,9 @@ var WitniumchainClient = class {
|
|
|
699
699
|
url.searchParams.set("code_challenge", challenge);
|
|
700
700
|
url.searchParams.set("code_challenge_method", "S256");
|
|
701
701
|
if (args.prompt) url.searchParams.set("prompt", args.prompt);
|
|
702
|
-
this.verifierStorageOrDefault()
|
|
703
|
-
|
|
702
|
+
const storage = this.verifierStorageOrDefault();
|
|
703
|
+
storage.set(state, verifier);
|
|
704
|
+
storage.set(redirectUriKey(state), args.redirectUri);
|
|
704
705
|
return { authorizationUrl: url.toString(), state };
|
|
705
706
|
}
|
|
706
707
|
/**
|
|
@@ -753,24 +754,24 @@ var WitniumchainClient = class {
|
|
|
753
754
|
message: "WitniumchainClient: no PKCE verifier stored for this `state`. The login flow either was not started in this tab, was already completed, or the sessionStorage entry was cleared."
|
|
754
755
|
});
|
|
755
756
|
}
|
|
756
|
-
const
|
|
757
|
-
if (!
|
|
757
|
+
const redirectUri = storage.get(redirectUriKey(state));
|
|
758
|
+
if (!redirectUri) {
|
|
758
759
|
storage.remove(state);
|
|
759
760
|
throw new WitniumchainApiError({
|
|
760
761
|
status: 0,
|
|
761
|
-
message: "WitniumchainClient: PKCE verifier exists but the redirectUri for this `state`
|
|
762
|
+
message: "WitniumchainClient: PKCE verifier exists but the redirectUri stored with it for this `state` is missing."
|
|
762
763
|
});
|
|
763
764
|
}
|
|
764
765
|
const discovery = await this.fetchDiscovery();
|
|
765
766
|
const form = new URLSearchParams();
|
|
766
767
|
form.set("grant_type", "authorization_code");
|
|
767
768
|
form.set("code", code);
|
|
768
|
-
form.set("redirect_uri",
|
|
769
|
+
form.set("redirect_uri", redirectUri);
|
|
769
770
|
form.set("client_id", this.oauthClientId);
|
|
770
771
|
form.set("code_verifier", verifier);
|
|
771
772
|
const tokens = await this.postTokenEndpoint(discovery.token_endpoint, form);
|
|
772
773
|
storage.remove(state);
|
|
773
|
-
|
|
774
|
+
storage.remove(redirectUriKey(state));
|
|
774
775
|
return this.persistTokenResponse(tokens);
|
|
775
776
|
}
|
|
776
777
|
/**
|
|
@@ -791,14 +792,16 @@ var WitniumchainClient = class {
|
|
|
791
792
|
*/
|
|
792
793
|
async refreshAccessToken() {
|
|
793
794
|
if (this.refreshInFlight) return this.refreshInFlight;
|
|
794
|
-
|
|
795
|
+
let flight = void 0;
|
|
796
|
+
flight = (async () => {
|
|
795
797
|
try {
|
|
796
798
|
return await this.refreshAccessTokenInternal();
|
|
797
799
|
} finally {
|
|
798
|
-
this.refreshInFlight = void 0;
|
|
800
|
+
if (this.refreshInFlight === flight) this.refreshInFlight = void 0;
|
|
799
801
|
}
|
|
800
802
|
})();
|
|
801
|
-
|
|
803
|
+
this.refreshInFlight = flight;
|
|
804
|
+
return flight;
|
|
802
805
|
}
|
|
803
806
|
/**
|
|
804
807
|
* Exchange the logged-in session (the `wac_session` cookie) for a short-lived
|
|
@@ -823,9 +826,12 @@ var WitniumchainClient = class {
|
|
|
823
826
|
*/
|
|
824
827
|
async exchangeSessionForToken() {
|
|
825
828
|
if (this.sessionExchangeInFlight) return this.sessionExchangeInFlight;
|
|
826
|
-
|
|
829
|
+
const generation = this.credentialGeneration;
|
|
830
|
+
let flight = void 0;
|
|
831
|
+
flight = (async () => {
|
|
827
832
|
try {
|
|
828
833
|
const res = await this.req("POST", "/v1/auth/access-token", { auth: "SessionCookie" });
|
|
834
|
+
this.assertCredentialGeneration(generation);
|
|
829
835
|
this.accessToken = res.accessToken;
|
|
830
836
|
this.accessTokenFromSession = true;
|
|
831
837
|
this.refreshToken = void 0;
|
|
@@ -835,10 +841,32 @@ var WitniumchainClient = class {
|
|
|
835
841
|
expiresAt: Math.floor(new Date(res.expiresAt).getTime() / 1e3)
|
|
836
842
|
};
|
|
837
843
|
} finally {
|
|
838
|
-
this.sessionExchangeInFlight = void 0;
|
|
844
|
+
if (this.sessionExchangeInFlight === flight) this.sessionExchangeInFlight = void 0;
|
|
839
845
|
}
|
|
840
846
|
})();
|
|
841
|
-
|
|
847
|
+
this.sessionExchangeInFlight = flight;
|
|
848
|
+
return flight;
|
|
849
|
+
}
|
|
850
|
+
assertCredentialGeneration(generation) {
|
|
851
|
+
if (generation !== this.credentialGeneration) {
|
|
852
|
+
throw new WitniumchainApiError({
|
|
853
|
+
status: 0,
|
|
854
|
+
message: "WitniumchainClient: the session changed (sign-out or MFA confirmation) while a token request was in flight; its token was discarded."
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
/**
|
|
859
|
+
* Forget every cached credential and orphan in-flight mints/refreshes, so
|
|
860
|
+
* none of them can store a token afterwards.
|
|
861
|
+
*/
|
|
862
|
+
invalidateCredentials() {
|
|
863
|
+
this.credentialGeneration++;
|
|
864
|
+
this.accessToken = void 0;
|
|
865
|
+
this.accessTokenFromSession = false;
|
|
866
|
+
this.refreshToken = void 0;
|
|
867
|
+
this.hasRefreshCookie = false;
|
|
868
|
+
this.sessionExchangeInFlight = void 0;
|
|
869
|
+
this.refreshInFlight = void 0;
|
|
842
870
|
}
|
|
843
871
|
/**
|
|
844
872
|
* Best-effort transparent mint for the `BearerJWT` path: if no access token
|
|
@@ -850,7 +878,8 @@ var WitniumchainClient = class {
|
|
|
850
878
|
if (this.accessToken) return;
|
|
851
879
|
try {
|
|
852
880
|
await this.exchangeSessionForToken();
|
|
853
|
-
} catch {
|
|
881
|
+
} catch (err) {
|
|
882
|
+
if (!(err instanceof WitniumchainApiError && err.status === 401)) throw err;
|
|
854
883
|
}
|
|
855
884
|
}
|
|
856
885
|
/**
|
|
@@ -891,11 +920,7 @@ var WitniumchainClient = class {
|
|
|
891
920
|
* dropped — which it is, here.
|
|
892
921
|
*/
|
|
893
922
|
signOut() {
|
|
894
|
-
this.
|
|
895
|
-
this.accessTokenFromSession = false;
|
|
896
|
-
this.refreshToken = void 0;
|
|
897
|
-
this.hasRefreshCookie = false;
|
|
898
|
-
this.pendingLogins.clear();
|
|
923
|
+
this.invalidateCredentials();
|
|
899
924
|
}
|
|
900
925
|
// ────────────────────────────────────────────────────────────────────────
|
|
901
926
|
// Internal: OAuth flow helpers
|
|
@@ -1014,6 +1039,7 @@ var WitniumchainClient = class {
|
|
|
1014
1039
|
message: "WitniumchainClient: no refresh credential available. Call `completeOAuthLogin` first \u2014 the server delivers the refresh token either in the response body (Node) or as an HttpOnly cookie (browser)."
|
|
1015
1040
|
});
|
|
1016
1041
|
}
|
|
1042
|
+
const generation = this.credentialGeneration;
|
|
1017
1043
|
const discovery = await this.fetchDiscovery();
|
|
1018
1044
|
const form = new URLSearchParams();
|
|
1019
1045
|
form.set("grant_type", "refresh_token");
|
|
@@ -1021,15 +1047,18 @@ var WitniumchainClient = class {
|
|
|
1021
1047
|
if (this.refreshToken) {
|
|
1022
1048
|
form.set("refresh_token", this.refreshToken);
|
|
1023
1049
|
}
|
|
1050
|
+
let tokens;
|
|
1024
1051
|
try {
|
|
1025
|
-
|
|
1026
|
-
return this.persistTokenResponse(tokens);
|
|
1052
|
+
tokens = await this.postTokenEndpoint(discovery.token_endpoint, form);
|
|
1027
1053
|
} catch (err) {
|
|
1054
|
+
this.assertCredentialGeneration(generation);
|
|
1028
1055
|
this.accessToken = void 0;
|
|
1029
1056
|
this.refreshToken = void 0;
|
|
1030
1057
|
this.hasRefreshCookie = false;
|
|
1031
1058
|
throw err;
|
|
1032
1059
|
}
|
|
1060
|
+
this.assertCredentialGeneration(generation);
|
|
1061
|
+
return this.persistTokenResponse(tokens);
|
|
1033
1062
|
}
|
|
1034
1063
|
// ────────────────────────────────────────────────────────────────────────
|
|
1035
1064
|
// Internal: fetch wrapper that maps non-2xx → WitniumchainApiError
|
|
@@ -1472,6 +1501,9 @@ var MfaRecoveryCodes = class {
|
|
|
1472
1501
|
return this.client.regenerateRecoveryCodes(proof);
|
|
1473
1502
|
}
|
|
1474
1503
|
};
|
|
1504
|
+
function redirectUriKey(state) {
|
|
1505
|
+
return `${state}.redirect_uri`;
|
|
1506
|
+
}
|
|
1475
1507
|
function sleep(ms) {
|
|
1476
1508
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1477
1509
|
}
|