@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
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import axios from 'axios';
3
+ import { io } from 'socket.io-client';
3
4
  import { UpfilesClient, ImageManager } from '@thetechfossil/upfiles';
4
5
  import React, { createContext, forwardRef, useContext, useState, useCallback, useEffect, useMemo, useRef } from 'react';
5
6
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -10,6 +11,16 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
10
11
  if (typeof require !== "undefined") return require.apply(this, arguments);
11
12
  throw Error('Dynamic require of "' + x + '" is not supported');
12
13
  });
14
+ var ERROR_MESSAGES = {
15
+ NETWORK_ERROR: "Unable to connect to the server. Please check your internet connection and try again.",
16
+ TIMEOUT: "The request took too long. Please try again.",
17
+ SERVER_ERROR: "Something went wrong on our end. Please try again later.",
18
+ UNAUTHORIZED: "Your session has expired. Please log in again.",
19
+ FORBIDDEN: "You do not have permission to perform this action.",
20
+ NOT_FOUND: "The requested resource was not found.",
21
+ RATE_LIMITED: "Too many requests. Please wait a moment and try again.",
22
+ UNKNOWN: "An unexpected error occurred. Please try again."
23
+ };
13
24
  var HttpClient = class {
14
25
  constructor(baseUrl, defaultHeaders = {}) {
15
26
  this.csrfToken = null;
@@ -44,7 +55,7 @@ var HttpClient = class {
44
55
  }
45
56
  return config;
46
57
  },
47
- (error) => Promise.reject(error)
58
+ (error) => Promise.reject(this.createUserFriendlyError(error))
48
59
  );
49
60
  this.axiosInstance.interceptors.response.use(
50
61
  (response) => response,
@@ -59,18 +70,71 @@ var HttpClient = class {
59
70
  }
60
71
  return this.axiosInstance(originalRequest);
61
72
  } catch (refreshError) {
62
- return Promise.reject(refreshError);
73
+ return Promise.reject(this.createUserFriendlyError(refreshError));
63
74
  }
64
75
  }
65
- if (error.response && error.response.data && error.response.data.message) {
66
- const customError = new Error(error.response.data.message);
67
- customError.response = error.response;
68
- return Promise.reject(customError);
69
- }
70
- return Promise.reject(error);
76
+ return Promise.reject(this.createUserFriendlyError(error));
71
77
  }
72
78
  );
73
79
  }
80
+ /**
81
+ * Creates a user-friendly error message from an Axios error
82
+ */
83
+ createUserFriendlyError(error) {
84
+ if (error instanceof Error && !error.isAxiosError) {
85
+ return error;
86
+ }
87
+ let message;
88
+ let statusCode;
89
+ if (axios.isAxiosError(error)) {
90
+ statusCode = error.response?.status;
91
+ const responseData = error.response?.data;
92
+ if (responseData?.message) {
93
+ message = responseData.message;
94
+ } else if (!error.response) {
95
+ if (error.code === "ECONNABORTED" || error.message.includes("timeout")) {
96
+ message = ERROR_MESSAGES.TIMEOUT;
97
+ } else if (error.code === "ERR_NETWORK" || error.message === "Network Error") {
98
+ message = ERROR_MESSAGES.NETWORK_ERROR;
99
+ } else {
100
+ message = ERROR_MESSAGES.NETWORK_ERROR;
101
+ }
102
+ } else {
103
+ switch (statusCode) {
104
+ case 400:
105
+ message = responseData?.message || "Invalid request. Please check your input.";
106
+ break;
107
+ case 401:
108
+ message = responseData?.message || ERROR_MESSAGES.UNAUTHORIZED;
109
+ break;
110
+ case 403:
111
+ message = responseData?.message || ERROR_MESSAGES.FORBIDDEN;
112
+ break;
113
+ case 404:
114
+ message = responseData?.message || ERROR_MESSAGES.NOT_FOUND;
115
+ break;
116
+ case 429:
117
+ message = ERROR_MESSAGES.RATE_LIMITED;
118
+ break;
119
+ case 500:
120
+ case 502:
121
+ case 503:
122
+ case 504:
123
+ message = ERROR_MESSAGES.SERVER_ERROR;
124
+ break;
125
+ default:
126
+ message = responseData?.message || ERROR_MESSAGES.UNKNOWN;
127
+ }
128
+ }
129
+ } else {
130
+ message = error?.message || ERROR_MESSAGES.UNKNOWN;
131
+ }
132
+ const customError = new Error(message);
133
+ customError.response = error?.response;
134
+ customError.statusCode = statusCode;
135
+ customError.originalError = error;
136
+ return customError;
137
+ }
74
138
  async get(endpoint, headers) {
75
139
  const response = await this.axiosInstance.get(endpoint, { headers });
76
140
  return response.data;
@@ -121,16 +185,134 @@ var HttpClient = class {
121
185
  }
122
186
  }
123
187
  };
188
+ var SocketService = class {
189
+ constructor(config) {
190
+ this.socket = null;
191
+ this.token = null;
192
+ this.eventHandlers = /* @__PURE__ */ new Map();
193
+ this.isConnecting = false;
194
+ this.config = {
195
+ autoConnect: false,
196
+ reconnection: true,
197
+ reconnectionAttempts: 5,
198
+ reconnectionDelay: 1e3,
199
+ ...config
200
+ };
201
+ }
202
+ connect(token) {
203
+ if (this.socket?.connected || this.isConnecting) {
204
+ return;
205
+ }
206
+ this.token = token;
207
+ this.isConnecting = true;
208
+ this.socket = io(this.config.baseUrl, {
209
+ auth: { token },
210
+ autoConnect: true,
211
+ reconnection: this.config.reconnection,
212
+ reconnectionAttempts: this.config.reconnectionAttempts,
213
+ reconnectionDelay: this.config.reconnectionDelay,
214
+ transports: ["websocket", "polling"]
215
+ });
216
+ this.setupEventListeners();
217
+ }
218
+ setupEventListeners() {
219
+ if (!this.socket) return;
220
+ this.socket.on("connect", () => {
221
+ this.isConnecting = false;
222
+ console.log("[Auth SDK] Socket connected");
223
+ this.emit("connected", {});
224
+ });
225
+ this.socket.on("disconnect", (reason) => {
226
+ console.log("[Auth SDK] Socket disconnected:", reason);
227
+ this.emit("disconnected", { reason });
228
+ });
229
+ this.socket.on("connect_error", (error) => {
230
+ this.isConnecting = false;
231
+ console.error("[Auth SDK] Socket connection error:", error.message);
232
+ this.emit("error", { error: error.message });
233
+ });
234
+ this.socket.on("user:updated", (data) => {
235
+ this.emit("user:updated", data);
236
+ });
237
+ this.socket.on("session:revoked", (data) => {
238
+ this.emit("session:revoked", data);
239
+ });
240
+ this.socket.on("session:all-revoked", () => {
241
+ this.emit("session:all-revoked", {});
242
+ });
243
+ this.socket.on("auth:password-changed", () => {
244
+ this.emit("auth:password-changed", {});
245
+ });
246
+ this.socket.on("auth:2fa-changed", (data) => {
247
+ this.emit("auth:2fa-changed", data);
248
+ });
249
+ this.socket.on("user:refresh", () => {
250
+ this.emit("user:refresh", {});
251
+ });
252
+ }
253
+ disconnect() {
254
+ if (this.socket) {
255
+ this.socket.disconnect();
256
+ this.socket = null;
257
+ this.token = null;
258
+ this.isConnecting = false;
259
+ }
260
+ }
261
+ isConnected() {
262
+ return this.socket?.connected ?? false;
263
+ }
264
+ // Event subscription
265
+ on(event, handler) {
266
+ if (!this.eventHandlers.has(event)) {
267
+ this.eventHandlers.set(event, /* @__PURE__ */ new Set());
268
+ }
269
+ this.eventHandlers.get(event).add(handler);
270
+ return () => {
271
+ this.eventHandlers.get(event)?.delete(handler);
272
+ };
273
+ }
274
+ off(event, handler) {
275
+ if (handler) {
276
+ this.eventHandlers.get(event)?.delete(handler);
277
+ } else {
278
+ this.eventHandlers.delete(event);
279
+ }
280
+ }
281
+ emit(event, data) {
282
+ const handlers = this.eventHandlers.get(event);
283
+ if (handlers) {
284
+ handlers.forEach((handler) => {
285
+ try {
286
+ handler(data);
287
+ } catch (error) {
288
+ console.error(`[Auth SDK] Error in event handler for ${event}:`, error);
289
+ }
290
+ });
291
+ }
292
+ }
293
+ // Request fresh user data from server
294
+ requestUserRefresh() {
295
+ if (this.socket?.connected) {
296
+ this.socket.emit("request:user");
297
+ }
298
+ }
299
+ };
124
300
  var AuthService = class {
301
+ // 5 minutes cache
125
302
  constructor(config) {
126
303
  this.token = null;
127
304
  this.upfilesClient = null;
305
+ this.cachedUser = null;
306
+ this.userCacheTimestamp = 0;
307
+ this.USER_CACHE_TTL = 5 * 60 * 1e3;
128
308
  this.config = {
129
309
  localStorageKey: "auth_token",
130
310
  csrfEnabled: true,
311
+ enableSocket: true,
131
312
  ...config
132
313
  };
133
314
  this.httpClient = new HttpClient(this.config.baseUrl);
315
+ this.socketService = new SocketService({ baseUrl: this.config.baseUrl });
134
316
  this.loadTokenFromStorage();
135
317
  if (this.config.upfilesConfig) {
136
318
  this.upfilesClient = new UpfilesClient({
@@ -147,6 +329,9 @@ var AuthService = class {
147
329
  this.httpClient.setFrontendBaseUrl(frontendBaseUrl);
148
330
  }
149
331
  }
332
+ if (this.token && this.config.enableSocket !== false) {
333
+ this.connectSocket();
334
+ }
150
335
  }
151
336
  loadTokenFromStorage() {
152
337
  if (typeof window !== "undefined" && this.config.localStorageKey) {
@@ -179,6 +364,57 @@ var AuthService = class {
179
364
  }
180
365
  }
181
366
  }
367
+ // Socket connection management
368
+ connectSocket() {
369
+ if (this.token && this.config.enableSocket !== false && typeof window !== "undefined") {
370
+ this.socketService.connect(this.token);
371
+ }
372
+ }
373
+ disconnectSocket() {
374
+ this.socketService.disconnect();
375
+ }
376
+ // Socket event subscription
377
+ onUserUpdated(handler) {
378
+ return this.socketService.on("user:updated", (data) => {
379
+ if (data.user) {
380
+ this.cachedUser = data.user;
381
+ this.userCacheTimestamp = Date.now();
382
+ }
383
+ handler(data);
384
+ });
385
+ }
386
+ onSessionRevoked(handler) {
387
+ return this.socketService.on("session:revoked", handler);
388
+ }
389
+ onAllSessionsRevoked(handler) {
390
+ return this.socketService.on("session:all-revoked", handler);
391
+ }
392
+ onPasswordChanged(handler) {
393
+ return this.socketService.on("auth:password-changed", handler);
394
+ }
395
+ on2FAChanged(handler) {
396
+ return this.socketService.on("auth:2fa-changed", handler);
397
+ }
398
+ onSocketConnected(handler) {
399
+ return this.socketService.on("connected", handler);
400
+ }
401
+ onSocketDisconnected(handler) {
402
+ return this.socketService.on("disconnected", handler);
403
+ }
404
+ onSocketError(handler) {
405
+ return this.socketService.on("error", handler);
406
+ }
407
+ isSocketConnected() {
408
+ return this.socketService.isConnected();
409
+ }
410
+ // Cache management
411
+ clearUserCache() {
412
+ this.cachedUser = null;
413
+ this.userCacheTimestamp = 0;
414
+ }
415
+ isCacheValid() {
416
+ return this.cachedUser !== null && Date.now() - this.userCacheTimestamp < this.USER_CACHE_TTL;
417
+ }
182
418
  isAuthenticated() {
183
419
  return !!this.token;
184
420
  }
@@ -244,6 +480,11 @@ var AuthService = class {
244
480
  this.token = response.token;
245
481
  this.httpClient.setAuthToken(response.token);
246
482
  this.saveTokenToStorage(response.token);
483
+ if (response.user) {
484
+ this.cachedUser = response.user;
485
+ this.userCacheTimestamp = Date.now();
486
+ }
487
+ this.connectSocket();
247
488
  return response;
248
489
  }
249
490
  if (response.success && (response.message === "OTP sent to your email." || response.message === "OTP sent to your phone number.")) {
@@ -253,6 +494,11 @@ var AuthService = class {
253
494
  this.token = response.token;
254
495
  this.httpClient.setAuthToken(response.token);
255
496
  this.saveTokenToStorage(response.token);
497
+ if (response.user) {
498
+ this.cachedUser = response.user;
499
+ this.userCacheTimestamp = Date.now();
500
+ }
501
+ this.connectSocket();
256
502
  return response;
257
503
  }
258
504
  throw new Error(response.message || "Login failed");
@@ -296,21 +542,29 @@ var AuthService = class {
296
542
  }
297
543
  }
298
544
  async logout() {
545
+ this.disconnectSocket();
299
546
  try {
300
547
  await this.httpClient.post("/api/v1/auth/logout", {});
301
548
  } catch (error) {
302
549
  console.warn("Failed to call logout endpoint:", error);
303
550
  }
304
551
  this.token = null;
552
+ this.cachedUser = null;
553
+ this.userCacheTimestamp = 0;
305
554
  this.httpClient.removeAuthToken();
306
555
  this.httpClient.removeCsrfToken();
307
556
  this.removeTokenFromStorage();
308
557
  }
309
- async getProfile() {
558
+ async getProfile(forceRefresh = false) {
310
559
  if (!this.token) {
311
560
  throw new Error("Not authenticated");
312
561
  }
562
+ if (!forceRefresh && this.isCacheValid() && this.cachedUser) {
563
+ return this.cachedUser;
564
+ }
313
565
  const response = await this.httpClient.get("/api/v1/user/me");
566
+ this.cachedUser = response.user;
567
+ this.userCacheTimestamp = Date.now();
314
568
  return response.user;
315
569
  }
316
570
  async updateProfile(data) {
@@ -474,6 +728,16 @@ var AuthService = class {
474
728
  );
475
729
  return response;
476
730
  }
731
+ async adminCreateUser(data) {
732
+ if (!this.token) {
733
+ throw new Error("Not authenticated");
734
+ }
735
+ const response = await this.httpClient.post(
736
+ "/api/v1/admin/create-user",
737
+ data
738
+ );
739
+ return response;
740
+ }
477
741
  async adminVerifyUser(userId) {
478
742
  if (!this.token) {
479
743
  throw new Error("Not authenticated");
@@ -709,21 +973,49 @@ var AuthProvider = ({ children, config }) => {
709
973
  const [user, setUser] = useState(null);
710
974
  const [isLoaded, setIsLoaded] = useState(false);
711
975
  const [loading, setLoading] = useState(false);
712
- const checkAuthStatus = useCallback(async () => {
976
+ const [isSocketConnected, setIsSocketConnected] = useState(false);
977
+ useEffect(() => {
978
+ const unsubUserUpdated = authService.onUserUpdated(({ user: updatedUser }) => {
979
+ if (updatedUser) {
980
+ setUser(updatedUser);
981
+ }
982
+ });
983
+ const unsubSessionRevoked = authService.onSessionRevoked(() => {
984
+ authService.logout().then(() => {
985
+ setUser(null);
986
+ });
987
+ });
988
+ const unsubAllSessionsRevoked = authService.onAllSessionsRevoked(() => {
989
+ authService.logout().then(() => {
990
+ setUser(null);
991
+ });
992
+ });
993
+ const unsubPasswordChanged = authService.onPasswordChanged(() => {
994
+ authService.logout().then(() => {
995
+ setUser(null);
996
+ });
997
+ });
998
+ const unsubConnected = authService.onSocketConnected(() => {
999
+ setIsSocketConnected(true);
1000
+ });
1001
+ const unsubDisconnected = authService.onSocketDisconnected(() => {
1002
+ setIsSocketConnected(false);
1003
+ });
1004
+ return () => {
1005
+ unsubUserUpdated();
1006
+ unsubSessionRevoked();
1007
+ unsubAllSessionsRevoked();
1008
+ unsubPasswordChanged();
1009
+ unsubConnected();
1010
+ unsubDisconnected();
1011
+ };
1012
+ }, [authService]);
1013
+ useEffect(() => {
713
1014
  const authenticated = authService.isAuthenticated();
714
1015
  if (authenticated) {
715
- try {
716
- const freshUser = await authService.getProfile();
717
- setUser(freshUser);
718
- } catch (error) {
719
- console.error("Failed to fetch fresh user profile, falling back to token:", error);
720
- try {
721
- const currentUser = authService.getCurrentUser();
722
- setUser(currentUser);
723
- } catch (fallbackError) {
724
- console.error("Failed to get current user from token:", fallbackError);
725
- setUser(null);
726
- }
1016
+ const currentUser = authService.getCurrentUser();
1017
+ if (currentUser) {
1018
+ setUser(currentUser);
727
1019
  }
728
1020
  } else {
729
1021
  setUser(null);
@@ -731,8 +1023,21 @@ var AuthProvider = ({ children, config }) => {
731
1023
  setIsLoaded(true);
732
1024
  }, [authService]);
733
1025
  useEffect(() => {
734
- checkAuthStatus();
735
- }, [checkAuthStatus]);
1026
+ if (!isLoaded) return;
1027
+ const authenticated = authService.isAuthenticated();
1028
+ if (!authenticated) return;
1029
+ const fetchFreshUser = async () => {
1030
+ try {
1031
+ const freshUser = await authService.getProfile();
1032
+ setUser(freshUser);
1033
+ } catch (error) {
1034
+ console.warn("[Auth SDK] Failed to fetch fresh user profile:", error);
1035
+ }
1036
+ };
1037
+ if (isSocketConnected) {
1038
+ fetchFreshUser();
1039
+ }
1040
+ }, [authService, isLoaded, isSocketConnected]);
736
1041
  const signIn = useCallback(async (data) => {
737
1042
  setLoading(true);
738
1043
  try {
@@ -954,6 +1259,7 @@ var AuthProvider = ({ children, config }) => {
954
1259
  isLoaded,
955
1260
  isSignedIn: !!user,
956
1261
  loading,
1262
+ isSocketConnected,
957
1263
  signIn,
958
1264
  signUp,
959
1265
  signOut,
@@ -4462,11 +4768,11 @@ var ChangePassword = ({ onSuccess, appearance }) => {
4462
4768
 
4463
4769
  // src/react/components/utils/injectModalStyles.ts
4464
4770
  var injectModalStyles = () => {
4465
- if (document.getElementById("ttf-auth-modal-styles")) {
4771
+ if (document.getElementById("ktw-auth-modal-styles")) {
4466
4772
  return;
4467
4773
  }
4468
4774
  const styleElement = document.createElement("style");
4469
- styleElement.id = "ttf-auth-modal-styles";
4775
+ styleElement.id = "ktw-auth-modal-styles";
4470
4776
  styleElement.textContent = `
4471
4777
  /* ImageManager Modal Styles - Critical for proper modal display */
4472
4778
  /* Radix UI Dialog styles - Force visibility */