@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.
Files changed (32) hide show
  1. package/lib/commonjs/core/AuthManager.js +352 -0
  2. package/lib/commonjs/core/AuthManager.js.map +1 -0
  3. package/lib/commonjs/core/index.js +13 -2
  4. package/lib/commonjs/core/index.js.map +1 -1
  5. package/lib/commonjs/ui/context/OxyContext.js +87 -456
  6. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  7. package/lib/commonjs/ui/hooks/useAuthFetch.js +22 -60
  8. package/lib/commonjs/ui/hooks/useAuthFetch.js.map +1 -1
  9. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  10. package/lib/module/core/AuthManager.js +347 -0
  11. package/lib/module/core/AuthManager.js.map +1 -0
  12. package/lib/module/core/index.js +6 -2
  13. package/lib/module/core/index.js.map +1 -1
  14. package/lib/module/ui/context/OxyContext.js +87 -456
  15. package/lib/module/ui/context/OxyContext.js.map +1 -1
  16. package/lib/module/ui/hooks/useAuthFetch.js +22 -60
  17. package/lib/module/ui/hooks/useAuthFetch.js.map +1 -1
  18. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  19. package/lib/typescript/core/AuthManager.d.ts +100 -0
  20. package/lib/typescript/core/AuthManager.d.ts.map +1 -0
  21. package/lib/typescript/core/index.d.ts +6 -2
  22. package/lib/typescript/core/index.d.ts.map +1 -1
  23. package/lib/typescript/ui/context/OxyContext.d.ts +7 -10
  24. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  25. package/lib/typescript/ui/hooks/useAuthFetch.d.ts +4 -9
  26. package/lib/typescript/ui/hooks/useAuthFetch.d.ts.map +1 -1
  27. package/package.json +1 -1
  28. package/src/core/AuthManager.ts +366 -0
  29. package/src/core/index.ts +7 -2
  30. package/src/ui/context/OxyContext.tsx +99 -508
  31. package/src/ui/hooks/useAuthFetch.ts +22 -60
  32. package/src/ui/screens/SessionManagementScreen.tsx +9 -9
@@ -1,33 +1,23 @@
1
1
  /**
2
- * Zero Config Authenticated Fetch Hook
2
+ * Simplified Authenticated Fetch Hook
3
3
  *
4
- * Simple hook that provides fetch-like API with automatic authentication
5
- * Leverages the existing useOxy hook and OxyProvider infrastructure
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; // Allow any type for body, we'll JSON.stringify if needed
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 existing OxyServices instance from useOxy context
30
+ * Uses the centralized AuthManager for all authentication operations
41
31
  */
42
32
  export function useAuthFetch(): AuthFetchAPI {
43
- const { oxyServices, isAuthenticated, user, login, logout, signUp, activeSessionId } = useOxy();
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, oxyServices.getBaseURL());
48
- const options = await addAuthHeaders(init, oxyServices, activeSessionId || undefined, isAuthenticated);
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 oxyServices.refreshTokens();
58
- const retryOptions = await addAuthHeaders(init, oxyServices, activeSessionId || undefined, isAuthenticated);
45
+ await authManager.refreshToken();
46
+ const retryOptions = await addAuthHeaders(init, authManager);
59
47
  response = await fetch(url, retryOptions);
60
48
  } catch (refreshError) {
61
- // Refresh failed, user needs to login again
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 error:', error);
56
+ console.error('[AuthFetch] Request failed:', error);
70
57
  throw error;
71
58
  }
72
- }, [oxyServices, activeSessionId, isAuthenticated]);
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 to the main function
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, oxyServices?: any, activeSessionId?: string, isAuthenticated?: boolean): Promise<RequestInit> {
131
+ async function addAuthHeaders(init?: AuthFetchOptions, authManager?: any): Promise<RequestInit> {
148
132
  const headers = new Headers(init?.headers);
149
133
 
150
- // Debug logging
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
- // First try to get regular JWT access token
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
- console.log('[Auth API] Added Authorization header successfully');
175
- } else {
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('[Auth API] Error getting access token:', 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} />