@explorins/pers-sdk-react-native 2.0.3 → 2.0.5
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/dist/hooks/useAuth.d.ts +2 -1
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +4 -3
- package/dist/index.js +136 -21
- package/dist/index.js.map +1 -1
- package/dist/providers/PersSDKProvider.d.ts.map +1 -1
- package/dist/providers/PersSDKProvider.js +65 -1
- package/dist/providers/react-native-auth-provider.d.ts +3 -3
- package/dist/providers/react-native-auth-provider.d.ts.map +1 -1
- package/dist/providers/react-native-auth-provider.js +2 -2
- package/dist/storage/async-storage-token-storage.d.ts.map +1 -1
- package/dist/storage/async-storage-token-storage.js +15 -5
- package/package.json +2 -2
- package/src/hooks/useAuth.ts +4 -3
- package/src/providers/PersSDKProvider.tsx +74 -1
- package/src/providers/react-native-auth-provider.ts +4 -4
- package/src/storage/async-storage-token-storage.ts +14 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PersSDKProvider.d.ts","sourceRoot":"","sources":["../../src/providers/PersSDKProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAuC,SAAS,EAA2C,MAAM,OAAO,CAAC;AAEvH,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAIpF,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,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;IAGhC,UAAU,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,sBAAsB,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,EAAE,eAAe,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5F,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,
|
|
1
|
+
{"version":3,"file":"PersSDKProvider.d.ts","sourceRoot":"","sources":["../../src/providers/PersSDKProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAuC,SAAS,EAA2C,MAAM,OAAO,CAAC;AAEvH,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAIpF,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,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;IAGhC,UAAU,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,sBAAsB,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,IAAI,EAAE,eAAe,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5F,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,CAwNA,CAAC;AAGF,eAAO,MAAM,UAAU,QAAO,cAQ7B,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { createContext, useContext, useState, useCallback, useRef, useEffect, useMemo } from 'react';
|
|
3
|
-
import { Platform } from 'react-native';
|
|
3
|
+
import { Platform, AppState } from 'react-native';
|
|
4
4
|
import { PersSDK } from '@explorins/pers-sdk/core';
|
|
5
5
|
import { ReactNativeHttpClient } from './react-native-http-client';
|
|
6
6
|
import { createReactNativeAuthProvider } from './react-native-auth-provider';
|
|
@@ -93,6 +93,70 @@ export const PersSDKProvider = ({ children, config }) => {
|
|
|
93
93
|
throw error;
|
|
94
94
|
}
|
|
95
95
|
}, [sdk, isAuthenticated, isInitialized]);
|
|
96
|
+
// Listen for authentication status changes and refresh user data when tokens are renewed
|
|
97
|
+
useEffect(() => {
|
|
98
|
+
if (!authProvider || !isInitialized)
|
|
99
|
+
return;
|
|
100
|
+
// Access the config object with proper type safety
|
|
101
|
+
const providerConfig = authProvider.config;
|
|
102
|
+
if (!providerConfig)
|
|
103
|
+
return;
|
|
104
|
+
// Set up auth status change handler
|
|
105
|
+
const originalHandler = providerConfig.onAuthStatusChange;
|
|
106
|
+
const authStatusHandler = async (status) => {
|
|
107
|
+
console.log('[PersSDK] Auth status changed:', status);
|
|
108
|
+
// Call original handler first if it exists
|
|
109
|
+
if (originalHandler) {
|
|
110
|
+
await originalHandler(status);
|
|
111
|
+
}
|
|
112
|
+
// If token was refreshed successfully and user is authenticated, reload user data
|
|
113
|
+
if (status === 'authenticated' && isAuthenticated && sdk) {
|
|
114
|
+
try {
|
|
115
|
+
console.log('[PersSDK] Token refreshed, reloading user data...');
|
|
116
|
+
await refreshUserData();
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error('[PersSDK] Failed to refresh user data after token renewal:', error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
// If authentication failed, clear state
|
|
123
|
+
if (status === 'auth_failed') {
|
|
124
|
+
console.log('[PersSDK] Authentication failed, clearing state');
|
|
125
|
+
setAuthenticationState(null, false);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
// Inject our handler into the auth provider config
|
|
129
|
+
providerConfig.onAuthStatusChange = authStatusHandler;
|
|
130
|
+
// Cleanup
|
|
131
|
+
return () => {
|
|
132
|
+
if (originalHandler) {
|
|
133
|
+
providerConfig.onAuthStatusChange = originalHandler;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
}, [authProvider, isInitialized, isAuthenticated, sdk, refreshUserData, setAuthenticationState]);
|
|
137
|
+
// iOS/Android: Monitor app state and validate tokens when app becomes active
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
if (!sdk || Platform.OS === 'web') {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const handleAppStateChange = async (nextAppState) => {
|
|
143
|
+
if (nextAppState === 'active' && isAuthenticated) {
|
|
144
|
+
console.log('[PersSDK] App became active - validating tokens');
|
|
145
|
+
try {
|
|
146
|
+
// Trigger token validation when app resumes from background
|
|
147
|
+
// This ensures tokens are checked after extended inactivity
|
|
148
|
+
await sdk.auth.ensureValidToken();
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
console.warn('[PersSDK] Token validation failed on app resume:', error);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const subscription = AppState.addEventListener('change', handleAppStateChange);
|
|
156
|
+
return () => {
|
|
157
|
+
subscription?.remove();
|
|
158
|
+
};
|
|
159
|
+
}, [sdk, isAuthenticated]);
|
|
96
160
|
const contextValue = useMemo(() => ({
|
|
97
161
|
// Main SDK instance
|
|
98
162
|
sdk,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - Web: Uses LocalStorageTokenStorage from core SDK
|
|
6
6
|
* - Mobile: Uses AsyncStorage-based storage
|
|
7
7
|
*/
|
|
8
|
-
import { DefaultAuthProvider,
|
|
8
|
+
import { DefaultAuthProvider, AccountOwnerType } from '@explorins/pers-sdk/core';
|
|
9
9
|
import type { TokenStorage } from '@explorins/pers-sdk/core';
|
|
10
10
|
/**
|
|
11
11
|
* Configuration for React Native Auth Provider
|
|
@@ -17,8 +17,8 @@ export interface ReactNativeAuthConfig {
|
|
|
17
17
|
debug?: boolean;
|
|
18
18
|
/** Custom token storage implementation */
|
|
19
19
|
customStorage?: TokenStorage;
|
|
20
|
-
/** Authentication type (default:
|
|
21
|
-
authType?:
|
|
20
|
+
/** Authentication type (default: AccountOwnerType.USER) */
|
|
21
|
+
authType?: AccountOwnerType;
|
|
22
22
|
/** DPoP Configuration (optional) */
|
|
23
23
|
dpop?: {
|
|
24
24
|
enabled?: boolean;
|
|
@@ -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,
|
|
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,gBAAgB,EAA4B,MAAM,0BAA0B,CAAC;AAGpH,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,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,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,CAsCrB"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - Mobile: Uses AsyncStorage-based storage
|
|
7
7
|
*/
|
|
8
8
|
import { Platform } from 'react-native';
|
|
9
|
-
import { DefaultAuthProvider, LocalStorageTokenStorage } from '@explorins/pers-sdk/core';
|
|
9
|
+
import { DefaultAuthProvider, AccountOwnerType, LocalStorageTokenStorage } from '@explorins/pers-sdk/core';
|
|
10
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';
|
|
@@ -25,7 +25,7 @@ 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 =
|
|
28
|
+
const { keyPrefix = `pers_${projectKey.slice(0, 8)}_`, debug = false, customStorage, authType = AccountOwnerType.USER, dpop } = config;
|
|
29
29
|
// Platform-specific storage selection
|
|
30
30
|
const tokenStorage = customStorage || (isWebPlatform
|
|
31
31
|
? new LocalStorageTokenStorage()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"async-storage-token-storage.d.ts","sourceRoot":"","sources":["../../src/storage/async-storage-token-storage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AA8D7D;;;;;GAKG;AACH,qBAAa,wBAAyB,YAAW,YAAY;IAC3D,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAA8B;gBAEtC,SAAS,GAAE,MAAuB;IAexC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"async-storage-token-storage.d.ts","sourceRoot":"","sources":["../../src/storage/async-storage-token-storage.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AA8D7D;;;;;GAKG;AACH,qBAAa,wBAAyB,YAAW,YAAY;IAC3D,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,YAAY,CAA8B;gBAEtC,SAAS,GAAE,MAAuB;IAexC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW9C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAYxC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQlC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAgB7B"}
|
|
@@ -74,19 +74,24 @@ export class AsyncStorageTokenStorage {
|
|
|
74
74
|
}
|
|
75
75
|
async set(key, value) {
|
|
76
76
|
try {
|
|
77
|
-
|
|
77
|
+
const fullKey = `${this.keyPrefix}${key}`;
|
|
78
|
+
await this.asyncStorage.setItem(fullKey, value);
|
|
79
|
+
console.log(`[AsyncStorage] Token stored: ${key}`);
|
|
78
80
|
}
|
|
79
81
|
catch (error) {
|
|
80
|
-
console.error(`Failed to store token ${key}:`, error);
|
|
82
|
+
console.error(`[AsyncStorage] Failed to store token ${key}:`, error);
|
|
81
83
|
throw new Error(`Token storage failed: ${error}`);
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
async get(key) {
|
|
85
87
|
try {
|
|
86
|
-
|
|
88
|
+
const fullKey = `${this.keyPrefix}${key}`;
|
|
89
|
+
const value = await this.asyncStorage.getItem(fullKey);
|
|
90
|
+
console.log(`[AsyncStorage] Token retrieved: ${key} - ${value ? 'exists' : 'null'}`);
|
|
91
|
+
return value;
|
|
87
92
|
}
|
|
88
93
|
catch (error) {
|
|
89
|
-
console.error(`Failed to retrieve token ${key}:`, error);
|
|
94
|
+
console.error(`[AsyncStorage] Failed to retrieve token ${key}:`, error);
|
|
90
95
|
return null;
|
|
91
96
|
}
|
|
92
97
|
}
|
|
@@ -103,11 +108,16 @@ export class AsyncStorageTokenStorage {
|
|
|
103
108
|
const allKeys = await this.asyncStorage.getAllKeys();
|
|
104
109
|
const ourKeys = allKeys.filter(key => key.startsWith(this.keyPrefix));
|
|
105
110
|
if (ourKeys.length > 0) {
|
|
111
|
+
console.log(`[AsyncStorage] Clearing ${ourKeys.length} token(s):`, ourKeys);
|
|
106
112
|
await this.asyncStorage.multiRemove(ourKeys);
|
|
113
|
+
console.log('[AsyncStorage] All tokens cleared successfully');
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
console.log('[AsyncStorage] No tokens to clear');
|
|
107
117
|
}
|
|
108
118
|
}
|
|
109
119
|
catch (error) {
|
|
110
|
-
console.error('Failed to clear token storage:', error);
|
|
120
|
+
console.error('[AsyncStorage] Failed to clear token storage:', error);
|
|
111
121
|
}
|
|
112
122
|
}
|
|
113
123
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@explorins/pers-sdk-react-native",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
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": "^2.0.
|
|
40
|
+
"@explorins/pers-sdk": "^2.0.6",
|
|
41
41
|
"@explorins/pers-shared": "^2.1.64",
|
|
42
42
|
"@explorins/pers-signer": "^1.0.33",
|
|
43
43
|
"@explorins/web3-ts": "^0.3.77",
|
package/src/hooks/useAuth.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import { usePersSDK } from '../providers/PersSDKProvider';
|
|
3
3
|
import type { RawUserData } from '@explorins/pers-sdk/core';
|
|
4
|
+
import { AccountOwnerType } from '@explorins/pers-sdk/core';
|
|
4
5
|
import type { UserDTO, AdminDTO, SessionAuthContextResponseDTO } from '@explorins/pers-shared';
|
|
5
6
|
|
|
6
7
|
// Re-export RawUserData for convenience
|
|
@@ -58,18 +59,18 @@ export const useAuth = () => {
|
|
|
58
59
|
* Authenticates a user with a JWT token
|
|
59
60
|
*
|
|
60
61
|
* @param jwtToken - The JWT token to authenticate with
|
|
61
|
-
* @param userType - The type of user (
|
|
62
|
+
* @param userType - The type of user (AccountOwnerType), defaults to USER
|
|
62
63
|
* @returns Promise resolving to session auth context with user data
|
|
63
64
|
* @throws Error if SDK is not initialized or authentication fails
|
|
64
65
|
*
|
|
65
66
|
* @example
|
|
66
67
|
* ```typescript
|
|
67
68
|
* const { login } = useAuth();
|
|
68
|
-
* const result = await login(jwtToken,
|
|
69
|
+
* const result = await login(jwtToken, AccountOwnerType.USER);
|
|
69
70
|
* console.log('Logged in user:', result.user);
|
|
70
71
|
* ```
|
|
71
72
|
*/
|
|
72
|
-
const login = useCallback(async (jwtToken: string, userType:
|
|
73
|
+
const login = useCallback(async (jwtToken: string, userType: AccountOwnerType = AccountOwnerType.USER): Promise<SessionAuthContextResponseDTO> => {
|
|
73
74
|
if (!sdk || !authProvider) {
|
|
74
75
|
throw new Error('SDK not initialized. Call initialize() first.');
|
|
75
76
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { createContext, useContext, useState, ReactNode, useCallback, useRef, useEffect, useMemo } from 'react';
|
|
2
|
-
import { Platform } from 'react-native';
|
|
2
|
+
import { Platform, AppState, AppStateStatus } from 'react-native';
|
|
3
3
|
import { PersSDK, PersConfig, DefaultAuthProvider } from '@explorins/pers-sdk/core';
|
|
4
4
|
import { ReactNativeHttpClient } from './react-native-http-client';
|
|
5
5
|
import { createReactNativeAuthProvider } from './react-native-auth-provider';
|
|
@@ -160,6 +160,79 @@ export const PersSDKProvider: React.FC<{
|
|
|
160
160
|
}
|
|
161
161
|
}, [sdk, isAuthenticated, isInitialized]);
|
|
162
162
|
|
|
163
|
+
// Listen for authentication status changes and refresh user data when tokens are renewed
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
if (!authProvider || !isInitialized) return;
|
|
166
|
+
|
|
167
|
+
// Access the config object with proper type safety
|
|
168
|
+
const providerConfig = (authProvider as any).config;
|
|
169
|
+
if (!providerConfig) return;
|
|
170
|
+
|
|
171
|
+
// Set up auth status change handler
|
|
172
|
+
const originalHandler = providerConfig.onAuthStatusChange;
|
|
173
|
+
|
|
174
|
+
const authStatusHandler = async (status: string) => {
|
|
175
|
+
console.log('[PersSDK] Auth status changed:', status);
|
|
176
|
+
|
|
177
|
+
// Call original handler first if it exists
|
|
178
|
+
if (originalHandler) {
|
|
179
|
+
await originalHandler(status);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// If token was refreshed successfully and user is authenticated, reload user data
|
|
183
|
+
if (status === 'authenticated' && isAuthenticated && sdk) {
|
|
184
|
+
try {
|
|
185
|
+
console.log('[PersSDK] Token refreshed, reloading user data...');
|
|
186
|
+
await refreshUserData();
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error('[PersSDK] Failed to refresh user data after token renewal:', error);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// If authentication failed, clear state
|
|
193
|
+
if (status === 'auth_failed') {
|
|
194
|
+
console.log('[PersSDK] Authentication failed, clearing state');
|
|
195
|
+
setAuthenticationState(null, false);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Inject our handler into the auth provider config
|
|
200
|
+
providerConfig.onAuthStatusChange = authStatusHandler;
|
|
201
|
+
|
|
202
|
+
// Cleanup
|
|
203
|
+
return () => {
|
|
204
|
+
if (originalHandler) {
|
|
205
|
+
providerConfig.onAuthStatusChange = originalHandler;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
}, [authProvider, isInitialized, isAuthenticated, sdk, refreshUserData, setAuthenticationState]);
|
|
209
|
+
|
|
210
|
+
// iOS/Android: Monitor app state and validate tokens when app becomes active
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (!sdk || Platform.OS === 'web') {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const handleAppStateChange = async (nextAppState: AppStateStatus) => {
|
|
217
|
+
if (nextAppState === 'active' && isAuthenticated) {
|
|
218
|
+
console.log('[PersSDK] App became active - validating tokens');
|
|
219
|
+
try {
|
|
220
|
+
// Trigger token validation when app resumes from background
|
|
221
|
+
// This ensures tokens are checked after extended inactivity
|
|
222
|
+
await sdk.auth.ensureValidToken();
|
|
223
|
+
} catch (error) {
|
|
224
|
+
console.warn('[PersSDK] Token validation failed on app resume:', error);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const subscription = AppState.addEventListener('change', handleAppStateChange);
|
|
230
|
+
|
|
231
|
+
return () => {
|
|
232
|
+
subscription?.remove();
|
|
233
|
+
};
|
|
234
|
+
}, [sdk, isAuthenticated]);
|
|
235
|
+
|
|
163
236
|
const contextValue: PersSDKContext = useMemo(() => ({
|
|
164
237
|
// Main SDK instance
|
|
165
238
|
sdk,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { Platform } from 'react-native';
|
|
10
|
-
import { DefaultAuthProvider, AuthApi,
|
|
10
|
+
import { DefaultAuthProvider, AuthApi, AccountOwnerType, LocalStorageTokenStorage } from '@explorins/pers-sdk/core';
|
|
11
11
|
import { AsyncStorageTokenStorage } from '../storage/async-storage-token-storage';
|
|
12
12
|
import { ReactNativeSecureStorage } from '../storage/rn-secure-storage';
|
|
13
13
|
import type { TokenStorage } from '@explorins/pers-sdk/core';
|
|
@@ -25,8 +25,8 @@ export interface ReactNativeAuthConfig {
|
|
|
25
25
|
debug?: boolean;
|
|
26
26
|
/** Custom token storage implementation */
|
|
27
27
|
customStorage?: TokenStorage;
|
|
28
|
-
/** Authentication type (default:
|
|
29
|
-
authType?:
|
|
28
|
+
/** Authentication type (default: AccountOwnerType.USER) */
|
|
29
|
+
authType?: AccountOwnerType;
|
|
30
30
|
/** DPoP Configuration (optional) */
|
|
31
31
|
dpop?: {
|
|
32
32
|
enabled?: boolean;
|
|
@@ -57,7 +57,7 @@ export function createReactNativeAuthProvider(
|
|
|
57
57
|
keyPrefix = `pers_${projectKey.slice(0, 8)}_`,
|
|
58
58
|
debug = false,
|
|
59
59
|
customStorage,
|
|
60
|
-
authType =
|
|
60
|
+
authType = AccountOwnerType.USER,
|
|
61
61
|
dpop
|
|
62
62
|
} = config;
|
|
63
63
|
|
|
@@ -94,18 +94,23 @@ export class AsyncStorageTokenStorage implements TokenStorage {
|
|
|
94
94
|
|
|
95
95
|
async set(key: string, value: string): Promise<void> {
|
|
96
96
|
try {
|
|
97
|
-
|
|
97
|
+
const fullKey = `${this.keyPrefix}${key}`;
|
|
98
|
+
await this.asyncStorage.setItem(fullKey, value);
|
|
99
|
+
console.log(`[AsyncStorage] Token stored: ${key}`);
|
|
98
100
|
} catch (error) {
|
|
99
|
-
console.error(`Failed to store token ${key}:`, error);
|
|
101
|
+
console.error(`[AsyncStorage] Failed to store token ${key}:`, error);
|
|
100
102
|
throw new Error(`Token storage failed: ${error}`);
|
|
101
103
|
}
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
async get(key: string): Promise<string | null> {
|
|
105
107
|
try {
|
|
106
|
-
|
|
108
|
+
const fullKey = `${this.keyPrefix}${key}`;
|
|
109
|
+
const value = await this.asyncStorage.getItem(fullKey);
|
|
110
|
+
console.log(`[AsyncStorage] Token retrieved: ${key} - ${value ? 'exists' : 'null'}`);
|
|
111
|
+
return value;
|
|
107
112
|
} catch (error) {
|
|
108
|
-
console.error(`Failed to retrieve token ${key}:`, error);
|
|
113
|
+
console.error(`[AsyncStorage] Failed to retrieve token ${key}:`, error);
|
|
109
114
|
return null;
|
|
110
115
|
}
|
|
111
116
|
}
|
|
@@ -124,10 +129,14 @@ export class AsyncStorageTokenStorage implements TokenStorage {
|
|
|
124
129
|
const ourKeys = allKeys.filter(key => key.startsWith(this.keyPrefix));
|
|
125
130
|
|
|
126
131
|
if (ourKeys.length > 0) {
|
|
132
|
+
console.log(`[AsyncStorage] Clearing ${ourKeys.length} token(s):`, ourKeys);
|
|
127
133
|
await this.asyncStorage.multiRemove(ourKeys);
|
|
134
|
+
console.log('[AsyncStorage] All tokens cleared successfully');
|
|
135
|
+
} else {
|
|
136
|
+
console.log('[AsyncStorage] No tokens to clear');
|
|
128
137
|
}
|
|
129
138
|
} catch (error) {
|
|
130
|
-
console.error('Failed to clear token storage:', error);
|
|
139
|
+
console.error('[AsyncStorage] Failed to clear token storage:', error);
|
|
131
140
|
}
|
|
132
141
|
}
|
|
133
142
|
}
|