@oxyhq/services 5.5.4 → 5.5.6
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 +352 -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 +22 -60
- 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 +347 -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 +22 -60
- 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 +366 -0
- package/src/core/index.ts +7 -2
- package/src/ui/context/OxyContext.tsx +99 -508
- package/src/ui/hooks/useAuthFetch.ts +22 -60
- 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,36 @@ 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();
|
|
44
34
|
|
|
45
|
-
// Main fetch function with automatic auth headers
|
|
46
35
|
const authFetch = useCallback(async (input: RequestInfo | URL, init?: AuthFetchOptions): Promise<Response> => {
|
|
47
|
-
const url = resolveURL(input,
|
|
48
|
-
const options = await addAuthHeaders(init,
|
|
36
|
+
const url = resolveURL(input, authManager.getBaseURL());
|
|
37
|
+
const options = await addAuthHeaders(init, authManager);
|
|
49
38
|
|
|
50
39
|
try {
|
|
51
40
|
let response = await fetch(url, options);
|
|
52
41
|
|
|
53
42
|
// Handle token expiry and automatic refresh
|
|
54
43
|
if (response.status === 401 && isAuthenticated) {
|
|
55
|
-
// Try to refresh token and retry
|
|
56
44
|
try {
|
|
57
|
-
await
|
|
58
|
-
const retryOptions = await addAuthHeaders(init,
|
|
45
|
+
await authManager.refreshToken();
|
|
46
|
+
const retryOptions = await addAuthHeaders(init, authManager);
|
|
59
47
|
response = await fetch(url, retryOptions);
|
|
60
48
|
} catch (refreshError) {
|
|
61
|
-
|
|
62
|
-
console.warn('Token refresh failed, user needs to re-authenticate');
|
|
49
|
+
console.warn('[AuthFetch] Token refresh failed:', refreshError);
|
|
63
50
|
throw new Error('Authentication expired. Please login again.');
|
|
64
51
|
}
|
|
65
52
|
}
|
|
66
53
|
|
|
67
54
|
return response;
|
|
68
55
|
} catch (error) {
|
|
69
|
-
console.error('AuthFetch
|
|
56
|
+
console.error('[AuthFetch] Request failed:', error);
|
|
70
57
|
throw error;
|
|
71
58
|
}
|
|
72
|
-
}, [
|
|
59
|
+
}, [authManager, isAuthenticated]);
|
|
73
60
|
|
|
74
61
|
// JSON convenience methods
|
|
75
62
|
const get = useCallback(async (endpoint: string, options?: AuthFetchOptions) => {
|
|
@@ -108,7 +95,7 @@ export function useAuthFetch(): AuthFetchAPI {
|
|
|
108
95
|
return handleJsonResponse(response);
|
|
109
96
|
}, [authFetch]);
|
|
110
97
|
|
|
111
|
-
// Attach convenience methods and auth state
|
|
98
|
+
// Attach convenience methods and auth state
|
|
112
99
|
const fetchWithMethods = authFetch as AuthFetchAPI;
|
|
113
100
|
fetchWithMethods.get = get;
|
|
114
101
|
fetchWithMethods.post = post;
|
|
@@ -124,62 +111,38 @@ export function useAuthFetch(): AuthFetchAPI {
|
|
|
124
111
|
}
|
|
125
112
|
|
|
126
113
|
/**
|
|
127
|
-
* Helper functions
|
|
114
|
+
* Helper functions - much simpler now
|
|
128
115
|
*/
|
|
129
116
|
|
|
130
117
|
function resolveURL(input: RequestInfo | URL, baseURL: string): string {
|
|
131
118
|
const url = input.toString();
|
|
132
119
|
|
|
133
|
-
// If it's already a full URL, return as is
|
|
134
120
|
if (url.startsWith('http://') || url.startsWith('https://')) {
|
|
135
121
|
return url;
|
|
136
122
|
}
|
|
137
123
|
|
|
138
|
-
// If it starts with /, it's relative to base URL
|
|
139
124
|
if (url.startsWith('/')) {
|
|
140
125
|
return `${baseURL}${url}`;
|
|
141
126
|
}
|
|
142
127
|
|
|
143
|
-
// Otherwise, append to base URL with /
|
|
144
128
|
return `${baseURL}/${url}`;
|
|
145
129
|
}
|
|
146
130
|
|
|
147
|
-
async function addAuthHeaders(init?: AuthFetchOptions,
|
|
131
|
+
async function addAuthHeaders(init?: AuthFetchOptions, authManager?: any): Promise<RequestInit> {
|
|
148
132
|
const headers = new Headers(init?.headers);
|
|
149
133
|
|
|
150
|
-
|
|
151
|
-
console.log('[Auth API Debug] isAuthenticated:', isAuthenticated, 'activeSessionId:', activeSessionId, 'oxyServices:', !!oxyServices);
|
|
152
|
-
|
|
153
|
-
// Add auth header if user is authenticated (use context state instead of getCurrentUserId)
|
|
154
|
-
if (isAuthenticated && oxyServices && !headers.has('Authorization')) {
|
|
134
|
+
if (authManager && !headers.has('Authorization')) {
|
|
155
135
|
try {
|
|
156
|
-
|
|
157
|
-
let accessToken = oxyServices.getAccessToken?.();
|
|
158
|
-
console.log('[Auth API Debug] JWT accessToken from getAccessToken():', !!accessToken);
|
|
159
|
-
|
|
160
|
-
// If no JWT token but we have a secure session, try to get token from session
|
|
161
|
-
if (!accessToken && activeSessionId) {
|
|
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
|
-
}
|
|
171
|
-
|
|
136
|
+
const accessToken = await authManager.getAccessToken();
|
|
172
137
|
if (accessToken) {
|
|
173
138
|
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
console.warn('[Auth API] No authentication token available - JWT token:', !!oxyServices.getAccessToken?.(), 'activeSessionId:', activeSessionId);
|
|
139
|
+
} else if (authManager.isAuthenticated()) {
|
|
140
|
+
throw new Error('Authentication state inconsistent. Please login again.');
|
|
177
141
|
}
|
|
178
142
|
} catch (error) {
|
|
179
|
-
console.error('[
|
|
143
|
+
console.error('[AuthFetch] Failed to get access token:', error);
|
|
144
|
+
throw error;
|
|
180
145
|
}
|
|
181
|
-
} else {
|
|
182
|
-
console.warn('[Auth API] Cannot authenticate - isAuthenticated:', isAuthenticated, 'oxyServices:', !!oxyServices, 'hasAuthHeader:', headers.has('Authorization'));
|
|
183
146
|
}
|
|
184
147
|
|
|
185
148
|
const body = init?.body;
|
|
@@ -214,7 +177,6 @@ async function handleJsonResponse(response: Response): Promise<any> {
|
|
|
214
177
|
try {
|
|
215
178
|
return await response.json();
|
|
216
179
|
} catch {
|
|
217
|
-
// If response isn't JSON, return the response itself
|
|
218
180
|
return response;
|
|
219
181
|
}
|
|
220
182
|
}
|
|
@@ -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} />
|