@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,7 +1,18 @@
1
1
  import axios from 'axios';
2
+ import { io } from 'socket.io-client';
2
3
  import { UpfilesClient } from '@thetechfossil/upfiles';
3
4
 
4
5
  // src/core/http-client.ts
6
+ var ERROR_MESSAGES = {
7
+ NETWORK_ERROR: "Unable to connect to the server. Please check your internet connection and try again.",
8
+ TIMEOUT: "The request took too long. Please try again.",
9
+ SERVER_ERROR: "Something went wrong on our end. Please try again later.",
10
+ UNAUTHORIZED: "Your session has expired. Please log in again.",
11
+ FORBIDDEN: "You do not have permission to perform this action.",
12
+ NOT_FOUND: "The requested resource was not found.",
13
+ RATE_LIMITED: "Too many requests. Please wait a moment and try again.",
14
+ UNKNOWN: "An unexpected error occurred. Please try again."
15
+ };
5
16
  var HttpClient = class {
6
17
  constructor(baseUrl, defaultHeaders = {}) {
7
18
  this.csrfToken = null;
@@ -36,7 +47,7 @@ var HttpClient = class {
36
47
  }
37
48
  return config;
38
49
  },
39
- (error) => Promise.reject(error)
50
+ (error) => Promise.reject(this.createUserFriendlyError(error))
40
51
  );
41
52
  this.axiosInstance.interceptors.response.use(
42
53
  (response) => response,
@@ -51,18 +62,71 @@ var HttpClient = class {
51
62
  }
52
63
  return this.axiosInstance(originalRequest);
53
64
  } catch (refreshError) {
54
- return Promise.reject(refreshError);
65
+ return Promise.reject(this.createUserFriendlyError(refreshError));
55
66
  }
56
67
  }
57
- if (error.response && error.response.data && error.response.data.message) {
58
- const customError = new Error(error.response.data.message);
59
- customError.response = error.response;
60
- return Promise.reject(customError);
61
- }
62
- return Promise.reject(error);
68
+ return Promise.reject(this.createUserFriendlyError(error));
63
69
  }
64
70
  );
65
71
  }
72
+ /**
73
+ * Creates a user-friendly error message from an Axios error
74
+ */
75
+ createUserFriendlyError(error) {
76
+ if (error instanceof Error && !error.isAxiosError) {
77
+ return error;
78
+ }
79
+ let message;
80
+ let statusCode;
81
+ if (axios.isAxiosError(error)) {
82
+ statusCode = error.response?.status;
83
+ const responseData = error.response?.data;
84
+ if (responseData?.message) {
85
+ message = responseData.message;
86
+ } else if (!error.response) {
87
+ if (error.code === "ECONNABORTED" || error.message.includes("timeout")) {
88
+ message = ERROR_MESSAGES.TIMEOUT;
89
+ } else if (error.code === "ERR_NETWORK" || error.message === "Network Error") {
90
+ message = ERROR_MESSAGES.NETWORK_ERROR;
91
+ } else {
92
+ message = ERROR_MESSAGES.NETWORK_ERROR;
93
+ }
94
+ } else {
95
+ switch (statusCode) {
96
+ case 400:
97
+ message = responseData?.message || "Invalid request. Please check your input.";
98
+ break;
99
+ case 401:
100
+ message = responseData?.message || ERROR_MESSAGES.UNAUTHORIZED;
101
+ break;
102
+ case 403:
103
+ message = responseData?.message || ERROR_MESSAGES.FORBIDDEN;
104
+ break;
105
+ case 404:
106
+ message = responseData?.message || ERROR_MESSAGES.NOT_FOUND;
107
+ break;
108
+ case 429:
109
+ message = ERROR_MESSAGES.RATE_LIMITED;
110
+ break;
111
+ case 500:
112
+ case 502:
113
+ case 503:
114
+ case 504:
115
+ message = ERROR_MESSAGES.SERVER_ERROR;
116
+ break;
117
+ default:
118
+ message = responseData?.message || ERROR_MESSAGES.UNKNOWN;
119
+ }
120
+ }
121
+ } else {
122
+ message = error?.message || ERROR_MESSAGES.UNKNOWN;
123
+ }
124
+ const customError = new Error(message);
125
+ customError.response = error?.response;
126
+ customError.statusCode = statusCode;
127
+ customError.originalError = error;
128
+ return customError;
129
+ }
66
130
  async get(endpoint, headers) {
67
131
  const response = await this.axiosInstance.get(endpoint, { headers });
68
132
  return response.data;
@@ -113,16 +177,134 @@ var HttpClient = class {
113
177
  }
114
178
  }
115
179
  };
180
+ var SocketService = class {
181
+ constructor(config) {
182
+ this.socket = null;
183
+ this.token = null;
184
+ this.eventHandlers = /* @__PURE__ */ new Map();
185
+ this.isConnecting = false;
186
+ this.config = {
187
+ autoConnect: false,
188
+ reconnection: true,
189
+ reconnectionAttempts: 5,
190
+ reconnectionDelay: 1e3,
191
+ ...config
192
+ };
193
+ }
194
+ connect(token) {
195
+ if (this.socket?.connected || this.isConnecting) {
196
+ return;
197
+ }
198
+ this.token = token;
199
+ this.isConnecting = true;
200
+ this.socket = io(this.config.baseUrl, {
201
+ auth: { token },
202
+ autoConnect: true,
203
+ reconnection: this.config.reconnection,
204
+ reconnectionAttempts: this.config.reconnectionAttempts,
205
+ reconnectionDelay: this.config.reconnectionDelay,
206
+ transports: ["websocket", "polling"]
207
+ });
208
+ this.setupEventListeners();
209
+ }
210
+ setupEventListeners() {
211
+ if (!this.socket) return;
212
+ this.socket.on("connect", () => {
213
+ this.isConnecting = false;
214
+ console.log("[Auth SDK] Socket connected");
215
+ this.emit("connected", {});
216
+ });
217
+ this.socket.on("disconnect", (reason) => {
218
+ console.log("[Auth SDK] Socket disconnected:", reason);
219
+ this.emit("disconnected", { reason });
220
+ });
221
+ this.socket.on("connect_error", (error) => {
222
+ this.isConnecting = false;
223
+ console.error("[Auth SDK] Socket connection error:", error.message);
224
+ this.emit("error", { error: error.message });
225
+ });
226
+ this.socket.on("user:updated", (data) => {
227
+ this.emit("user:updated", data);
228
+ });
229
+ this.socket.on("session:revoked", (data) => {
230
+ this.emit("session:revoked", data);
231
+ });
232
+ this.socket.on("session:all-revoked", () => {
233
+ this.emit("session:all-revoked", {});
234
+ });
235
+ this.socket.on("auth:password-changed", () => {
236
+ this.emit("auth:password-changed", {});
237
+ });
238
+ this.socket.on("auth:2fa-changed", (data) => {
239
+ this.emit("auth:2fa-changed", data);
240
+ });
241
+ this.socket.on("user:refresh", () => {
242
+ this.emit("user:refresh", {});
243
+ });
244
+ }
245
+ disconnect() {
246
+ if (this.socket) {
247
+ this.socket.disconnect();
248
+ this.socket = null;
249
+ this.token = null;
250
+ this.isConnecting = false;
251
+ }
252
+ }
253
+ isConnected() {
254
+ return this.socket?.connected ?? false;
255
+ }
256
+ // Event subscription
257
+ on(event, handler) {
258
+ if (!this.eventHandlers.has(event)) {
259
+ this.eventHandlers.set(event, /* @__PURE__ */ new Set());
260
+ }
261
+ this.eventHandlers.get(event).add(handler);
262
+ return () => {
263
+ this.eventHandlers.get(event)?.delete(handler);
264
+ };
265
+ }
266
+ off(event, handler) {
267
+ if (handler) {
268
+ this.eventHandlers.get(event)?.delete(handler);
269
+ } else {
270
+ this.eventHandlers.delete(event);
271
+ }
272
+ }
273
+ emit(event, data) {
274
+ const handlers = this.eventHandlers.get(event);
275
+ if (handlers) {
276
+ handlers.forEach((handler) => {
277
+ try {
278
+ handler(data);
279
+ } catch (error) {
280
+ console.error(`[Auth SDK] Error in event handler for ${event}:`, error);
281
+ }
282
+ });
283
+ }
284
+ }
285
+ // Request fresh user data from server
286
+ requestUserRefresh() {
287
+ if (this.socket?.connected) {
288
+ this.socket.emit("request:user");
289
+ }
290
+ }
291
+ };
116
292
  var AuthService = class {
293
+ // 5 minutes cache
117
294
  constructor(config) {
118
295
  this.token = null;
119
296
  this.upfilesClient = null;
297
+ this.cachedUser = null;
298
+ this.userCacheTimestamp = 0;
299
+ this.USER_CACHE_TTL = 5 * 60 * 1e3;
120
300
  this.config = {
121
301
  localStorageKey: "auth_token",
122
302
  csrfEnabled: true,
303
+ enableSocket: true,
123
304
  ...config
124
305
  };
125
306
  this.httpClient = new HttpClient(this.config.baseUrl);
307
+ this.socketService = new SocketService({ baseUrl: this.config.baseUrl });
126
308
  this.loadTokenFromStorage();
127
309
  if (this.config.upfilesConfig) {
128
310
  this.upfilesClient = new UpfilesClient({
@@ -139,6 +321,9 @@ var AuthService = class {
139
321
  this.httpClient.setFrontendBaseUrl(frontendBaseUrl);
140
322
  }
141
323
  }
324
+ if (this.token && this.config.enableSocket !== false) {
325
+ this.connectSocket();
326
+ }
142
327
  }
143
328
  loadTokenFromStorage() {
144
329
  if (typeof window !== "undefined" && this.config.localStorageKey) {
@@ -171,6 +356,57 @@ var AuthService = class {
171
356
  }
172
357
  }
173
358
  }
359
+ // Socket connection management
360
+ connectSocket() {
361
+ if (this.token && this.config.enableSocket !== false && typeof window !== "undefined") {
362
+ this.socketService.connect(this.token);
363
+ }
364
+ }
365
+ disconnectSocket() {
366
+ this.socketService.disconnect();
367
+ }
368
+ // Socket event subscription
369
+ onUserUpdated(handler) {
370
+ return this.socketService.on("user:updated", (data) => {
371
+ if (data.user) {
372
+ this.cachedUser = data.user;
373
+ this.userCacheTimestamp = Date.now();
374
+ }
375
+ handler(data);
376
+ });
377
+ }
378
+ onSessionRevoked(handler) {
379
+ return this.socketService.on("session:revoked", handler);
380
+ }
381
+ onAllSessionsRevoked(handler) {
382
+ return this.socketService.on("session:all-revoked", handler);
383
+ }
384
+ onPasswordChanged(handler) {
385
+ return this.socketService.on("auth:password-changed", handler);
386
+ }
387
+ on2FAChanged(handler) {
388
+ return this.socketService.on("auth:2fa-changed", handler);
389
+ }
390
+ onSocketConnected(handler) {
391
+ return this.socketService.on("connected", handler);
392
+ }
393
+ onSocketDisconnected(handler) {
394
+ return this.socketService.on("disconnected", handler);
395
+ }
396
+ onSocketError(handler) {
397
+ return this.socketService.on("error", handler);
398
+ }
399
+ isSocketConnected() {
400
+ return this.socketService.isConnected();
401
+ }
402
+ // Cache management
403
+ clearUserCache() {
404
+ this.cachedUser = null;
405
+ this.userCacheTimestamp = 0;
406
+ }
407
+ isCacheValid() {
408
+ return this.cachedUser !== null && Date.now() - this.userCacheTimestamp < this.USER_CACHE_TTL;
409
+ }
174
410
  isAuthenticated() {
175
411
  return !!this.token;
176
412
  }
@@ -236,6 +472,11 @@ var AuthService = class {
236
472
  this.token = response.token;
237
473
  this.httpClient.setAuthToken(response.token);
238
474
  this.saveTokenToStorage(response.token);
475
+ if (response.user) {
476
+ this.cachedUser = response.user;
477
+ this.userCacheTimestamp = Date.now();
478
+ }
479
+ this.connectSocket();
239
480
  return response;
240
481
  }
241
482
  if (response.success && (response.message === "OTP sent to your email." || response.message === "OTP sent to your phone number.")) {
@@ -245,6 +486,11 @@ var AuthService = class {
245
486
  this.token = response.token;
246
487
  this.httpClient.setAuthToken(response.token);
247
488
  this.saveTokenToStorage(response.token);
489
+ if (response.user) {
490
+ this.cachedUser = response.user;
491
+ this.userCacheTimestamp = Date.now();
492
+ }
493
+ this.connectSocket();
248
494
  return response;
249
495
  }
250
496
  throw new Error(response.message || "Login failed");
@@ -288,21 +534,29 @@ var AuthService = class {
288
534
  }
289
535
  }
290
536
  async logout() {
537
+ this.disconnectSocket();
291
538
  try {
292
539
  await this.httpClient.post("/api/v1/auth/logout", {});
293
540
  } catch (error) {
294
541
  console.warn("Failed to call logout endpoint:", error);
295
542
  }
296
543
  this.token = null;
544
+ this.cachedUser = null;
545
+ this.userCacheTimestamp = 0;
297
546
  this.httpClient.removeAuthToken();
298
547
  this.httpClient.removeCsrfToken();
299
548
  this.removeTokenFromStorage();
300
549
  }
301
- async getProfile() {
550
+ async getProfile(forceRefresh = false) {
302
551
  if (!this.token) {
303
552
  throw new Error("Not authenticated");
304
553
  }
554
+ if (!forceRefresh && this.isCacheValid() && this.cachedUser) {
555
+ return this.cachedUser;
556
+ }
305
557
  const response = await this.httpClient.get("/api/v1/user/me");
558
+ this.cachedUser = response.user;
559
+ this.userCacheTimestamp = Date.now();
306
560
  return response.user;
307
561
  }
308
562
  async updateProfile(data) {
@@ -466,6 +720,16 @@ var AuthService = class {
466
720
  );
467
721
  return response;
468
722
  }
723
+ async adminCreateUser(data) {
724
+ if (!this.token) {
725
+ throw new Error("Not authenticated");
726
+ }
727
+ const response = await this.httpClient.post(
728
+ "/api/v1/admin/create-user",
729
+ data
730
+ );
731
+ return response;
732
+ }
469
733
  async adminVerifyUser(userId) {
470
734
  if (!this.token) {
471
735
  throw new Error("Not authenticated");
@@ -574,6 +838,135 @@ var AuthClient = class extends AuthService {
574
838
  }
575
839
  };
576
840
 
577
- export { AuthClient, AuthService, HttpClient };
841
+ // src/node/token-verifier.ts
842
+ var TokenVerifier = class {
843
+ constructor(config) {
844
+ this.cache = /* @__PURE__ */ new Map();
845
+ this.cleanupInterval = null;
846
+ this.config = {
847
+ cacheTTL: 6e4,
848
+ // 1 minute default
849
+ cacheEnabled: true,
850
+ ...config
851
+ };
852
+ if (this.config.cacheEnabled) {
853
+ this.startCleanup();
854
+ }
855
+ }
856
+ /**
857
+ * Verify a JWT token and get user data
858
+ * Returns cached user if available and valid
859
+ */
860
+ async verifyToken(token) {
861
+ if (!token) {
862
+ return null;
863
+ }
864
+ if (this.config.cacheEnabled) {
865
+ const cached = this.getFromCache(token);
866
+ if (cached) {
867
+ return cached;
868
+ }
869
+ }
870
+ try {
871
+ const response = await fetch(`${this.config.authServiceUrl}/api/v1/user/me`, {
872
+ headers: {
873
+ "Authorization": `Bearer ${token}`,
874
+ "Content-Type": "application/json"
875
+ }
876
+ });
877
+ if (!response.ok) {
878
+ this.cache.delete(token);
879
+ return null;
880
+ }
881
+ const data = await response.json();
882
+ const user = data.user || null;
883
+ if (user && this.config.cacheEnabled) {
884
+ this.setCache(token, user);
885
+ }
886
+ return user;
887
+ } catch (error) {
888
+ console.error("[TokenVerifier] Auth service verification failed:", error);
889
+ return null;
890
+ }
891
+ }
892
+ /**
893
+ * Invalidate cache for a specific token
894
+ */
895
+ invalidateToken(token) {
896
+ this.cache.delete(token);
897
+ }
898
+ /**
899
+ * Clear all cached tokens
900
+ */
901
+ clearCache() {
902
+ this.cache.clear();
903
+ }
904
+ /**
905
+ * Get cache statistics
906
+ */
907
+ getCacheStats() {
908
+ return {
909
+ size: this.cache.size,
910
+ enabled: this.config.cacheEnabled,
911
+ ttl: this.config.cacheTTL
912
+ };
913
+ }
914
+ /**
915
+ * Stop the cleanup interval (call when shutting down)
916
+ */
917
+ destroy() {
918
+ if (this.cleanupInterval) {
919
+ clearInterval(this.cleanupInterval);
920
+ this.cleanupInterval = null;
921
+ }
922
+ this.cache.clear();
923
+ }
924
+ getFromCache(token) {
925
+ const entry = this.cache.get(token);
926
+ if (!entry) {
927
+ return null;
928
+ }
929
+ if (Date.now() > entry.expiresAt) {
930
+ this.cache.delete(token);
931
+ return null;
932
+ }
933
+ return entry.user;
934
+ }
935
+ setCache(token, user) {
936
+ this.cache.set(token, {
937
+ user,
938
+ expiresAt: Date.now() + this.config.cacheTTL
939
+ });
940
+ }
941
+ startCleanup() {
942
+ this.cleanupInterval = setInterval(() => {
943
+ const now = Date.now();
944
+ for (const [token, entry] of this.cache.entries()) {
945
+ if (now > entry.expiresAt) {
946
+ this.cache.delete(token);
947
+ }
948
+ }
949
+ }, 6e4);
950
+ if (this.cleanupInterval && this.cleanupInterval.unref) {
951
+ this.cleanupInterval.unref();
952
+ }
953
+ }
954
+ };
955
+ var defaultVerifier = null;
956
+ function createTokenVerifier(config) {
957
+ defaultVerifier = new TokenVerifier(config);
958
+ return defaultVerifier;
959
+ }
960
+ function getTokenVerifier() {
961
+ if (!defaultVerifier) {
962
+ throw new Error("TokenVerifier not initialized. Call createTokenVerifier() first.");
963
+ }
964
+ return defaultVerifier;
965
+ }
966
+ async function verifyToken(token) {
967
+ return getTokenVerifier().verifyToken(token);
968
+ }
969
+
970
+ export { AuthClient, AuthService, HttpClient, TokenVerifier, createTokenVerifier, getTokenVerifier, verifyToken };
578
971
  //# sourceMappingURL=index.node.mjs.map
579
972
  //# sourceMappingURL=index.node.mjs.map