@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.
@@ -28,6 +28,7 @@ export interface PersSDKContext {
28
28
  }
29
29
  export declare const PersSDKProvider: React.FC<{
30
30
  children: ReactNode;
31
+ config?: PersConfig;
31
32
  }>;
32
33
  export declare const usePersSDK: () => PersSDKContext;
33
34
  //# sourceMappingURL=PersSDKProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PersSDKProvider.d.ts","sourceRoot":"","sources":["../../src/providers/PersSDKProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAuC,SAAS,EAAuB,MAAM,OAAO,CAAC;AAEnG,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;CACrB,CA4HA,CAAC;AAGF,eAAO,MAAM,UAAU,QAAO,cAQ7B,CAAC"}
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;CACrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,qBAA0B,GACjC,mBAAmB,CAyBrB"}
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;IAgB/C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAyBzC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB5B,OAAO,CAAC,UAAU;IAOlB,OAAO,CAAC,QAAQ;CAOjB"}
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
- // Service name acts as the key identifier in simple usage
39
- await Keychain.setGenericPassword(prefixedKey, stringValue, { service: prefixedKey });
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
- const credentials = await Keychain.getGenericPassword({ service: prefixedKey });
51
- if (credentials) {
52
- return this.tryParse(credentials.password);
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
- return null; // Key not found or access denied
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
- await Keychain.resetGenericPassword({ service: prefixedKey });
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
- await Keychain.resetGenericPassword({ service: this.getKeyName(key) });
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.28",
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": "file:.yalc/@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
- }> = ({ children }) => {
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
- // Service name acts as the key identifier in simple usage
46
- await Keychain.setGenericPassword(prefixedKey, stringValue, { service: prefixedKey });
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
- const credentials = await Keychain.getGenericPassword({ service: prefixedKey });
59
- if (credentials) {
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
- return null; // Key not found or access denied
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
- await Keychain.resetGenericPassword({ service: prefixedKey });
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
- await Keychain.resetGenericPassword({ service: this.getKeyName(key) });
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