@explorins/pers-sdk-react-native 1.5.28 → 1.5.30
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 +22 -0
- package/dist/index.js +80 -24
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts +1 -0
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/dist/providers/PersSDKProvider.js +27 -14
- package/dist/providers/react-native-auth-provider.d.ts +5 -0
- package/dist/providers/react-native-auth-provider.d.ts.map +1 -1
- package/dist/providers/react-native-auth-provider.js +8 -2
- package/dist/storage/rn-secure-storage.d.ts.map +1 -1
- package/dist/storage/rn-secure-storage.js +46 -9
- package/package.json +2 -2
- package/src/providers/PersSDKProvider.tsx +32 -15
- package/src/providers/react-native-auth-provider.ts +15 -2
- package/src/storage/rn-secure-storage.ts +41 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PersSDKProvider.d.ts","sourceRoot":"","sources":["../../src/providers/PersSDKProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAuC,SAAS,
|
|
1
|
+
{"version":3,"file":"PersSDKProvider.d.ts","sourceRoot":"","sources":["../../src/providers/PersSDKProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAuC,SAAS,EAAkC,MAAM,OAAO,CAAC;AAE9G,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAsB,MAAM,0BAA0B,CAAC;AAIxG,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAG3D,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,YAAY,EACZ,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAE3D,MAAM,WAAW,cAAc;IAE7B,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;IAGpB,IAAI,EAAE,WAAW,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IAC1B,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAClC,WAAW,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACtC,YAAY,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACxC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAGlC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IAGjC,YAAY,EAAE,mBAAmB,GAAG,IAAI,CAAC;IAGzC,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAG9B,UAAU,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,sBAAsB,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,EAAE,eAAe,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3H,eAAe,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAMD,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC;IACrC,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB,CA4IA,CAAC;AAGF,eAAO,MAAM,UAAU,QAAO,cAQ7B,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { createContext, useContext, useState, useCallback, useRef } from 'react';
|
|
2
|
+
import { createContext, useContext, useState, useCallback, useRef, useEffect } from 'react';
|
|
3
3
|
import { Platform } from 'react-native';
|
|
4
4
|
import { PersSDK } from '@explorins/pers-sdk/core';
|
|
5
5
|
import { ReactNativeHttpClient } from './react-native-http-client';
|
|
@@ -8,7 +8,7 @@ import { ReactNativeDPoPProvider } from './rn-dpop-provider';
|
|
|
8
8
|
// Create the context
|
|
9
9
|
const SDKContext = createContext(null);
|
|
10
10
|
// Provider component
|
|
11
|
-
export const PersSDKProvider = ({ children }) => {
|
|
11
|
+
export const PersSDKProvider = ({ children, config }) => {
|
|
12
12
|
const initializingRef = useRef(false);
|
|
13
13
|
const [sdk, setSdk] = useState(null);
|
|
14
14
|
const [authProvider, setAuthProvider] = useState(null);
|
|
@@ -25,6 +25,18 @@ export const PersSDKProvider = ({ children }) => {
|
|
|
25
25
|
try {
|
|
26
26
|
// Create HTTP client
|
|
27
27
|
const httpClient = new ReactNativeHttpClient();
|
|
28
|
+
// Ensure DPoP is enabled by default for all platforms
|
|
29
|
+
const dpopConfig = config.dpop || {};
|
|
30
|
+
const isDpopEnabled = dpopConfig.enabled ?? true;
|
|
31
|
+
// Prepare DPoP settings for Auth Provider
|
|
32
|
+
const dpopSettings = {
|
|
33
|
+
enabled: isDpopEnabled,
|
|
34
|
+
cryptoProvider: dpopConfig.cryptoProvider
|
|
35
|
+
};
|
|
36
|
+
// Inject Native DPoP Provider (crypto-based) for mobile platforms if not already provided
|
|
37
|
+
if (Platform.OS !== 'web' && isDpopEnabled && !dpopSettings.cryptoProvider) {
|
|
38
|
+
dpopSettings.cryptoProvider = new ReactNativeDPoPProvider();
|
|
39
|
+
}
|
|
28
40
|
// Create platform-aware auth provider if not provided
|
|
29
41
|
let authProvider;
|
|
30
42
|
if (config.authProvider) {
|
|
@@ -33,23 +45,16 @@ export const PersSDKProvider = ({ children }) => {
|
|
|
33
45
|
else {
|
|
34
46
|
// Use our platform-aware auth provider that automatically selects LocalStorage (web) or AsyncStorage (mobile)
|
|
35
47
|
authProvider = createReactNativeAuthProvider(config.apiProjectKey || 'default-project', {
|
|
36
|
-
debug: false
|
|
48
|
+
debug: false,
|
|
49
|
+
dpop: dpopSettings
|
|
37
50
|
});
|
|
38
51
|
}
|
|
39
52
|
// Enhanced config with platform-appropriate auth provider
|
|
40
53
|
const sdkConfig = {
|
|
41
54
|
...config,
|
|
42
|
-
authProvider
|
|
55
|
+
authProvider,
|
|
56
|
+
dpop: dpopSettings
|
|
43
57
|
};
|
|
44
|
-
// Inject Native DPoP Provider (crypto-based) for mobile platforms
|
|
45
|
-
// Web platforms use built-in WebCrypto provider by default
|
|
46
|
-
if (Platform.OS !== 'web' && (!config.dpop?.cryptoProvider)) {
|
|
47
|
-
sdkConfig.dpop = {
|
|
48
|
-
...(config.dpop || {}),
|
|
49
|
-
enabled: config.dpop?.enabled ?? true,
|
|
50
|
-
cryptoProvider: new ReactNativeDPoPProvider()
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
58
|
// Initialize PersSDK with platform-aware auth provider
|
|
54
59
|
const sdkInstance = new PersSDK(httpClient, sdkConfig);
|
|
55
60
|
setAuthProvider(authProvider);
|
|
@@ -64,7 +69,15 @@ export const PersSDKProvider = ({ children }) => {
|
|
|
64
69
|
finally {
|
|
65
70
|
initializingRef.current = false;
|
|
66
71
|
}
|
|
67
|
-
}, []);
|
|
72
|
+
}, [isInitialized]);
|
|
73
|
+
// Auto-initialize if config is provided
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (config && !isInitialized && !initializingRef.current) {
|
|
76
|
+
initialize(config).catch(err => {
|
|
77
|
+
console.error('Auto-initialization failed:', err);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}, [config, isInitialized, initialize]);
|
|
68
81
|
const setAuthenticationState = useCallback((user, accountAddress, isAuthenticated) => {
|
|
69
82
|
setUser(user);
|
|
70
83
|
setAccountAddress(accountAddress);
|
|
@@ -19,6 +19,11 @@ export interface ReactNativeAuthConfig {
|
|
|
19
19
|
customStorage?: TokenStorage;
|
|
20
20
|
/** Authentication type (default: 'user') */
|
|
21
21
|
authType?: AuthType;
|
|
22
|
+
/** DPoP Configuration (optional) */
|
|
23
|
+
dpop?: {
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
cryptoProvider?: import('@explorins/pers-sdk/core').DPoPCryptoProvider;
|
|
26
|
+
};
|
|
22
27
|
}
|
|
23
28
|
/**
|
|
24
29
|
* Create a React Native auth provider with platform-appropriate storage
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"react-native-auth-provider.d.ts","sourceRoot":"","sources":["../../src/providers/react-native-auth-provider.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,mBAAmB,EAAW,QAAQ,EAA4B,MAAM,0BAA0B,CAAC;AAG5G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAK7D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0CAA0C;IAC1C,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"react-native-auth-provider.d.ts","sourceRoot":"","sources":["../../src/providers/react-native-auth-provider.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,mBAAmB,EAAW,QAAQ,EAA4B,MAAM,0BAA0B,CAAC;AAG5G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAK7D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,gCAAgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0CAA0C;IAC1C,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,oCAAoC;IACpC,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,cAAc,CAAC,EAAE,OAAO,0BAA0B,EAAE,kBAAkB,CAAC;KACxE,CAAC;CACH;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,qBAA0B,GACjC,mBAAmB,CAiCrB"}
|
|
@@ -25,15 +25,21 @@ export function createReactNativeAuthProvider(projectKey, config = {}) {
|
|
|
25
25
|
if (!projectKey || typeof projectKey !== 'string') {
|
|
26
26
|
throw new Error('createReactNativeAuthProvider: projectKey is required and must be a string');
|
|
27
27
|
}
|
|
28
|
-
const { keyPrefix = `pers_${projectKey.slice(0, 8)}_`, debug = false, customStorage, authType = 'user' } = config;
|
|
28
|
+
const { keyPrefix = `pers_${projectKey.slice(0, 8)}_`, debug = false, customStorage, authType = 'user', dpop } = config;
|
|
29
29
|
// Platform-specific storage selection
|
|
30
30
|
const tokenStorage = customStorage || (isWebPlatform
|
|
31
31
|
? new LocalStorageTokenStorage()
|
|
32
32
|
: new ReactNativeSecureStorage(keyPrefix));
|
|
33
|
+
// Prepare DPoP config ensuring enabled is boolean (default true)
|
|
34
|
+
const dpopConfig = dpop ? {
|
|
35
|
+
enabled: dpop.enabled ?? true,
|
|
36
|
+
cryptoProvider: dpop.cryptoProvider
|
|
37
|
+
} : undefined;
|
|
33
38
|
// Return DefaultAuthProvider configured with platform-appropriate storage
|
|
34
39
|
return new DefaultAuthProvider({
|
|
35
40
|
authType,
|
|
36
41
|
projectKey,
|
|
37
|
-
storage: tokenStorage
|
|
42
|
+
storage: tokenStorage,
|
|
43
|
+
dpop: dpopConfig
|
|
38
44
|
});
|
|
39
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rn-secure-storage.d.ts","sourceRoot":"","sources":["../../src/storage/rn-secure-storage.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EAGb,MAAM,0BAA0B,CAAC;AAYlC;;;GAGG;AACH,qBAAa,wBAAyB,YAAW,YAAY;IAY/C,OAAO,CAAC,SAAS;IAV7B,QAAQ,CAAC,eAAe,SAAS;IAGjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAKzB;gBAEiB,SAAS,GAAE,MAAuB;IAEhD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"rn-secure-storage.d.ts","sourceRoot":"","sources":["../../src/storage/rn-secure-storage.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EAGb,MAAM,0BAA0B,CAAC;AAYlC;;;GAGG;AACH,qBAAa,wBAAyB,YAAW,YAAY;IAY/C,OAAO,CAAC,SAAS;IAV7B,QAAQ,CAAC,eAAe,SAAS;IAGjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAKzB;gBAEiB,SAAS,GAAE,MAAuB;IAEhD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IA0B/C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAkCzC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAwB5B,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,QAAQ;CAOjB"}
|
|
@@ -35,8 +35,20 @@ export class ReactNativeSecureStorage {
|
|
|
35
35
|
// In runtime, 'key' is just a string, so this is safe.
|
|
36
36
|
if (this.SECURE_KEYS.has(key)) {
|
|
37
37
|
// Store in Keychain/Keystore
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
try {
|
|
39
|
+
if (Keychain) {
|
|
40
|
+
// Service name acts as the key identifier in simple usage
|
|
41
|
+
await Keychain.setGenericPassword(prefixedKey, stringValue, { service: prefixedKey });
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
throw new Error('RN Keychain not available');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (e) {
|
|
48
|
+
console.warn(`[ReactNativeSecureStorage] Keychain set failed for ${key}, falling back to AsyncStorage`, e);
|
|
49
|
+
// Fallback to AsyncStorage if Keychain fails
|
|
50
|
+
await AsyncStorage.setItem(prefixedKey, stringValue);
|
|
51
|
+
}
|
|
40
52
|
}
|
|
41
53
|
else {
|
|
42
54
|
// Store standard config/metadata in AsyncStorage
|
|
@@ -47,15 +59,24 @@ export class ReactNativeSecureStorage {
|
|
|
47
59
|
const prefixedKey = this.getKeyName(key);
|
|
48
60
|
if (this.SECURE_KEYS.has(key)) {
|
|
49
61
|
try {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
if (Keychain) {
|
|
63
|
+
const credentials = await Keychain.getGenericPassword({ service: prefixedKey });
|
|
64
|
+
if (credentials) {
|
|
65
|
+
return this.tryParse(credentials.password);
|
|
66
|
+
}
|
|
53
67
|
}
|
|
54
|
-
return null;
|
|
55
68
|
}
|
|
56
69
|
catch (e) {
|
|
57
70
|
console.warn(`[ReactNativeSecureStorage] Failed to access keychain for ${key}`, e);
|
|
58
|
-
|
|
71
|
+
// Continue to fallback check...
|
|
72
|
+
}
|
|
73
|
+
// Fallback: Check AsyncStorage if not found in Keychain or Keychain failed
|
|
74
|
+
try {
|
|
75
|
+
const val = await AsyncStorage.getItem(prefixedKey);
|
|
76
|
+
return val ? this.tryParse(val) : null;
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
return null;
|
|
59
80
|
}
|
|
60
81
|
}
|
|
61
82
|
else {
|
|
@@ -72,7 +93,16 @@ export class ReactNativeSecureStorage {
|
|
|
72
93
|
async remove(key) {
|
|
73
94
|
const prefixedKey = this.getKeyName(key);
|
|
74
95
|
if (this.SECURE_KEYS.has(key)) {
|
|
75
|
-
|
|
96
|
+
try {
|
|
97
|
+
if (Keychain) {
|
|
98
|
+
await Keychain.resetGenericPassword({ service: prefixedKey });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
console.warn(`[ReactNativeSecureStorage] Failed to reset keychain for ${key}`, e);
|
|
103
|
+
}
|
|
104
|
+
// Always remove from fallback storage too, just in case
|
|
105
|
+
await AsyncStorage.removeItem(prefixedKey);
|
|
76
106
|
}
|
|
77
107
|
else {
|
|
78
108
|
await AsyncStorage.removeItem(prefixedKey);
|
|
@@ -81,7 +111,14 @@ export class ReactNativeSecureStorage {
|
|
|
81
111
|
async clear() {
|
|
82
112
|
// Clear all known secure keys
|
|
83
113
|
for (const key of this.SECURE_KEYS) {
|
|
84
|
-
|
|
114
|
+
try {
|
|
115
|
+
if (Keychain) {
|
|
116
|
+
await Keychain.resetGenericPassword({ service: this.getKeyName(key) });
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
console.warn(`[ReactNativeSecureStorage] Failed to clear keychain key ${key}`, e);
|
|
121
|
+
}
|
|
85
122
|
}
|
|
86
123
|
// Clear AsyncStorage keys related to PERS
|
|
87
124
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@explorins/pers-sdk-react-native",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.30",
|
|
4
4
|
"description": "React Native SDK for PERS Platform - Tourism Loyalty System with Blockchain Transaction Signing and WebAuthn Authentication",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"author": "eXplorins",
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@explorins/pers-sdk": "
|
|
40
|
+
"@explorins/pers-sdk": "^1.6.36",
|
|
41
41
|
"@explorins/pers-shared": "^2.1.52",
|
|
42
42
|
"@explorins/pers-signer": "^1.0.32",
|
|
43
43
|
"@explorins/web3-ts": "^0.3.75",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { createContext, useContext, useState, ReactNode, useCallback, useRef } from 'react';
|
|
1
|
+
import React, { createContext, useContext, useState, ReactNode, useCallback, useRef, useEffect } from 'react';
|
|
2
2
|
import { Platform } from 'react-native';
|
|
3
3
|
import { PersSDK, PersConfig, DefaultAuthProvider, DPoPCryptoProvider } from '@explorins/pers-sdk/core';
|
|
4
4
|
import { ReactNativeHttpClient } from './react-native-http-client';
|
|
@@ -65,7 +65,8 @@ const SDKContext = createContext<PersSDKContext | null>(null);
|
|
|
65
65
|
// Provider component
|
|
66
66
|
export const PersSDKProvider: React.FC<{
|
|
67
67
|
children: ReactNode;
|
|
68
|
-
|
|
68
|
+
config?: PersConfig;
|
|
69
|
+
}> = ({ children, config }) => {
|
|
69
70
|
const initializingRef = useRef(false);
|
|
70
71
|
const [sdk, setSdk] = useState<PersSDK | null>(null);
|
|
71
72
|
const [authProvider, setAuthProvider] = useState<DefaultAuthProvider | null>(null);
|
|
@@ -87,6 +88,21 @@ export const PersSDKProvider: React.FC<{
|
|
|
87
88
|
// Create HTTP client
|
|
88
89
|
const httpClient = new ReactNativeHttpClient();
|
|
89
90
|
|
|
91
|
+
// Ensure DPoP is enabled by default for all platforms
|
|
92
|
+
const dpopConfig = config.dpop || {};
|
|
93
|
+
const isDpopEnabled = dpopConfig.enabled ?? true;
|
|
94
|
+
|
|
95
|
+
// Prepare DPoP settings for Auth Provider
|
|
96
|
+
const dpopSettings = {
|
|
97
|
+
enabled: isDpopEnabled,
|
|
98
|
+
cryptoProvider: dpopConfig.cryptoProvider
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Inject Native DPoP Provider (crypto-based) for mobile platforms if not already provided
|
|
102
|
+
if (Platform.OS !== 'web' && isDpopEnabled && !dpopSettings.cryptoProvider) {
|
|
103
|
+
dpopSettings.cryptoProvider = new ReactNativeDPoPProvider();
|
|
104
|
+
}
|
|
105
|
+
|
|
90
106
|
// Create platform-aware auth provider if not provided
|
|
91
107
|
let authProvider: DefaultAuthProvider;
|
|
92
108
|
if (config.authProvider) {
|
|
@@ -94,26 +110,18 @@ export const PersSDKProvider: React.FC<{
|
|
|
94
110
|
} else {
|
|
95
111
|
// Use our platform-aware auth provider that automatically selects LocalStorage (web) or AsyncStorage (mobile)
|
|
96
112
|
authProvider = createReactNativeAuthProvider(config.apiProjectKey || 'default-project', {
|
|
97
|
-
debug: false
|
|
113
|
+
debug: false,
|
|
114
|
+
dpop: dpopSettings
|
|
98
115
|
});
|
|
99
116
|
}
|
|
100
117
|
|
|
101
118
|
// Enhanced config with platform-appropriate auth provider
|
|
102
119
|
const sdkConfig: PersConfig = {
|
|
103
120
|
...config,
|
|
104
|
-
authProvider
|
|
121
|
+
authProvider,
|
|
122
|
+
dpop: dpopSettings
|
|
105
123
|
};
|
|
106
124
|
|
|
107
|
-
// Inject Native DPoP Provider (crypto-based) for mobile platforms
|
|
108
|
-
// Web platforms use built-in WebCrypto provider by default
|
|
109
|
-
if (Platform.OS !== 'web' && (!config.dpop?.cryptoProvider)) {
|
|
110
|
-
sdkConfig.dpop = {
|
|
111
|
-
...(config.dpop || {}),
|
|
112
|
-
enabled: config.dpop?.enabled ?? true,
|
|
113
|
-
cryptoProvider: new ReactNativeDPoPProvider()
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
125
|
// Initialize PersSDK with platform-aware auth provider
|
|
118
126
|
const sdkInstance = new PersSDK(httpClient, sdkConfig);
|
|
119
127
|
|
|
@@ -127,7 +135,16 @@ export const PersSDKProvider: React.FC<{
|
|
|
127
135
|
} finally {
|
|
128
136
|
initializingRef.current = false;
|
|
129
137
|
}
|
|
130
|
-
}, []);
|
|
138
|
+
}, [isInitialized]);
|
|
139
|
+
|
|
140
|
+
// Auto-initialize if config is provided
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
if (config && !isInitialized && !initializingRef.current) {
|
|
143
|
+
initialize(config).catch(err => {
|
|
144
|
+
console.error('Auto-initialization failed:', err);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}, [config, isInitialized, initialize]);
|
|
131
148
|
|
|
132
149
|
const setAuthenticationState = useCallback((user: UserDTO | AdminDTO | null, accountAddress: string | null, isAuthenticated: boolean) => {
|
|
133
150
|
setUser(user);
|
|
@@ -27,6 +27,11 @@ export interface ReactNativeAuthConfig {
|
|
|
27
27
|
customStorage?: TokenStorage;
|
|
28
28
|
/** Authentication type (default: 'user') */
|
|
29
29
|
authType?: AuthType;
|
|
30
|
+
/** DPoP Configuration (optional) */
|
|
31
|
+
dpop?: {
|
|
32
|
+
enabled?: boolean;
|
|
33
|
+
cryptoProvider?: import('@explorins/pers-sdk/core').DPoPCryptoProvider;
|
|
34
|
+
};
|
|
30
35
|
}
|
|
31
36
|
|
|
32
37
|
/**
|
|
@@ -52,7 +57,8 @@ export function createReactNativeAuthProvider(
|
|
|
52
57
|
keyPrefix = `pers_${projectKey.slice(0, 8)}_`,
|
|
53
58
|
debug = false,
|
|
54
59
|
customStorage,
|
|
55
|
-
authType = 'user'
|
|
60
|
+
authType = 'user',
|
|
61
|
+
dpop
|
|
56
62
|
} = config;
|
|
57
63
|
|
|
58
64
|
// Platform-specific storage selection
|
|
@@ -62,11 +68,18 @@ export function createReactNativeAuthProvider(
|
|
|
62
68
|
: new ReactNativeSecureStorage(keyPrefix)
|
|
63
69
|
);
|
|
64
70
|
|
|
71
|
+
// Prepare DPoP config ensuring enabled is boolean (default true)
|
|
72
|
+
const dpopConfig = dpop ? {
|
|
73
|
+
enabled: dpop.enabled ?? true,
|
|
74
|
+
cryptoProvider: dpop.cryptoProvider
|
|
75
|
+
} : undefined;
|
|
76
|
+
|
|
65
77
|
// Return DefaultAuthProvider configured with platform-appropriate storage
|
|
66
78
|
return new DefaultAuthProvider({
|
|
67
79
|
authType,
|
|
68
80
|
projectKey,
|
|
69
|
-
storage: tokenStorage
|
|
81
|
+
storage: tokenStorage,
|
|
82
|
+
dpop: dpopConfig
|
|
70
83
|
});
|
|
71
84
|
}
|
|
72
85
|
|
|
@@ -42,8 +42,18 @@ export class ReactNativeSecureStorage implements TokenStorage {
|
|
|
42
42
|
// In runtime, 'key' is just a string, so this is safe.
|
|
43
43
|
if (this.SECURE_KEYS.has(key as any)) {
|
|
44
44
|
// Store in Keychain/Keystore
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
try {
|
|
46
|
+
if (Keychain) {
|
|
47
|
+
// Service name acts as the key identifier in simple usage
|
|
48
|
+
await Keychain.setGenericPassword(prefixedKey, stringValue, { service: prefixedKey });
|
|
49
|
+
} else {
|
|
50
|
+
throw new Error('RN Keychain not available');
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {
|
|
53
|
+
console.warn(`[ReactNativeSecureStorage] Keychain set failed for ${key}, falling back to AsyncStorage`, e);
|
|
54
|
+
// Fallback to AsyncStorage if Keychain fails
|
|
55
|
+
await AsyncStorage.setItem(prefixedKey, stringValue);
|
|
56
|
+
}
|
|
47
57
|
} else {
|
|
48
58
|
// Store standard config/metadata in AsyncStorage
|
|
49
59
|
await AsyncStorage.setItem(prefixedKey, stringValue);
|
|
@@ -55,14 +65,23 @@ export class ReactNativeSecureStorage implements TokenStorage {
|
|
|
55
65
|
|
|
56
66
|
if (this.SECURE_KEYS.has(key as any)) {
|
|
57
67
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
if (Keychain) {
|
|
69
|
+
const credentials = await Keychain.getGenericPassword({ service: prefixedKey });
|
|
70
|
+
if (credentials) {
|
|
60
71
|
return this.tryParse(credentials.password);
|
|
72
|
+
}
|
|
61
73
|
}
|
|
62
|
-
return null;
|
|
63
74
|
} catch (e) {
|
|
64
75
|
console.warn(`[ReactNativeSecureStorage] Failed to access keychain for ${key}`, e);
|
|
65
|
-
|
|
76
|
+
// Continue to fallback check...
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Fallback: Check AsyncStorage if not found in Keychain or Keychain failed
|
|
80
|
+
try {
|
|
81
|
+
const val = await AsyncStorage.getItem(prefixedKey);
|
|
82
|
+
return val ? this.tryParse(val) : null;
|
|
83
|
+
} catch (e) {
|
|
84
|
+
return null;
|
|
66
85
|
}
|
|
67
86
|
} else {
|
|
68
87
|
try {
|
|
@@ -79,7 +98,15 @@ export class ReactNativeSecureStorage implements TokenStorage {
|
|
|
79
98
|
const prefixedKey = this.getKeyName(key);
|
|
80
99
|
|
|
81
100
|
if (this.SECURE_KEYS.has(key as any)) {
|
|
82
|
-
|
|
101
|
+
try {
|
|
102
|
+
if (Keychain) {
|
|
103
|
+
await Keychain.resetGenericPassword({ service: prefixedKey });
|
|
104
|
+
}
|
|
105
|
+
} catch (e) {
|
|
106
|
+
console.warn(`[ReactNativeSecureStorage] Failed to reset keychain for ${key}`, e);
|
|
107
|
+
}
|
|
108
|
+
// Always remove from fallback storage too, just in case
|
|
109
|
+
await AsyncStorage.removeItem(prefixedKey);
|
|
83
110
|
} else {
|
|
84
111
|
await AsyncStorage.removeItem(prefixedKey);
|
|
85
112
|
}
|
|
@@ -88,7 +115,13 @@ export class ReactNativeSecureStorage implements TokenStorage {
|
|
|
88
115
|
async clear(): Promise<void> {
|
|
89
116
|
// Clear all known secure keys
|
|
90
117
|
for (const key of this.SECURE_KEYS) {
|
|
91
|
-
|
|
118
|
+
try {
|
|
119
|
+
if (Keychain) {
|
|
120
|
+
await Keychain.resetGenericPassword({ service: this.getKeyName(key) });
|
|
121
|
+
}
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.warn(`[ReactNativeSecureStorage] Failed to clear keychain key ${key}`, e);
|
|
124
|
+
}
|
|
92
125
|
}
|
|
93
126
|
|
|
94
127
|
// Clear AsyncStorage keys related to PERS
|