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