@oxyhq/services 5.5.5 → 5.5.7
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/lib/commonjs/core/AuthManager.js +378 -0
- package/lib/commonjs/core/AuthManager.js.map +1 -0
- package/lib/commonjs/core/index.js +13 -2
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +87 -456
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAuthFetch.js +36 -58
- package/lib/commonjs/ui/hooks/useAuthFetch.js.map +1 -1
- package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/module/core/AuthManager.js +373 -0
- package/lib/module/core/AuthManager.js.map +1 -0
- package/lib/module/core/index.js +6 -2
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +87 -456
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/useAuthFetch.js +36 -58
- package/lib/module/ui/hooks/useAuthFetch.js.map +1 -1
- package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
- package/lib/typescript/core/AuthManager.d.ts +100 -0
- package/lib/typescript/core/AuthManager.d.ts.map +1 -0
- package/lib/typescript/core/index.d.ts +6 -2
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +7 -10
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useAuthFetch.d.ts +4 -9
- package/lib/typescript/ui/hooks/useAuthFetch.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/AuthManager.ts +389 -0
- package/src/core/index.ts +7 -2
- package/src/ui/context/OxyContext.tsx +99 -508
- package/src/ui/hooks/useAuthFetch.ts +37 -55
- package/src/ui/screens/SessionManagementScreen.tsx +9 -9
|
@@ -1,33 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Simplified Authenticated Fetch Hook
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* const authFetch = useAuthFetch();
|
|
9
|
-
* const response = await authFetch('/api/protected');
|
|
10
|
-
* const data = await authFetch.get('/api/users');
|
|
4
|
+
* Clean, professional hook that uses the centralized AuthManager
|
|
5
|
+
* No more duplicate authentication logic or race conditions
|
|
11
6
|
*/
|
|
12
7
|
|
|
13
8
|
import { useCallback } from 'react';
|
|
14
9
|
import { useOxy } from '../context/OxyContext';
|
|
15
10
|
|
|
16
11
|
export interface AuthFetchOptions extends Omit<RequestInit, 'body'> {
|
|
17
|
-
body?: any;
|
|
12
|
+
body?: any;
|
|
18
13
|
}
|
|
19
14
|
|
|
20
15
|
export interface AuthFetchAPI {
|
|
21
|
-
// Main fetch function (drop-in replacement)
|
|
22
16
|
(input: RequestInfo | URL, init?: AuthFetchOptions): Promise<Response>;
|
|
23
|
-
|
|
24
|
-
// Convenience methods for JSON APIs
|
|
25
17
|
get: (endpoint: string, options?: AuthFetchOptions) => Promise<any>;
|
|
26
18
|
post: (endpoint: string, data?: any, options?: AuthFetchOptions) => Promise<any>;
|
|
27
19
|
put: (endpoint: string, data?: any, options?: AuthFetchOptions) => Promise<any>;
|
|
28
20
|
delete: (endpoint: string, options?: AuthFetchOptions) => Promise<any>;
|
|
29
|
-
|
|
30
|
-
// Access to auth state and methods
|
|
31
21
|
isAuthenticated: boolean;
|
|
32
22
|
user: any;
|
|
33
23
|
login: (username: string, password: string) => Promise<any>;
|
|
@@ -37,39 +27,41 @@ export interface AuthFetchAPI {
|
|
|
37
27
|
|
|
38
28
|
/**
|
|
39
29
|
* Hook that provides authenticated fetch functionality
|
|
40
|
-
* Uses the
|
|
30
|
+
* Uses the centralized AuthManager for all authentication operations
|
|
41
31
|
*/
|
|
42
32
|
export function useAuthFetch(): AuthFetchAPI {
|
|
43
|
-
const {
|
|
33
|
+
const { authManager, isAuthenticated, user, login, logout, signUp } = useOxy();
|
|
34
|
+
|
|
35
|
+
console.log('[useAuthFetch] Hook initialized');
|
|
36
|
+
console.log('[useAuthFetch] authManager:', !!authManager);
|
|
37
|
+
console.log('[useAuthFetch] isAuthenticated:', isAuthenticated);
|
|
38
|
+
console.log('[useAuthFetch] user:', !!user);
|
|
44
39
|
|
|
45
|
-
// Main fetch function with automatic auth headers
|
|
46
40
|
const authFetch = useCallback(async (input: RequestInfo | URL, init?: AuthFetchOptions): Promise<Response> => {
|
|
47
|
-
const url = resolveURL(input,
|
|
48
|
-
const options = await addAuthHeaders(init,
|
|
41
|
+
const url = resolveURL(input, authManager.getBaseURL());
|
|
42
|
+
const options = await addAuthHeaders(init, authManager);
|
|
49
43
|
|
|
50
44
|
try {
|
|
51
45
|
let response = await fetch(url, options);
|
|
52
46
|
|
|
53
47
|
// Handle token expiry and automatic refresh
|
|
54
48
|
if (response.status === 401 && isAuthenticated) {
|
|
55
|
-
// Try to refresh token and retry
|
|
56
49
|
try {
|
|
57
|
-
await
|
|
58
|
-
const retryOptions = await addAuthHeaders(init,
|
|
50
|
+
await authManager.refreshToken();
|
|
51
|
+
const retryOptions = await addAuthHeaders(init, authManager);
|
|
59
52
|
response = await fetch(url, retryOptions);
|
|
60
53
|
} catch (refreshError) {
|
|
61
|
-
|
|
62
|
-
console.warn('Token refresh failed, user needs to re-authenticate');
|
|
54
|
+
console.warn('[AuthFetch] Token refresh failed:', refreshError);
|
|
63
55
|
throw new Error('Authentication expired. Please login again.');
|
|
64
56
|
}
|
|
65
57
|
}
|
|
66
58
|
|
|
67
59
|
return response;
|
|
68
60
|
} catch (error) {
|
|
69
|
-
console.error('AuthFetch
|
|
61
|
+
console.error('[AuthFetch] Request failed:', error);
|
|
70
62
|
throw error;
|
|
71
63
|
}
|
|
72
|
-
}, [
|
|
64
|
+
}, [authManager, isAuthenticated]);
|
|
73
65
|
|
|
74
66
|
// JSON convenience methods
|
|
75
67
|
const get = useCallback(async (endpoint: string, options?: AuthFetchOptions) => {
|
|
@@ -108,7 +100,7 @@ export function useAuthFetch(): AuthFetchAPI {
|
|
|
108
100
|
return handleJsonResponse(response);
|
|
109
101
|
}, [authFetch]);
|
|
110
102
|
|
|
111
|
-
// Attach convenience methods and auth state
|
|
103
|
+
// Attach convenience methods and auth state
|
|
112
104
|
const fetchWithMethods = authFetch as AuthFetchAPI;
|
|
113
105
|
fetchWithMethods.get = get;
|
|
114
106
|
fetchWithMethods.post = post;
|
|
@@ -124,62 +116,53 @@ export function useAuthFetch(): AuthFetchAPI {
|
|
|
124
116
|
}
|
|
125
117
|
|
|
126
118
|
/**
|
|
127
|
-
* Helper functions
|
|
119
|
+
* Helper functions - much simpler now
|
|
128
120
|
*/
|
|
129
121
|
|
|
130
122
|
function resolveURL(input: RequestInfo | URL, baseURL: string): string {
|
|
131
123
|
const url = input.toString();
|
|
132
124
|
|
|
133
|
-
// If it's already a full URL, return as is
|
|
134
125
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
135
126
|
return url;
|
|
136
127
|
}
|
|
137
128
|
|
|
138
|
-
// If it starts with /, it's relative to base URL
|
|
139
129
|
if (url.startsWith('/')) {
|
|
140
130
|
return `${baseURL}${url}`;
|
|
141
131
|
}
|
|
142
132
|
|
|
143
|
-
// Otherwise, append to base URL with /
|
|
144
133
|
return `${baseURL}/${url}`;
|
|
145
134
|
}
|
|
146
135
|
|
|
147
|
-
async function addAuthHeaders(init?: AuthFetchOptions,
|
|
136
|
+
async function addAuthHeaders(init?: AuthFetchOptions, authManager?: any): Promise<RequestInit> {
|
|
148
137
|
const headers = new Headers(init?.headers);
|
|
149
138
|
|
|
150
|
-
|
|
151
|
-
console.log('[
|
|
139
|
+
console.log('[AuthFetch] addAuthHeaders called');
|
|
140
|
+
console.log('[AuthFetch] Has authManager:', !!authManager);
|
|
141
|
+
console.log('[AuthFetch] AuthManager type:', typeof authManager);
|
|
142
|
+
console.log('[AuthFetch] Has Authorization header already:', headers.has('Authorization'));
|
|
152
143
|
|
|
153
|
-
|
|
154
|
-
if (isAuthenticated && oxyServices && !headers.has('Authorization')) {
|
|
144
|
+
if (authManager && !headers.has('Authorization')) {
|
|
155
145
|
try {
|
|
156
|
-
|
|
157
|
-
let accessToken = oxyServices.getAccessToken?.();
|
|
158
|
-
console.log('[Auth API Debug] JWT accessToken from getAccessToken():', !!accessToken);
|
|
146
|
+
console.log('[AuthFetch] AuthManager.isAuthenticated():', authManager.isAuthenticated());
|
|
159
147
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
console.log('[Auth API] No JWT token, trying to get token from secure session:', activeSessionId);
|
|
163
|
-
try {
|
|
164
|
-
const tokenData = await oxyServices.getTokenBySession(activeSessionId);
|
|
165
|
-
accessToken = tokenData.accessToken;
|
|
166
|
-
console.log('[Auth API] Got token from session successfully');
|
|
167
|
-
} catch (error) {
|
|
168
|
-
console.warn('[Auth API] Failed to get token from session:', error);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
148
|
+
const accessToken = await authManager.getAccessToken();
|
|
149
|
+
console.log('[AuthFetch] Got access token:', !!accessToken);
|
|
171
150
|
|
|
172
151
|
if (accessToken) {
|
|
173
152
|
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
174
|
-
console.log('[
|
|
153
|
+
console.log('[AuthFetch] Added Authorization header successfully');
|
|
154
|
+
} else if (authManager.isAuthenticated()) {
|
|
155
|
+
console.error('[AuthFetch] User is authenticated but no token available');
|
|
156
|
+
throw new Error('Authentication state inconsistent. Please login again.');
|
|
175
157
|
} else {
|
|
176
|
-
console.
|
|
158
|
+
console.log('[AuthFetch] User not authenticated, skipping auth header');
|
|
177
159
|
}
|
|
178
160
|
} catch (error) {
|
|
179
|
-
console.error('[
|
|
161
|
+
console.error('[AuthFetch] Failed to get access token:', error);
|
|
162
|
+
throw error;
|
|
180
163
|
}
|
|
181
164
|
} else {
|
|
182
|
-
console.
|
|
165
|
+
console.log('[AuthFetch] Skipping auth header - authManager:', !!authManager, 'hasAuthHeader:', headers.has('Authorization'));
|
|
183
166
|
}
|
|
184
167
|
|
|
185
168
|
const body = init?.body;
|
|
@@ -214,7 +197,6 @@ async function handleJsonResponse(response: Response): Promise<any> {
|
|
|
214
197
|
try {
|
|
215
198
|
return await response.json();
|
|
216
199
|
} catch {
|
|
217
|
-
// If response isn't JSON, return the response itself
|
|
218
200
|
return response;
|
|
219
201
|
}
|
|
220
202
|
}
|
|
@@ -82,10 +82,10 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
|
|
|
82
82
|
try {
|
|
83
83
|
setActionLoading(sessionId);
|
|
84
84
|
await logout(sessionId);
|
|
85
|
-
|
|
85
|
+
|
|
86
86
|
// Refresh sessions to update the list
|
|
87
87
|
await refreshSessions();
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
toast.success('Session logged out successfully');
|
|
90
90
|
} catch (error) {
|
|
91
91
|
console.error('Logout session failed:', error);
|
|
@@ -100,8 +100,8 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
|
|
|
100
100
|
};
|
|
101
101
|
|
|
102
102
|
const handleLogoutOtherSessions = async () => {
|
|
103
|
-
const otherSessionsCount = userSessions.filter(s => s.sessionId !== activeSessionId).length;
|
|
104
|
-
|
|
103
|
+
const otherSessionsCount = userSessions.filter((s: any) => s.sessionId !== activeSessionId).length;
|
|
104
|
+
|
|
105
105
|
if (otherSessionsCount === 0) {
|
|
106
106
|
toast.info('No other sessions to logout.');
|
|
107
107
|
return;
|
|
@@ -124,10 +124,10 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
|
|
|
124
124
|
await logout(session.sessionId);
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
|
-
|
|
127
|
+
|
|
128
128
|
// Refresh sessions to update the list
|
|
129
129
|
await refreshSessions();
|
|
130
|
-
|
|
130
|
+
|
|
131
131
|
toast.success('Other sessions logged out successfully');
|
|
132
132
|
} catch (error) {
|
|
133
133
|
console.error('Logout other sessions failed:', error);
|
|
@@ -175,7 +175,7 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
|
|
|
175
175
|
if (diffInMinutes < 60) return `${diffInMinutes}m ago`;
|
|
176
176
|
if (diffInMinutes < 1440) return `${Math.floor(diffInMinutes / 60)}h ago`;
|
|
177
177
|
if (diffInMinutes < 10080) return `${Math.floor(diffInMinutes / 1440)}d ago`;
|
|
178
|
-
|
|
178
|
+
|
|
179
179
|
return date.toLocaleDateString();
|
|
180
180
|
};
|
|
181
181
|
|
|
@@ -230,7 +230,7 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
|
|
|
230
230
|
>
|
|
231
231
|
{userSessions.length > 0 ? (
|
|
232
232
|
<>
|
|
233
|
-
{userSessions.map((session) => (
|
|
233
|
+
{userSessions.map((session: any) => (
|
|
234
234
|
<View
|
|
235
235
|
key={session.sessionId}
|
|
236
236
|
style={[
|
|
@@ -294,7 +294,7 @@ const SessionManagementScreen: React.FC<BaseScreenProps> = ({
|
|
|
294
294
|
<TouchableOpacity
|
|
295
295
|
style={[styles.bulkActionButton, { backgroundColor: isDarkTheme ? '#1A1A1A' : '#F0F0F0', borderColor }]}
|
|
296
296
|
onPress={handleLogoutOtherSessions}
|
|
297
|
-
disabled={actionLoading === 'others' || userSessions.filter(s => s.sessionId !== activeSessionId).length === 0}
|
|
297
|
+
disabled={actionLoading === 'others' || userSessions.filter((s: any) => s.sessionId !== activeSessionId).length === 0}
|
|
298
298
|
>
|
|
299
299
|
{actionLoading === 'others' ? (
|
|
300
300
|
<ActivityIndicator size="small" color={primaryColor} />
|