@oxyhq/services 5.17.8 → 5.17.9

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 (91) hide show
  1. package/lib/commonjs/crypto/index.js +0 -23
  2. package/lib/commonjs/crypto/index.js.map +1 -1
  3. package/lib/commonjs/index.js +0 -15
  4. package/lib/commonjs/index.js.map +1 -1
  5. package/lib/commonjs/ui/components/Icon.js.map +1 -1
  6. package/lib/commonjs/ui/components/IconButton/utils.js.map +1 -1
  7. package/lib/commonjs/ui/components/TextField/Adornment/utils.js.map +1 -1
  8. package/lib/commonjs/ui/components/TextField/helpers.js.map +1 -1
  9. package/lib/commonjs/ui/components/TouchableRipple/utils.js.map +1 -1
  10. package/lib/commonjs/ui/components/Typography/AnimatedText.js.map +1 -1
  11. package/lib/commonjs/ui/context/OxyContext.js +23 -15
  12. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  13. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +63 -111
  14. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  15. package/lib/commonjs/ui/screens/OxyAuthScreen.js +0 -1
  16. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
  17. package/lib/commonjs/ui/stores/authStore.js +52 -15
  18. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  19. package/lib/commonjs/ui/utils/avatarUtils.js +2 -32
  20. package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
  21. package/lib/module/crypto/index.js +4 -6
  22. package/lib/module/crypto/index.js.map +1 -1
  23. package/lib/module/index.js +6 -3
  24. package/lib/module/index.js.map +1 -1
  25. package/lib/module/ui/components/Icon.js.map +1 -1
  26. package/lib/module/ui/components/IconButton/utils.js.map +1 -1
  27. package/lib/module/ui/components/TextField/Adornment/utils.js.map +1 -1
  28. package/lib/module/ui/components/TextField/helpers.js.map +1 -1
  29. package/lib/module/ui/components/TouchableRipple/utils.js.map +1 -1
  30. package/lib/module/ui/components/Typography/AnimatedText.js.map +1 -1
  31. package/lib/module/ui/context/OxyContext.js +23 -15
  32. package/lib/module/ui/context/OxyContext.js.map +1 -1
  33. package/lib/module/ui/context/hooks/useAuthOperations.js +63 -111
  34. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  35. package/lib/module/ui/screens/OxyAuthScreen.js +0 -1
  36. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
  37. package/lib/module/ui/stores/authStore.js +52 -15
  38. package/lib/module/ui/stores/authStore.js.map +1 -1
  39. package/lib/module/ui/utils/avatarUtils.js +2 -32
  40. package/lib/module/ui/utils/avatarUtils.js.map +1 -1
  41. package/lib/typescript/crypto/index.d.ts +2 -5
  42. package/lib/typescript/crypto/index.d.ts.map +1 -1
  43. package/lib/typescript/crypto/types.d.ts +2 -2
  44. package/lib/typescript/index.d.ts +4 -2
  45. package/lib/typescript/index.d.ts.map +1 -1
  46. package/lib/typescript/ui/components/IconButton/utils.d.ts +1 -1
  47. package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts +1 -1
  48. package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts.map +1 -1
  49. package/lib/typescript/ui/components/TextField/helpers.d.ts +6 -6
  50. package/lib/typescript/ui/components/types.d.ts +0 -4
  51. package/lib/typescript/ui/components/types.d.ts.map +1 -1
  52. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  53. package/lib/typescript/ui/context/OxyContextBase.d.ts +2 -2
  54. package/lib/typescript/ui/context/OxyContextBase.d.ts.map +1 -1
  55. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +9 -5
  56. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  57. package/lib/typescript/ui/stores/authStore.d.ts +27 -4
  58. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  59. package/lib/typescript/ui/utils/avatarUtils.d.ts +0 -2
  60. package/lib/typescript/ui/utils/avatarUtils.d.ts.map +1 -1
  61. package/package.json +2 -2
  62. package/src/crypto/index.ts +3 -11
  63. package/src/crypto/types.ts +2 -2
  64. package/src/index.ts +6 -11
  65. package/src/ui/components/Icon.tsx +1 -1
  66. package/src/ui/components/IconButton/utils.ts +1 -1
  67. package/src/ui/components/TextField/Adornment/utils.ts +2 -2
  68. package/src/ui/components/TextField/helpers.tsx +8 -8
  69. package/src/ui/components/TouchableRipple/utils.ts +2 -2
  70. package/src/ui/components/Typography/AnimatedText.tsx +2 -2
  71. package/src/ui/components/types.tsx +0 -6
  72. package/src/ui/context/OxyContext.tsx +23 -14
  73. package/src/ui/context/OxyContextBase.tsx +4 -4
  74. package/src/ui/context/hooks/useAuthOperations.ts +83 -134
  75. package/src/ui/screens/OxyAuthScreen.tsx +1 -1
  76. package/src/ui/stores/authStore.ts +57 -18
  77. package/src/ui/utils/avatarUtils.ts +4 -36
  78. package/lib/commonjs/crypto/keyManager.js +0 -356
  79. package/lib/commonjs/crypto/keyManager.js.map +0 -1
  80. package/lib/commonjs/crypto/signatureService.js +0 -269
  81. package/lib/commonjs/crypto/signatureService.js.map +0 -1
  82. package/lib/module/crypto/keyManager.js +0 -353
  83. package/lib/module/crypto/keyManager.js.map +0 -1
  84. package/lib/module/crypto/signatureService.js +0 -266
  85. package/lib/module/crypto/signatureService.js.map +0 -1
  86. package/lib/typescript/crypto/keyManager.d.ts +0 -80
  87. package/lib/typescript/crypto/keyManager.d.ts.map +0 -1
  88. package/lib/typescript/crypto/signatureService.d.ts +0 -77
  89. package/lib/typescript/crypto/signatureService.d.ts.map +0 -1
  90. package/src/crypto/keyManager.ts +0 -379
  91. package/src/crypto/signatureService.ts +0 -301
@@ -1,13 +1,11 @@
1
1
  import { useCallback } from 'react';
2
2
  import type { ApiError, User } from '../../../models/interfaces';
3
3
  import type { AuthState } from '../../stores/authStore';
4
- import type { ClientSession } from '../../../models/session';
5
- import { DeviceManager } from '../../../utils/deviceManager';
4
+ import type { ClientSession, SessionLoginResponse } from '../../../models/session';
6
5
  import { fetchSessionsWithFallback } from '../../utils/sessionHelpers';
7
6
  import { handleAuthError, isInvalidSessionError } from '../../utils/errorHandlers';
8
7
  import type { StorageInterface } from '../../utils/storageHelpers';
9
8
  import type { OxyServices } from '../../../core';
10
- import { KeyManager, SignatureService } from '../../../crypto';
11
9
 
12
10
  export interface UseAuthOperationsOptions {
13
11
  oxyServices: OxyServices;
@@ -30,22 +28,25 @@ export interface UseAuthOperationsOptions {
30
28
  }
31
29
 
32
30
  export interface UseAuthOperationsResult {
33
- /** Sign in with existing identity on device */
34
- signIn: (deviceName?: string) => Promise<User>;
31
+ /** Complete sign-in after external challenge-response (Accounts app signs, Services completes session) */
32
+ completeSignIn: (sessionResponse: SessionLoginResponse) => Promise<User>;
35
33
  /** Logout from current session */
36
34
  logout: (targetSessionId?: string) => Promise<void>;
37
35
  /** Logout from all sessions */
38
36
  logoutAll: () => Promise<void>;
39
37
  }
40
38
 
41
- const LOGIN_ERROR_CODE = 'LOGIN_ERROR';
42
39
  const LOGOUT_ERROR_CODE = 'LOGOUT_ERROR';
43
40
  const LOGOUT_ALL_ERROR_CODE = 'LOGOUT_ALL_ERROR';
44
41
 
45
42
 
46
43
  /**
47
- * Authentication operations using public key cryptography.
48
- * No passwords required - identity is based on ECDSA key pairs.
44
+ * Authentication operations for Services SDK.
45
+ *
46
+ * ARCHITECTURE:
47
+ * - Services SDK only handles tokens/sessions (NOT identity)
48
+ * - Accounts app handles identity (KeyManager, SignatureService, challenge signing)
49
+ * - completeSignIn() accepts already-verified session from Accounts app
49
50
  */
50
51
  export const useAuthOperations = ({
51
52
  oxyServices,
@@ -68,148 +69,82 @@ export const useAuthOperations = ({
68
69
  }: UseAuthOperationsOptions): UseAuthOperationsResult => {
69
70
 
70
71
  /**
71
- * Internal function to perform challenge-response sign in
72
+ * Complete sign-in after external authentication.
73
+ *
74
+ * This is called by Accounts app AFTER it has:
75
+ * 1. Requested challenge from backend
76
+ * 2. Signed challenge locally with private key
77
+ * 3. Verified challenge with backend to get sessionResponse
78
+ *
79
+ * Services SDK only stores tokens and manages session state.
80
+ *
81
+ * @param sessionResponse - Session data from verifyChallenge API call
72
82
  */
73
- const performSignIn = useCallback(
74
- async (publicKey: string, deviceName?: string): Promise<User> => {
75
- const deviceFingerprintObj = DeviceManager.getDeviceFingerprint();
76
- const deviceFingerprint = JSON.stringify(deviceFingerprintObj);
77
- const deviceInfo = await DeviceManager.getDeviceInfo();
78
- const defaultDeviceName = deviceInfo.deviceName || DeviceManager.getDefaultDeviceName();
79
- const finalDeviceName = deviceName || defaultDeviceName;
80
-
81
- // Look up user by public key
82
- const userLookup = await oxyServices.getUserByPublicKey(publicKey);
83
- const userId = userLookup.id;
84
-
85
- // Request challenge
86
- const challengeResponse = await oxyServices.requestChallenge(userId);
87
- const challenge = challengeResponse.challenge;
88
-
89
- // Sign the challenge
90
- const { challenge: signature, timestamp } = await SignatureService.signChallenge(challenge);
91
-
92
- // Verify and create session
93
- let sessionResponse;
94
- try {
95
- sessionResponse = await oxyServices.verifyChallenge(
96
- userId,
97
- challenge,
98
- signature,
99
- timestamp,
100
- finalDeviceName,
101
- deviceFingerprint,
102
- );
103
- } catch (verifyError) {
104
- if (__DEV__) {
105
- console.error('[useAuthOperations] verifyChallenge failed:', {
106
- error: verifyError,
107
- userId,
108
- challengeLength: challenge?.length,
109
- signatureLength: signature?.length,
110
- timestamp,
111
- timeSinceChallenge: Date.now() - timestamp,
112
- });
113
- }
114
- throw verifyError;
115
- }
116
-
117
- // Store tokens
118
- oxyServices.setTokens(sessionResponse.accessToken, sessionResponse.refreshToken);
83
+ const completeSignIn = useCallback(
84
+ async (sessionResponse: SessionLoginResponse): Promise<User> => {
85
+ if (!storage) throw new Error('Storage not initialized');
119
86
 
120
- // Get full user data
121
- const fullUser = await oxyServices.getUserBySession(sessionResponse.sessionId);
87
+ setAuthState({ isLoading: true, error: null });
122
88
 
123
- // Fetch device sessions
124
- let allDeviceSessions: ClientSession[] = [];
125
89
  try {
126
- allDeviceSessions = await fetchSessionsWithFallback(oxyServices, sessionResponse.sessionId, {
127
- fallbackDeviceId: sessionResponse.deviceId,
128
- fallbackUserId: fullUser.id,
129
- logger,
130
- });
131
- } catch (error) {
132
- if (__DEV__) {
133
- console.warn('Failed to fetch device sessions after login:', error);
134
- }
135
- }
90
+ // Store tokens (Services' only responsibility)
91
+ oxyServices.setTokens(sessionResponse.accessToken, sessionResponse.refreshToken);
136
92
 
137
- // Check for existing session for same user
138
- const existingSession = allDeviceSessions.find(
139
- (session) =>
140
- session.userId?.toString() === fullUser.id?.toString() &&
141
- session.sessionId !== sessionResponse.sessionId,
142
- );
93
+ // Get full user data
94
+ const fullUser = await oxyServices.getUserBySession(sessionResponse.sessionId);
143
95
 
144
- if (existingSession) {
145
- // Logout duplicate session
96
+ // Fetch device sessions
97
+ let allDeviceSessions: ClientSession[] = [];
146
98
  try {
147
- await oxyServices.logoutSession(sessionResponse.sessionId, sessionResponse.sessionId);
148
- } catch (logoutError) {
99
+ allDeviceSessions = await fetchSessionsWithFallback(oxyServices, sessionResponse.sessionId, {
100
+ fallbackDeviceId: sessionResponse.deviceId,
101
+ fallbackUserId: fullUser.id,
102
+ logger,
103
+ });
104
+ } catch (error) {
149
105
  if (__DEV__) {
150
- console.warn('Failed to logout duplicate session:', logoutError);
106
+ console.warn('Failed to fetch device sessions after login:', error);
151
107
  }
152
108
  }
153
- await switchSession(existingSession.sessionId);
154
- updateSessions(
155
- allDeviceSessions.filter((session) => session.sessionId !== sessionResponse.sessionId),
156
- { merge: false },
157
- );
158
- onAuthStateChange?.(fullUser);
159
- return fullUser;
160
- }
161
-
162
- setActiveSessionId(sessionResponse.sessionId);
163
- await saveActiveSessionId(sessionResponse.sessionId);
164
- updateSessions(allDeviceSessions, { merge: true });
165
-
166
- await applyLanguagePreference(fullUser);
167
- loginSuccess();
168
- onAuthStateChange?.(fullUser);
169
-
170
- return fullUser;
171
- },
172
- [
173
- applyLanguagePreference,
174
- logger,
175
- loginSuccess,
176
- onAuthStateChange,
177
- oxyServices,
178
- saveActiveSessionId,
179
- setActiveSessionId,
180
- switchSession,
181
- updateSessions,
182
- ],
183
- );
184
-
185
-
186
- /**
187
- * Sign in with existing identity on device
188
- */
189
- const signIn = useCallback(
190
- async (deviceName?: string): Promise<User> => {
191
- if (!storage) throw new Error('Storage not initialized');
192
109
 
193
- setAuthState({ isLoading: true, error: null });
110
+ // Check for existing session for same user
111
+ const existingSession = allDeviceSessions.find(
112
+ (session) =>
113
+ session.userId?.toString() === fullUser.id?.toString() &&
114
+ session.sessionId !== sessionResponse.sessionId,
115
+ );
194
116
 
195
- try {
196
- // Get stored public key
197
- const publicKey = await KeyManager.getPublicKey();
198
- if (!publicKey) {
199
- throw new Error('No identity found on this device. Please create or import an identity.');
117
+ if (existingSession) {
118
+ // Logout duplicate session
119
+ try {
120
+ await oxyServices.logoutSession(sessionResponse.sessionId, sessionResponse.sessionId);
121
+ } catch (logoutError) {
122
+ if (__DEV__) {
123
+ console.warn('Failed to logout duplicate session:', logoutError);
124
+ }
125
+ }
126
+ await switchSession(existingSession.sessionId);
127
+ updateSessions(
128
+ allDeviceSessions.filter((session) => session.sessionId !== sessionResponse.sessionId),
129
+ { merge: false },
130
+ );
131
+ onAuthStateChange?.(fullUser);
132
+ return fullUser;
200
133
  }
201
134
 
202
- // Ensure identity is registered before attempting sign-in
203
- const { registered } = await oxyServices.checkPublicKeyRegistered(publicKey);
204
- if (!registered) {
205
- throw new Error('Identity is not registered. Please register your identity in the Accounts app before signing in.');
206
- }
135
+ setActiveSessionId(sessionResponse.sessionId);
136
+ await saveActiveSessionId(sessionResponse.sessionId);
137
+ updateSessions(allDeviceSessions, { merge: true });
207
138
 
208
- return await performSignIn(publicKey, deviceName);
139
+ await applyLanguagePreference(fullUser);
140
+ loginSuccess();
141
+ onAuthStateChange?.(fullUser);
142
+
143
+ return fullUser;
209
144
  } catch (error) {
210
145
  const message = handleAuthError(error, {
211
146
  defaultMessage: 'Sign in failed',
212
- code: LOGIN_ERROR_CODE,
147
+ code: 'COMPLETE_SIGNIN_ERROR',
213
148
  onError,
214
149
  setAuthError: (msg: string) => setAuthState({ error: msg }),
215
150
  logger,
@@ -220,7 +155,21 @@ export const useAuthOperations = ({
220
155
  setAuthState({ isLoading: false });
221
156
  }
222
157
  },
223
- [storage, setAuthState, performSignIn, loginFailure, onError, logger, oxyServices],
158
+ [
159
+ storage,
160
+ setAuthState,
161
+ oxyServices,
162
+ logger,
163
+ saveActiveSessionId,
164
+ setActiveSessionId,
165
+ switchSession,
166
+ updateSessions,
167
+ applyLanguagePreference,
168
+ loginSuccess,
169
+ onAuthStateChange,
170
+ onError,
171
+ loginFailure,
172
+ ],
224
173
  );
225
174
 
226
175
 
@@ -304,7 +253,7 @@ export const useAuthOperations = ({
304
253
  }, [activeSessionId, clearSessionState, logger, onError, oxyServices, setAuthState]);
305
254
 
306
255
  return {
307
- signIn,
256
+ completeSignIn,
308
257
  logout,
309
258
  logoutAll,
310
259
  };
@@ -60,7 +60,7 @@ const OxyAuthScreen: React.FC<BaseScreenProps> = ({
60
60
  }) => {
61
61
  const themeValue = (theme === 'light' || theme === 'dark') ? theme : 'light';
62
62
  const colors = useThemeColors(themeValue);
63
- const { oxyServices, signIn, switchSession } = useOxy();
63
+ const { oxyServices, switchSession } = useOxy();
64
64
 
65
65
  const [authSession, setAuthSession] = useState<AuthSession | null>(null);
66
66
  const [isLoading, setIsLoading] = useState(true);
@@ -1,44 +1,83 @@
1
+ /**
2
+ * Auth Store (Services SDK)
3
+ *
4
+ * === ARCHITECTURE: Services owns sessions/tokens, NOT identity ===
5
+ *
6
+ * This store manages authentication state:
7
+ * - isAuthenticated: Valid tokens AND online (REQUIRES NETWORK)
8
+ * - isOnline: Network connectivity state
9
+ * - isLoading: Auth operation in progress
10
+ * - error: Last auth error
11
+ *
12
+ * CRITICAL RULES:
13
+ * ✓ isAuthenticated = true ONLY when:
14
+ * 1. Valid access/refresh tokens exist
15
+ * 2. Network is available (isOnline = true)
16
+ *
17
+ * ✗ isAuthenticated must be false when:
18
+ * - Offline (even if tokens exist)
19
+ * - No valid tokens
20
+ * - Tokens expired
21
+ *
22
+ * WHY: Authentication requires server validation. Without network,
23
+ * we cannot verify tokens are still valid or refresh them.
24
+ */
25
+
1
26
  import { create } from 'zustand';
2
27
 
3
28
  export interface AuthState {
4
29
  isAuthenticated: boolean;
30
+ isOnline: boolean;
5
31
  isLoading: boolean;
6
32
  error: string | null;
7
33
 
8
- // Identity sync state (offline-first)
9
- isIdentitySynced: boolean;
10
- isSyncing: boolean;
11
-
34
+ // Actions
35
+ setOnline: (online: boolean) => void;
12
36
  loginSuccess: () => void;
13
37
  loginFailure: (error: string) => void;
14
38
  logout: () => void;
15
39
 
16
- // Identity sync actions
17
- setIdentitySynced: (synced: boolean) => void;
18
- setSyncing: (syncing: boolean) => void;
40
+ // Helpers
41
+ canAuthenticate: () => boolean;
19
42
  }
20
43
 
21
- export const useAuthStore = create<AuthState>((set: (state: Partial<AuthState>) => void) => ({
44
+ export const useAuthStore = create<AuthState>((set, get) => ({
22
45
  isAuthenticated: false,
46
+ isOnline: true, // Assume online initially
23
47
  isLoading: false,
24
48
  error: null,
25
49
 
26
- // Identity sync state (offline-first)
27
- isIdentitySynced: false, // Registration/identity sync not confirmed until done
28
- isSyncing: false,
50
+ setOnline: (online: boolean) => {
51
+ set({ isOnline: online });
52
+ // If we go offline, we can't be authenticated
53
+ if (!online) {
54
+ set({ isAuthenticated: false });
55
+ }
56
+ },
29
57
 
30
58
  loginSuccess: () => set({
31
59
  isLoading: false,
32
- isAuthenticated: true,
33
- isIdentitySynced: true, // If login succeeded, registration is complete
60
+ isAuthenticated: get().isOnline, // Only authenticated if online
61
+ error: null,
34
62
  }),
35
- loginFailure: (error: string) => set({ isLoading: false, error }),
63
+
64
+ loginFailure: (error: string) => set({
65
+ isLoading: false,
66
+ isAuthenticated: false,
67
+ error
68
+ }),
69
+
36
70
  logout: () => set({
37
71
  isAuthenticated: false,
38
- isSyncing: false,
72
+ error: null,
39
73
  }),
40
74
 
41
- // Identity sync actions
42
- setIdentitySynced: (synced: boolean) => set({ isIdentitySynced: synced }),
43
- setSyncing: (syncing: boolean) => set({ isSyncing: syncing }),
75
+ /**
76
+ * Check if user can authenticate
77
+ * Requires both valid tokens (checked by caller) and network
78
+ */
79
+ canAuthenticate: () => {
80
+ const state = get();
81
+ return state.isOnline;
82
+ },
44
83
  }));
@@ -61,7 +61,6 @@ export function refreshAvatarInStore(
61
61
  * @param oxyServices - OxyServices instance
62
62
  * @param activeSessionId - Active session ID
63
63
  * @param queryClient - TanStack Query client
64
- * @param syncIdentity - Optional sync identity function for handling auth errors
65
64
  * @returns Promise that resolves with updated user data
66
65
  */
67
66
  export async function updateProfileWithAvatar(
@@ -69,29 +68,15 @@ export async function updateProfileWithAvatar(
69
68
  oxyServices: OxyServices,
70
69
  activeSessionId: string | null,
71
70
  queryClient: QueryClient,
72
- options?: { syncIdentity?: () => Promise<User>; deviceId?: string }
71
+ options?: { deviceId?: string }
73
72
  ): Promise<User> {
74
- const { syncIdentity, deviceId } = options || {};
73
+ const { deviceId } = options || {};
75
74
  // Ensure we have a valid token before making the request
76
75
  if (!oxyServices.hasValidToken() && activeSessionId) {
77
76
  try {
78
77
  await oxyServices.getTokenBySession(activeSessionId, deviceId);
79
78
  } catch (tokenError) {
80
- const errorMessage = tokenError instanceof Error ? tokenError.message : String(tokenError);
81
- if (errorMessage.includes('AUTH_REQUIRED_OFFLINE_SESSION') || errorMessage.includes('offline')) {
82
- if (syncIdentity) {
83
- try {
84
- await syncIdentity();
85
- await oxyServices.getTokenBySession(activeSessionId, deviceId);
86
- } catch (syncError) {
87
- throw new Error('Session needs to be synced. Please try again.');
88
- }
89
- } else {
90
- throw tokenError;
91
- }
92
- } else {
93
- throw tokenError;
94
- }
79
+ throw tokenError;
95
80
  }
96
81
  }
97
82
 
@@ -120,24 +105,7 @@ export async function updateProfileWithAvatar(
120
105
 
121
106
  // Handle authentication errors
122
107
  if (status === 401 || errorMessage.includes('Authentication required') || errorMessage.includes('Invalid or missing authorization header')) {
123
- if (activeSessionId && syncIdentity) {
124
- try {
125
- await syncIdentity();
126
- await oxyServices.getTokenBySession(activeSessionId, deviceId);
127
- // Retry the update after getting token
128
- return await updateProfileWithAvatar(
129
- updates,
130
- oxyServices,
131
- activeSessionId,
132
- queryClient,
133
- { syncIdentity, deviceId }
134
- );
135
- } catch (retryError) {
136
- throw new Error('Authentication failed. Please sign in again.');
137
- }
138
- } else {
139
- throw new Error('No active session. Please sign in.');
140
- }
108
+ throw new Error('Authentication failed. Please sign in again.');
141
109
  }
142
110
 
143
111
  throw error;