@explorins/pers-sdk-react-native 1.5.25 → 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.
@@ -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;AACnG,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGpF,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,CAkHA,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,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,8 +1,10 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useContext, useState, useCallback, useRef } from 'react';
3
+ import { Platform } from 'react-native';
3
4
  import { PersSDK } from '@explorins/pers-sdk/core';
4
5
  import { ReactNativeHttpClient } from './react-native-http-client';
5
6
  import { createReactNativeAuthProvider } from './react-native-auth-provider';
7
+ import { ReactNativeDPoPProvider } from './rn-dpop-provider';
6
8
  // Create the context
7
9
  const SDKContext = createContext(null);
8
10
  // Provider component
@@ -39,6 +41,15 @@ export const PersSDKProvider = ({ children }) => {
39
41
  ...config,
40
42
  authProvider
41
43
  };
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
+ }
42
53
  // Initialize PersSDK with platform-aware auth provider
43
54
  const sdkInstance = new PersSDK(httpClient, sdkConfig);
44
55
  setAuthProvider(authProvider);
@@ -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;AAE5G,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;CACrB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,6BAA6B,CAC3C,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,qBAA0B,GACjC,mBAAmB,CAyBrB"}
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { Platform } from 'react-native';
9
9
  import { DefaultAuthProvider, LocalStorageTokenStorage } from '@explorins/pers-sdk/core';
10
- import { AsyncStorageTokenStorage } from '../storage/async-storage-token-storage';
10
+ import { ReactNativeSecureStorage } from '../storage/rn-secure-storage';
11
11
  // Use React Native's built-in platform detection
12
12
  const isWebPlatform = Platform.OS === 'web';
13
13
  /**
@@ -29,7 +29,7 @@ export function createReactNativeAuthProvider(projectKey, config = {}) {
29
29
  // Platform-specific storage selection
30
30
  const tokenStorage = customStorage || (isWebPlatform
31
31
  ? new LocalStorageTokenStorage()
32
- : new AsyncStorageTokenStorage(keyPrefix));
32
+ : new ReactNativeSecureStorage(keyPrefix));
33
33
  // Return DefaultAuthProvider configured with platform-appropriate storage
34
34
  return new DefaultAuthProvider({
35
35
  authType,
@@ -0,0 +1,19 @@
1
+ import { DPoPCryptoProvider, DPoPKeyPair } from '@explorins/pers-sdk/core';
2
+ export declare class ReactNativeDPoPProvider implements DPoPCryptoProvider {
3
+ /**
4
+ * Generates a new key pair (ES256 recommended)
5
+ *
6
+ * Note: options.extractable is ignored because for React Native Keychain storage,
7
+ * we MUST export the keys as JWK/strings. We rely on the Keychain encryption
8
+ * for security at rest (High Security), making the key "Extractable" in memory
9
+ * but protected by hardware encryption when stored.
10
+ */
11
+ generateKeyPair(options?: {
12
+ extractable?: boolean;
13
+ }): Promise<DPoPKeyPair>;
14
+ signProof(payload: Record<string, any>, keyPair: DPoPKeyPair): Promise<string>;
15
+ hashAccessToken(accessToken: string): Promise<string>;
16
+ private base64Url;
17
+ private base64UrlBuffer;
18
+ }
19
+ //# sourceMappingURL=rn-dpop-provider.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,76 @@
1
+ import crypto from 'react-native-quick-crypto';
2
+ import { Buffer } from 'buffer';
3
+ export class ReactNativeDPoPProvider {
4
+ /**
5
+ * Generates a new key pair (ES256 recommended)
6
+ *
7
+ * Note: options.extractable is ignored because for React Native Keychain storage,
8
+ * we MUST export the keys as JWK/strings. We rely on the Keychain encryption
9
+ * for security at rest (High Security), making the key "Extractable" in memory
10
+ * but protected by hardware encryption when stored.
11
+ */
12
+ async generateKeyPair(options) {
13
+ // Generate P-256 Key Pair
14
+ return new Promise((resolve, reject) => {
15
+ crypto.generateKeyPair('ec', { namedCurve: 'P-256' }, (err, publicKey, privateKey) => {
16
+ if (err)
17
+ return reject(err);
18
+ // Export to JWK for SDK Compatibility
19
+ // We use 'export' because supportsObjects=false in our storage forces the SDK
20
+ // to expect serializable keys.
21
+ const pubJwk = publicKey.export({ format: 'jwk' });
22
+ const privJwk = privateKey.export({ format: 'jwk' });
23
+ resolve({
24
+ publicKey: pubJwk,
25
+ privateKey: privJwk
26
+ });
27
+ });
28
+ });
29
+ }
30
+ async signProof(payload, keyPair) {
31
+ // 1. Construct Header
32
+ const header = {
33
+ typ: 'dpop+jwt',
34
+ alg: 'ES256',
35
+ jwk: keyPair.publicKey
36
+ };
37
+ // 2. Add Claims (iat/jti)
38
+ const finalPayload = {
39
+ ...payload,
40
+ iat: payload.iat || Math.floor(Date.now() / 1000),
41
+ jti: payload.jti || crypto.randomUUID()
42
+ };
43
+ // 3. Encode
44
+ const b64Header = this.base64Url(JSON.stringify(header));
45
+ const b64Payload = this.base64Url(JSON.stringify(finalPayload));
46
+ const signingInput = `${b64Header}.${b64Payload}`;
47
+ // 4. Sign
48
+ const sign = crypto.createSign('SHA256');
49
+ sign.update(signingInput);
50
+ // sign.end() is not required/available in quick-crypto as it doesn't strictly follow stream interface
51
+ // Import private key back from JWK to sign
52
+ // The keyPair.privateKey is a JsonWebKey object because we exported it earlier.
53
+ // quick-crypto createPrivateKey expects jwk object if format is jwk
54
+ const privateKeyObj = crypto.createPrivateKey({ key: keyPair.privateKey, format: 'jwk' });
55
+ const signature = sign.sign(privateKeyObj);
56
+ // signature is a Buffer in quick-crypto
57
+ return `${signingInput}.${this.base64UrlBuffer(signature)}`;
58
+ }
59
+ async hashAccessToken(accessToken) {
60
+ const hash = crypto.createHash('sha256').update(accessToken).digest();
61
+ // digest returns Buffer in quick-crypto
62
+ return this.base64UrlBuffer(hash);
63
+ }
64
+ // --- Helpers ---
65
+ base64Url(str) {
66
+ return this.base64UrlBuffer(Buffer.from(str));
67
+ }
68
+ base64UrlBuffer(buf) {
69
+ // Ensure we have a Buffer
70
+ const buffer = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
71
+ return buffer.toString('base64')
72
+ .replace(/=/g, '')
73
+ .replace(/\+/g, '-')
74
+ .replace(/\//g, '_');
75
+ }
76
+ }
@@ -0,0 +1,18 @@
1
+ import { TokenStorage } from '@explorins/pers-sdk/core';
2
+ /**
3
+ * Secure Storage implementation for React Native
4
+ * Uses Keychain/Keystore for sensitive data and AsyncStorage for non-sensitive data.
5
+ */
6
+ export declare class ReactNativeSecureStorage implements TokenStorage {
7
+ private keyPrefix;
8
+ readonly supportsObjects = false;
9
+ private readonly SECURE_KEYS;
10
+ constructor(keyPrefix?: string);
11
+ set(key: string, value: unknown): Promise<void>;
12
+ get(key: string): Promise<unknown | null>;
13
+ remove(key: string): Promise<void>;
14
+ clear(): Promise<void>;
15
+ private getKeyName;
16
+ private tryParse;
17
+ }
18
+ //# sourceMappingURL=rn-secure-storage.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,102 @@
1
+ import * as Keychain from 'react-native-keychain';
2
+ import AsyncStorage from '@react-native-async-storage/async-storage';
3
+ import { DPOP_STORAGE_KEYS, AUTH_STORAGE_KEYS } from '@explorins/pers-sdk/core';
4
+ /**
5
+ * Secure Storage implementation for React Native
6
+ * Uses Keychain/Keystore for sensitive data and AsyncStorage for non-sensitive data.
7
+ */
8
+ export class ReactNativeSecureStorage {
9
+ constructor(keyPrefix = 'pers_tokens_') {
10
+ this.keyPrefix = keyPrefix;
11
+ // Set to false because Keychain stores strings, so we need extractable (serializable) keys
12
+ this.supportsObjects = false;
13
+ // Define which keys MUST go to secure hardware storage
14
+ this.SECURE_KEYS = new Set([
15
+ DPOP_STORAGE_KEYS.PRIVATE,
16
+ AUTH_STORAGE_KEYS.ACCESS_TOKEN,
17
+ AUTH_STORAGE_KEYS.REFRESH_TOKEN,
18
+ AUTH_STORAGE_KEYS.PROVIDER_TOKEN
19
+ ]);
20
+ }
21
+ async set(key, value) {
22
+ const prefixedKey = this.getKeyName(key);
23
+ const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
24
+ // Cast key to any to bypass strict literal type checking of the Set.has method
25
+ // In runtime, 'key' is just a string, so this is safe.
26
+ if (this.SECURE_KEYS.has(key)) {
27
+ // Store in Keychain/Keystore
28
+ // Service name acts as the key identifier in simple usage
29
+ await Keychain.setGenericPassword(prefixedKey, stringValue, { service: prefixedKey });
30
+ }
31
+ else {
32
+ // Store standard config/metadata in AsyncStorage
33
+ await AsyncStorage.setItem(prefixedKey, stringValue);
34
+ }
35
+ }
36
+ async get(key) {
37
+ const prefixedKey = this.getKeyName(key);
38
+ if (this.SECURE_KEYS.has(key)) {
39
+ try {
40
+ const credentials = await Keychain.getGenericPassword({ service: prefixedKey });
41
+ if (credentials) {
42
+ return this.tryParse(credentials.password);
43
+ }
44
+ return null;
45
+ }
46
+ catch (e) {
47
+ console.warn(`[ReactNativeSecureStorage] Failed to access keychain for ${key}`, e);
48
+ return null; // Key not found or access denied
49
+ }
50
+ }
51
+ else {
52
+ try {
53
+ const val = await AsyncStorage.getItem(prefixedKey);
54
+ return val ? this.tryParse(val) : null;
55
+ }
56
+ catch (e) {
57
+ console.warn(`[ReactNativeSecureStorage] Failed to access AsyncStorage for ${key}`, e);
58
+ return null;
59
+ }
60
+ }
61
+ }
62
+ async remove(key) {
63
+ const prefixedKey = this.getKeyName(key);
64
+ if (this.SECURE_KEYS.has(key)) {
65
+ await Keychain.resetGenericPassword({ service: prefixedKey });
66
+ }
67
+ else {
68
+ await AsyncStorage.removeItem(prefixedKey);
69
+ }
70
+ }
71
+ async clear() {
72
+ // Clear all known secure keys
73
+ for (const key of this.SECURE_KEYS) {
74
+ await Keychain.resetGenericPassword({ service: this.getKeyName(key) });
75
+ }
76
+ // Clear AsyncStorage keys related to PERS
77
+ try {
78
+ const allKeys = await AsyncStorage.getAllKeys();
79
+ const ourKeys = allKeys.filter(k => k.startsWith(this.keyPrefix));
80
+ if (ourKeys.length > 0) {
81
+ await AsyncStorage.multiRemove(ourKeys);
82
+ }
83
+ }
84
+ catch (e) {
85
+ console.warn('[ReactNativeSecureStorage] Failed to clear AsyncStorage', e);
86
+ }
87
+ }
88
+ getKeyName(key) {
89
+ // For Keychain, we might want to avoid prefixes if we want to share across apps,
90
+ // but for simple isolation, prefix is fine to avoid collisions if multiple instances exist.
91
+ // However, Keychain services are global to the app bundle ID usually.
92
+ return `${this.keyPrefix}${key}`;
93
+ }
94
+ tryParse(val) {
95
+ try {
96
+ return JSON.parse(val);
97
+ }
98
+ catch {
99
+ return val;
100
+ }
101
+ }
102
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@explorins/pers-sdk-react-native",
3
- "version": "1.5.25",
3
+ "version": "1.5.27",
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,15 +37,16 @@
37
37
  "author": "eXplorins",
38
38
  "license": "MIT",
39
39
  "dependencies": {
40
- "@explorins/pers-sdk": "^1.6.28",
41
- "@explorins/pers-shared": "^2.1.44",
42
- "@explorins/pers-signer": "^1.0.26",
40
+ "@explorins/pers-sdk": "file:.yalc/@explorins/pers-sdk",
41
+ "@explorins/pers-shared": "^2.1.52",
42
+ "@explorins/pers-signer": "^1.0.32",
43
43
  "@explorins/web3-ts": "^0.3.75",
44
44
  "buffer": "^6.0.3",
45
45
  "ethers": "^6.15.0",
46
46
  "react-native-get-random-values": "^2.0.0",
47
47
  "react-native-keychain": "^10.0.0",
48
- "react-native-passkey": "^3.3.2"
48
+ "react-native-passkey": "^3.3.2",
49
+ "react-native-quick-crypto": "^1.0.6"
49
50
  },
50
51
  "browser": {
51
52
  "crypto": false,
@@ -1,81 +1,9 @@
1
1
  import { useCallback } from 'react';
2
2
  import { usePersSDK } from '../providers/PersSDKProvider';
3
-
4
- // Import analytics types directly from core SDK
5
- interface TransactionAnalyticsFilters {
6
- status?: string;
7
- tokenType?: string;
8
- triggerProcessType?: string;
9
- tenantId?: string;
10
- chainId?: number;
11
- type?: string;
12
- [key: string]: any;
13
- }
14
-
15
- type TransactionAnalyticsGroupBy =
16
- | 'month' | 'week' | 'day' | 'year' | 'quarter'
17
- | 'tokenType' | 'status' | 'chainId' | 'triggerProcessType' | 'type'
18
- | 'senderAddress' | 'recipientAddress' | 'senderId' | 'recipientId' | 'senderOwnerType' | 'recipientOwnerType' | 'createdAt';
19
-
20
- type TransactionAnalyticsMetric =
21
- | 'count' | 'sum' | 'avg' | 'min' | 'max';
22
-
23
- interface TransactionAnalyticsGroupByExpression {
24
- expression: string;
25
- alias: string;
26
- }
27
-
28
- /**
29
- * Request DTO for transaction analytics
30
- *
31
- * @interface TransactionAnalyticsRequestDTO
32
- */
33
- export interface TransactionAnalyticsRequestDTO {
34
- filters?: TransactionAnalyticsFilters;
35
- groupBy?: TransactionAnalyticsGroupBy[];
36
- groupByExpressions?: TransactionAnalyticsGroupByExpression[];
37
- metrics?: TransactionAnalyticsMetric[];
38
- orderBy?: string;
39
- orderDirection?: 'ASC' | 'DESC';
40
- limit?: number;
41
- startDate?: string;
42
- endDate?: string;
43
- }
44
-
45
- interface TransactionAnalyticsResultItem {
46
- [key: string]: string | number | Date | undefined;
47
- count?: string;
48
- sum?: number;
49
- avg?: number;
50
- min?: number;
51
- max?: number;
52
- tokentype?: string;
53
- tokenType?: string;
54
- year?: string;
55
- month?: string;
56
- type?: string;
57
- senderaddress?: string;
58
- senderAddress?: string;
59
- recipientaddress?: string;
60
- recipientAddress?: string;
61
- triggerprocesstype?: string;
62
- triggerProcessType?: string;
63
- userid?: string;
64
- userId?: string;
65
- }
66
-
67
- /**
68
- * Response DTO for transaction analytics
69
- *
70
- * @interface TransactionAnalyticsResponseDTO
71
- */
72
- export interface TransactionAnalyticsResponseDTO {
73
- results: TransactionAnalyticsResultItem[];
74
- totalGroups: number;
75
- metadata: {
76
- executionTime: string;
77
- };
78
- }
3
+ import {
4
+ TransactionAnalyticsRequestDTO,
5
+ TransactionAnalyticsResponseDTO
6
+ } from '@explorins/pers-shared';
79
7
 
80
8
  /**
81
9
  * React hook for analytics operations in the PERS SDK
@@ -5,9 +5,9 @@ import type {
5
5
  TransactionRequestDTO,
6
6
  TransactionRequestResponseDTO,
7
7
  TransactionDTO,
8
- TransactionRole
8
+ TransactionRole,
9
+ TransactionPaginationRequestDTO
9
10
  } from '@explorins/pers-shared';
10
- import type { TransactionPaginationParams } from '@explorins/pers-sdk/transaction';
11
11
 
12
12
  /**
13
13
  * React hook for transaction operations in the PERS SDK
@@ -196,7 +196,7 @@ export const useTransactions = () => {
196
196
  }
197
197
  }, [sdk, isInitialized]);
198
198
 
199
- const getPaginatedTransactions = useCallback(async (params: TransactionPaginationParams): Promise<any> => {
199
+ const getPaginatedTransactions = useCallback(async (params: TransactionPaginationRequestDTO): Promise<any> => {
200
200
  if (!isInitialized || !sdk) {
201
201
  throw new Error('SDK not initialized. Call initialize() first.');
202
202
  }
package/src/index.ts CHANGED
@@ -68,6 +68,19 @@ export {
68
68
  // SECURE STORAGE
69
69
  // ==============================================================================
70
70
 
71
+ /**
72
+ * Secure Token Storage with Keychain Integration
73
+ *
74
+ * Implements the TokenStorage interface using React Native Keychain for sensitive data
75
+ * (Private Keys, Access Tokens) and AsyncStorage for configuration/public data.
76
+ *
77
+ * - Encryption: Hardware-backed keystore
78
+ * - Persistence: Survives app uninstall (on iOS usually) or clears on uninstall (Android) if configured
79
+ *
80
+ * @see {@link ReactNativeSecureStorage} - The implementation class
81
+ */
82
+ export { ReactNativeSecureStorage } from './storage/rn-secure-storage';
83
+
71
84
  /**
72
85
  * Secure token storage using React Native AsyncStorage
73
86
  *
@@ -75,11 +88,22 @@ export {
75
88
  * Integrates with React Native Keychain for enhanced security on supported devices.
76
89
  *
77
90
  * @see {@link AsyncStorageTokenStorage} - Secure storage implementation
91
+ * @deprecated Use ReactNativeSecureStorage for better security (Keychain integration)
78
92
  */
79
93
  export {
80
94
  AsyncStorageTokenStorage,
81
95
  } from './storage/async-storage-token-storage';
82
96
 
97
+ // ==============================================================================
98
+ // DPoP PROVIDER
99
+ // ==============================================================================
100
+
101
+ /**
102
+ * React Native DPoP Crypto Provider
103
+ * implements DPoPCryptoProvider using react-native-quick-crypto
104
+ */
105
+ export { ReactNativeDPoPProvider } from './providers/rn-dpop-provider';
106
+
83
107
  // ==============================================================================
84
108
  // CORE PROVIDER & CONTEXT
85
109
  // ==============================================================================
@@ -1,7 +1,9 @@
1
1
  import React, { createContext, useContext, useState, ReactNode, useCallback, useRef } from 'react';
2
- import { PersSDK, PersConfig, DefaultAuthProvider } from '@explorins/pers-sdk/core';
2
+ import { Platform } from 'react-native';
3
+ import { PersSDK, PersConfig, DefaultAuthProvider, DPoPCryptoProvider } from '@explorins/pers-sdk/core';
3
4
  import { ReactNativeHttpClient } from './react-native-http-client';
4
5
  import { createReactNativeAuthProvider } from './react-native-auth-provider';
6
+ import { ReactNativeDPoPProvider } from './rn-dpop-provider';
5
7
  import { UserDTO, AdminDTO } from '@explorins/pers-shared';
6
8
 
7
9
  // Import manager types for TypeScript
@@ -102,6 +104,16 @@ export const PersSDKProvider: React.FC<{
102
104
  authProvider
103
105
  };
104
106
 
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
+
105
117
  // Initialize PersSDK with platform-aware auth provider
106
118
  const sdkInstance = new PersSDK(httpClient, sdkConfig);
107
119
 
@@ -9,6 +9,7 @@
9
9
  import { Platform } from 'react-native';
10
10
  import { DefaultAuthProvider, AuthApi, AuthType, LocalStorageTokenStorage } from '@explorins/pers-sdk/core';
11
11
  import { AsyncStorageTokenStorage } from '../storage/async-storage-token-storage';
12
+ import { ReactNativeSecureStorage } from '../storage/rn-secure-storage';
12
13
  import type { TokenStorage } from '@explorins/pers-sdk/core';
13
14
 
14
15
  // Use React Native's built-in platform detection
@@ -58,7 +59,7 @@ export function createReactNativeAuthProvider(
58
59
  const tokenStorage = customStorage || (
59
60
  isWebPlatform
60
61
  ? new LocalStorageTokenStorage()
61
- : new AsyncStorageTokenStorage(keyPrefix)
62
+ : new ReactNativeSecureStorage(keyPrefix)
62
63
  );
63
64
 
64
65
  // Return DefaultAuthProvider configured with platform-appropriate storage
@@ -0,0 +1,88 @@
1
+ import crypto from 'react-native-quick-crypto';
2
+ import { Buffer } from 'buffer';
3
+ import { DPoPCryptoProvider, DPoPKeyPair } from '@explorins/pers-sdk/core';
4
+
5
+ export class ReactNativeDPoPProvider implements DPoPCryptoProvider {
6
+ /**
7
+ * Generates a new key pair (ES256 recommended)
8
+ *
9
+ * Note: options.extractable is ignored because for React Native Keychain storage,
10
+ * we MUST export the keys as JWK/strings. We rely on the Keychain encryption
11
+ * for security at rest (High Security), making the key "Extractable" in memory
12
+ * but protected by hardware encryption when stored.
13
+ */
14
+ async generateKeyPair(options?: { extractable?: boolean }): Promise<DPoPKeyPair> {
15
+ // Generate P-256 Key Pair
16
+ return new Promise((resolve, reject) => {
17
+ crypto.generateKeyPair('ec', { namedCurve: 'P-256' }, (err: any, publicKey: any, privateKey: any) => {
18
+ if (err) return reject(err);
19
+
20
+ // Export to JWK for SDK Compatibility
21
+ // We use 'export' because supportsObjects=false in our storage forces the SDK
22
+ // to expect serializable keys.
23
+ const pubJwk = publicKey.export({ format: 'jwk' });
24
+ const privJwk = privateKey.export({ format: 'jwk' });
25
+
26
+ resolve({
27
+ publicKey: pubJwk,
28
+ privateKey: privJwk
29
+ });
30
+ });
31
+ });
32
+ }
33
+
34
+ async signProof(payload: Record<string, any>, keyPair: DPoPKeyPair): Promise<string> {
35
+ // 1. Construct Header
36
+ const header = {
37
+ typ: 'dpop+jwt',
38
+ alg: 'ES256',
39
+ jwk: keyPair.publicKey
40
+ };
41
+
42
+ // 2. Add Claims (iat/jti)
43
+ const finalPayload = {
44
+ ...payload,
45
+ iat: payload.iat || Math.floor(Date.now() / 1000),
46
+ jti: payload.jti || crypto.randomUUID()
47
+ };
48
+
49
+ // 3. Encode
50
+ const b64Header = this.base64Url(JSON.stringify(header));
51
+ const b64Payload = this.base64Url(JSON.stringify(finalPayload));
52
+ const signingInput = `${b64Header}.${b64Payload}`;
53
+
54
+ // 4. Sign
55
+ const sign = crypto.createSign('SHA256');
56
+ sign.update(signingInput);
57
+ // sign.end() is not required/available in quick-crypto as it doesn't strictly follow stream interface
58
+
59
+ // Import private key back from JWK to sign
60
+ // The keyPair.privateKey is a JsonWebKey object because we exported it earlier.
61
+ // quick-crypto createPrivateKey expects jwk object if format is jwk
62
+ const privateKeyObj = crypto.createPrivateKey({ key: keyPair.privateKey as any, format: 'jwk' });
63
+ const signature = sign.sign(privateKeyObj);
64
+ // signature is a Buffer in quick-crypto
65
+
66
+ return `${signingInput}.${this.base64UrlBuffer(signature)}`;
67
+ }
68
+
69
+ async hashAccessToken(accessToken: string): Promise<string> {
70
+ const hash = crypto.createHash('sha256').update(accessToken).digest();
71
+ // digest returns Buffer in quick-crypto
72
+ return this.base64UrlBuffer(hash);
73
+ }
74
+
75
+ // --- Helpers ---
76
+ private base64Url(str: string): string {
77
+ return this.base64UrlBuffer(Buffer.from(str));
78
+ }
79
+
80
+ private base64UrlBuffer(buf: Buffer | Uint8Array): string {
81
+ // Ensure we have a Buffer
82
+ const buffer = Buffer.isBuffer(buf) ? buf : Buffer.from(buf);
83
+ return buffer.toString('base64')
84
+ .replace(/=/g, '')
85
+ .replace(/\+/g, '-')
86
+ .replace(/\//g, '_');
87
+ }
88
+ }