@explorins/pers-sdk-react-native 1.5.26 → 1.5.27
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/README.md +84 -73
- package/dist/hooks/useAnalytics.d.ts +1 -65
- package/dist/hooks/useAnalytics.d.ts.map +1 -1
- package/dist/hooks/useTransactions.d.ts +2 -3
- package/dist/hooks/useTransactions.d.ts.map +1 -1
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +735 -161
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/dist/providers/PersSDKProvider.js +11 -0
- package/dist/providers/react-native-auth-provider.d.ts.map +1 -1
- package/dist/providers/react-native-auth-provider.js +2 -2
- package/dist/providers/rn-dpop-provider.d.ts +19 -0
- package/dist/providers/rn-dpop-provider.d.ts.map +1 -0
- package/dist/providers/rn-dpop-provider.js +76 -0
- package/dist/storage/rn-secure-storage.d.ts +18 -0
- package/dist/storage/rn-secure-storage.d.ts.map +1 -0
- package/dist/storage/rn-secure-storage.js +102 -0
- package/package.json +6 -5
- package/src/hooks/useAnalytics.ts +4 -76
- package/src/hooks/useTransactions.ts +3 -3
- package/src/index.ts +24 -0
- package/src/providers/PersSDKProvider.tsx +13 -1
- package/src/providers/react-native-auth-provider.ts +2 -1
- package/src/providers/rn-dpop-provider.ts +88 -0
- package/src/storage/rn-secure-storage.ts +110 -0
package/dist/index.js
CHANGED
|
@@ -8,10 +8,31 @@ var reactNative = require('react-native');
|
|
|
8
8
|
require('@ethereumjs/tx');
|
|
9
9
|
require('@ethereumjs/util');
|
|
10
10
|
require('@ethereumjs/common');
|
|
11
|
+
var Keychain = require('react-native-keychain');
|
|
11
12
|
var AsyncStorage = require('@react-native-async-storage/async-storage');
|
|
13
|
+
var crypto$1 = require('react-native-quick-crypto');
|
|
12
14
|
var jsxRuntime = require('react/jsx-runtime');
|
|
13
15
|
var react = require('react');
|
|
14
16
|
|
|
17
|
+
function _interopNamespaceDefault(e) {
|
|
18
|
+
var n = Object.create(null);
|
|
19
|
+
if (e) {
|
|
20
|
+
Object.keys(e).forEach(function (k) {
|
|
21
|
+
if (k !== 'default') {
|
|
22
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
23
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
get: function () { return e[k]; }
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
n.default = e;
|
|
31
|
+
return Object.freeze(n);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
var Keychain__namespace = /*#__PURE__*/_interopNamespaceDefault(Keychain);
|
|
35
|
+
|
|
15
36
|
var buffer = {};
|
|
16
37
|
|
|
17
38
|
var base64Js = {};
|
|
@@ -2631,6 +2652,7 @@ exports.ApiKeyType = void 0;
|
|
|
2631
2652
|
ApiKeyType["USER_JWT_REFRESH_TOKEN"] = "USER_JWT_REFRESH_TOKEN";
|
|
2632
2653
|
ApiKeyType["BLOCKCHAIN_WRITER_JWT"] = "BLOCKCHAIN_WRITER_JWT";
|
|
2633
2654
|
ApiKeyType["BLOCKCHAIN_READER_JWT"] = "BLOCKCHAIN_READER_JWT";
|
|
2655
|
+
ApiKeyType["TRANSACTION_JWT_ACCESS_TOKEN"] = "TRANSACTION_JWT_ACCESS_TOKEN";
|
|
2634
2656
|
// TODO: this needs to be removed. However, there is still dependency
|
|
2635
2657
|
ApiKeyType["TENANT_LEGACY_ADMIN"] = "TENANT_ADMIN_JWT";
|
|
2636
2658
|
// TENANT_SYSTEM_API_SECRET = 'TENANT_SYSTEM_API_SECRET',
|
|
@@ -2656,23 +2678,6 @@ exports.Web3ContractTypes = void 0;
|
|
|
2656
2678
|
Web3ContractTypes["TOKEN_CONTRACT"] = "TOKEN_CONTRACT";
|
|
2657
2679
|
})(exports.Web3ContractTypes || (exports.Web3ContractTypes = {}));
|
|
2658
2680
|
|
|
2659
|
-
exports.CampaignConditionType = void 0;
|
|
2660
|
-
(function (CampaignConditionType) {
|
|
2661
|
-
CampaignConditionType["EQUALS"] = "EQUALS";
|
|
2662
|
-
CampaignConditionType["NOT_EQUALS"] = "NOT_EQUALS";
|
|
2663
|
-
CampaignConditionType["GREATER_THAN"] = "GREATER_THAN";
|
|
2664
|
-
CampaignConditionType["LESS_THAN"] = "LESS_THAN";
|
|
2665
|
-
CampaignConditionType["CONTAINS"] = "CONTAINS";
|
|
2666
|
-
CampaignConditionType["IS_PART_OF"] = "IS_PART_OF";
|
|
2667
|
-
})(exports.CampaignConditionType || (exports.CampaignConditionType = {}));
|
|
2668
|
-
|
|
2669
|
-
exports.CampaignTriggerType = void 0;
|
|
2670
|
-
(function (CampaignTriggerType) {
|
|
2671
|
-
CampaignTriggerType["CLAIM_BY_USER"] = "CLAIM_BY_USER";
|
|
2672
|
-
CampaignTriggerType["CLAIM_BY_SYSTEM"] = "CLAIM_BY_SYSTEM";
|
|
2673
|
-
CampaignTriggerType["CLAIM_BY_BUSINESS"] = "CLAIM_BY_BUSINESS";
|
|
2674
|
-
})(exports.CampaignTriggerType || (exports.CampaignTriggerType = {}));
|
|
2675
|
-
|
|
2676
2681
|
/**
|
|
2677
2682
|
* Entity ownership types - defines what types of entities can own resources
|
|
2678
2683
|
*
|
|
@@ -2817,6 +2822,45 @@ exports.RedemptionRedeemStatus = void 0;
|
|
|
2817
2822
|
RedemptionRedeemStatus["FAILED"] = "FAILED"; // Processing failed
|
|
2818
2823
|
})(exports.RedemptionRedeemStatus || (exports.RedemptionRedeemStatus = {}));
|
|
2819
2824
|
|
|
2825
|
+
/**
|
|
2826
|
+
* Trigger Source Types
|
|
2827
|
+
* Defines the different types of triggers that can activate a campaign flow
|
|
2828
|
+
*/
|
|
2829
|
+
exports.TriggerSourceType = void 0;
|
|
2830
|
+
(function (TriggerSourceType) {
|
|
2831
|
+
TriggerSourceType["QR_CODE"] = "QR_CODE";
|
|
2832
|
+
// NFC_TAG = 'NFC_TAG',
|
|
2833
|
+
TriggerSourceType["API_WEBHOOK"] = "API_WEBHOOK";
|
|
2834
|
+
// TRANSACTION = 'TRANSACTION',
|
|
2835
|
+
})(exports.TriggerSourceType || (exports.TriggerSourceType = {}));
|
|
2836
|
+
/**
|
|
2837
|
+
* Source Logic Types
|
|
2838
|
+
* Defines how multiple trigger sources combine to activate a flow
|
|
2839
|
+
* Currently only ANY (OR logic) is implemented
|
|
2840
|
+
*/
|
|
2841
|
+
exports.SourceLogicType = void 0;
|
|
2842
|
+
(function (SourceLogicType) {
|
|
2843
|
+
SourceLogicType["ANY"] = "any";
|
|
2844
|
+
// Future: ALL = 'all', SEQUENCE = 'sequence', WEIGHTED = 'weighted'
|
|
2845
|
+
})(exports.SourceLogicType || (exports.SourceLogicType = {}));
|
|
2846
|
+
|
|
2847
|
+
exports.CampaignConditionType = void 0;
|
|
2848
|
+
(function (CampaignConditionType) {
|
|
2849
|
+
CampaignConditionType["EQUALS"] = "EQUALS";
|
|
2850
|
+
CampaignConditionType["NOT_EQUALS"] = "NOT_EQUALS";
|
|
2851
|
+
CampaignConditionType["GREATER_THAN"] = "GREATER_THAN";
|
|
2852
|
+
CampaignConditionType["LESS_THAN"] = "LESS_THAN";
|
|
2853
|
+
CampaignConditionType["CONTAINS"] = "CONTAINS";
|
|
2854
|
+
CampaignConditionType["IS_PART_OF"] = "IS_PART_OF";
|
|
2855
|
+
})(exports.CampaignConditionType || (exports.CampaignConditionType = {}));
|
|
2856
|
+
|
|
2857
|
+
exports.CampaignTriggerType = void 0;
|
|
2858
|
+
(function (CampaignTriggerType) {
|
|
2859
|
+
CampaignTriggerType["CLAIM_BY_USER"] = "CLAIM_BY_USER";
|
|
2860
|
+
CampaignTriggerType["CLAIM_BY_SYSTEM"] = "CLAIM_BY_SYSTEM";
|
|
2861
|
+
CampaignTriggerType["CLAIM_BY_BUSINESS"] = "CLAIM_BY_BUSINESS";
|
|
2862
|
+
})(exports.CampaignTriggerType || (exports.CampaignTriggerType = {}));
|
|
2863
|
+
|
|
2820
2864
|
const apiPublicKeyTestPrefix = 'pk_test_';
|
|
2821
2865
|
const testnetPrefix = 'testnet-';
|
|
2822
2866
|
const testnetShortPrefix = 'tn-';
|
|
@@ -2937,8 +2981,6 @@ const NativeTokenTypes = {
|
|
|
2937
2981
|
ERC721: 'ERC721'
|
|
2938
2982
|
};
|
|
2939
2983
|
|
|
2940
|
-
/* // ✅ EXPRESSION ALIASES: Results from CASE WHEN expressions and other computed fields
|
|
2941
|
-
& { [key: string]: string | number | Date | undefined }; */
|
|
2942
2984
|
const ANALYTICS_METRIC_TYPES = ['count', 'sum', 'avg', 'min', 'max'];
|
|
2943
2985
|
const ANALYTICS_GROUP_BY_TYPES = ['month', 'week', 'day', 'year', 'quarter'];
|
|
2944
2986
|
|
|
@@ -23836,12 +23878,12 @@ class CampaignApi {
|
|
|
23836
23878
|
params.push(`tag=${encodeURIComponent(options.tag)}`);
|
|
23837
23879
|
if (options.limit)
|
|
23838
23880
|
params.push(`limit=${options.limit}`);
|
|
23839
|
-
if (options.
|
|
23840
|
-
params.push(`
|
|
23841
|
-
if (options.
|
|
23842
|
-
params.push(`
|
|
23843
|
-
if (options.
|
|
23844
|
-
params.push(`
|
|
23881
|
+
if (options.page)
|
|
23882
|
+
params.push(`page=${options.page}`);
|
|
23883
|
+
if (options.sortBy)
|
|
23884
|
+
params.push(`sortBy=${options.sortBy}`);
|
|
23885
|
+
if (options.sortOrder)
|
|
23886
|
+
params.push(`sortOrder=${options.sortOrder}`);
|
|
23845
23887
|
}
|
|
23846
23888
|
if (params.length > 0) {
|
|
23847
23889
|
url += `?${params.join('&')}`;
|
|
@@ -23950,10 +23992,25 @@ class CampaignApi {
|
|
|
23950
23992
|
// ==========================================
|
|
23951
23993
|
/**
|
|
23952
23994
|
* PUBLIC: Get campaign triggers catalog
|
|
23953
|
-
* NEW: GET /
|
|
23995
|
+
* NEW: GET /trigger-sources
|
|
23954
23996
|
*/
|
|
23955
|
-
async getCampaignTriggers() {
|
|
23956
|
-
|
|
23997
|
+
async getCampaignTriggers(options) {
|
|
23998
|
+
let url = '/trigger-sources';
|
|
23999
|
+
const params = [];
|
|
24000
|
+
if (options) {
|
|
24001
|
+
if (options.limit)
|
|
24002
|
+
params.push(`limit=${options.limit}`);
|
|
24003
|
+
if (options.page)
|
|
24004
|
+
params.push(`page=${options.page}`);
|
|
24005
|
+
if (options.sortBy)
|
|
24006
|
+
params.push(`sortBy=${options.sortBy}`);
|
|
24007
|
+
if (options.sortOrder)
|
|
24008
|
+
params.push(`sortOrder=${options.sortOrder}`);
|
|
24009
|
+
}
|
|
24010
|
+
if (params.length > 0) {
|
|
24011
|
+
url += `?${params.join('&')}`;
|
|
24012
|
+
}
|
|
24013
|
+
return this.apiClient.get(url);
|
|
23957
24014
|
}
|
|
23958
24015
|
/**
|
|
23959
24016
|
* ADMIN: Create campaign trigger
|
|
@@ -23983,26 +24040,12 @@ class CampaignApi {
|
|
|
23983
24040
|
async setCampaignTrigger(campaignId, triggerId) {
|
|
23984
24041
|
return this.apiClient.put(`/campaign-triggers/${triggerId}/assign/${campaignId}`, {});
|
|
23985
24042
|
}
|
|
23986
|
-
/**
|
|
23987
|
-
* ADMIN: Create trigger condition
|
|
23988
|
-
* NEW: POST /campaign-triggers/conditions
|
|
23989
|
-
*/
|
|
23990
|
-
async createTriggerCondition(condition) {
|
|
23991
|
-
return this.apiClient.post('/campaign-triggers/conditions', condition);
|
|
23992
|
-
}
|
|
23993
|
-
/**
|
|
23994
|
-
* ADMIN: Update trigger condition
|
|
23995
|
-
* NEW: PUT /campaign-triggers/conditions/{id}
|
|
23996
|
-
*/
|
|
23997
|
-
async updateTriggerCondition(conditionId, condition) {
|
|
23998
|
-
return this.apiClient.put(`/campaign-triggers/conditions/${conditionId}`, condition);
|
|
23999
|
-
}
|
|
24000
24043
|
/**
|
|
24001
24044
|
* ADMIN: Add/Remove condition to trigger
|
|
24002
24045
|
* NEW: PUT /campaign-triggers/{triggerId}/conditions/{conditionId}
|
|
24003
24046
|
*/
|
|
24004
|
-
async addOrRemoveConditionToTrigger(triggerId,
|
|
24005
|
-
return this.apiClient.put(`/campaign-triggers/${triggerId}/conditions
|
|
24047
|
+
async addOrRemoveConditionToTrigger(triggerId, condition) {
|
|
24048
|
+
return this.apiClient.put(`/campaign-triggers/${triggerId}/conditions`, condition);
|
|
24006
24049
|
}
|
|
24007
24050
|
// ==========================================
|
|
24008
24051
|
// BUSINESS ENGAGEMENTS (/campaign-engagements)
|
|
@@ -24589,26 +24632,11 @@ class TransactionApi {
|
|
|
24589
24632
|
/**
|
|
24590
24633
|
* AUTH: Submit client-side signed transaction
|
|
24591
24634
|
*
|
|
24592
|
-
* NEW ENDPOINT: POST /transactions/
|
|
24635
|
+
* NEW ENDPOINT: POST /transactions/submit
|
|
24593
24636
|
*/
|
|
24594
24637
|
async submitSignedTransaction(signedTxData) {
|
|
24595
24638
|
return this.apiClient.post(`${this.basePath}/submit`, signedTxData);
|
|
24596
24639
|
}
|
|
24597
|
-
/**
|
|
24598
|
-
* AUTH: Burn user tokens
|
|
24599
|
-
*
|
|
24600
|
-
* UPDATED: Uses new user transaction endpoint with burn-specific parameters
|
|
24601
|
-
* Note: This might need backend confirmation on burn functionality implementation
|
|
24602
|
-
*/
|
|
24603
|
-
/* async burnUserTokens(request: UserBurnTokenRequestDTO): Promise<TransactionRequestResponseDTO> {
|
|
24604
|
-
// Map burn request to TransactionRequestDTO format for new endpoint
|
|
24605
|
-
const transactionRequest: TransactionRequestDTO = {
|
|
24606
|
-
...request,
|
|
24607
|
-
// Add any specific burn transaction parameters here
|
|
24608
|
-
} as any;
|
|
24609
|
-
|
|
24610
|
-
return this.apiClient.post<TransactionRequestResponseDTO>(`${this.basePath}`, transactionRequest);
|
|
24611
|
-
} */
|
|
24612
24640
|
// ==========================================
|
|
24613
24641
|
// BUSINESS OPERATIONS
|
|
24614
24642
|
// ==========================================
|
|
@@ -24633,9 +24661,9 @@ class TransactionApi {
|
|
|
24633
24661
|
return this.apiClient.post<TransactionDTO>(`${this.basePath}/system`, request);
|
|
24634
24662
|
} */
|
|
24635
24663
|
/**
|
|
24636
|
-
* AUTH: Prepare client signed transaction
|
|
24664
|
+
* AUTH: Prepare client signed transaction (Create new transaction for signing)
|
|
24637
24665
|
*
|
|
24638
|
-
* UPDATED: /transaction/auth/prepare-signing → /transactions
|
|
24666
|
+
* UPDATED: /transaction/auth/prepare-signing → /transactions (POST)
|
|
24639
24667
|
*/
|
|
24640
24668
|
async prepareClientSignedTransaction(request) {
|
|
24641
24669
|
return this.apiClient.post(`${this.basePath}`, request);
|
|
@@ -24659,45 +24687,7 @@ class TransactionApi {
|
|
|
24659
24687
|
* UPDATED: /transaction/admin → /transactions (same endpoint, better structure)
|
|
24660
24688
|
*/
|
|
24661
24689
|
async getPaginatedTransactions(params) {
|
|
24662
|
-
|
|
24663
|
-
const queryParams = {
|
|
24664
|
-
page: params.page.toString(),
|
|
24665
|
-
limit: params.limit.toString()
|
|
24666
|
-
};
|
|
24667
|
-
// Add sorting parameters if provided
|
|
24668
|
-
if (params.sortBy) {
|
|
24669
|
-
queryParams['sortBy'] = params.sortBy;
|
|
24670
|
-
}
|
|
24671
|
-
if (params.sortOrder) {
|
|
24672
|
-
queryParams['sortOrder'] = params.sortOrder;
|
|
24673
|
-
}
|
|
24674
|
-
// Add user-specific filtering if provided
|
|
24675
|
-
if (params.participantId) {
|
|
24676
|
-
queryParams['participantId'] = params.participantId;
|
|
24677
|
-
}
|
|
24678
|
-
// Add status filtering if provided
|
|
24679
|
-
if (params.status) {
|
|
24680
|
-
queryParams['status'] = params.status;
|
|
24681
|
-
}
|
|
24682
|
-
// Add additional filters if provided
|
|
24683
|
-
if (params.filters) {
|
|
24684
|
-
if (params.filters.startDate) {
|
|
24685
|
-
queryParams['startDate'] = params.filters.startDate;
|
|
24686
|
-
}
|
|
24687
|
-
if (params.filters.endDate) {
|
|
24688
|
-
queryParams['endDate'] = params.filters.endDate;
|
|
24689
|
-
}
|
|
24690
|
-
if (params.filters.type && params.filters.type.length > 0) {
|
|
24691
|
-
queryParams['type'] = params.filters.type.join(',');
|
|
24692
|
-
}
|
|
24693
|
-
if (params.filters.tokenType && params.filters.tokenType.length > 0) {
|
|
24694
|
-
queryParams['tokenType'] = params.filters.tokenType.join(',');
|
|
24695
|
-
}
|
|
24696
|
-
}
|
|
24697
|
-
// Build query string
|
|
24698
|
-
const queryString = Object.entries(queryParams)
|
|
24699
|
-
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
|
|
24700
|
-
.join('&');
|
|
24690
|
+
const queryString = this.buildQueryParams(params).toString();
|
|
24701
24691
|
return this.apiClient.get(`${this.basePath}?${queryString}`);
|
|
24702
24692
|
}
|
|
24703
24693
|
/**
|
|
@@ -24750,6 +24740,43 @@ class TransactionApi {
|
|
|
24750
24740
|
async getTransactionAnalytics(analyticsRequest) {
|
|
24751
24741
|
return this.apiClient.post(`${this.basePath}/analytics`, analyticsRequest);
|
|
24752
24742
|
}
|
|
24743
|
+
/**
|
|
24744
|
+
* Helper to convert DTO object to URLSearchParams
|
|
24745
|
+
* Handles nested 'filters' object and arrays correctly.
|
|
24746
|
+
*/
|
|
24747
|
+
buildQueryParams(params) {
|
|
24748
|
+
const query = new URLSearchParams();
|
|
24749
|
+
// 1. Handle Root Pagination Fields
|
|
24750
|
+
if (params.page !== undefined)
|
|
24751
|
+
query.append('page', params.page.toString());
|
|
24752
|
+
if (params.limit !== undefined)
|
|
24753
|
+
query.append('limit', params.limit.toString());
|
|
24754
|
+
if (params.sortBy)
|
|
24755
|
+
query.append('sortBy', params.sortBy);
|
|
24756
|
+
if (params.sortOrder)
|
|
24757
|
+
query.append('sortOrder', params.sortOrder);
|
|
24758
|
+
// 2. Handle Nested Filters
|
|
24759
|
+
if (params.filters) {
|
|
24760
|
+
Object.entries(params.filters).forEach(([key, value]) => {
|
|
24761
|
+
// Skip undefined/null values
|
|
24762
|
+
if (value === undefined || value === null || value === '')
|
|
24763
|
+
return;
|
|
24764
|
+
if (Array.isArray(value)) {
|
|
24765
|
+
// Handle Arrays: NestJS expects 'status=A&status=B'
|
|
24766
|
+
value.forEach((item) => query.append(key, String(item)));
|
|
24767
|
+
}
|
|
24768
|
+
else {
|
|
24769
|
+
// Handle Single Values: 'search=0x123'
|
|
24770
|
+
// NOTE: Backend Controller expects flat query params for filters
|
|
24771
|
+
// e.g. ?search=...&status=... NOT ?filters[search]=...
|
|
24772
|
+
// This mapping flattens 'filters.search' -> 'search' to match @Query('search') in Controller
|
|
24773
|
+
const stringValue = (value instanceof Date) ? value.toISOString() : String(value);
|
|
24774
|
+
query.append(key, stringValue);
|
|
24775
|
+
}
|
|
24776
|
+
});
|
|
24777
|
+
}
|
|
24778
|
+
return query;
|
|
24779
|
+
}
|
|
24753
24780
|
}
|
|
24754
24781
|
|
|
24755
24782
|
/**
|
|
@@ -24792,17 +24819,17 @@ class TransactionService {
|
|
|
24792
24819
|
return this.transactionApi.getUserTransactionHistory(role, limit);
|
|
24793
24820
|
}
|
|
24794
24821
|
/**
|
|
24795
|
-
* AUTH: Prepare client
|
|
24822
|
+
* AUTH: Prepare existing transaction for client-side signing
|
|
24796
24823
|
*/
|
|
24797
|
-
|
|
24798
|
-
|
|
24799
|
-
}
|
|
24824
|
+
async prepareExistingTransaction(transactionId) {
|
|
24825
|
+
return this.transactionApi.prepareExistingTransaction(transactionId);
|
|
24826
|
+
}
|
|
24800
24827
|
/**
|
|
24801
|
-
* AUTH:
|
|
24828
|
+
* AUTH: Prepare client signed transaction
|
|
24802
24829
|
*/
|
|
24803
|
-
|
|
24804
|
-
|
|
24805
|
-
}
|
|
24830
|
+
async prepareClientSignedTransaction(request) {
|
|
24831
|
+
return this.transactionApi.prepareClientSignedTransaction(request);
|
|
24832
|
+
}
|
|
24806
24833
|
// ==========================================
|
|
24807
24834
|
// BUSINESS OPERATIONS
|
|
24808
24835
|
// ==========================================
|
|
@@ -24839,6 +24866,27 @@ class TransactionService {
|
|
|
24839
24866
|
async exportTransactionsCSV() {
|
|
24840
24867
|
return this.transactionApi.exportTransactionsCSV();
|
|
24841
24868
|
}
|
|
24869
|
+
// ==========================================
|
|
24870
|
+
// QUERY & ANALYTICS OPERATIONS
|
|
24871
|
+
// ==========================================
|
|
24872
|
+
/**
|
|
24873
|
+
* Query transactions by sender
|
|
24874
|
+
*/
|
|
24875
|
+
async queryTransactionsBySender(accountSelector) {
|
|
24876
|
+
return this.transactionApi.queryTransactionsBySender(accountSelector);
|
|
24877
|
+
}
|
|
24878
|
+
/**
|
|
24879
|
+
* Query transactions by recipient
|
|
24880
|
+
*/
|
|
24881
|
+
async queryTransactionsByRecipient(accountSelector) {
|
|
24882
|
+
return this.transactionApi.queryTransactionsByRecipient(accountSelector);
|
|
24883
|
+
}
|
|
24884
|
+
/**
|
|
24885
|
+
* ADMIN: Get transaction analytics
|
|
24886
|
+
*/
|
|
24887
|
+
async getTransactionAnalytics(analyticsRequest) {
|
|
24888
|
+
return this.transactionApi.getTransactionAnalytics(analyticsRequest);
|
|
24889
|
+
}
|
|
24842
24890
|
}
|
|
24843
24891
|
|
|
24844
24892
|
/**
|
|
@@ -26172,14 +26220,19 @@ const AUTH_STORAGE_KEYS = {
|
|
|
26172
26220
|
};
|
|
26173
26221
|
/**
|
|
26174
26222
|
* LocalStorage implementation
|
|
26223
|
+
* Note: Forces string conversion for compatibility
|
|
26175
26224
|
*/
|
|
26176
26225
|
class LocalStorageTokenStorage {
|
|
26226
|
+
constructor() {
|
|
26227
|
+
this.supportsObjects = false;
|
|
26228
|
+
}
|
|
26177
26229
|
async get(key) {
|
|
26178
26230
|
return typeof localStorage !== 'undefined' ? localStorage.getItem(key) : null;
|
|
26179
26231
|
}
|
|
26180
26232
|
async set(key, value) {
|
|
26181
26233
|
if (typeof localStorage !== 'undefined') {
|
|
26182
|
-
|
|
26234
|
+
const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
26235
|
+
localStorage.setItem(key, stringValue);
|
|
26183
26236
|
}
|
|
26184
26237
|
}
|
|
26185
26238
|
async remove(key) {
|
|
@@ -26200,6 +26253,7 @@ class LocalStorageTokenStorage {
|
|
|
26200
26253
|
*/
|
|
26201
26254
|
class MemoryTokenStorage {
|
|
26202
26255
|
constructor() {
|
|
26256
|
+
this.supportsObjects = true;
|
|
26203
26257
|
this.store = new Map();
|
|
26204
26258
|
}
|
|
26205
26259
|
async get(key) {
|
|
@@ -26223,26 +26277,30 @@ class AuthTokenManager {
|
|
|
26223
26277
|
this.storage = storage;
|
|
26224
26278
|
}
|
|
26225
26279
|
async getAccessToken() {
|
|
26226
|
-
|
|
26280
|
+
const val = await this.storage.get(AUTH_STORAGE_KEYS.ACCESS_TOKEN);
|
|
26281
|
+
// Ensure we return string for tokens
|
|
26282
|
+
return typeof val === 'string' ? val : null;
|
|
26227
26283
|
}
|
|
26228
26284
|
async setAccessToken(token) {
|
|
26229
26285
|
await this.storage.set(AUTH_STORAGE_KEYS.ACCESS_TOKEN, token);
|
|
26230
26286
|
}
|
|
26231
26287
|
async getRefreshToken() {
|
|
26232
|
-
|
|
26288
|
+
const val = await this.storage.get(AUTH_STORAGE_KEYS.REFRESH_TOKEN);
|
|
26289
|
+
return typeof val === 'string' ? val : null;
|
|
26233
26290
|
}
|
|
26234
26291
|
async setRefreshToken(token) {
|
|
26235
26292
|
await this.storage.set(AUTH_STORAGE_KEYS.REFRESH_TOKEN, token);
|
|
26236
26293
|
}
|
|
26237
26294
|
async getProviderToken() {
|
|
26238
|
-
|
|
26295
|
+
const val = await this.storage.get(AUTH_STORAGE_KEYS.PROVIDER_TOKEN);
|
|
26296
|
+
return typeof val === 'string' ? val : null;
|
|
26239
26297
|
}
|
|
26240
26298
|
async setProviderToken(token) {
|
|
26241
26299
|
await this.storage.set(AUTH_STORAGE_KEYS.PROVIDER_TOKEN, token);
|
|
26242
26300
|
}
|
|
26243
26301
|
async getAuthType() {
|
|
26244
26302
|
const authType = await this.storage.get(AUTH_STORAGE_KEYS.AUTH_TYPE);
|
|
26245
|
-
return authType;
|
|
26303
|
+
return typeof authType === 'string' ? authType : null;
|
|
26246
26304
|
}
|
|
26247
26305
|
async setAuthType(authType) {
|
|
26248
26306
|
await this.storage.set(AUTH_STORAGE_KEYS.AUTH_TYPE, authType);
|
|
@@ -26267,6 +26325,218 @@ class AuthTokenManager {
|
|
|
26267
26325
|
}
|
|
26268
26326
|
}
|
|
26269
26327
|
|
|
26328
|
+
class WebDPoPCryptoProvider {
|
|
26329
|
+
get crypto() {
|
|
26330
|
+
// Basic environment check
|
|
26331
|
+
if (typeof crypto !== 'undefined')
|
|
26332
|
+
return crypto;
|
|
26333
|
+
if (typeof window !== 'undefined' && window.crypto)
|
|
26334
|
+
return window.crypto;
|
|
26335
|
+
if (typeof globalThis !== 'undefined' && globalThis.crypto)
|
|
26336
|
+
return globalThis.crypto;
|
|
26337
|
+
throw new Error('WebCrypto API not found. Please polyfill crypto.subtle or use a different DPoPCryptoProvider.');
|
|
26338
|
+
}
|
|
26339
|
+
async generateKeyPair(options) {
|
|
26340
|
+
const extractable = options?.extractable ?? true;
|
|
26341
|
+
const keyPair = await this.crypto.subtle.generateKey({
|
|
26342
|
+
name: 'ECDSA',
|
|
26343
|
+
namedCurve: 'P-256',
|
|
26344
|
+
}, extractable, // Configurable extractability
|
|
26345
|
+
['sign', 'verify']);
|
|
26346
|
+
const publicKey = await this.crypto.subtle.exportKey('jwk', keyPair.publicKey);
|
|
26347
|
+
// If extractable, export private key as JWK (plain object)
|
|
26348
|
+
// If NOT extractable, keep it as CryptoKey (native handle)
|
|
26349
|
+
// However, DPoPKeyPair interface currently expects JsonWebKey | CryptoKey
|
|
26350
|
+
// We need to check DPoPKeyPair type definition.
|
|
26351
|
+
// Wait, DPoPKeyPair definition in dpop.types.ts says:
|
|
26352
|
+
// publicKey: JsonWebKey; privateKey: JsonWebKey;
|
|
26353
|
+
// I need to change that to support CryptoKey.
|
|
26354
|
+
// Let's re-read dpop.types.ts
|
|
26355
|
+
// I assumed it might be compatible but let's verify.
|
|
26356
|
+
// If I change privateKey to unknown or JsonWebKey | CryptoKey, it will be safer.
|
|
26357
|
+
let privateKey;
|
|
26358
|
+
if (extractable) {
|
|
26359
|
+
privateKey = await this.crypto.subtle.exportKey('jwk', keyPair.privateKey);
|
|
26360
|
+
}
|
|
26361
|
+
else {
|
|
26362
|
+
privateKey = keyPair.privateKey;
|
|
26363
|
+
}
|
|
26364
|
+
return { publicKey, privateKey };
|
|
26365
|
+
}
|
|
26366
|
+
async signProof(payload, keyPair) {
|
|
26367
|
+
// 1. Prepare Header
|
|
26368
|
+
const header = {
|
|
26369
|
+
typ: 'dpop+jwt',
|
|
26370
|
+
alg: 'ES256',
|
|
26371
|
+
jwk: keyPair.publicKey
|
|
26372
|
+
};
|
|
26373
|
+
// 2. Prepare Payload
|
|
26374
|
+
// Ensure jti and iat if not present (though Manager usually provides them)
|
|
26375
|
+
const finalPayload = {
|
|
26376
|
+
...payload,
|
|
26377
|
+
iat: payload.iat || Math.floor(Date.now() / 1000),
|
|
26378
|
+
jti: payload.jti || this.generateUUID()
|
|
26379
|
+
};
|
|
26380
|
+
// 3. Encode segments
|
|
26381
|
+
const encodedHeader = this.base64UrlEncode(JSON.stringify(header));
|
|
26382
|
+
const encodedPayload = this.base64UrlEncode(JSON.stringify(finalPayload));
|
|
26383
|
+
const signingInput = `${encodedHeader}.${encodedPayload}`;
|
|
26384
|
+
// 4. Import private key for signing
|
|
26385
|
+
let privateKey;
|
|
26386
|
+
// Check if it's already a CryptoKey (non-extractable handle)
|
|
26387
|
+
// We check for 'algorithm' property which signals a CryptoKey object
|
|
26388
|
+
if (keyPair.privateKey.algorithm) {
|
|
26389
|
+
privateKey = keyPair.privateKey;
|
|
26390
|
+
}
|
|
26391
|
+
else {
|
|
26392
|
+
// Otherwise import from JWK
|
|
26393
|
+
privateKey = await this.crypto.subtle.importKey('jwk', keyPair.privateKey, {
|
|
26394
|
+
name: 'ECDSA',
|
|
26395
|
+
namedCurve: 'P-256',
|
|
26396
|
+
}, false, ['sign']);
|
|
26397
|
+
}
|
|
26398
|
+
// 5. Sign
|
|
26399
|
+
const signatureBuffer = await this.crypto.subtle.sign({
|
|
26400
|
+
name: 'ECDSA',
|
|
26401
|
+
hash: { name: 'SHA-256' },
|
|
26402
|
+
}, privateKey, new TextEncoder().encode(signingInput));
|
|
26403
|
+
const encodedSignature = this.base64UrlEncodeBuffer(signatureBuffer);
|
|
26404
|
+
return `${signingInput}.${encodedSignature}`;
|
|
26405
|
+
}
|
|
26406
|
+
async hashAccessToken(accessToken) {
|
|
26407
|
+
const encoder = new TextEncoder();
|
|
26408
|
+
const data = encoder.encode(accessToken);
|
|
26409
|
+
const hashBuffer = await this.crypto.subtle.digest('SHA-256', data);
|
|
26410
|
+
return this.base64UrlEncodeBuffer(hashBuffer);
|
|
26411
|
+
}
|
|
26412
|
+
// --- Helpers ---
|
|
26413
|
+
generateUUID() {
|
|
26414
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
26415
|
+
return crypto.randomUUID();
|
|
26416
|
+
}
|
|
26417
|
+
// Fallback for older envs
|
|
26418
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
26419
|
+
const r = (Math.random() * 16) | 0;
|
|
26420
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
26421
|
+
return v.toString(16);
|
|
26422
|
+
});
|
|
26423
|
+
}
|
|
26424
|
+
base64UrlEncode(str) {
|
|
26425
|
+
// Use TextEncoder to handle UTF-8 properly
|
|
26426
|
+
const encoder = new TextEncoder();
|
|
26427
|
+
return this.base64UrlEncodeBuffer(encoder.encode(str));
|
|
26428
|
+
}
|
|
26429
|
+
base64UrlEncodeBuffer(buffer) {
|
|
26430
|
+
const bytes = new Uint8Array(buffer);
|
|
26431
|
+
let binary = '';
|
|
26432
|
+
const len = bytes.byteLength;
|
|
26433
|
+
// Chunk processing for large buffers if needed, but proofs are small
|
|
26434
|
+
for (let i = 0; i < len; i++) {
|
|
26435
|
+
binary += String.fromCharCode(bytes[i]);
|
|
26436
|
+
}
|
|
26437
|
+
// btoa is widely available in browsers.
|
|
26438
|
+
// In Node (non-globals), we might need Buffer.
|
|
26439
|
+
// For universal 'platform agnostic' aiming at standard web-like envs:
|
|
26440
|
+
let base64 = '';
|
|
26441
|
+
if (typeof btoa === 'function') {
|
|
26442
|
+
base64 = btoa(binary);
|
|
26443
|
+
}
|
|
26444
|
+
else if (typeof Buffer !== 'undefined') {
|
|
26445
|
+
base64 = Buffer.from(binary, 'binary').toString('base64');
|
|
26446
|
+
}
|
|
26447
|
+
else {
|
|
26448
|
+
throw new Error('Base64 encoding not supported in this environment');
|
|
26449
|
+
}
|
|
26450
|
+
return base64
|
|
26451
|
+
.replace(/\+/g, '-')
|
|
26452
|
+
.replace(/\//g, '_')
|
|
26453
|
+
.replace(/=+$/, '');
|
|
26454
|
+
}
|
|
26455
|
+
}
|
|
26456
|
+
|
|
26457
|
+
const DPOP_STORAGE_KEYS = {
|
|
26458
|
+
PUBLIC: 'pers_dpop_public_key',
|
|
26459
|
+
PRIVATE: 'pers_dpop_private_key'
|
|
26460
|
+
};
|
|
26461
|
+
class DPoPManager {
|
|
26462
|
+
constructor(storage, cryptoProvider) {
|
|
26463
|
+
this.storage = storage;
|
|
26464
|
+
this.memoryKeyPair = null;
|
|
26465
|
+
this.cryptoProvider = cryptoProvider || new WebDPoPCryptoProvider();
|
|
26466
|
+
}
|
|
26467
|
+
/**
|
|
26468
|
+
* Ensures a DPoP key pair exists, loading from storage or generating new one.
|
|
26469
|
+
*/
|
|
26470
|
+
async ensureKeyPair() {
|
|
26471
|
+
if (this.memoryKeyPair)
|
|
26472
|
+
return this.memoryKeyPair;
|
|
26473
|
+
const storedPublic = await this.storage.get(DPOP_STORAGE_KEYS.PUBLIC);
|
|
26474
|
+
const storedPrivate = await this.storage.get(DPOP_STORAGE_KEYS.PRIVATE);
|
|
26475
|
+
if (storedPublic && storedPrivate) {
|
|
26476
|
+
try {
|
|
26477
|
+
// Handle polymorphic storage: String (JSON) or Object (CryptoKey)
|
|
26478
|
+
const publicKey = typeof storedPublic === 'string' ? JSON.parse(storedPublic) : storedPublic;
|
|
26479
|
+
const privateKey = typeof storedPrivate === 'string' ? JSON.parse(storedPrivate) : storedPrivate;
|
|
26480
|
+
this.memoryKeyPair = { publicKey, privateKey };
|
|
26481
|
+
return this.memoryKeyPair;
|
|
26482
|
+
}
|
|
26483
|
+
catch (e) {
|
|
26484
|
+
console.warn('Corrupted DPoP keys in storage, regenerating.');
|
|
26485
|
+
}
|
|
26486
|
+
}
|
|
26487
|
+
// Generate new key pair
|
|
26488
|
+
// Adaptation: If storage supports raw objects (like IndexedDB or Native Keychain),
|
|
26489
|
+
// we can generate Non-Extractable keys for maximum security.
|
|
26490
|
+
// If storage is text-only (LocalStorage), we must use Extractable keys to serialize them.
|
|
26491
|
+
const useHighSecurity = !!this.storage.supportsObjects;
|
|
26492
|
+
const keyPair = await this.cryptoProvider.generateKeyPair({
|
|
26493
|
+
extractable: !useHighSecurity
|
|
26494
|
+
});
|
|
26495
|
+
// Save to storage
|
|
26496
|
+
await this.storage.set(DPOP_STORAGE_KEYS.PUBLIC, keyPair.publicKey);
|
|
26497
|
+
await this.storage.set(DPOP_STORAGE_KEYS.PRIVATE, keyPair.privateKey);
|
|
26498
|
+
this.memoryKeyPair = keyPair;
|
|
26499
|
+
return keyPair;
|
|
26500
|
+
}
|
|
26501
|
+
/**
|
|
26502
|
+
* Generates the DPoP proof header value.
|
|
26503
|
+
* @param method HTTP method
|
|
26504
|
+
* @param url Request URL
|
|
26505
|
+
* @param accessToken Optional access token to bind the proof to (ath claim)
|
|
26506
|
+
*/
|
|
26507
|
+
async generateProofHeader(method, url, accessToken) {
|
|
26508
|
+
const keyPair = await this.ensureKeyPair();
|
|
26509
|
+
const payload = {
|
|
26510
|
+
htm: method,
|
|
26511
|
+
htu: url,
|
|
26512
|
+
};
|
|
26513
|
+
if (accessToken) {
|
|
26514
|
+
payload.ath = await this.cryptoProvider.hashAccessToken(accessToken);
|
|
26515
|
+
}
|
|
26516
|
+
return this.cryptoProvider.signProof(payload, keyPair);
|
|
26517
|
+
}
|
|
26518
|
+
/**
|
|
26519
|
+
* Clears DPoP keys (e.g. on logout)
|
|
26520
|
+
*/
|
|
26521
|
+
async clearKeys() {
|
|
26522
|
+
this.memoryKeyPair = null;
|
|
26523
|
+
await this.storage.remove(DPOP_STORAGE_KEYS.PUBLIC);
|
|
26524
|
+
await this.storage.remove(DPOP_STORAGE_KEYS.PRIVATE);
|
|
26525
|
+
}
|
|
26526
|
+
/**
|
|
26527
|
+
* Gets the public key (for debugging/inspection)
|
|
26528
|
+
*/
|
|
26529
|
+
async getPublicKey() {
|
|
26530
|
+
try {
|
|
26531
|
+
const kp = await this.ensureKeyPair();
|
|
26532
|
+
return kp.publicKey;
|
|
26533
|
+
}
|
|
26534
|
+
catch {
|
|
26535
|
+
return null;
|
|
26536
|
+
}
|
|
26537
|
+
}
|
|
26538
|
+
}
|
|
26539
|
+
|
|
26270
26540
|
/**
|
|
26271
26541
|
* Default Authentication Provider
|
|
26272
26542
|
* Simple implementation of PERS authentication with token lifecycle management
|
|
@@ -26280,6 +26550,9 @@ class DefaultAuthProvider {
|
|
|
26280
26550
|
this.authType = config.authType || 'user';
|
|
26281
26551
|
const storage = config.storage || this.createStorage();
|
|
26282
26552
|
this.tokenManager = new AuthTokenManager(storage);
|
|
26553
|
+
if (config.dpop?.enabled) {
|
|
26554
|
+
this.dpopManager = new DPoPManager(storage, config.dpop.cryptoProvider);
|
|
26555
|
+
}
|
|
26283
26556
|
}
|
|
26284
26557
|
createStorage() {
|
|
26285
26558
|
return typeof window === 'undefined' || typeof localStorage === 'undefined'
|
|
@@ -26289,6 +26562,26 @@ class DefaultAuthProvider {
|
|
|
26289
26562
|
getToken() {
|
|
26290
26563
|
return this.tokenManager.getAccessToken();
|
|
26291
26564
|
}
|
|
26565
|
+
async getAuthHeaders(token, method, url) {
|
|
26566
|
+
const headers = {};
|
|
26567
|
+
if (this.dpopManager && method && url) {
|
|
26568
|
+
// Generate DPoP Proof
|
|
26569
|
+
// Note: For 'bypassAuth' requests (token is null), we still generate proof (bound to public key, no ath)
|
|
26570
|
+
// This covers Token Requests (Login) which need DPoP header to bind the issued token.
|
|
26571
|
+
const proof = await this.dpopManager.generateProofHeader(method, url, token || undefined);
|
|
26572
|
+
headers['DPoP'] = proof;
|
|
26573
|
+
if (token) {
|
|
26574
|
+
headers['Authorization'] = `DPoP ${token}`;
|
|
26575
|
+
}
|
|
26576
|
+
}
|
|
26577
|
+
else {
|
|
26578
|
+
// Standard Bearer
|
|
26579
|
+
if (token) {
|
|
26580
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
26581
|
+
}
|
|
26582
|
+
}
|
|
26583
|
+
return headers;
|
|
26584
|
+
}
|
|
26292
26585
|
async getProjectKey() {
|
|
26293
26586
|
return this.config.projectKey || null;
|
|
26294
26587
|
}
|
|
@@ -26323,6 +26616,9 @@ class DefaultAuthProvider {
|
|
|
26323
26616
|
}
|
|
26324
26617
|
async clearTokens() {
|
|
26325
26618
|
await this.tokenManager.clearAllTokens();
|
|
26619
|
+
if (this.dpopManager) {
|
|
26620
|
+
await this.dpopManager.clearKeys();
|
|
26621
|
+
}
|
|
26326
26622
|
}
|
|
26327
26623
|
async setTokens(accessToken, refreshToken, providerToken) {
|
|
26328
26624
|
await this.tokenManager.setTokens(accessToken, refreshToken, providerToken);
|
|
@@ -26371,7 +26667,11 @@ class PersApiClient {
|
|
|
26371
26667
|
this.mergedConfig.authProvider = new DefaultAuthProvider({
|
|
26372
26668
|
authType: this.mergedConfig.authType || 'user',
|
|
26373
26669
|
projectKey: this.mergedConfig.apiProjectKey,
|
|
26374
|
-
storage: this.mergedConfig.authStorage // Support custom storage
|
|
26670
|
+
storage: this.mergedConfig.authStorage, // Support custom storage
|
|
26671
|
+
dpop: {
|
|
26672
|
+
enabled: this.mergedConfig.dpop?.enabled ?? true, // Enable DPoP by default
|
|
26673
|
+
cryptoProvider: this.mergedConfig.dpop?.cryptoProvider
|
|
26674
|
+
}
|
|
26375
26675
|
});
|
|
26376
26676
|
}
|
|
26377
26677
|
this.authService = new AuthService(this.authApi, this.mergedConfig.authProvider);
|
|
@@ -26434,7 +26734,7 @@ class PersApiClient {
|
|
|
26434
26734
|
});
|
|
26435
26735
|
}
|
|
26436
26736
|
const requestOptions = {
|
|
26437
|
-
headers: await this.getHeaders(!bypassAuth),
|
|
26737
|
+
headers: await this.getHeaders(!bypassAuth, method, url),
|
|
26438
26738
|
timeout: this.mergedConfig.timeout,
|
|
26439
26739
|
responseType
|
|
26440
26740
|
};
|
|
@@ -26529,26 +26829,45 @@ class PersApiClient {
|
|
|
26529
26829
|
* Get request headers with optional auth token and project key
|
|
26530
26830
|
*
|
|
26531
26831
|
* @param includeAuth - Whether to include the Authorization header (default: true)
|
|
26832
|
+
* @param method - HTTP Method (required for DPoP)
|
|
26833
|
+
* @param url - Full Request URL (required for DPoP)
|
|
26532
26834
|
*/
|
|
26533
|
-
async getHeaders(includeAuth = true) {
|
|
26835
|
+
async getHeaders(includeAuth = true, method, url) {
|
|
26534
26836
|
const headers = {
|
|
26535
26837
|
'Content-Type': 'application/json',
|
|
26536
26838
|
};
|
|
26537
|
-
if (
|
|
26538
|
-
|
|
26539
|
-
if (
|
|
26540
|
-
|
|
26541
|
-
|
|
26542
|
-
|
|
26839
|
+
if (this.mergedConfig.authProvider) {
|
|
26840
|
+
let token = null;
|
|
26841
|
+
if (includeAuth) {
|
|
26842
|
+
token = await this.mergedConfig.authProvider.getToken();
|
|
26843
|
+
}
|
|
26844
|
+
// Handle Authentication Headers (Bearer or DPoP)
|
|
26845
|
+
if (this.mergedConfig.authProvider.getAuthHeaders) {
|
|
26846
|
+
// Provider supports advanced headers (DPoP)
|
|
26847
|
+
// We pass token (null if includeAuth=false) so it can generate DPoP proofs for login requests too
|
|
26848
|
+
try {
|
|
26849
|
+
const authHeaders = await this.mergedConfig.authProvider.getAuthHeaders(token, method, url);
|
|
26850
|
+
Object.assign(headers, authHeaders);
|
|
26543
26851
|
}
|
|
26544
|
-
|
|
26545
|
-
console.warn('[PersApiClient]
|
|
26852
|
+
catch (e) {
|
|
26853
|
+
console.warn('[PersApiClient] Failed to generate auth headers:', e);
|
|
26854
|
+
// Fallback if token exists but DPoP generation failed?
|
|
26855
|
+
// Better to fail or let standardized fallback below handle it?
|
|
26856
|
+
// If DPoP fails, falling back to Bearer might be insecure if server enforces DPoP.
|
|
26857
|
+
// For now, valid token -> Bearer fallback logic mostly for non-DPoP legacy providers.
|
|
26546
26858
|
}
|
|
26547
26859
|
}
|
|
26548
|
-
|
|
26549
|
-
|
|
26860
|
+
// Fallback: If provider didn't give headers but we have token and NO Authorization header yet
|
|
26861
|
+
if (includeAuth && token && !headers['Authorization']) {
|
|
26862
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
26863
|
+
}
|
|
26864
|
+
else if (includeAuth && !token) {
|
|
26865
|
+
console.warn('[PersApiClient] No token available from auth provider');
|
|
26550
26866
|
}
|
|
26551
26867
|
}
|
|
26868
|
+
else if (includeAuth) {
|
|
26869
|
+
console.warn('[PersApiClient] No auth provider configured');
|
|
26870
|
+
}
|
|
26552
26871
|
// Add project key
|
|
26553
26872
|
if (this.mergedConfig.authProvider) {
|
|
26554
26873
|
const projectKey = await this.mergedConfig.authProvider.getProjectKey();
|
|
@@ -29567,6 +29886,75 @@ class TransactionManager {
|
|
|
29567
29886
|
async exportTransactionsCSV() {
|
|
29568
29887
|
return this.transactionService.exportTransactionsCSV();
|
|
29569
29888
|
}
|
|
29889
|
+
/**
|
|
29890
|
+
* Prepare existing transaction for client-side signing
|
|
29891
|
+
*
|
|
29892
|
+
* Retrieves the necessary data to sign an existing transaction on the client side.
|
|
29893
|
+
* This is typically used when a transaction has been created but requires user signature.
|
|
29894
|
+
*
|
|
29895
|
+
* @param transactionId - The ID of the transaction to prepare
|
|
29896
|
+
* @returns Promise resolving to the transaction preparation data
|
|
29897
|
+
*/
|
|
29898
|
+
async prepareExistingTransaction(transactionId) {
|
|
29899
|
+
return this.transactionService.prepareExistingTransaction(transactionId);
|
|
29900
|
+
}
|
|
29901
|
+
/**
|
|
29902
|
+
* Prepare a new transaction for client-side signing
|
|
29903
|
+
*
|
|
29904
|
+
* Creates a new transaction and immediately returns the data needed for client-side signing.
|
|
29905
|
+
* This combines creation and preparation into a single flow for client-signed operations.
|
|
29906
|
+
*
|
|
29907
|
+
* @param request - The transaction request details
|
|
29908
|
+
* @returns Promise resolving to the transaction preparation data
|
|
29909
|
+
*/
|
|
29910
|
+
async prepareClientSignedTransaction(request) {
|
|
29911
|
+
return this.transactionService.prepareClientSignedTransaction(request);
|
|
29912
|
+
}
|
|
29913
|
+
/**
|
|
29914
|
+
* Submit a signed transaction
|
|
29915
|
+
*
|
|
29916
|
+
* Submits a transaction that has been signed on the client side to the backend for processing.
|
|
29917
|
+
*
|
|
29918
|
+
* @param signedTxData - The signed transaction data
|
|
29919
|
+
* @returns Promise resolving to the submission result
|
|
29920
|
+
*/
|
|
29921
|
+
async submitSignedTransaction(signedTxData) {
|
|
29922
|
+
return this.transactionService.submitSignedTransaction(signedTxData);
|
|
29923
|
+
}
|
|
29924
|
+
/**
|
|
29925
|
+
* Query transactions by sender
|
|
29926
|
+
*
|
|
29927
|
+
* Retrieves transactions where the specified account is the sender.
|
|
29928
|
+
*
|
|
29929
|
+
* @param accountSelector - Selector for the sender account
|
|
29930
|
+
* @returns Promise resolving to paginated transactions
|
|
29931
|
+
*/
|
|
29932
|
+
async queryTransactionsBySender(accountSelector) {
|
|
29933
|
+
return this.transactionService.queryTransactionsBySender(accountSelector);
|
|
29934
|
+
}
|
|
29935
|
+
/**
|
|
29936
|
+
* Query transactions by recipient
|
|
29937
|
+
*
|
|
29938
|
+
* Retrieves transactions where the specified account is the recipient.
|
|
29939
|
+
*
|
|
29940
|
+
* @param accountSelector - Selector for the recipient account
|
|
29941
|
+
* @returns Promise resolving to paginated transactions
|
|
29942
|
+
*/
|
|
29943
|
+
async queryTransactionsByRecipient(accountSelector) {
|
|
29944
|
+
return this.transactionService.queryTransactionsByRecipient(accountSelector);
|
|
29945
|
+
}
|
|
29946
|
+
/**
|
|
29947
|
+
* Get transaction analytics
|
|
29948
|
+
*
|
|
29949
|
+
* Retrieves analytics data for transactions based on the provided request criteria.
|
|
29950
|
+
* Useful for generating reports and dashboards.
|
|
29951
|
+
*
|
|
29952
|
+
* @param analyticsRequest - The analytics query parameters
|
|
29953
|
+
* @returns Promise resolving to the analytics data
|
|
29954
|
+
*/
|
|
29955
|
+
async getTransactionAnalytics(analyticsRequest) {
|
|
29956
|
+
return this.transactionService.getTransactionAnalytics(analyticsRequest);
|
|
29957
|
+
}
|
|
29570
29958
|
/**
|
|
29571
29959
|
* Get the full transaction service for advanced operations
|
|
29572
29960
|
*
|
|
@@ -31677,6 +32065,143 @@ class PersSDK {
|
|
|
31677
32065
|
}
|
|
31678
32066
|
}
|
|
31679
32067
|
|
|
32068
|
+
/**
|
|
32069
|
+
* Secure Storage implementation for React Native
|
|
32070
|
+
* Uses Keychain/Keystore for sensitive data and AsyncStorage for non-sensitive data.
|
|
32071
|
+
*/
|
|
32072
|
+
class ReactNativeSecureStorage {
|
|
32073
|
+
constructor(keyPrefix = 'pers_tokens_') {
|
|
32074
|
+
this.keyPrefix = keyPrefix;
|
|
32075
|
+
// Set to false because Keychain stores strings, so we need extractable (serializable) keys
|
|
32076
|
+
this.supportsObjects = false;
|
|
32077
|
+
// Define which keys MUST go to secure hardware storage
|
|
32078
|
+
this.SECURE_KEYS = new Set([
|
|
32079
|
+
DPOP_STORAGE_KEYS.PRIVATE,
|
|
32080
|
+
AUTH_STORAGE_KEYS.ACCESS_TOKEN,
|
|
32081
|
+
AUTH_STORAGE_KEYS.REFRESH_TOKEN,
|
|
32082
|
+
AUTH_STORAGE_KEYS.PROVIDER_TOKEN
|
|
32083
|
+
]);
|
|
32084
|
+
}
|
|
32085
|
+
async set(key, value) {
|
|
32086
|
+
const prefixedKey = this.getKeyName(key);
|
|
32087
|
+
const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
|
|
32088
|
+
// Cast key to any to bypass strict literal type checking of the Set.has method
|
|
32089
|
+
// In runtime, 'key' is just a string, so this is safe.
|
|
32090
|
+
if (this.SECURE_KEYS.has(key)) {
|
|
32091
|
+
// Store in Keychain/Keystore
|
|
32092
|
+
// Service name acts as the key identifier in simple usage
|
|
32093
|
+
await Keychain__namespace.setGenericPassword(prefixedKey, stringValue, { service: prefixedKey });
|
|
32094
|
+
}
|
|
32095
|
+
else {
|
|
32096
|
+
// Store standard config/metadata in AsyncStorage
|
|
32097
|
+
await AsyncStorage.setItem(prefixedKey, stringValue);
|
|
32098
|
+
}
|
|
32099
|
+
}
|
|
32100
|
+
async get(key) {
|
|
32101
|
+
const prefixedKey = this.getKeyName(key);
|
|
32102
|
+
if (this.SECURE_KEYS.has(key)) {
|
|
32103
|
+
try {
|
|
32104
|
+
const credentials = await Keychain__namespace.getGenericPassword({ service: prefixedKey });
|
|
32105
|
+
if (credentials) {
|
|
32106
|
+
return this.tryParse(credentials.password);
|
|
32107
|
+
}
|
|
32108
|
+
return null;
|
|
32109
|
+
}
|
|
32110
|
+
catch (e) {
|
|
32111
|
+
console.warn(`[ReactNativeSecureStorage] Failed to access keychain for ${key}`, e);
|
|
32112
|
+
return null; // Key not found or access denied
|
|
32113
|
+
}
|
|
32114
|
+
}
|
|
32115
|
+
else {
|
|
32116
|
+
try {
|
|
32117
|
+
const val = await AsyncStorage.getItem(prefixedKey);
|
|
32118
|
+
return val ? this.tryParse(val) : null;
|
|
32119
|
+
}
|
|
32120
|
+
catch (e) {
|
|
32121
|
+
console.warn(`[ReactNativeSecureStorage] Failed to access AsyncStorage for ${key}`, e);
|
|
32122
|
+
return null;
|
|
32123
|
+
}
|
|
32124
|
+
}
|
|
32125
|
+
}
|
|
32126
|
+
async remove(key) {
|
|
32127
|
+
const prefixedKey = this.getKeyName(key);
|
|
32128
|
+
if (this.SECURE_KEYS.has(key)) {
|
|
32129
|
+
await Keychain__namespace.resetGenericPassword({ service: prefixedKey });
|
|
32130
|
+
}
|
|
32131
|
+
else {
|
|
32132
|
+
await AsyncStorage.removeItem(prefixedKey);
|
|
32133
|
+
}
|
|
32134
|
+
}
|
|
32135
|
+
async clear() {
|
|
32136
|
+
// Clear all known secure keys
|
|
32137
|
+
for (const key of this.SECURE_KEYS) {
|
|
32138
|
+
await Keychain__namespace.resetGenericPassword({ service: this.getKeyName(key) });
|
|
32139
|
+
}
|
|
32140
|
+
// Clear AsyncStorage keys related to PERS
|
|
32141
|
+
try {
|
|
32142
|
+
const allKeys = await AsyncStorage.getAllKeys();
|
|
32143
|
+
const ourKeys = allKeys.filter(k => k.startsWith(this.keyPrefix));
|
|
32144
|
+
if (ourKeys.length > 0) {
|
|
32145
|
+
await AsyncStorage.multiRemove(ourKeys);
|
|
32146
|
+
}
|
|
32147
|
+
}
|
|
32148
|
+
catch (e) {
|
|
32149
|
+
console.warn('[ReactNativeSecureStorage] Failed to clear AsyncStorage', e);
|
|
32150
|
+
}
|
|
32151
|
+
}
|
|
32152
|
+
getKeyName(key) {
|
|
32153
|
+
// For Keychain, we might want to avoid prefixes if we want to share across apps,
|
|
32154
|
+
// but for simple isolation, prefix is fine to avoid collisions if multiple instances exist.
|
|
32155
|
+
// However, Keychain services are global to the app bundle ID usually.
|
|
32156
|
+
return `${this.keyPrefix}${key}`;
|
|
32157
|
+
}
|
|
32158
|
+
tryParse(val) {
|
|
32159
|
+
try {
|
|
32160
|
+
return JSON.parse(val);
|
|
32161
|
+
}
|
|
32162
|
+
catch {
|
|
32163
|
+
return val;
|
|
32164
|
+
}
|
|
32165
|
+
}
|
|
32166
|
+
}
|
|
32167
|
+
|
|
32168
|
+
/**
|
|
32169
|
+
* React Native Unified Auth Provider
|
|
32170
|
+
*
|
|
32171
|
+
* Creates a platform-specific auth provider that automatically selects the appropriate storage:
|
|
32172
|
+
* - Web: Uses LocalStorageTokenStorage from core SDK
|
|
32173
|
+
* - Mobile: Uses AsyncStorage-based storage
|
|
32174
|
+
*/
|
|
32175
|
+
// Use React Native's built-in platform detection
|
|
32176
|
+
const isWebPlatform = reactNative.Platform.OS === 'web';
|
|
32177
|
+
/**
|
|
32178
|
+
* Create a React Native auth provider with platform-appropriate storage
|
|
32179
|
+
*
|
|
32180
|
+
* Automatically selects storage implementation:
|
|
32181
|
+
* - Web: Uses LocalStorageTokenStorage (from core SDK)
|
|
32182
|
+
* - Mobile: Uses AsyncStorageTokenStorage (React Native specific)
|
|
32183
|
+
*
|
|
32184
|
+
* @param projectKey - PERS project key
|
|
32185
|
+
* @param config - Configuration options
|
|
32186
|
+
* @returns DefaultAuthProvider configured for the current platform
|
|
32187
|
+
*/
|
|
32188
|
+
function createReactNativeAuthProvider(projectKey, config = {}) {
|
|
32189
|
+
if (!projectKey || typeof projectKey !== 'string') {
|
|
32190
|
+
throw new Error('createReactNativeAuthProvider: projectKey is required and must be a string');
|
|
32191
|
+
}
|
|
32192
|
+
const { keyPrefix = `pers_${projectKey.slice(0, 8)}_`, debug = false, customStorage, authType = 'user' } = config;
|
|
32193
|
+
// Platform-specific storage selection
|
|
32194
|
+
const tokenStorage = customStorage || (isWebPlatform
|
|
32195
|
+
? new LocalStorageTokenStorage()
|
|
32196
|
+
: new ReactNativeSecureStorage(keyPrefix));
|
|
32197
|
+
// Return DefaultAuthProvider configured with platform-appropriate storage
|
|
32198
|
+
return new DefaultAuthProvider({
|
|
32199
|
+
authType,
|
|
32200
|
+
projectKey,
|
|
32201
|
+
storage: tokenStorage
|
|
32202
|
+
});
|
|
32203
|
+
}
|
|
32204
|
+
|
|
31680
32205
|
/**
|
|
31681
32206
|
* AsyncStorage Token Storage for React Native Mobile Platforms
|
|
31682
32207
|
*
|
|
@@ -31790,41 +32315,79 @@ class AsyncStorageTokenStorage {
|
|
|
31790
32315
|
}
|
|
31791
32316
|
}
|
|
31792
32317
|
|
|
31793
|
-
|
|
31794
|
-
|
|
31795
|
-
|
|
31796
|
-
|
|
31797
|
-
|
|
31798
|
-
|
|
31799
|
-
|
|
31800
|
-
|
|
31801
|
-
|
|
31802
|
-
|
|
31803
|
-
|
|
31804
|
-
|
|
31805
|
-
|
|
31806
|
-
|
|
31807
|
-
|
|
31808
|
-
|
|
31809
|
-
|
|
31810
|
-
|
|
31811
|
-
|
|
31812
|
-
|
|
31813
|
-
|
|
31814
|
-
|
|
31815
|
-
|
|
32318
|
+
class ReactNativeDPoPProvider {
|
|
32319
|
+
/**
|
|
32320
|
+
* Generates a new key pair (ES256 recommended)
|
|
32321
|
+
*
|
|
32322
|
+
* Note: options.extractable is ignored because for React Native Keychain storage,
|
|
32323
|
+
* we MUST export the keys as JWK/strings. We rely on the Keychain encryption
|
|
32324
|
+
* for security at rest (High Security), making the key "Extractable" in memory
|
|
32325
|
+
* but protected by hardware encryption when stored.
|
|
32326
|
+
*/
|
|
32327
|
+
async generateKeyPair(options) {
|
|
32328
|
+
// Generate P-256 Key Pair
|
|
32329
|
+
return new Promise((resolve, reject) => {
|
|
32330
|
+
crypto$1.generateKeyPair('ec', { namedCurve: 'P-256' }, (err, publicKey, privateKey) => {
|
|
32331
|
+
if (err)
|
|
32332
|
+
return reject(err);
|
|
32333
|
+
// Export to JWK for SDK Compatibility
|
|
32334
|
+
// We use 'export' because supportsObjects=false in our storage forces the SDK
|
|
32335
|
+
// to expect serializable keys.
|
|
32336
|
+
const pubJwk = publicKey.export({ format: 'jwk' });
|
|
32337
|
+
const privJwk = privateKey.export({ format: 'jwk' });
|
|
32338
|
+
resolve({
|
|
32339
|
+
publicKey: pubJwk,
|
|
32340
|
+
privateKey: privJwk
|
|
32341
|
+
});
|
|
32342
|
+
});
|
|
32343
|
+
});
|
|
32344
|
+
}
|
|
32345
|
+
async signProof(payload, keyPair) {
|
|
32346
|
+
// 1. Construct Header
|
|
32347
|
+
const header = {
|
|
32348
|
+
typ: 'dpop+jwt',
|
|
32349
|
+
alg: 'ES256',
|
|
32350
|
+
jwk: keyPair.publicKey
|
|
32351
|
+
};
|
|
32352
|
+
// 2. Add Claims (iat/jti)
|
|
32353
|
+
const finalPayload = {
|
|
32354
|
+
...payload,
|
|
32355
|
+
iat: payload.iat || Math.floor(Date.now() / 1000),
|
|
32356
|
+
jti: payload.jti || crypto$1.randomUUID()
|
|
32357
|
+
};
|
|
32358
|
+
// 3. Encode
|
|
32359
|
+
const b64Header = this.base64Url(JSON.stringify(header));
|
|
32360
|
+
const b64Payload = this.base64Url(JSON.stringify(finalPayload));
|
|
32361
|
+
const signingInput = `${b64Header}.${b64Payload}`;
|
|
32362
|
+
// 4. Sign
|
|
32363
|
+
const sign = crypto$1.createSign('SHA256');
|
|
32364
|
+
sign.update(signingInput);
|
|
32365
|
+
// sign.end() is not required/available in quick-crypto as it doesn't strictly follow stream interface
|
|
32366
|
+
// Import private key back from JWK to sign
|
|
32367
|
+
// The keyPair.privateKey is a JsonWebKey object because we exported it earlier.
|
|
32368
|
+
// quick-crypto createPrivateKey expects jwk object if format is jwk
|
|
32369
|
+
const privateKeyObj = crypto$1.createPrivateKey({ key: keyPair.privateKey, format: 'jwk' });
|
|
32370
|
+
const signature = sign.sign(privateKeyObj);
|
|
32371
|
+
// signature is a Buffer in quick-crypto
|
|
32372
|
+
return `${signingInput}.${this.base64UrlBuffer(signature)}`;
|
|
32373
|
+
}
|
|
32374
|
+
async hashAccessToken(accessToken) {
|
|
32375
|
+
const hash = crypto$1.createHash('sha256').update(accessToken).digest();
|
|
32376
|
+
// digest returns Buffer in quick-crypto
|
|
32377
|
+
return this.base64UrlBuffer(hash);
|
|
32378
|
+
}
|
|
32379
|
+
// --- Helpers ---
|
|
32380
|
+
base64Url(str) {
|
|
32381
|
+
return this.base64UrlBuffer(buffer.Buffer.from(str));
|
|
32382
|
+
}
|
|
32383
|
+
base64UrlBuffer(buf) {
|
|
32384
|
+
// Ensure we have a Buffer
|
|
32385
|
+
const buffer$1 = buffer.Buffer.isBuffer(buf) ? buf : buffer.Buffer.from(buf);
|
|
32386
|
+
return buffer$1.toString('base64')
|
|
32387
|
+
.replace(/=/g, '')
|
|
32388
|
+
.replace(/\+/g, '-')
|
|
32389
|
+
.replace(/\//g, '_');
|
|
31816
32390
|
}
|
|
31817
|
-
const { keyPrefix = `pers_${projectKey.slice(0, 8)}_`, debug = false, customStorage, authType = 'user' } = config;
|
|
31818
|
-
// Platform-specific storage selection
|
|
31819
|
-
const tokenStorage = customStorage || (isWebPlatform
|
|
31820
|
-
? new LocalStorageTokenStorage()
|
|
31821
|
-
: new AsyncStorageTokenStorage(keyPrefix));
|
|
31822
|
-
// Return DefaultAuthProvider configured with platform-appropriate storage
|
|
31823
|
-
return new DefaultAuthProvider({
|
|
31824
|
-
authType,
|
|
31825
|
-
projectKey,
|
|
31826
|
-
storage: tokenStorage
|
|
31827
|
-
});
|
|
31828
32391
|
}
|
|
31829
32392
|
|
|
31830
32393
|
/**
|
|
@@ -31958,6 +32521,15 @@ const PersSDKProvider = ({ children }) => {
|
|
|
31958
32521
|
...config,
|
|
31959
32522
|
authProvider
|
|
31960
32523
|
};
|
|
32524
|
+
// Inject Native DPoP Provider (crypto-based) for mobile platforms
|
|
32525
|
+
// Web platforms use built-in WebCrypto provider by default
|
|
32526
|
+
if (reactNative.Platform.OS !== 'web' && (!config.dpop?.cryptoProvider)) {
|
|
32527
|
+
sdkConfig.dpop = {
|
|
32528
|
+
...(config.dpop || {}),
|
|
32529
|
+
enabled: config.dpop?.enabled ?? true,
|
|
32530
|
+
cryptoProvider: new ReactNativeDPoPProvider()
|
|
32531
|
+
};
|
|
32532
|
+
}
|
|
31961
32533
|
// Initialize PersSDK with platform-aware auth provider
|
|
31962
32534
|
const sdkInstance = new PersSDK(httpClient, sdkConfig);
|
|
31963
32535
|
setAuthProvider(authProvider);
|
|
@@ -34419,7 +34991,9 @@ exports.ANALYTICS_METRIC_TYPES = ANALYTICS_METRIC_TYPES;
|
|
|
34419
34991
|
exports.AsyncStorageTokenStorage = AsyncStorageTokenStorage;
|
|
34420
34992
|
exports.NativeTokenTypes = NativeTokenTypes;
|
|
34421
34993
|
exports.PersSDKProvider = PersSDKProvider;
|
|
34994
|
+
exports.ReactNativeDPoPProvider = ReactNativeDPoPProvider;
|
|
34422
34995
|
exports.ReactNativeHttpClient = ReactNativeHttpClient;
|
|
34996
|
+
exports.ReactNativeSecureStorage = ReactNativeSecureStorage;
|
|
34423
34997
|
exports.TRANSACTION_FORMATS = TRANSACTION_FORMATS;
|
|
34424
34998
|
exports.TRANSACTION_FORMAT_DESCRIPTIONS = TRANSACTION_FORMAT_DESCRIPTIONS;
|
|
34425
34999
|
exports.apiPublicKeyTestPrefix = apiPublicKeyTestPrefix;
|