@thetechfossil/auth2 1.2.19 → 1.2.21

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 (38) hide show
  1. package/README.md +1 -1
  2. package/dist/index.components.d.mts +1 -0
  3. package/dist/index.components.d.ts +1 -0
  4. package/dist/index.components.js +275 -11
  5. package/dist/index.components.js.map +1 -1
  6. package/dist/index.components.mjs +275 -11
  7. package/dist/index.components.mjs.map +1 -1
  8. package/dist/index.d.mts +111 -3
  9. package/dist/index.d.ts +111 -3
  10. package/dist/index.js +470 -30
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +471 -32
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/index.next.d.mts +54 -1
  15. package/dist/index.next.d.ts +54 -1
  16. package/dist/index.next.js +332 -26
  17. package/dist/index.next.js.map +1 -1
  18. package/dist/index.next.mjs +332 -26
  19. package/dist/index.next.mjs.map +1 -1
  20. package/dist/index.next.server.d.mts +81 -2
  21. package/dist/index.next.server.d.ts +81 -2
  22. package/dist/index.next.server.js +406 -9
  23. package/dist/index.next.server.js.map +1 -1
  24. package/dist/index.next.server.mjs +403 -10
  25. package/dist/index.next.server.mjs.map +1 -1
  26. package/dist/index.node.d.mts +81 -2
  27. package/dist/index.node.d.ts +81 -2
  28. package/dist/index.node.js +406 -9
  29. package/dist/index.node.js.map +1 -1
  30. package/dist/index.node.mjs +403 -10
  31. package/dist/index.node.mjs.map +1 -1
  32. package/dist/index.react-native.d.mts +227 -0
  33. package/dist/index.react-native.d.ts +227 -0
  34. package/dist/index.react-native.js +1684 -0
  35. package/dist/index.react-native.js.map +1 -0
  36. package/dist/index.react-native.mjs +1648 -0
  37. package/dist/index.react-native.mjs.map +1 -0
  38. package/package.json +119 -102
@@ -2,6 +2,7 @@
2
2
  'use strict';
3
3
 
4
4
  var axios = require('axios');
5
+ var socket_ioClient = require('socket.io-client');
5
6
  var upfiles = require('@thetechfossil/upfiles');
6
7
  var React = require('react');
7
8
  var jsxRuntime = require('react/jsx-runtime');
@@ -17,6 +18,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
17
18
  if (typeof require !== "undefined") return require.apply(this, arguments);
18
19
  throw Error('Dynamic require of "' + x + '" is not supported');
19
20
  });
21
+ var ERROR_MESSAGES = {
22
+ NETWORK_ERROR: "Unable to connect to the server. Please check your internet connection and try again.",
23
+ TIMEOUT: "The request took too long. Please try again.",
24
+ SERVER_ERROR: "Something went wrong on our end. Please try again later.",
25
+ UNAUTHORIZED: "Your session has expired. Please log in again.",
26
+ FORBIDDEN: "You do not have permission to perform this action.",
27
+ NOT_FOUND: "The requested resource was not found.",
28
+ RATE_LIMITED: "Too many requests. Please wait a moment and try again.",
29
+ UNKNOWN: "An unexpected error occurred. Please try again."
30
+ };
20
31
  var HttpClient = class {
21
32
  constructor(baseUrl, defaultHeaders = {}) {
22
33
  this.csrfToken = null;
@@ -51,7 +62,7 @@ var HttpClient = class {
51
62
  }
52
63
  return config;
53
64
  },
54
- (error) => Promise.reject(error)
65
+ (error) => Promise.reject(this.createUserFriendlyError(error))
55
66
  );
56
67
  this.axiosInstance.interceptors.response.use(
57
68
  (response) => response,
@@ -66,18 +77,71 @@ var HttpClient = class {
66
77
  }
67
78
  return this.axiosInstance(originalRequest);
68
79
  } catch (refreshError) {
69
- return Promise.reject(refreshError);
80
+ return Promise.reject(this.createUserFriendlyError(refreshError));
70
81
  }
71
82
  }
72
- if (error.response && error.response.data && error.response.data.message) {
73
- const customError = new Error(error.response.data.message);
74
- customError.response = error.response;
75
- return Promise.reject(customError);
76
- }
77
- return Promise.reject(error);
83
+ return Promise.reject(this.createUserFriendlyError(error));
78
84
  }
79
85
  );
80
86
  }
87
+ /**
88
+ * Creates a user-friendly error message from an Axios error
89
+ */
90
+ createUserFriendlyError(error) {
91
+ if (error instanceof Error && !error.isAxiosError) {
92
+ return error;
93
+ }
94
+ let message;
95
+ let statusCode;
96
+ if (axios__default.default.isAxiosError(error)) {
97
+ statusCode = error.response?.status;
98
+ const responseData = error.response?.data;
99
+ if (responseData?.message) {
100
+ message = responseData.message;
101
+ } else if (!error.response) {
102
+ if (error.code === "ECONNABORTED" || error.message.includes("timeout")) {
103
+ message = ERROR_MESSAGES.TIMEOUT;
104
+ } else if (error.code === "ERR_NETWORK" || error.message === "Network Error") {
105
+ message = ERROR_MESSAGES.NETWORK_ERROR;
106
+ } else {
107
+ message = ERROR_MESSAGES.NETWORK_ERROR;
108
+ }
109
+ } else {
110
+ switch (statusCode) {
111
+ case 400:
112
+ message = responseData?.message || "Invalid request. Please check your input.";
113
+ break;
114
+ case 401:
115
+ message = responseData?.message || ERROR_MESSAGES.UNAUTHORIZED;
116
+ break;
117
+ case 403:
118
+ message = responseData?.message || ERROR_MESSAGES.FORBIDDEN;
119
+ break;
120
+ case 404:
121
+ message = responseData?.message || ERROR_MESSAGES.NOT_FOUND;
122
+ break;
123
+ case 429:
124
+ message = ERROR_MESSAGES.RATE_LIMITED;
125
+ break;
126
+ case 500:
127
+ case 502:
128
+ case 503:
129
+ case 504:
130
+ message = ERROR_MESSAGES.SERVER_ERROR;
131
+ break;
132
+ default:
133
+ message = responseData?.message || ERROR_MESSAGES.UNKNOWN;
134
+ }
135
+ }
136
+ } else {
137
+ message = error?.message || ERROR_MESSAGES.UNKNOWN;
138
+ }
139
+ const customError = new Error(message);
140
+ customError.response = error?.response;
141
+ customError.statusCode = statusCode;
142
+ customError.originalError = error;
143
+ return customError;
144
+ }
81
145
  async get(endpoint, headers) {
82
146
  const response = await this.axiosInstance.get(endpoint, { headers });
83
147
  return response.data;
@@ -128,16 +192,134 @@ var HttpClient = class {
128
192
  }
129
193
  }
130
194
  };
195
+ var SocketService = class {
196
+ constructor(config) {
197
+ this.socket = null;
198
+ this.token = null;
199
+ this.eventHandlers = /* @__PURE__ */ new Map();
200
+ this.isConnecting = false;
201
+ this.config = {
202
+ autoConnect: false,
203
+ reconnection: true,
204
+ reconnectionAttempts: 5,
205
+ reconnectionDelay: 1e3,
206
+ ...config
207
+ };
208
+ }
209
+ connect(token) {
210
+ if (this.socket?.connected || this.isConnecting) {
211
+ return;
212
+ }
213
+ this.token = token;
214
+ this.isConnecting = true;
215
+ this.socket = socket_ioClient.io(this.config.baseUrl, {
216
+ auth: { token },
217
+ autoConnect: true,
218
+ reconnection: this.config.reconnection,
219
+ reconnectionAttempts: this.config.reconnectionAttempts,
220
+ reconnectionDelay: this.config.reconnectionDelay,
221
+ transports: ["websocket", "polling"]
222
+ });
223
+ this.setupEventListeners();
224
+ }
225
+ setupEventListeners() {
226
+ if (!this.socket) return;
227
+ this.socket.on("connect", () => {
228
+ this.isConnecting = false;
229
+ console.log("[Auth SDK] Socket connected");
230
+ this.emit("connected", {});
231
+ });
232
+ this.socket.on("disconnect", (reason) => {
233
+ console.log("[Auth SDK] Socket disconnected:", reason);
234
+ this.emit("disconnected", { reason });
235
+ });
236
+ this.socket.on("connect_error", (error) => {
237
+ this.isConnecting = false;
238
+ console.error("[Auth SDK] Socket connection error:", error.message);
239
+ this.emit("error", { error: error.message });
240
+ });
241
+ this.socket.on("user:updated", (data) => {
242
+ this.emit("user:updated", data);
243
+ });
244
+ this.socket.on("session:revoked", (data) => {
245
+ this.emit("session:revoked", data);
246
+ });
247
+ this.socket.on("session:all-revoked", () => {
248
+ this.emit("session:all-revoked", {});
249
+ });
250
+ this.socket.on("auth:password-changed", () => {
251
+ this.emit("auth:password-changed", {});
252
+ });
253
+ this.socket.on("auth:2fa-changed", (data) => {
254
+ this.emit("auth:2fa-changed", data);
255
+ });
256
+ this.socket.on("user:refresh", () => {
257
+ this.emit("user:refresh", {});
258
+ });
259
+ }
260
+ disconnect() {
261
+ if (this.socket) {
262
+ this.socket.disconnect();
263
+ this.socket = null;
264
+ this.token = null;
265
+ this.isConnecting = false;
266
+ }
267
+ }
268
+ isConnected() {
269
+ return this.socket?.connected ?? false;
270
+ }
271
+ // Event subscription
272
+ on(event, handler) {
273
+ if (!this.eventHandlers.has(event)) {
274
+ this.eventHandlers.set(event, /* @__PURE__ */ new Set());
275
+ }
276
+ this.eventHandlers.get(event).add(handler);
277
+ return () => {
278
+ this.eventHandlers.get(event)?.delete(handler);
279
+ };
280
+ }
281
+ off(event, handler) {
282
+ if (handler) {
283
+ this.eventHandlers.get(event)?.delete(handler);
284
+ } else {
285
+ this.eventHandlers.delete(event);
286
+ }
287
+ }
288
+ emit(event, data) {
289
+ const handlers = this.eventHandlers.get(event);
290
+ if (handlers) {
291
+ handlers.forEach((handler) => {
292
+ try {
293
+ handler(data);
294
+ } catch (error) {
295
+ console.error(`[Auth SDK] Error in event handler for ${event}:`, error);
296
+ }
297
+ });
298
+ }
299
+ }
300
+ // Request fresh user data from server
301
+ requestUserRefresh() {
302
+ if (this.socket?.connected) {
303
+ this.socket.emit("request:user");
304
+ }
305
+ }
306
+ };
131
307
  var AuthService = class {
308
+ // 5 minutes cache
132
309
  constructor(config) {
133
310
  this.token = null;
134
311
  this.upfilesClient = null;
312
+ this.cachedUser = null;
313
+ this.userCacheTimestamp = 0;
314
+ this.USER_CACHE_TTL = 5 * 60 * 1e3;
135
315
  this.config = {
136
316
  localStorageKey: "auth_token",
137
317
  csrfEnabled: true,
318
+ enableSocket: true,
138
319
  ...config
139
320
  };
140
321
  this.httpClient = new HttpClient(this.config.baseUrl);
322
+ this.socketService = new SocketService({ baseUrl: this.config.baseUrl });
141
323
  this.loadTokenFromStorage();
142
324
  if (this.config.upfilesConfig) {
143
325
  this.upfilesClient = new upfiles.UpfilesClient({
@@ -154,6 +336,9 @@ var AuthService = class {
154
336
  this.httpClient.setFrontendBaseUrl(frontendBaseUrl);
155
337
  }
156
338
  }
339
+ if (this.token && this.config.enableSocket !== false) {
340
+ this.connectSocket();
341
+ }
157
342
  }
158
343
  loadTokenFromStorage() {
159
344
  if (typeof window !== "undefined" && this.config.localStorageKey) {
@@ -186,6 +371,57 @@ var AuthService = class {
186
371
  }
187
372
  }
188
373
  }
374
+ // Socket connection management
375
+ connectSocket() {
376
+ if (this.token && this.config.enableSocket !== false && typeof window !== "undefined") {
377
+ this.socketService.connect(this.token);
378
+ }
379
+ }
380
+ disconnectSocket() {
381
+ this.socketService.disconnect();
382
+ }
383
+ // Socket event subscription
384
+ onUserUpdated(handler) {
385
+ return this.socketService.on("user:updated", (data) => {
386
+ if (data.user) {
387
+ this.cachedUser = data.user;
388
+ this.userCacheTimestamp = Date.now();
389
+ }
390
+ handler(data);
391
+ });
392
+ }
393
+ onSessionRevoked(handler) {
394
+ return this.socketService.on("session:revoked", handler);
395
+ }
396
+ onAllSessionsRevoked(handler) {
397
+ return this.socketService.on("session:all-revoked", handler);
398
+ }
399
+ onPasswordChanged(handler) {
400
+ return this.socketService.on("auth:password-changed", handler);
401
+ }
402
+ on2FAChanged(handler) {
403
+ return this.socketService.on("auth:2fa-changed", handler);
404
+ }
405
+ onSocketConnected(handler) {
406
+ return this.socketService.on("connected", handler);
407
+ }
408
+ onSocketDisconnected(handler) {
409
+ return this.socketService.on("disconnected", handler);
410
+ }
411
+ onSocketError(handler) {
412
+ return this.socketService.on("error", handler);
413
+ }
414
+ isSocketConnected() {
415
+ return this.socketService.isConnected();
416
+ }
417
+ // Cache management
418
+ clearUserCache() {
419
+ this.cachedUser = null;
420
+ this.userCacheTimestamp = 0;
421
+ }
422
+ isCacheValid() {
423
+ return this.cachedUser !== null && Date.now() - this.userCacheTimestamp < this.USER_CACHE_TTL;
424
+ }
189
425
  isAuthenticated() {
190
426
  return !!this.token;
191
427
  }
@@ -251,6 +487,11 @@ var AuthService = class {
251
487
  this.token = response.token;
252
488
  this.httpClient.setAuthToken(response.token);
253
489
  this.saveTokenToStorage(response.token);
490
+ if (response.user) {
491
+ this.cachedUser = response.user;
492
+ this.userCacheTimestamp = Date.now();
493
+ }
494
+ this.connectSocket();
254
495
  return response;
255
496
  }
256
497
  if (response.success && (response.message === "OTP sent to your email." || response.message === "OTP sent to your phone number.")) {
@@ -260,6 +501,11 @@ var AuthService = class {
260
501
  this.token = response.token;
261
502
  this.httpClient.setAuthToken(response.token);
262
503
  this.saveTokenToStorage(response.token);
504
+ if (response.user) {
505
+ this.cachedUser = response.user;
506
+ this.userCacheTimestamp = Date.now();
507
+ }
508
+ this.connectSocket();
263
509
  return response;
264
510
  }
265
511
  throw new Error(response.message || "Login failed");
@@ -303,21 +549,29 @@ var AuthService = class {
303
549
  }
304
550
  }
305
551
  async logout() {
552
+ this.disconnectSocket();
306
553
  try {
307
554
  await this.httpClient.post("/api/v1/auth/logout", {});
308
555
  } catch (error) {
309
556
  console.warn("Failed to call logout endpoint:", error);
310
557
  }
311
558
  this.token = null;
559
+ this.cachedUser = null;
560
+ this.userCacheTimestamp = 0;
312
561
  this.httpClient.removeAuthToken();
313
562
  this.httpClient.removeCsrfToken();
314
563
  this.removeTokenFromStorage();
315
564
  }
316
- async getProfile() {
565
+ async getProfile(forceRefresh = false) {
317
566
  if (!this.token) {
318
567
  throw new Error("Not authenticated");
319
568
  }
569
+ if (!forceRefresh && this.isCacheValid() && this.cachedUser) {
570
+ return this.cachedUser;
571
+ }
320
572
  const response = await this.httpClient.get("/api/v1/user/me");
573
+ this.cachedUser = response.user;
574
+ this.userCacheTimestamp = Date.now();
321
575
  return response.user;
322
576
  }
323
577
  async updateProfile(data) {
@@ -481,6 +735,16 @@ var AuthService = class {
481
735
  );
482
736
  return response;
483
737
  }
738
+ async adminCreateUser(data) {
739
+ if (!this.token) {
740
+ throw new Error("Not authenticated");
741
+ }
742
+ const response = await this.httpClient.post(
743
+ "/api/v1/admin/create-user",
744
+ data
745
+ );
746
+ return response;
747
+ }
484
748
  async adminVerifyUser(userId) {
485
749
  if (!this.token) {
486
750
  throw new Error("Not authenticated");
@@ -716,21 +980,49 @@ var AuthProvider = ({ children, config }) => {
716
980
  const [user, setUser] = React.useState(null);
717
981
  const [isLoaded, setIsLoaded] = React.useState(false);
718
982
  const [loading, setLoading] = React.useState(false);
719
- const checkAuthStatus = React.useCallback(async () => {
983
+ const [isSocketConnected, setIsSocketConnected] = React.useState(false);
984
+ React.useEffect(() => {
985
+ const unsubUserUpdated = authService.onUserUpdated(({ user: updatedUser }) => {
986
+ if (updatedUser) {
987
+ setUser(updatedUser);
988
+ }
989
+ });
990
+ const unsubSessionRevoked = authService.onSessionRevoked(() => {
991
+ authService.logout().then(() => {
992
+ setUser(null);
993
+ });
994
+ });
995
+ const unsubAllSessionsRevoked = authService.onAllSessionsRevoked(() => {
996
+ authService.logout().then(() => {
997
+ setUser(null);
998
+ });
999
+ });
1000
+ const unsubPasswordChanged = authService.onPasswordChanged(() => {
1001
+ authService.logout().then(() => {
1002
+ setUser(null);
1003
+ });
1004
+ });
1005
+ const unsubConnected = authService.onSocketConnected(() => {
1006
+ setIsSocketConnected(true);
1007
+ });
1008
+ const unsubDisconnected = authService.onSocketDisconnected(() => {
1009
+ setIsSocketConnected(false);
1010
+ });
1011
+ return () => {
1012
+ unsubUserUpdated();
1013
+ unsubSessionRevoked();
1014
+ unsubAllSessionsRevoked();
1015
+ unsubPasswordChanged();
1016
+ unsubConnected();
1017
+ unsubDisconnected();
1018
+ };
1019
+ }, [authService]);
1020
+ React.useEffect(() => {
720
1021
  const authenticated = authService.isAuthenticated();
721
1022
  if (authenticated) {
722
- try {
723
- const freshUser = await authService.getProfile();
724
- setUser(freshUser);
725
- } catch (error) {
726
- console.error("Failed to fetch fresh user profile, falling back to token:", error);
727
- try {
728
- const currentUser = authService.getCurrentUser();
729
- setUser(currentUser);
730
- } catch (fallbackError) {
731
- console.error("Failed to get current user from token:", fallbackError);
732
- setUser(null);
733
- }
1023
+ const currentUser = authService.getCurrentUser();
1024
+ if (currentUser) {
1025
+ setUser(currentUser);
734
1026
  }
735
1027
  } else {
736
1028
  setUser(null);
@@ -738,8 +1030,21 @@ var AuthProvider = ({ children, config }) => {
738
1030
  setIsLoaded(true);
739
1031
  }, [authService]);
740
1032
  React.useEffect(() => {
741
- checkAuthStatus();
742
- }, [checkAuthStatus]);
1033
+ if (!isLoaded) return;
1034
+ const authenticated = authService.isAuthenticated();
1035
+ if (!authenticated) return;
1036
+ const fetchFreshUser = async () => {
1037
+ try {
1038
+ const freshUser = await authService.getProfile();
1039
+ setUser(freshUser);
1040
+ } catch (error) {
1041
+ console.warn("[Auth SDK] Failed to fetch fresh user profile:", error);
1042
+ }
1043
+ };
1044
+ if (isSocketConnected) {
1045
+ fetchFreshUser();
1046
+ }
1047
+ }, [authService, isLoaded, isSocketConnected]);
743
1048
  const signIn = React.useCallback(async (data) => {
744
1049
  setLoading(true);
745
1050
  try {
@@ -961,6 +1266,7 @@ var AuthProvider = ({ children, config }) => {
961
1266
  isLoaded,
962
1267
  isSignedIn: !!user,
963
1268
  loading,
1269
+ isSocketConnected,
964
1270
  signIn,
965
1271
  signUp,
966
1272
  signOut,
@@ -4469,11 +4775,11 @@ var ChangePassword = ({ onSuccess, appearance }) => {
4469
4775
 
4470
4776
  // src/react/components/utils/injectModalStyles.ts
4471
4777
  var injectModalStyles = () => {
4472
- if (document.getElementById("ttf-auth-modal-styles")) {
4778
+ if (document.getElementById("ktw-auth-modal-styles")) {
4473
4779
  return;
4474
4780
  }
4475
4781
  const styleElement = document.createElement("style");
4476
- styleElement.id = "ttf-auth-modal-styles";
4782
+ styleElement.id = "ktw-auth-modal-styles";
4477
4783
  styleElement.textContent = `
4478
4784
  /* ImageManager Modal Styles - Critical for proper modal display */
4479
4785
  /* Radix UI Dialog styles - Force visibility */