@explorins/pers-sdk-react-native 1.5.27 → 1.5.29

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,CAqIA,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);
@@ -64,7 +64,15 @@ export const PersSDKProvider = ({ children }) => {
64
64
  finally {
65
65
  initializingRef.current = false;
66
66
  }
67
- }, []);
67
+ }, [isInitialized]);
68
+ // Auto-initialize if config is provided
69
+ useEffect(() => {
70
+ if (config && !isInitialized && !initializingRef.current) {
71
+ initialize(config).catch(err => {
72
+ console.error('Auto-initialization failed:', err);
73
+ });
74
+ }
75
+ }, [config, isInitialized, initialize]);
68
76
  const setAuthenticationState = useCallback((user, accountAddress, isAuthenticated) => {
69
77
  setUser(user);
70
78
  setAccountAddress(accountAddress);
@@ -1 +1 @@
1
- {"version":3,"file":"rn-dpop-provider.d.ts","sourceRoot":"","sources":["../../src/providers/rn-dpop-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE3E,qBAAa,uBAAwB,YAAW,kBAAkB;IAChE;;;;;;;OAOG;IACG,eAAe,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAoB1E,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAmC9E,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAO3D,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,eAAe;CAQxB"}
1
+ {"version":3,"file":"rn-dpop-provider.d.ts","sourceRoot":"","sources":["../../src/providers/rn-dpop-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAkB3E,qBAAa,uBAAwB,YAAW,kBAAkB;IAChE;;;;;;;OAOG;IACG,eAAe,CAAC,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAoB1E,SAAS,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAmC9E,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAO3D,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,eAAe;CAQxB"}
@@ -1,5 +1,22 @@
1
- import crypto from 'react-native-quick-crypto';
1
+ import { Platform } from 'react-native';
2
2
  import { Buffer } from 'buffer';
3
+ // Conditionally require quick-crypto for Native platforms only
4
+ let crypto;
5
+ if (Platform.OS !== 'web') {
6
+ try {
7
+ crypto = require('react-native-quick-crypto').default || require('react-native-quick-crypto');
8
+ }
9
+ catch (e) {
10
+ console.warn('ReactNativeDPoPProvider: Failed to load react-native-quick-crypto', e);
11
+ }
12
+ }
13
+ else {
14
+ // on Web, we shouldn't be using this provider anyway (Core SDK has WebDPoPProvider)
15
+ // But to be safe, we can mock or throw
16
+ crypto = {
17
+ generateKeyPair: () => { throw new Error('ReactNativeDPoPProvider not supported on Web'); }
18
+ };
19
+ }
3
20
  export class ReactNativeDPoPProvider {
4
21
  /**
5
22
  * Generates a new key pair (ES256 recommended)
@@ -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;AAElC;;;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"}
@@ -1,6 +1,16 @@
1
- import * as Keychain from 'react-native-keychain';
1
+ import { Platform } from 'react-native';
2
2
  import AsyncStorage from '@react-native-async-storage/async-storage';
3
3
  import { DPOP_STORAGE_KEYS, AUTH_STORAGE_KEYS } from '@explorins/pers-sdk/core';
4
+ // Conditionally require Keychain to avoid Web bundler errors
5
+ let Keychain;
6
+ if (Platform.OS !== 'web') {
7
+ try {
8
+ Keychain = require('react-native-keychain');
9
+ }
10
+ catch (e) {
11
+ console.warn('ReactNativeSecureStorage: Failed to load react-native-keychain', e);
12
+ }
13
+ }
4
14
  /**
5
15
  * Secure Storage implementation for React Native
6
16
  * Uses Keychain/Keystore for sensitive data and AsyncStorage for non-sensitive data.
@@ -25,8 +35,20 @@ export class ReactNativeSecureStorage {
25
35
  // In runtime, 'key' is just a string, so this is safe.
26
36
  if (this.SECURE_KEYS.has(key)) {
27
37
  // Store in Keychain/Keystore
28
- // Service name acts as the key identifier in simple usage
29
- 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
+ }
30
52
  }
31
53
  else {
32
54
  // Store standard config/metadata in AsyncStorage
@@ -37,15 +59,24 @@ export class ReactNativeSecureStorage {
37
59
  const prefixedKey = this.getKeyName(key);
38
60
  if (this.SECURE_KEYS.has(key)) {
39
61
  try {
40
- const credentials = await Keychain.getGenericPassword({ service: prefixedKey });
41
- if (credentials) {
42
- 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
+ }
43
67
  }
44
- return null;
45
68
  }
46
69
  catch (e) {
47
70
  console.warn(`[ReactNativeSecureStorage] Failed to access keychain for ${key}`, e);
48
- 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;
49
80
  }
50
81
  }
51
82
  else {
@@ -62,7 +93,16 @@ export class ReactNativeSecureStorage {
62
93
  async remove(key) {
63
94
  const prefixedKey = this.getKeyName(key);
64
95
  if (this.SECURE_KEYS.has(key)) {
65
- 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);
66
106
  }
67
107
  else {
68
108
  await AsyncStorage.removeItem(prefixedKey);
@@ -71,7 +111,14 @@ export class ReactNativeSecureStorage {
71
111
  async clear() {
72
112
  // Clear all known secure keys
73
113
  for (const key of this.SECURE_KEYS) {
74
- 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
+ }
75
122
  }
76
123
  // Clear AsyncStorage keys related to PERS
77
124
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@explorins/pers-sdk-react-native",
3
- "version": "1.5.27",
3
+ "version": "1.5.29",
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);
@@ -127,7 +128,16 @@ export const PersSDKProvider: React.FC<{
127
128
  } finally {
128
129
  initializingRef.current = false;
129
130
  }
130
- }, []);
131
+ }, [isInitialized]);
132
+
133
+ // Auto-initialize if config is provided
134
+ useEffect(() => {
135
+ if (config && !isInitialized && !initializingRef.current) {
136
+ initialize(config).catch(err => {
137
+ console.error('Auto-initialization failed:', err);
138
+ });
139
+ }
140
+ }, [config, isInitialized, initialize]);
131
141
 
132
142
  const setAuthenticationState = useCallback((user: UserDTO | AdminDTO | null, accountAddress: string | null, isAuthenticated: boolean) => {
133
143
  setUser(user);
@@ -1,7 +1,23 @@
1
- import crypto from 'react-native-quick-crypto';
1
+ import { Platform } from 'react-native';
2
2
  import { Buffer } from 'buffer';
3
3
  import { DPoPCryptoProvider, DPoPKeyPair } from '@explorins/pers-sdk/core';
4
4
 
5
+ // Conditionally require quick-crypto for Native platforms only
6
+ let crypto: any;
7
+ if (Platform.OS !== 'web') {
8
+ try {
9
+ crypto = require('react-native-quick-crypto').default || require('react-native-quick-crypto');
10
+ } catch (e) {
11
+ console.warn('ReactNativeDPoPProvider: Failed to load react-native-quick-crypto', e);
12
+ }
13
+ } else {
14
+ // on Web, we shouldn't be using this provider anyway (Core SDK has WebDPoPProvider)
15
+ // But to be safe, we can mock or throw
16
+ crypto = {
17
+ generateKeyPair: () => { throw new Error('ReactNativeDPoPProvider not supported on Web'); }
18
+ };
19
+ }
20
+
5
21
  export class ReactNativeDPoPProvider implements DPoPCryptoProvider {
6
22
  /**
7
23
  * Generates a new key pair (ES256 recommended)
@@ -1,4 +1,4 @@
1
- import * as Keychain from 'react-native-keychain';
1
+ import { Platform } from 'react-native';
2
2
  import AsyncStorage from '@react-native-async-storage/async-storage';
3
3
  import {
4
4
  TokenStorage,
@@ -6,6 +6,16 @@ import {
6
6
  AUTH_STORAGE_KEYS
7
7
  } from '@explorins/pers-sdk/core';
8
8
 
9
+ // Conditionally require Keychain to avoid Web bundler errors
10
+ let Keychain: any;
11
+ if (Platform.OS !== 'web') {
12
+ try {
13
+ Keychain = require('react-native-keychain');
14
+ } catch (e) {
15
+ console.warn('ReactNativeSecureStorage: Failed to load react-native-keychain', e);
16
+ }
17
+ }
18
+
9
19
  /**
10
20
  * Secure Storage implementation for React Native
11
21
  * Uses Keychain/Keystore for sensitive data and AsyncStorage for non-sensitive data.
@@ -32,8 +42,18 @@ export class ReactNativeSecureStorage implements TokenStorage {
32
42
  // In runtime, 'key' is just a string, so this is safe.
33
43
  if (this.SECURE_KEYS.has(key as any)) {
34
44
  // Store in Keychain/Keystore
35
- // Service name acts as the key identifier in simple usage
36
- 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
+ }
37
57
  } else {
38
58
  // Store standard config/metadata in AsyncStorage
39
59
  await AsyncStorage.setItem(prefixedKey, stringValue);
@@ -45,14 +65,23 @@ export class ReactNativeSecureStorage implements TokenStorage {
45
65
 
46
66
  if (this.SECURE_KEYS.has(key as any)) {
47
67
  try {
48
- const credentials = await Keychain.getGenericPassword({ service: prefixedKey });
49
- if (credentials) {
68
+ if (Keychain) {
69
+ const credentials = await Keychain.getGenericPassword({ service: prefixedKey });
70
+ if (credentials) {
50
71
  return this.tryParse(credentials.password);
72
+ }
51
73
  }
52
- return null;
53
74
  } catch (e) {
54
75
  console.warn(`[ReactNativeSecureStorage] Failed to access keychain for ${key}`, e);
55
- 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;
56
85
  }
57
86
  } else {
58
87
  try {
@@ -69,7 +98,15 @@ export class ReactNativeSecureStorage implements TokenStorage {
69
98
  const prefixedKey = this.getKeyName(key);
70
99
 
71
100
  if (this.SECURE_KEYS.has(key as any)) {
72
- 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);
73
110
  } else {
74
111
  await AsyncStorage.removeItem(prefixedKey);
75
112
  }
@@ -78,7 +115,13 @@ export class ReactNativeSecureStorage implements TokenStorage {
78
115
  async clear(): Promise<void> {
79
116
  // Clear all known secure keys
80
117
  for (const key of this.SECURE_KEYS) {
81
- 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
+ }
82
125
  }
83
126
 
84
127
  // Clear AsyncStorage keys related to PERS