@atxp/client 0.9.1 → 0.10.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/atxpFetcher.d.ts +8 -1
- package/dist/atxpFetcher.d.ts.map +1 -1
- package/dist/atxpFetcher.js +48 -29
- package/dist/atxpFetcher.js.map +1 -1
- package/dist/index.cjs +47 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +48 -29
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { crypto as crypto$1, OAuthResourceClient, ConsoleLogger, getErrorRecoveryHint, PAYMENT_REQUIRED_ERROR_CODE, isSSEResponse, parseMcpMessages, parsePaymentRequests, paymentRequiredError, DEFAULT_AUTHORIZATION_SERVER,
|
|
1
|
+
import { crypto as crypto$1, OAuthResourceClient, ConsoleLogger, getErrorRecoveryHint, PAYMENT_REQUIRED_ERROR_CODE, isSSEResponse, parseMcpMessages, parsePaymentRequests, paymentRequiredError, DEFAULT_AUTHORIZATION_SERVER, createReactNativeSafeFetch, getIsReactNative, isEnumValue, ChainEnum, CurrencyEnum, NetworkEnum, assertNever, DEFAULT_ATXP_ACCOUNTS_SERVER, MemoryOAuthDb, ATXPAccount } from '@atxp/common';
|
|
2
2
|
export { ATXPAccount } from '@atxp/common';
|
|
3
3
|
import * as oauth from 'oauth4webapi';
|
|
4
4
|
import { BigNumber } from 'bignumber.js';
|
|
@@ -453,6 +453,7 @@ function atxpFetch(config) {
|
|
|
453
453
|
}
|
|
454
454
|
class ATXPFetcher {
|
|
455
455
|
constructor(config) {
|
|
456
|
+
this.oauthClient = null;
|
|
456
457
|
this.defaultPaymentFailureHandler = async (context) => {
|
|
457
458
|
const { payment, error, attemptedNetworks, retryable } = context;
|
|
458
459
|
const recoveryHint = getErrorRecoveryHint(error);
|
|
@@ -512,8 +513,9 @@ class ATXPFetcher {
|
|
|
512
513
|
}
|
|
513
514
|
// Create prospective payment for approval (using first destination for display)
|
|
514
515
|
const firstDest = mappedDestinations[0];
|
|
516
|
+
const accountId = await this.account.getAccountId();
|
|
515
517
|
const prospectivePayment = {
|
|
516
|
-
accountId
|
|
518
|
+
accountId,
|
|
517
519
|
resourceUrl: paymentRequest.resource?.toString() ?? '',
|
|
518
520
|
resourceName: paymentRequest.payeeName ?? '',
|
|
519
521
|
currency: firstDest.currency,
|
|
@@ -547,7 +549,7 @@ class ATXPFetcher {
|
|
|
547
549
|
network: result.chain
|
|
548
550
|
});
|
|
549
551
|
// Submit payment to the server
|
|
550
|
-
const jwt = await paymentMaker.generateJWT({ paymentRequestId, codeChallenge: '', accountId
|
|
552
|
+
const jwt = await paymentMaker.generateJWT({ paymentRequestId, codeChallenge: '', accountId });
|
|
551
553
|
const response = await this.sideChannelFetch(paymentRequestUrl.toString(), {
|
|
552
554
|
method: 'PUT',
|
|
553
555
|
headers: {
|
|
@@ -675,7 +677,8 @@ class ATXPFetcher {
|
|
|
675
677
|
const paymentMakerCount = this.account.paymentMakers.length;
|
|
676
678
|
throw new Error(`Payment maker is missing generateJWT method. Available payment maker count: ${paymentMakerCount}. This indicates the payment maker object does not implement the PaymentMaker interface. If using TypeScript, ensure your payment maker properly implements the PaymentMaker interface.`);
|
|
677
679
|
}
|
|
678
|
-
const
|
|
680
|
+
const accountId = await this.account.getAccountId();
|
|
681
|
+
const authToken = await paymentMaker.generateJWT({ paymentRequestId: '', codeChallenge: codeChallenge, accountId });
|
|
679
682
|
// Make a fetch call to the authorization URL with the payment ID
|
|
680
683
|
// redirect=false is a hack
|
|
681
684
|
// The OAuth spec calls for the authorization url to return with a redirect, but fetch
|
|
@@ -729,42 +732,47 @@ class ATXPFetcher {
|
|
|
729
732
|
if (paymentMaker) {
|
|
730
733
|
// We can do the full OAuth flow - we'll generate a signed JWT and call /authorize on the
|
|
731
734
|
// AS to get a code, then exchange the code for an access token
|
|
732
|
-
const
|
|
735
|
+
const oauthClient = await this.getOAuthClient();
|
|
736
|
+
const authorizationUrl = await oauthClient.makeAuthorizationUrl(error.url, error.resourceServerUrl);
|
|
733
737
|
if (!this.isAllowedAuthServer(authorizationUrl)) {
|
|
734
738
|
throw new Error(`ATXP: resource server ${error.url} is requesting to use ${authorizationUrl} which is not in the allowed list of authorization servers ${this.allowedAuthorizationServers.join(', ')}`);
|
|
735
739
|
}
|
|
736
740
|
try {
|
|
737
741
|
const redirectUrl = await this.makeAuthRequestWithPaymentMaker(authorizationUrl, paymentMaker);
|
|
738
742
|
// Handle the OAuth callback
|
|
739
|
-
await this.
|
|
743
|
+
const oauthClient = await this.getOAuthClient();
|
|
744
|
+
await oauthClient.handleCallback(redirectUrl);
|
|
740
745
|
// Call onAuthorize callback after successful authorization
|
|
746
|
+
const accountId = await this.account.getAccountId();
|
|
741
747
|
await this.onAuthorize({
|
|
742
748
|
authorizationServer: authorizationUrl.origin,
|
|
743
|
-
userId:
|
|
749
|
+
userId: accountId
|
|
744
750
|
});
|
|
745
751
|
}
|
|
746
752
|
catch (authError) {
|
|
747
753
|
// Call onAuthorizeFailure callback if authorization fails
|
|
754
|
+
const accountId = await this.account.getAccountId();
|
|
748
755
|
await this.onAuthorizeFailure({
|
|
749
756
|
authorizationServer: authorizationUrl.origin,
|
|
750
|
-
userId:
|
|
757
|
+
userId: accountId,
|
|
751
758
|
error: authError
|
|
752
759
|
});
|
|
753
760
|
throw authError;
|
|
754
761
|
}
|
|
755
762
|
}
|
|
756
763
|
else {
|
|
757
|
-
// Else, we'll see if we've already got an OAuth token from OUR caller (if any).
|
|
764
|
+
// Else, we'll see if we've already got an OAuth token from OUR caller (if any).
|
|
758
765
|
// If we do, we'll use it to auth to the downstream resource
|
|
759
766
|
// (In pass-through scenarios, the atxpServer() middleware stores the incoming
|
|
760
767
|
// token in the DB under the '' resource URL).
|
|
761
|
-
const
|
|
768
|
+
const accountId = await this.account.getAccountId();
|
|
769
|
+
const existingToken = await this.db.getAccessToken(accountId, '');
|
|
762
770
|
if (!existingToken) {
|
|
763
771
|
this.logger.info(`ATXP: no token found for the current server - we can't exchange a token if we don't have one`);
|
|
764
772
|
throw error;
|
|
765
773
|
}
|
|
766
774
|
const newToken = await this.exchangeToken(existingToken, error.resourceServerUrl);
|
|
767
|
-
this.db.saveAccessToken(
|
|
775
|
+
this.db.saveAccessToken(accountId, error.resourceServerUrl, newToken);
|
|
768
776
|
}
|
|
769
777
|
};
|
|
770
778
|
this.exchangeToken = async (myToken, newResourceUrl) => {
|
|
@@ -808,9 +816,10 @@ class ATXPFetcher {
|
|
|
808
816
|
this.fetch = async (url, init) => {
|
|
809
817
|
let response = null;
|
|
810
818
|
let fetchError = null;
|
|
819
|
+
const oauthClient = await this.getOAuthClient();
|
|
811
820
|
try {
|
|
812
821
|
// Try to fetch the resource
|
|
813
|
-
response = await
|
|
822
|
+
response = await oauthClient.fetch(url, init);
|
|
814
823
|
await this.checkForATXPResponse(response);
|
|
815
824
|
return response;
|
|
816
825
|
}
|
|
@@ -822,7 +831,7 @@ class ATXPFetcher {
|
|
|
822
831
|
await this.authToService(error);
|
|
823
832
|
try {
|
|
824
833
|
// Retry the request once - we should be auth'd now
|
|
825
|
-
response = await
|
|
834
|
+
response = await oauthClient.fetch(url, init);
|
|
826
835
|
await this.checkForATXPResponse(response);
|
|
827
836
|
return response;
|
|
828
837
|
}
|
|
@@ -838,7 +847,7 @@ class ATXPFetcher {
|
|
|
838
847
|
this.logger.info(`Payment required - ATXP client starting payment flow ${mcpError?.data?.paymentRequestUrl}`);
|
|
839
848
|
if (await this.handlePaymentRequestError(mcpError)) {
|
|
840
849
|
// Retry the request once - we should be auth'd now
|
|
841
|
-
response = await
|
|
850
|
+
response = await oauthClient.fetch(url, init);
|
|
842
851
|
await this.checkForATXPResponse(response);
|
|
843
852
|
}
|
|
844
853
|
else {
|
|
@@ -857,26 +866,15 @@ class ATXPFetcher {
|
|
|
857
866
|
};
|
|
858
867
|
const { account, db, destinationMakers, fetchFn = fetch, sideChannelFetch = fetchFn, strict = true, allowInsecureRequests = process.env.NODE_ENV === 'development', allowedAuthorizationServers = [DEFAULT_AUTHORIZATION_SERVER], approvePayment = async () => true, logger = new ConsoleLogger(), onAuthorize = async () => { }, onAuthorizeFailure = async () => { }, onPayment = async () => { }, onPaymentFailure, onPaymentAttemptFailed } = config;
|
|
859
868
|
// Use React Native safe fetch if in React Native environment
|
|
860
|
-
|
|
869
|
+
this.safeFetchFn = getIsReactNative() ? createReactNativeSafeFetch(fetchFn) : fetchFn;
|
|
861
870
|
const safeSideChannelFetch = getIsReactNative() ? createReactNativeSafeFetch(sideChannelFetch) : sideChannelFetch;
|
|
862
|
-
//
|
|
863
|
-
// an authorization url which redirects back to the callback url, ATXPClient posts the payment
|
|
864
|
-
// directly to the authorization server, then does the token exchange itself
|
|
865
|
-
this.oauthClient = new OAuthClient({
|
|
866
|
-
userId: account.accountId,
|
|
867
|
-
db,
|
|
868
|
-
callbackUrl: 'http://localhost:3000/unused-dummy-atxp-callback',
|
|
869
|
-
isPublic: false,
|
|
870
|
-
fetchFn: safeFetchFn,
|
|
871
|
-
sideChannelFetch: safeSideChannelFetch,
|
|
872
|
-
strict,
|
|
873
|
-
allowInsecureRequests,
|
|
874
|
-
logger: logger
|
|
875
|
-
});
|
|
871
|
+
// OAuthClient is initialized lazily in getOAuthClient() since it needs async accountId
|
|
876
872
|
this.account = account;
|
|
877
873
|
this.destinationMakers = destinationMakers;
|
|
878
874
|
this.sideChannelFetch = safeSideChannelFetch;
|
|
879
875
|
this.db = db;
|
|
876
|
+
this.strict = strict;
|
|
877
|
+
this.allowInsecureRequests = allowInsecureRequests;
|
|
880
878
|
this.allowedAuthorizationServers = allowedAuthorizationServers;
|
|
881
879
|
this.approvePayment = approvePayment;
|
|
882
880
|
this.logger = logger;
|
|
@@ -886,6 +884,27 @@ class ATXPFetcher {
|
|
|
886
884
|
this.onPaymentFailure = onPaymentFailure || this.defaultPaymentFailureHandler;
|
|
887
885
|
this.onPaymentAttemptFailed = onPaymentAttemptFailed;
|
|
888
886
|
}
|
|
887
|
+
/**
|
|
888
|
+
* Get or create the OAuthClient (lazy initialization to support async accountId)
|
|
889
|
+
*/
|
|
890
|
+
async getOAuthClient() {
|
|
891
|
+
if (this.oauthClient) {
|
|
892
|
+
return this.oauthClient;
|
|
893
|
+
}
|
|
894
|
+
const accountId = await this.account.getAccountId();
|
|
895
|
+
this.oauthClient = new OAuthClient({
|
|
896
|
+
userId: accountId,
|
|
897
|
+
db: this.db,
|
|
898
|
+
callbackUrl: 'http://localhost:3000/unused-dummy-atxp-callback',
|
|
899
|
+
isPublic: false,
|
|
900
|
+
fetchFn: this.safeFetchFn,
|
|
901
|
+
sideChannelFetch: this.sideChannelFetch,
|
|
902
|
+
strict: this.strict,
|
|
903
|
+
allowInsecureRequests: this.allowInsecureRequests,
|
|
904
|
+
logger: this.logger
|
|
905
|
+
});
|
|
906
|
+
return this.oauthClient;
|
|
907
|
+
}
|
|
889
908
|
}
|
|
890
909
|
|
|
891
910
|
var util$1;
|