@oxyhq/services 5.8.11 → 5.9.0

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 (59) hide show
  1. package/lib/commonjs/core/index.js +207 -283
  2. package/lib/commonjs/core/index.js.map +1 -1
  3. package/lib/commonjs/node/index.js +0 -9
  4. package/lib/commonjs/node/index.js.map +1 -1
  5. package/lib/commonjs/ui/components/OxyProvider.js +3 -9
  6. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  7. package/lib/commonjs/ui/screens/FeedbackScreen.js +0 -4
  8. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  9. package/lib/commonjs/ui/screens/RecoverAccountScreen.js +0 -4
  10. package/lib/commonjs/ui/screens/RecoverAccountScreen.js.map +1 -1
  11. package/lib/commonjs/ui/screens/SignInScreen.js +16 -27
  12. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  13. package/lib/commonjs/ui/screens/SignUpScreen.js +4 -18
  14. package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
  15. package/lib/commonjs/ui/styles/authStyles.js +1 -2
  16. package/lib/commonjs/ui/styles/authStyles.js.map +1 -1
  17. package/lib/module/core/index.js +207 -283
  18. package/lib/module/core/index.js.map +1 -1
  19. package/lib/module/node/index.js +0 -4
  20. package/lib/module/node/index.js.map +1 -1
  21. package/lib/module/ui/components/OxyProvider.js +3 -9
  22. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  23. package/lib/module/ui/screens/FeedbackScreen.js +0 -4
  24. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  25. package/lib/module/ui/screens/RecoverAccountScreen.js +0 -4
  26. package/lib/module/ui/screens/RecoverAccountScreen.js.map +1 -1
  27. package/lib/module/ui/screens/SignInScreen.js +16 -27
  28. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  29. package/lib/module/ui/screens/SignUpScreen.js +4 -18
  30. package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
  31. package/lib/module/ui/styles/authStyles.js +1 -2
  32. package/lib/module/ui/styles/authStyles.js.map +1 -1
  33. package/lib/typescript/core/index.d.ts +29 -79
  34. package/lib/typescript/core/index.d.ts.map +1 -1
  35. package/lib/typescript/node/index.d.ts +0 -2
  36. package/lib/typescript/node/index.d.ts.map +1 -1
  37. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  38. package/lib/typescript/ui/screens/FeedbackScreen.d.ts.map +1 -1
  39. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts.map +1 -1
  40. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
  41. package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
  42. package/lib/typescript/ui/styles/authStyles.d.ts +0 -1
  43. package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -1
  44. package/package.json +6 -6
  45. package/src/core/index.ts +213 -254
  46. package/src/node/index.ts +0 -4
  47. package/src/ui/components/OxyProvider.tsx +3 -9
  48. package/src/ui/screens/FeedbackScreen.tsx +0 -4
  49. package/src/ui/screens/RecoverAccountScreen.tsx +0 -4
  50. package/src/ui/screens/SignInScreen.tsx +16 -20
  51. package/src/ui/screens/SignUpScreen.tsx +4 -14
  52. package/src/ui/styles/authStyles.ts +0 -1
  53. package/lib/commonjs/node/createAuth.js +0 -95
  54. package/lib/commonjs/node/createAuth.js.map +0 -1
  55. package/lib/module/node/createAuth.js +0 -90
  56. package/lib/module/node/createAuth.js.map +0 -1
  57. package/lib/typescript/node/createAuth.d.ts +0 -7
  58. package/lib/typescript/node/createAuth.d.ts.map +0 -1
  59. package/src/node/createAuth.ts +0 -116
@@ -63,7 +63,6 @@ const OXY_CLOUD_URL = exports.OXY_CLOUD_URL = 'https://cloud.oxy.so';
63
63
  class OxyServices {
64
64
  accessToken = null;
65
65
  refreshToken = null;
66
- refreshPromise = null;
67
66
 
68
67
  /**
69
68
  * Creates a new instance of the OxyServices client
@@ -79,14 +78,25 @@ class OxyServices {
79
78
  this.client.interceptors.request.use(async req => {
80
79
  if (!this.accessToken) {
81
80
  return req;
82
- } // Check if token is expired and refresh if needed
81
+ }
82
+
83
+ // Check if token is expired and refresh if needed
83
84
  try {
84
85
  const decoded = (0, _jwtDecode.jwtDecode)(this.accessToken);
85
86
  const currentTime = Math.floor(Date.now() / 1000);
86
87
 
87
88
  // If token expires in less than 60 seconds, refresh it
88
89
  if (decoded.exp - currentTime < 60) {
89
- await this.refreshTokens();
90
+ // For session-based tokens, get a new token from the session
91
+ if (decoded.sessionId) {
92
+ try {
93
+ const res = await this.client.get(`/secure-session/token/${decoded.sessionId}`);
94
+ this.accessToken = res.data.accessToken;
95
+ } catch (refreshError) {
96
+ // If refresh fails, clear tokens
97
+ this.clearTokens();
98
+ }
99
+ }
90
100
  }
91
101
  } catch (error) {
92
102
  // If token can't be decoded, continue with request and let server handle it
@@ -101,18 +111,24 @@ class OxyServices {
101
111
  this.client.interceptors.response.use(response => response, async error => {
102
112
  const originalRequest = error.config;
103
113
  // If the error is due to an expired token and we haven't tried refreshing yet
104
- if (error.response?.status === 401 && this.refreshToken && originalRequest && !originalRequest.headers?.['X-Retry-After-Refresh']) {
114
+ if (error.response?.status === 401 && this.accessToken && originalRequest && !originalRequest.headers?.['X-Retry-After-Refresh']) {
105
115
  try {
106
- await this.refreshTokens();
107
- // Retry the original request with new token
108
- const newRequest = {
109
- ...originalRequest
110
- };
111
- if (newRequest.headers) {
112
- newRequest.headers.Authorization = `Bearer ${this.accessToken}`;
113
- newRequest.headers['X-Retry-After-Refresh'] = 'true';
116
+ // Check if token is session-based and try to refresh
117
+ const decoded = (0, _jwtDecode.jwtDecode)(this.accessToken);
118
+ if (decoded.sessionId) {
119
+ const res = await this.client.get(`/secure-session/token/${decoded.sessionId}`);
120
+ this.accessToken = res.data.accessToken;
121
+
122
+ // Retry the original request with new token
123
+ const newRequest = {
124
+ ...originalRequest
125
+ };
126
+ if (newRequest.headers) {
127
+ newRequest.headers.Authorization = `Bearer ${this.accessToken}`;
128
+ newRequest.headers['X-Retry-After-Refresh'] = 'true';
129
+ }
130
+ return this.client(newRequest);
114
131
  }
115
- return this.client(newRequest);
116
132
  } catch (refreshError) {
117
133
  // If refresh fails, force user to login again
118
134
  this.clearTokens();
@@ -145,43 +161,17 @@ class OxyServices {
145
161
  }
146
162
 
147
163
  /**
148
- * Gets the currently authenticated user ID from the token
149
- * @returns The user ID or null if not authenticated
150
- */
151
- getCurrentUserId() {
152
- if (!this.accessToken) return null;
153
- try {
154
- const decoded = (0, _jwtDecode.jwtDecode)(this.accessToken);
155
-
156
- // Check for both userId (preferred) and id (fallback) for compatibility
157
- return decoded.userId || decoded.id || null;
158
- } catch (error) {
159
- return null;
160
- }
161
- }
162
-
163
- /**
164
- * Internal method to check if we have an access token
165
- * @private
166
- * @returns Boolean indicating if access token exists
167
- * @internal - Use `isAuthenticated` from useOxy() context in UI components instead
164
+ * Set authentication tokens manually
165
+ * @param accessToken - The access token
166
+ * @param refreshToken - The refresh token (optional for session-based auth)
168
167
  */
169
- hasAccessToken() {
170
- return this.accessToken !== null;
171
- }
172
-
173
- /**
174
- * Sets authentication tokens directly (useful for initializing from storage)
175
- * @param accessToken - JWT access token
176
- * @param refreshToken - Refresh token for getting new access tokens
177
- */
178
- setTokens(accessToken, refreshToken) {
168
+ setTokens(accessToken, refreshToken = '') {
179
169
  this.accessToken = accessToken;
180
170
  this.refreshToken = refreshToken;
181
171
  }
182
172
 
183
173
  /**
184
- * Clears all authentication tokens
174
+ * Clear stored authentication tokens
185
175
  */
186
176
  clearTokens() {
187
177
  this.accessToken = null;
@@ -189,120 +179,27 @@ class OxyServices {
189
179
  }
190
180
 
191
181
  /**
192
- * Sign up a new user
193
- * @param username - Desired username
194
- * @param email - User's email address
195
- * @param password - User's password
196
- * @returns Object containing the message, token and user data
197
- */
198
- async signUp(username, email, password) {
199
- try {
200
- const res = await this.client.post('/auth/signup', {
201
- username,
202
- email,
203
- password
204
- });
205
- const {
206
- message,
207
- token,
208
- user
209
- } = res.data;
210
- this.accessToken = token;
211
- return {
212
- message,
213
- token,
214
- user
215
- };
216
- } catch (error) {
217
- throw this.handleError(error);
218
- }
219
- }
220
-
221
- /**
222
- * Log in and store tokens
223
- * @param username - User's username or email
224
- * @param password - User's password
225
- * @returns Login response containing tokens and user data
226
- */
227
- async login(username, password) {
228
- try {
229
- const res = await this.client.post('/auth/login', {
230
- username,
231
- password
232
- });
233
- const {
234
- accessToken,
235
- refreshToken,
236
- user
237
- } = res.data;
238
- this.accessToken = accessToken;
239
- this.refreshToken = refreshToken;
240
- return {
241
- accessToken,
242
- refreshToken,
243
- user
244
- };
245
- } catch (error) {
246
- throw this.handleError(error);
247
- }
248
- }
249
-
250
- /**
251
- * Log out user
182
+ * Get the current user ID from the stored token
183
+ * @returns User ID or null if not authenticated
252
184
  */
253
- async logout() {
254
- if (!this.refreshToken) return;
185
+ getCurrentUserId() {
186
+ if (!this.accessToken) return null;
255
187
  try {
256
- await this.client.post('/auth/logout', {
257
- refreshToken: this.refreshToken
258
- });
188
+ const decoded = (0, _jwtDecode.jwtDecode)(this.accessToken);
189
+ return decoded.userId || decoded.id || null;
259
190
  } catch (error) {
260
- console.warn('Error during logout', error);
261
- } finally {
262
- this.accessToken = null;
263
- this.refreshToken = null;
191
+ return null;
264
192
  }
265
193
  }
266
194
 
267
195
  /**
268
- * Refresh access and refresh tokens
269
- * @returns New tokens
196
+ * Internal method to check if we have an access token
197
+ * @private
198
+ * @returns Boolean indicating if access token exists
199
+ * @internal - Use `isAuthenticated` from useOxy() context in UI components instead
270
200
  */
271
- async refreshTokens() {
272
- if (!this.refreshToken) {
273
- throw new Error('No refresh token available');
274
- }
275
-
276
- // If a refresh is already in progress, return that promise
277
- if (this.refreshPromise) {
278
- return this.refreshPromise;
279
- }
280
-
281
- // Create a new refresh promise
282
- this.refreshPromise = (async () => {
283
- try {
284
- const res = await this.client.post('/auth/refresh', {
285
- refreshToken: this.refreshToken
286
- });
287
- const {
288
- accessToken,
289
- refreshToken
290
- } = res.data;
291
- this.accessToken = accessToken;
292
- this.refreshToken = refreshToken;
293
- return {
294
- accessToken,
295
- refreshToken
296
- };
297
- } catch (error) {
298
- this.accessToken = null;
299
- this.refreshToken = null;
300
- throw this.handleError(error);
301
- } finally {
302
- this.refreshPromise = null;
303
- }
304
- })();
305
- return this.refreshPromise;
201
+ hasAccessToken() {
202
+ return this.accessToken !== null;
306
203
  }
307
204
 
308
205
  /**
@@ -311,6 +208,22 @@ class OxyServices {
311
208
  */
312
209
  async validate() {
313
210
  try {
211
+ // Check if token contains sessionId (new session-based system)
212
+ if (this.accessToken) {
213
+ try {
214
+ const decoded = (0, _jwtDecode.jwtDecode)(this.accessToken);
215
+ if (decoded.sessionId) {
216
+ // Use session-based validation
217
+ const res = await this.client.get(`/secure-session/validate/${decoded.sessionId}`);
218
+ return res.data.valid;
219
+ }
220
+ } catch (decodeError) {
221
+ // If token can't be decoded, fall back to old validation
222
+ console.warn('Error decoding JWT token for session validation:', decodeError);
223
+ }
224
+ }
225
+
226
+ // Fall back to old validation method
314
227
  const res = await this.client.get('/auth/validate');
315
228
  return res.data.valid;
316
229
  } catch (error) {
@@ -320,59 +233,6 @@ class OxyServices {
320
233
 
321
234
  /* Session Management Methods */
322
235
 
323
- /**
324
- * Get active sessions for the authenticated user
325
- * @returns Array of active session objects
326
- */
327
- async getUserSessions() {
328
- try {
329
- const res = await this.client.get('/sessions');
330
- return res.data;
331
- } catch (error) {
332
- throw this.handleError(error);
333
- }
334
- }
335
-
336
- /**
337
- * Logout from a specific session
338
- * @param sessionId - The session ID to logout from
339
- * @returns Success status
340
- */
341
- async logoutSession(sessionId) {
342
- try {
343
- const res = await this.client.delete(`/sessions/${sessionId}`);
344
- return res.data;
345
- } catch (error) {
346
- throw this.handleError(error);
347
- }
348
- }
349
-
350
- /**
351
- * Logout from all other sessions (keep current session active)
352
- * @returns Success status
353
- */
354
- async logoutOtherSessions() {
355
- try {
356
- const res = await this.client.post('/sessions/logout-others');
357
- return res.data;
358
- } catch (error) {
359
- throw this.handleError(error);
360
- }
361
- }
362
-
363
- /**
364
- * Logout from all sessions
365
- * @returns Success status
366
- */
367
- async logoutAllSessions() {
368
- try {
369
- const res = await this.client.post('/sessions/logout-all');
370
- return res.data;
371
- } catch (error) {
372
- throw this.handleError(error);
373
- }
374
- }
375
-
376
236
  /**
377
237
  * Get device sessions for a specific session ID
378
238
  * @param sessionId - The session ID to get device sessions for
@@ -1197,12 +1057,47 @@ class OxyServices {
1197
1057
  }
1198
1058
 
1199
1059
  /**
1200
- * Secure login that returns only session data (no tokens stored locally)
1060
+ * Sign up a new user and create a session
1061
+ * @param username - Desired username
1062
+ * @param email - User's email address
1063
+ * @param password - User's password
1064
+ * @returns Object containing the message, token and user data
1065
+ */
1066
+ async signUp(username, email, password) {
1067
+ try {
1068
+ // First, create the user account
1069
+ const res = await this.client.post('/secure-session/register', {
1070
+ username,
1071
+ email,
1072
+ password
1073
+ });
1074
+ const {
1075
+ message,
1076
+ user
1077
+ } = res.data;
1078
+
1079
+ // Then log them in to create a session
1080
+ const loginRes = await this.secureLogin(username, password);
1081
+
1082
+ // Get the access token for the session
1083
+ const tokenRes = await this.getTokenBySession(loginRes.sessionId);
1084
+ return {
1085
+ message,
1086
+ token: tokenRes.accessToken,
1087
+ user: loginRes.user
1088
+ };
1089
+ } catch (error) {
1090
+ throw this.handleError(error);
1091
+ }
1092
+ }
1093
+
1094
+ /**
1095
+ * Secure login that creates a device-based session
1201
1096
  * @param username - User's username or email
1202
1097
  * @param password - User's password
1203
- * @param deviceName - Optional device name for session tracking
1204
- * @param deviceFingerprint - Device fingerprint for enhanced security
1205
- * @returns Secure login response with session data
1098
+ * @param deviceName - Optional device name
1099
+ * @param deviceFingerprint - Optional device fingerprint
1100
+ * @returns Login response with session data
1206
1101
  */
1207
1102
  async secureLogin(username, password, deviceName, deviceFingerprint) {
1208
1103
  try {
@@ -1410,34 +1305,42 @@ class OxyServices {
1410
1305
  });
1411
1306
  }
1412
1307
 
1413
- // Create a temporary OxyServices instance with the token to validate it
1414
- const tempOxyServices = new OxyServices({
1415
- baseURL: this.client.defaults.baseURL || ''
1416
- });
1417
- tempOxyServices.setTokens(token, ''); // Set access token
1418
-
1419
- // Validate token using the validate method
1420
- const isValid = await tempOxyServices.validate();
1421
- if (!isValid) {
1422
- const error = {
1423
- message: 'Invalid or expired token',
1424
- code: 'INVALID_TOKEN',
1425
- status: 403
1426
- };
1427
- if (onError) {
1428
- return onError(error);
1429
- }
1430
- return res.status(403).json({
1431
- message: 'Invalid or expired token',
1432
- code: 'INVALID_TOKEN'
1433
- });
1434
- }
1435
-
1436
- // Get user ID from token using JWT decode instead of relying on getCurrentUserId
1308
+ // Check if token contains sessionId (new session-based system)
1309
+ let isValid = false;
1437
1310
  let userId = null;
1438
1311
  try {
1439
1312
  const decoded = (0, _jwtDecode.jwtDecode)(token);
1440
1313
  userId = decoded.userId || decoded.id;
1314
+ if (decoded.sessionId) {
1315
+ // Use session-based validation
1316
+ const tempOxyServices = new OxyServices({
1317
+ baseURL: this.client.defaults.baseURL || ''
1318
+ });
1319
+ tempOxyServices.setTokens(token, '');
1320
+ const validation = await tempOxyServices.validateSession(decoded.sessionId);
1321
+ isValid = validation.valid;
1322
+ if (isValid && loadFullUser) {
1323
+ req.user = validation.user;
1324
+ }
1325
+ } else {
1326
+ // Use old validation method
1327
+ const tempOxyServices = new OxyServices({
1328
+ baseURL: this.client.defaults.baseURL || ''
1329
+ });
1330
+ tempOxyServices.setTokens(token, '');
1331
+ isValid = await tempOxyServices.validate();
1332
+ if (isValid && loadFullUser) {
1333
+ try {
1334
+ const userProfile = await tempOxyServices.getUserById(userId);
1335
+ req.user = userProfile;
1336
+ } catch (userError) {
1337
+ // If we can't load user, continue with just ID
1338
+ req.user = {
1339
+ id: userId
1340
+ };
1341
+ }
1342
+ }
1343
+ }
1441
1344
  } catch (decodeError) {
1442
1345
  const error = {
1443
1346
  message: 'Invalid token payload',
@@ -1452,6 +1355,20 @@ class OxyServices {
1452
1355
  code: 'INVALID_PAYLOAD'
1453
1356
  });
1454
1357
  }
1358
+ if (!isValid) {
1359
+ const error = {
1360
+ message: 'Invalid or expired token',
1361
+ code: 'INVALID_TOKEN',
1362
+ status: 403
1363
+ };
1364
+ if (onError) {
1365
+ return onError(error);
1366
+ }
1367
+ return res.status(403).json({
1368
+ message: 'Invalid or expired token',
1369
+ code: 'INVALID_TOKEN'
1370
+ });
1371
+ }
1455
1372
  if (!userId) {
1456
1373
  const error = {
1457
1374
  message: 'Invalid token payload',
@@ -1471,18 +1388,8 @@ class OxyServices {
1471
1388
  req.userId = userId;
1472
1389
  req.accessToken = token;
1473
1390
 
1474
- // Optionally load full user data
1475
- if (loadFullUser) {
1476
- try {
1477
- const userProfile = await tempOxyServices.getUserById(userId);
1478
- req.user = userProfile;
1479
- } catch (userError) {
1480
- // If we can't load user, continue with just ID
1481
- req.user = {
1482
- id: userId
1483
- };
1484
- }
1485
- } else {
1391
+ // Set user object if not already set by session validation
1392
+ if (!req.user) {
1486
1393
  req.user = {
1487
1394
  id: userId
1488
1395
  };
@@ -1516,54 +1423,71 @@ class OxyServices {
1516
1423
  };
1517
1424
  }
1518
1425
 
1519
- // Create a temporary OxyServices instance with the token
1520
- const tempOxyServices = new OxyServices({
1521
- baseURL: this.client.defaults.baseURL || ''
1522
- });
1523
- tempOxyServices.setTokens(token, '');
1524
-
1525
- // Validate token
1526
- const isValid = await tempOxyServices.validate();
1527
- if (!isValid) {
1528
- return {
1529
- valid: false,
1530
- error: 'Invalid or expired token'
1531
- };
1532
- }
1533
-
1534
- // Get user ID from token using JWT decode
1535
- let userId = null;
1426
+ // Check if token contains sessionId (new session-based system)
1536
1427
  try {
1537
1428
  const decoded = (0, _jwtDecode.jwtDecode)(token);
1538
- userId = decoded.userId || decoded.id;
1429
+ const userId = decoded.userId || decoded.id;
1430
+ if (decoded.sessionId) {
1431
+ // Use session-based validation
1432
+ const tempOxyServices = new OxyServices({
1433
+ baseURL: this.client.defaults.baseURL || ''
1434
+ });
1435
+ tempOxyServices.setTokens(token, '');
1436
+ const validation = await tempOxyServices.validateSession(decoded.sessionId);
1437
+ if (validation.valid) {
1438
+ return {
1439
+ valid: true,
1440
+ userId,
1441
+ user: validation.user
1442
+ };
1443
+ } else {
1444
+ return {
1445
+ valid: false,
1446
+ error: 'Invalid or expired session'
1447
+ };
1448
+ }
1449
+ } else {
1450
+ // Use old validation method
1451
+ const tempOxyServices = new OxyServices({
1452
+ baseURL: this.client.defaults.baseURL || ''
1453
+ });
1454
+ tempOxyServices.setTokens(token, '');
1455
+ const isValid = await tempOxyServices.validate();
1456
+ if (!isValid) {
1457
+ return {
1458
+ valid: false,
1459
+ error: 'Invalid or expired token'
1460
+ };
1461
+ }
1462
+ if (!userId) {
1463
+ return {
1464
+ valid: false,
1465
+ error: 'Invalid token payload'
1466
+ };
1467
+ }
1468
+
1469
+ // Try to get user profile
1470
+ let user;
1471
+ try {
1472
+ user = await tempOxyServices.getUserById(userId);
1473
+ } catch (error) {
1474
+ // Continue without full user data
1475
+ user = {
1476
+ id: userId
1477
+ };
1478
+ }
1479
+ return {
1480
+ valid: true,
1481
+ userId,
1482
+ user
1483
+ };
1484
+ }
1539
1485
  } catch (decodeError) {
1540
1486
  return {
1541
1487
  valid: false,
1542
1488
  error: 'Invalid token payload'
1543
1489
  };
1544
1490
  }
1545
- if (!userId) {
1546
- return {
1547
- valid: false,
1548
- error: 'Invalid token payload'
1549
- };
1550
- }
1551
-
1552
- // Try to get user profile
1553
- let user;
1554
- try {
1555
- user = await tempOxyServices.getUserById(userId);
1556
- } catch (error) {
1557
- // Continue without full user data
1558
- user = {
1559
- id: userId
1560
- };
1561
- }
1562
- return {
1563
- valid: true,
1564
- userId,
1565
- user
1566
- };
1567
1491
  } catch (error) {
1568
1492
  return {
1569
1493
  valid: false,