@oxyhq/services 5.20.1 → 5.20.3

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 (33) hide show
  1. package/lib/commonjs/core/mixins/OxyServices.fedcm.js +26 -6
  2. package/lib/commonjs/core/mixins/OxyServices.fedcm.js.map +1 -1
  3. package/lib/commonjs/core/mixins/OxyServices.popup.js +40 -1
  4. package/lib/commonjs/core/mixins/OxyServices.popup.js.map +1 -1
  5. package/lib/commonjs/ui/context/OxyContext.js +23 -1
  6. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  7. package/lib/commonjs/ui/hooks/useAuth.js +15 -15
  8. package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
  9. package/lib/module/core/mixins/OxyServices.fedcm.js +26 -6
  10. package/lib/module/core/mixins/OxyServices.fedcm.js.map +1 -1
  11. package/lib/module/core/mixins/OxyServices.popup.js +40 -1
  12. package/lib/module/core/mixins/OxyServices.popup.js.map +1 -1
  13. package/lib/module/ui/context/OxyContext.js +23 -1
  14. package/lib/module/ui/context/OxyContext.js.map +1 -1
  15. package/lib/module/ui/hooks/useAuth.js +15 -15
  16. package/lib/module/ui/hooks/useAuth.js.map +1 -1
  17. package/lib/typescript/commonjs/core/mixins/OxyServices.fedcm.d.ts +3 -7
  18. package/lib/typescript/commonjs/core/mixins/OxyServices.fedcm.d.ts.map +1 -1
  19. package/lib/typescript/commonjs/core/mixins/OxyServices.popup.d.ts.map +1 -1
  20. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +11 -0
  21. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  22. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
  23. package/lib/typescript/module/core/mixins/OxyServices.fedcm.d.ts +3 -7
  24. package/lib/typescript/module/core/mixins/OxyServices.fedcm.d.ts.map +1 -1
  25. package/lib/typescript/module/core/mixins/OxyServices.popup.d.ts.map +1 -1
  26. package/lib/typescript/module/ui/context/OxyContext.d.ts +11 -0
  27. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  28. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
  29. package/package.json +1 -1
  30. package/src/core/mixins/OxyServices.fedcm.ts +26 -6
  31. package/src/core/mixins/OxyServices.popup.ts +39 -1
  32. package/src/ui/context/OxyContext.tsx +25 -0
  33. package/src/ui/hooks/useAuth.ts +15 -20
@@ -111,6 +111,25 @@ export function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBase>(Base
111
111
  this.httpService.setTokens((session as any).accessToken);
112
112
  }
113
113
 
114
+ // Fetch user data using the session ID
115
+ // The callback page only sends sessionId/accessToken, not user data
116
+ if (session && session.sessionId && !session.user) {
117
+ try {
118
+ const userData = await this.makeRequest<any>(
119
+ 'GET',
120
+ `/api/session/user/${session.sessionId}`,
121
+ undefined,
122
+ { cache: false }
123
+ );
124
+ if (userData) {
125
+ (session as any).user = userData;
126
+ }
127
+ } catch (userError) {
128
+ console.warn('[PopupAuth] Failed to fetch user data:', userError);
129
+ // Continue without user data - caller can fetch separately
130
+ }
131
+ }
132
+
114
133
  return session;
115
134
  } catch (error) {
116
135
  throw error;
@@ -238,8 +257,21 @@ export function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBase>(Base
238
257
  }, timeout);
239
258
 
240
259
  const messageHandler = (event: MessageEvent) => {
260
+ const authUrl = (this.constructor as any).AUTH_URL;
261
+
262
+ // Log all messages for debugging
263
+ if (event.data && typeof event.data === 'object' && event.data.type) {
264
+ console.log('[PopupAuth] Message received:', {
265
+ origin: event.origin,
266
+ expectedOrigin: authUrl,
267
+ type: event.data.type,
268
+ hasSession: !!event.data.session,
269
+ hasError: !!event.data.error,
270
+ });
271
+ }
272
+
241
273
  // CRITICAL: Verify origin to prevent XSS attacks
242
- if (event.origin !== (this.constructor as any).AUTH_URL) {
274
+ if (event.origin !== authUrl) {
243
275
  return;
244
276
  }
245
277
 
@@ -249,9 +281,12 @@ export function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBase>(Base
249
281
  return;
250
282
  }
251
283
 
284
+ console.log('[PopupAuth] Valid auth response:', { state, expectedState, hasSession: !!session, error });
285
+
252
286
  // Verify state parameter to prevent CSRF attacks
253
287
  if (state !== expectedState) {
254
288
  cleanup();
289
+ console.error('[PopupAuth] State mismatch');
255
290
  reject(new OxyAuthenticationError('Invalid state parameter. Possible CSRF attack.'));
256
291
  return;
257
292
  }
@@ -259,10 +294,13 @@ export function OxyServicesPopupAuthMixin<T extends typeof OxyServicesBase>(Base
259
294
  cleanup();
260
295
 
261
296
  if (error) {
297
+ console.error('[PopupAuth] Auth error:', error);
262
298
  reject(new OxyAuthenticationError(error));
263
299
  } else if (session) {
300
+ console.log('[PopupAuth] Session received successfully');
264
301
  resolve(session);
265
302
  } else {
303
+ console.error('[PopupAuth] No session in response');
266
304
  reject(new OxyAuthenticationError('No session received from authentication server'));
267
305
  }
268
306
  };
@@ -55,6 +55,18 @@ export interface OxyContextState {
55
55
  // Authentication
56
56
  signIn: (publicKey: string, deviceName?: string) => Promise<User>;
57
57
 
58
+ /**
59
+ * Handle session from popup authentication
60
+ * Updates auth state, persists session to storage
61
+ */
62
+ handlePopupSession: (session: {
63
+ sessionId: string;
64
+ accessToken?: string;
65
+ expiresAt?: string;
66
+ user: User;
67
+ deviceId?: string;
68
+ }) => Promise<void>;
69
+
58
70
  // Session management
59
71
  logout: (targetSessionId?: string) => Promise<void>;
60
72
  logoutAll: () => Promise<void>;
@@ -414,11 +426,19 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
414
426
  }, [restoreSessionsFromStorage, storage]);
415
427
 
416
428
  // Web SSO: Automatically check for cross-domain session on web platforms
429
+ // Also used for popup auth - updates all state and persists session
417
430
  const handleWebSSOSession = useCallback(async (session: any) => {
418
431
  if (!session?.user || !session?.sessionId) {
432
+ if (__DEV__) {
433
+ loggerUtil.warn('handleWebSSOSession called with invalid session', { component: 'OxyContext' }, { session });
434
+ }
419
435
  return;
420
436
  }
421
437
 
438
+ if (__DEV__) {
439
+ loggerUtil.debug('handleWebSSOSession: Updating auth state', { component: 'OxyContext' }, { sessionId: session.sessionId, userId: session.user?.id });
440
+ }
441
+
422
442
  // Update sessions state
423
443
  const clientSession = {
424
444
  sessionId: session.sessionId,
@@ -443,6 +463,9 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
443
463
  sessionIds.push(session.sessionId);
444
464
  await storage.setItem(storageKeys.sessionIds, JSON.stringify(sessionIds));
445
465
  }
466
+ if (__DEV__) {
467
+ loggerUtil.debug('handleWebSSOSession: Session persisted to storage', { component: 'OxyContext' });
468
+ }
446
469
  }
447
470
  }, [updateSessions, setActiveSessionId, loginSuccess, onAuthStateChange, storage, storageKeys]);
448
471
 
@@ -571,6 +594,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
571
594
  hasIdentity,
572
595
  getPublicKey,
573
596
  signIn,
597
+ handlePopupSession: handleWebSSOSession,
574
598
  logout,
575
599
  logoutAll,
576
600
  switchSession: switchSessionForContext,
@@ -589,6 +613,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
589
613
  }), [
590
614
  activeSessionId,
591
615
  signIn,
616
+ handleWebSSOSession,
592
617
  currentLanguage,
593
618
  currentLanguageMetadata,
594
619
  currentLanguageName,
@@ -91,6 +91,7 @@ export function useAuth(): UseAuthReturn {
91
91
  isTokenReady,
92
92
  error,
93
93
  signIn: oxySignIn,
94
+ handlePopupSession,
94
95
  logout,
95
96
  logoutAll,
96
97
  refreshSessions,
@@ -106,31 +107,25 @@ export function useAuth(): UseAuthReturn {
106
107
  const isIdentityProvider = isWebBrowser() &&
107
108
  window.location.hostname === 'auth.oxy.so';
108
109
 
109
- // Web (not on IdP): Use FedCM or popup-based authentication
110
+ // Web (not on IdP): Use popup-based authentication
111
+ // We skip FedCM here because:
112
+ // 1. Silent FedCM already ran on page load (via useWebSSO)
113
+ // 2. If user is clicking "Sign In", they need interactive auth
114
+ // 3. Doing FedCM first loses the "user gesture" needed for popups
110
115
  if (isWebBrowser() && !publicKey && !isIdentityProvider) {
111
116
  try {
112
- // Try FedCM first (instant if user already signed in)
113
- if ((oxyServices as any).isFedCMSupported?.()) {
114
- const fedcmSession = await (oxyServices as any).signInWithFedCM?.();
115
- if (fedcmSession?.user) {
116
- return fedcmSession.user;
117
- }
118
- }
119
-
120
- // Fallback to popup (opens auth.oxy.so in popup window)
121
117
  const popupSession = await (oxyServices as any).signInWithPopup?.();
122
118
  if (popupSession?.user) {
119
+ // Update context state with the session (this updates user, sessions, storage)
120
+ await handlePopupSession(popupSession);
123
121
  return popupSession.user;
124
122
  }
125
-
126
- throw new Error('Sign-in failed');
127
- } catch (error) {
128
- // If popup blocked or FedCM failed, suggest redirect
129
- throw new Error(
130
- error instanceof Error && error.message.includes('blocked')
131
- ? 'Popup blocked. Please allow popups or try again.'
132
- : 'Sign-in failed. Please try again.'
133
- );
123
+ throw new Error('Sign-in failed. Please try again.');
124
+ } catch (popupError) {
125
+ if (popupError instanceof Error && popupError.message.includes('blocked')) {
126
+ throw new Error('Popup blocked. Please allow popups for this site.');
127
+ }
128
+ throw popupError;
134
129
  }
135
130
  }
136
131
 
@@ -169,7 +164,7 @@ export function useAuth(): UseAuthReturn {
169
164
  }
170
165
 
171
166
  throw new Error('No authentication method available');
172
- }, [oxySignIn, hasIdentity, getPublicKey, showBottomSheet, oxyServices]);
167
+ }, [oxySignIn, hasIdentity, getPublicKey, showBottomSheet, oxyServices, handlePopupSession]);
173
168
 
174
169
  const signOut = useCallback(async (): Promise<void> => {
175
170
  await logout();