@oxyhq/services 5.8.10 → 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 (53) 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/screens/FeedbackScreen.js +0 -4
  6. package/lib/commonjs/ui/screens/FeedbackScreen.js.map +1 -1
  7. package/lib/commonjs/ui/screens/RecoverAccountScreen.js +0 -4
  8. package/lib/commonjs/ui/screens/RecoverAccountScreen.js.map +1 -1
  9. package/lib/commonjs/ui/screens/SignInScreen.js +16 -27
  10. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  11. package/lib/commonjs/ui/screens/SignUpScreen.js +4 -18
  12. package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
  13. package/lib/commonjs/ui/styles/authStyles.js +1 -2
  14. package/lib/commonjs/ui/styles/authStyles.js.map +1 -1
  15. package/lib/module/core/index.js +207 -283
  16. package/lib/module/core/index.js.map +1 -1
  17. package/lib/module/node/index.js +0 -4
  18. package/lib/module/node/index.js.map +1 -1
  19. package/lib/module/ui/screens/FeedbackScreen.js +0 -4
  20. package/lib/module/ui/screens/FeedbackScreen.js.map +1 -1
  21. package/lib/module/ui/screens/RecoverAccountScreen.js +0 -4
  22. package/lib/module/ui/screens/RecoverAccountScreen.js.map +1 -1
  23. package/lib/module/ui/screens/SignInScreen.js +16 -27
  24. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  25. package/lib/module/ui/screens/SignUpScreen.js +4 -18
  26. package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
  27. package/lib/module/ui/styles/authStyles.js +1 -2
  28. package/lib/module/ui/styles/authStyles.js.map +1 -1
  29. package/lib/typescript/core/index.d.ts +29 -79
  30. package/lib/typescript/core/index.d.ts.map +1 -1
  31. package/lib/typescript/node/index.d.ts +0 -2
  32. package/lib/typescript/node/index.d.ts.map +1 -1
  33. package/lib/typescript/ui/screens/FeedbackScreen.d.ts.map +1 -1
  34. package/lib/typescript/ui/screens/RecoverAccountScreen.d.ts.map +1 -1
  35. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
  36. package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
  37. package/lib/typescript/ui/styles/authStyles.d.ts +0 -1
  38. package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -1
  39. package/package.json +6 -6
  40. package/src/core/index.ts +213 -254
  41. package/src/node/index.ts +0 -4
  42. package/src/ui/screens/FeedbackScreen.tsx +0 -4
  43. package/src/ui/screens/RecoverAccountScreen.tsx +0 -4
  44. package/src/ui/screens/SignInScreen.tsx +16 -20
  45. package/src/ui/screens/SignUpScreen.tsx +4 -14
  46. package/src/ui/styles/authStyles.ts +0 -1
  47. package/lib/commonjs/node/createAuth.js +0 -95
  48. package/lib/commonjs/node/createAuth.js.map +0 -1
  49. package/lib/module/node/createAuth.js +0 -90
  50. package/lib/module/node/createAuth.js.map +0 -1
  51. package/lib/typescript/node/createAuth.d.ts +0 -7
  52. package/lib/typescript/node/createAuth.d.ts.map +0 -1
  53. package/src/node/createAuth.ts +0 -116
@@ -23,7 +23,6 @@ export { DeviceManager } from '../utils/deviceManager';
23
23
  export class OxyServices {
24
24
  accessToken = null;
25
25
  refreshToken = null;
26
- refreshPromise = null;
27
26
 
28
27
  /**
29
28
  * Creates a new instance of the OxyServices client
@@ -39,14 +38,25 @@ export class OxyServices {
39
38
  this.client.interceptors.request.use(async req => {
40
39
  if (!this.accessToken) {
41
40
  return req;
42
- } // Check if token is expired and refresh if needed
41
+ }
42
+
43
+ // Check if token is expired and refresh if needed
43
44
  try {
44
45
  const decoded = jwtDecode(this.accessToken);
45
46
  const currentTime = Math.floor(Date.now() / 1000);
46
47
 
47
48
  // If token expires in less than 60 seconds, refresh it
48
49
  if (decoded.exp - currentTime < 60) {
49
- await this.refreshTokens();
50
+ // For session-based tokens, get a new token from the session
51
+ if (decoded.sessionId) {
52
+ try {
53
+ const res = await this.client.get(`/secure-session/token/${decoded.sessionId}`);
54
+ this.accessToken = res.data.accessToken;
55
+ } catch (refreshError) {
56
+ // If refresh fails, clear tokens
57
+ this.clearTokens();
58
+ }
59
+ }
50
60
  }
51
61
  } catch (error) {
52
62
  // If token can't be decoded, continue with request and let server handle it
@@ -61,18 +71,24 @@ export class OxyServices {
61
71
  this.client.interceptors.response.use(response => response, async error => {
62
72
  const originalRequest = error.config;
63
73
  // If the error is due to an expired token and we haven't tried refreshing yet
64
- if (error.response?.status === 401 && this.refreshToken && originalRequest && !originalRequest.headers?.['X-Retry-After-Refresh']) {
74
+ if (error.response?.status === 401 && this.accessToken && originalRequest && !originalRequest.headers?.['X-Retry-After-Refresh']) {
65
75
  try {
66
- await this.refreshTokens();
67
- // Retry the original request with new token
68
- const newRequest = {
69
- ...originalRequest
70
- };
71
- if (newRequest.headers) {
72
- newRequest.headers.Authorization = `Bearer ${this.accessToken}`;
73
- newRequest.headers['X-Retry-After-Refresh'] = 'true';
76
+ // Check if token is session-based and try to refresh
77
+ const decoded = jwtDecode(this.accessToken);
78
+ if (decoded.sessionId) {
79
+ const res = await this.client.get(`/secure-session/token/${decoded.sessionId}`);
80
+ this.accessToken = res.data.accessToken;
81
+
82
+ // Retry the original request with new token
83
+ const newRequest = {
84
+ ...originalRequest
85
+ };
86
+ if (newRequest.headers) {
87
+ newRequest.headers.Authorization = `Bearer ${this.accessToken}`;
88
+ newRequest.headers['X-Retry-After-Refresh'] = 'true';
89
+ }
90
+ return this.client(newRequest);
74
91
  }
75
- return this.client(newRequest);
76
92
  } catch (refreshError) {
77
93
  // If refresh fails, force user to login again
78
94
  this.clearTokens();
@@ -105,43 +121,17 @@ export class OxyServices {
105
121
  }
106
122
 
107
123
  /**
108
- * Gets the currently authenticated user ID from the token
109
- * @returns The user ID or null if not authenticated
110
- */
111
- getCurrentUserId() {
112
- if (!this.accessToken) return null;
113
- try {
114
- const decoded = jwtDecode(this.accessToken);
115
-
116
- // Check for both userId (preferred) and id (fallback) for compatibility
117
- return decoded.userId || decoded.id || null;
118
- } catch (error) {
119
- return null;
120
- }
121
- }
122
-
123
- /**
124
- * Internal method to check if we have an access token
125
- * @private
126
- * @returns Boolean indicating if access token exists
127
- * @internal - Use `isAuthenticated` from useOxy() context in UI components instead
124
+ * Set authentication tokens manually
125
+ * @param accessToken - The access token
126
+ * @param refreshToken - The refresh token (optional for session-based auth)
128
127
  */
129
- hasAccessToken() {
130
- return this.accessToken !== null;
131
- }
132
-
133
- /**
134
- * Sets authentication tokens directly (useful for initializing from storage)
135
- * @param accessToken - JWT access token
136
- * @param refreshToken - Refresh token for getting new access tokens
137
- */
138
- setTokens(accessToken, refreshToken) {
128
+ setTokens(accessToken, refreshToken = '') {
139
129
  this.accessToken = accessToken;
140
130
  this.refreshToken = refreshToken;
141
131
  }
142
132
 
143
133
  /**
144
- * Clears all authentication tokens
134
+ * Clear stored authentication tokens
145
135
  */
146
136
  clearTokens() {
147
137
  this.accessToken = null;
@@ -149,120 +139,27 @@ export class OxyServices {
149
139
  }
150
140
 
151
141
  /**
152
- * Sign up a new user
153
- * @param username - Desired username
154
- * @param email - User's email address
155
- * @param password - User's password
156
- * @returns Object containing the message, token and user data
157
- */
158
- async signUp(username, email, password) {
159
- try {
160
- const res = await this.client.post('/auth/signup', {
161
- username,
162
- email,
163
- password
164
- });
165
- const {
166
- message,
167
- token,
168
- user
169
- } = res.data;
170
- this.accessToken = token;
171
- return {
172
- message,
173
- token,
174
- user
175
- };
176
- } catch (error) {
177
- throw this.handleError(error);
178
- }
179
- }
180
-
181
- /**
182
- * Log in and store tokens
183
- * @param username - User's username or email
184
- * @param password - User's password
185
- * @returns Login response containing tokens and user data
186
- */
187
- async login(username, password) {
188
- try {
189
- const res = await this.client.post('/auth/login', {
190
- username,
191
- password
192
- });
193
- const {
194
- accessToken,
195
- refreshToken,
196
- user
197
- } = res.data;
198
- this.accessToken = accessToken;
199
- this.refreshToken = refreshToken;
200
- return {
201
- accessToken,
202
- refreshToken,
203
- user
204
- };
205
- } catch (error) {
206
- throw this.handleError(error);
207
- }
208
- }
209
-
210
- /**
211
- * Log out user
142
+ * Get the current user ID from the stored token
143
+ * @returns User ID or null if not authenticated
212
144
  */
213
- async logout() {
214
- if (!this.refreshToken) return;
145
+ getCurrentUserId() {
146
+ if (!this.accessToken) return null;
215
147
  try {
216
- await this.client.post('/auth/logout', {
217
- refreshToken: this.refreshToken
218
- });
148
+ const decoded = jwtDecode(this.accessToken);
149
+ return decoded.userId || decoded.id || null;
219
150
  } catch (error) {
220
- console.warn('Error during logout', error);
221
- } finally {
222
- this.accessToken = null;
223
- this.refreshToken = null;
151
+ return null;
224
152
  }
225
153
  }
226
154
 
227
155
  /**
228
- * Refresh access and refresh tokens
229
- * @returns New tokens
156
+ * Internal method to check if we have an access token
157
+ * @private
158
+ * @returns Boolean indicating if access token exists
159
+ * @internal - Use `isAuthenticated` from useOxy() context in UI components instead
230
160
  */
231
- async refreshTokens() {
232
- if (!this.refreshToken) {
233
- throw new Error('No refresh token available');
234
- }
235
-
236
- // If a refresh is already in progress, return that promise
237
- if (this.refreshPromise) {
238
- return this.refreshPromise;
239
- }
240
-
241
- // Create a new refresh promise
242
- this.refreshPromise = (async () => {
243
- try {
244
- const res = await this.client.post('/auth/refresh', {
245
- refreshToken: this.refreshToken
246
- });
247
- const {
248
- accessToken,
249
- refreshToken
250
- } = res.data;
251
- this.accessToken = accessToken;
252
- this.refreshToken = refreshToken;
253
- return {
254
- accessToken,
255
- refreshToken
256
- };
257
- } catch (error) {
258
- this.accessToken = null;
259
- this.refreshToken = null;
260
- throw this.handleError(error);
261
- } finally {
262
- this.refreshPromise = null;
263
- }
264
- })();
265
- return this.refreshPromise;
161
+ hasAccessToken() {
162
+ return this.accessToken !== null;
266
163
  }
267
164
 
268
165
  /**
@@ -271,6 +168,22 @@ export class OxyServices {
271
168
  */
272
169
  async validate() {
273
170
  try {
171
+ // Check if token contains sessionId (new session-based system)
172
+ if (this.accessToken) {
173
+ try {
174
+ const decoded = jwtDecode(this.accessToken);
175
+ if (decoded.sessionId) {
176
+ // Use session-based validation
177
+ const res = await this.client.get(`/secure-session/validate/${decoded.sessionId}`);
178
+ return res.data.valid;
179
+ }
180
+ } catch (decodeError) {
181
+ // If token can't be decoded, fall back to old validation
182
+ console.warn('Error decoding JWT token for session validation:', decodeError);
183
+ }
184
+ }
185
+
186
+ // Fall back to old validation method
274
187
  const res = await this.client.get('/auth/validate');
275
188
  return res.data.valid;
276
189
  } catch (error) {
@@ -280,59 +193,6 @@ export class OxyServices {
280
193
 
281
194
  /* Session Management Methods */
282
195
 
283
- /**
284
- * Get active sessions for the authenticated user
285
- * @returns Array of active session objects
286
- */
287
- async getUserSessions() {
288
- try {
289
- const res = await this.client.get('/sessions');
290
- return res.data;
291
- } catch (error) {
292
- throw this.handleError(error);
293
- }
294
- }
295
-
296
- /**
297
- * Logout from a specific session
298
- * @param sessionId - The session ID to logout from
299
- * @returns Success status
300
- */
301
- async logoutSession(sessionId) {
302
- try {
303
- const res = await this.client.delete(`/sessions/${sessionId}`);
304
- return res.data;
305
- } catch (error) {
306
- throw this.handleError(error);
307
- }
308
- }
309
-
310
- /**
311
- * Logout from all other sessions (keep current session active)
312
- * @returns Success status
313
- */
314
- async logoutOtherSessions() {
315
- try {
316
- const res = await this.client.post('/sessions/logout-others');
317
- return res.data;
318
- } catch (error) {
319
- throw this.handleError(error);
320
- }
321
- }
322
-
323
- /**
324
- * Logout from all sessions
325
- * @returns Success status
326
- */
327
- async logoutAllSessions() {
328
- try {
329
- const res = await this.client.post('/sessions/logout-all');
330
- return res.data;
331
- } catch (error) {
332
- throw this.handleError(error);
333
- }
334
- }
335
-
336
196
  /**
337
197
  * Get device sessions for a specific session ID
338
198
  * @param sessionId - The session ID to get device sessions for
@@ -1157,12 +1017,47 @@ export class OxyServices {
1157
1017
  }
1158
1018
 
1159
1019
  /**
1160
- * Secure login that returns only session data (no tokens stored locally)
1020
+ * Sign up a new user and create a session
1021
+ * @param username - Desired username
1022
+ * @param email - User's email address
1023
+ * @param password - User's password
1024
+ * @returns Object containing the message, token and user data
1025
+ */
1026
+ async signUp(username, email, password) {
1027
+ try {
1028
+ // First, create the user account
1029
+ const res = await this.client.post('/secure-session/register', {
1030
+ username,
1031
+ email,
1032
+ password
1033
+ });
1034
+ const {
1035
+ message,
1036
+ user
1037
+ } = res.data;
1038
+
1039
+ // Then log them in to create a session
1040
+ const loginRes = await this.secureLogin(username, password);
1041
+
1042
+ // Get the access token for the session
1043
+ const tokenRes = await this.getTokenBySession(loginRes.sessionId);
1044
+ return {
1045
+ message,
1046
+ token: tokenRes.accessToken,
1047
+ user: loginRes.user
1048
+ };
1049
+ } catch (error) {
1050
+ throw this.handleError(error);
1051
+ }
1052
+ }
1053
+
1054
+ /**
1055
+ * Secure login that creates a device-based session
1161
1056
  * @param username - User's username or email
1162
1057
  * @param password - User's password
1163
- * @param deviceName - Optional device name for session tracking
1164
- * @param deviceFingerprint - Device fingerprint for enhanced security
1165
- * @returns Secure login response with session data
1058
+ * @param deviceName - Optional device name
1059
+ * @param deviceFingerprint - Optional device fingerprint
1060
+ * @returns Login response with session data
1166
1061
  */
1167
1062
  async secureLogin(username, password, deviceName, deviceFingerprint) {
1168
1063
  try {
@@ -1370,34 +1265,42 @@ export class OxyServices {
1370
1265
  });
1371
1266
  }
1372
1267
 
1373
- // Create a temporary OxyServices instance with the token to validate it
1374
- const tempOxyServices = new OxyServices({
1375
- baseURL: this.client.defaults.baseURL || ''
1376
- });
1377
- tempOxyServices.setTokens(token, ''); // Set access token
1378
-
1379
- // Validate token using the validate method
1380
- const isValid = await tempOxyServices.validate();
1381
- if (!isValid) {
1382
- const error = {
1383
- message: 'Invalid or expired token',
1384
- code: 'INVALID_TOKEN',
1385
- status: 403
1386
- };
1387
- if (onError) {
1388
- return onError(error);
1389
- }
1390
- return res.status(403).json({
1391
- message: 'Invalid or expired token',
1392
- code: 'INVALID_TOKEN'
1393
- });
1394
- }
1395
-
1396
- // Get user ID from token using JWT decode instead of relying on getCurrentUserId
1268
+ // Check if token contains sessionId (new session-based system)
1269
+ let isValid = false;
1397
1270
  let userId = null;
1398
1271
  try {
1399
1272
  const decoded = jwtDecode(token);
1400
1273
  userId = decoded.userId || decoded.id;
1274
+ if (decoded.sessionId) {
1275
+ // Use session-based validation
1276
+ const tempOxyServices = new OxyServices({
1277
+ baseURL: this.client.defaults.baseURL || ''
1278
+ });
1279
+ tempOxyServices.setTokens(token, '');
1280
+ const validation = await tempOxyServices.validateSession(decoded.sessionId);
1281
+ isValid = validation.valid;
1282
+ if (isValid && loadFullUser) {
1283
+ req.user = validation.user;
1284
+ }
1285
+ } else {
1286
+ // Use old validation method
1287
+ const tempOxyServices = new OxyServices({
1288
+ baseURL: this.client.defaults.baseURL || ''
1289
+ });
1290
+ tempOxyServices.setTokens(token, '');
1291
+ isValid = await tempOxyServices.validate();
1292
+ if (isValid && loadFullUser) {
1293
+ try {
1294
+ const userProfile = await tempOxyServices.getUserById(userId);
1295
+ req.user = userProfile;
1296
+ } catch (userError) {
1297
+ // If we can't load user, continue with just ID
1298
+ req.user = {
1299
+ id: userId
1300
+ };
1301
+ }
1302
+ }
1303
+ }
1401
1304
  } catch (decodeError) {
1402
1305
  const error = {
1403
1306
  message: 'Invalid token payload',
@@ -1412,6 +1315,20 @@ export class OxyServices {
1412
1315
  code: 'INVALID_PAYLOAD'
1413
1316
  });
1414
1317
  }
1318
+ if (!isValid) {
1319
+ const error = {
1320
+ message: 'Invalid or expired token',
1321
+ code: 'INVALID_TOKEN',
1322
+ status: 403
1323
+ };
1324
+ if (onError) {
1325
+ return onError(error);
1326
+ }
1327
+ return res.status(403).json({
1328
+ message: 'Invalid or expired token',
1329
+ code: 'INVALID_TOKEN'
1330
+ });
1331
+ }
1415
1332
  if (!userId) {
1416
1333
  const error = {
1417
1334
  message: 'Invalid token payload',
@@ -1431,18 +1348,8 @@ export class OxyServices {
1431
1348
  req.userId = userId;
1432
1349
  req.accessToken = token;
1433
1350
 
1434
- // Optionally load full user data
1435
- if (loadFullUser) {
1436
- try {
1437
- const userProfile = await tempOxyServices.getUserById(userId);
1438
- req.user = userProfile;
1439
- } catch (userError) {
1440
- // If we can't load user, continue with just ID
1441
- req.user = {
1442
- id: userId
1443
- };
1444
- }
1445
- } else {
1351
+ // Set user object if not already set by session validation
1352
+ if (!req.user) {
1446
1353
  req.user = {
1447
1354
  id: userId
1448
1355
  };
@@ -1476,54 +1383,71 @@ export class OxyServices {
1476
1383
  };
1477
1384
  }
1478
1385
 
1479
- // Create a temporary OxyServices instance with the token
1480
- const tempOxyServices = new OxyServices({
1481
- baseURL: this.client.defaults.baseURL || ''
1482
- });
1483
- tempOxyServices.setTokens(token, '');
1484
-
1485
- // Validate token
1486
- const isValid = await tempOxyServices.validate();
1487
- if (!isValid) {
1488
- return {
1489
- valid: false,
1490
- error: 'Invalid or expired token'
1491
- };
1492
- }
1493
-
1494
- // Get user ID from token using JWT decode
1495
- let userId = null;
1386
+ // Check if token contains sessionId (new session-based system)
1496
1387
  try {
1497
1388
  const decoded = jwtDecode(token);
1498
- userId = decoded.userId || decoded.id;
1389
+ const userId = decoded.userId || decoded.id;
1390
+ if (decoded.sessionId) {
1391
+ // Use session-based validation
1392
+ const tempOxyServices = new OxyServices({
1393
+ baseURL: this.client.defaults.baseURL || ''
1394
+ });
1395
+ tempOxyServices.setTokens(token, '');
1396
+ const validation = await tempOxyServices.validateSession(decoded.sessionId);
1397
+ if (validation.valid) {
1398
+ return {
1399
+ valid: true,
1400
+ userId,
1401
+ user: validation.user
1402
+ };
1403
+ } else {
1404
+ return {
1405
+ valid: false,
1406
+ error: 'Invalid or expired session'
1407
+ };
1408
+ }
1409
+ } else {
1410
+ // Use old validation method
1411
+ const tempOxyServices = new OxyServices({
1412
+ baseURL: this.client.defaults.baseURL || ''
1413
+ });
1414
+ tempOxyServices.setTokens(token, '');
1415
+ const isValid = await tempOxyServices.validate();
1416
+ if (!isValid) {
1417
+ return {
1418
+ valid: false,
1419
+ error: 'Invalid or expired token'
1420
+ };
1421
+ }
1422
+ if (!userId) {
1423
+ return {
1424
+ valid: false,
1425
+ error: 'Invalid token payload'
1426
+ };
1427
+ }
1428
+
1429
+ // Try to get user profile
1430
+ let user;
1431
+ try {
1432
+ user = await tempOxyServices.getUserById(userId);
1433
+ } catch (error) {
1434
+ // Continue without full user data
1435
+ user = {
1436
+ id: userId
1437
+ };
1438
+ }
1439
+ return {
1440
+ valid: true,
1441
+ userId,
1442
+ user
1443
+ };
1444
+ }
1499
1445
  } catch (decodeError) {
1500
1446
  return {
1501
1447
  valid: false,
1502
1448
  error: 'Invalid token payload'
1503
1449
  };
1504
1450
  }
1505
- if (!userId) {
1506
- return {
1507
- valid: false,
1508
- error: 'Invalid token payload'
1509
- };
1510
- }
1511
-
1512
- // Try to get user profile
1513
- let user;
1514
- try {
1515
- user = await tempOxyServices.getUserById(userId);
1516
- } catch (error) {
1517
- // Continue without full user data
1518
- user = {
1519
- id: userId
1520
- };
1521
- }
1522
- return {
1523
- valid: true,
1524
- userId,
1525
- user
1526
- };
1527
1451
  } catch (error) {
1528
1452
  return {
1529
1453
  valid: false,