@explorins/pers-sdk 1.2.4 → 1.2.6

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 (97) hide show
  1. package/dist/analytics.cjs +6 -0
  2. package/dist/analytics.cjs.map +1 -1
  3. package/dist/analytics.js +2 -0
  4. package/dist/analytics.js.map +1 -1
  5. package/dist/business.cjs +6 -0
  6. package/dist/business.cjs.map +1 -1
  7. package/dist/business.js +2 -0
  8. package/dist/business.js.map +1 -1
  9. package/dist/campaign.cjs +6 -0
  10. package/dist/campaign.cjs.map +1 -1
  11. package/dist/campaign.js +2 -0
  12. package/dist/campaign.js.map +1 -1
  13. package/dist/{auth-admin/api/auth-admin-api.d.ts → core/auth/api/auth-api.d.ts} +7 -7
  14. package/dist/core/auth/api/auth-api.d.ts.map +1 -0
  15. package/dist/core/auth/auth-provider.interface.d.ts +25 -1
  16. package/dist/core/auth/auth-provider.interface.d.ts.map +1 -1
  17. package/dist/core/auth/create-auth-provider.d.ts +3 -3
  18. package/dist/core/auth/create-auth-provider.d.ts.map +1 -1
  19. package/dist/core/auth/index.d.ts +38 -0
  20. package/dist/core/auth/index.d.ts.map +1 -0
  21. package/dist/core/auth/services/auth-service.d.ts +49 -0
  22. package/dist/core/auth/services/auth-service.d.ts.map +1 -0
  23. package/dist/core/auth/simple-sdk-auth-provider.d.ts +27 -0
  24. package/dist/core/auth/simple-sdk-auth-provider.d.ts.map +1 -0
  25. package/dist/core/index.d.ts +1 -2
  26. package/dist/core/index.d.ts.map +1 -1
  27. package/dist/core/pers-api-client.d.ts +25 -3
  28. package/dist/core/pers-api-client.d.ts.map +1 -1
  29. package/dist/core.cjs +470 -156
  30. package/dist/core.cjs.map +1 -1
  31. package/dist/core.js +464 -157
  32. package/dist/core.js.map +1 -1
  33. package/dist/donation.cjs +6 -0
  34. package/dist/donation.cjs.map +1 -1
  35. package/dist/donation.js +2 -0
  36. package/dist/donation.js.map +1 -1
  37. package/dist/index.cjs +523 -346
  38. package/dist/index.cjs.map +1 -1
  39. package/dist/index.d.ts +0 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +518 -345
  42. package/dist/index.js.map +1 -1
  43. package/dist/package.json +5 -5
  44. package/dist/payment.cjs +6 -0
  45. package/dist/payment.cjs.map +1 -1
  46. package/dist/payment.js +2 -0
  47. package/dist/payment.js.map +1 -1
  48. package/dist/redemption/api/redemption-api.d.ts +26 -64
  49. package/dist/redemption/api/redemption-api.d.ts.map +1 -1
  50. package/dist/redemption/services/redemption-service.d.ts +21 -3
  51. package/dist/redemption/services/redemption-service.d.ts.map +1 -1
  52. package/dist/redemption.cjs +61 -94
  53. package/dist/redemption.cjs.map +1 -1
  54. package/dist/redemption.js +57 -94
  55. package/dist/redemption.js.map +1 -1
  56. package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts +1 -1
  57. package/dist/shared/interfaces/pers-shared-lib.interfaces.d.ts.map +1 -1
  58. package/dist/tenant.cjs +6 -0
  59. package/dist/tenant.cjs.map +1 -1
  60. package/dist/tenant.js +2 -0
  61. package/dist/tenant.js.map +1 -1
  62. package/dist/token.cjs +6 -0
  63. package/dist/token.cjs.map +1 -1
  64. package/dist/token.js +2 -0
  65. package/dist/token.js.map +1 -1
  66. package/dist/transaction.cjs +4 -0
  67. package/dist/transaction.cjs.map +1 -1
  68. package/dist/transaction.js +1 -0
  69. package/dist/transaction.js.map +1 -1
  70. package/dist/user-status.cjs +6 -0
  71. package/dist/user-status.cjs.map +1 -1
  72. package/dist/user-status.js +2 -0
  73. package/dist/user-status.js.map +1 -1
  74. package/dist/user.cjs +6 -0
  75. package/dist/user.cjs.map +1 -1
  76. package/dist/user.js +2 -0
  77. package/dist/user.js.map +1 -1
  78. package/dist/web3-chain.cjs +5 -0
  79. package/dist/web3-chain.cjs.map +1 -1
  80. package/dist/web3-chain.js +1 -0
  81. package/dist/web3-chain.js.map +1 -1
  82. package/dist/web3.cjs +1 -0
  83. package/dist/web3.cjs.map +1 -1
  84. package/dist/web3.js +1 -0
  85. package/dist/web3.js.map +1 -1
  86. package/package.json +5 -5
  87. package/dist/auth-admin/api/auth-admin-api.d.ts.map +0 -1
  88. package/dist/auth-admin/index.d.ts +0 -26
  89. package/dist/auth-admin/index.d.ts.map +0 -1
  90. package/dist/auth-admin/services/auth-admin-service.d.ts +0 -23
  91. package/dist/auth-admin/services/auth-admin-service.d.ts.map +0 -1
  92. package/dist/auth-admin.cjs +0 -101
  93. package/dist/auth-admin.cjs.map +0 -1
  94. package/dist/auth-admin.js +0 -97
  95. package/dist/auth-admin.js.map +0 -1
  96. package/dist/core/auth/simple-auth-config.interface.d.ts +0 -15
  97. package/dist/core/auth/simple-auth-config.interface.d.ts.map +0 -1
package/dist/index.cjs CHANGED
@@ -44,6 +44,241 @@ function mergeWithDefaults(config) {
44
44
  };
45
45
  }
46
46
 
47
+ /**
48
+ * Platform-Agnostic Auth Admin API Client
49
+ *
50
+ * Handles authentication and authorization admin operations using the PERS backend.
51
+ * Uses @explorins/pers-shared DTOs for consistency with backend.
52
+ *
53
+ * Note: Special header handling (bypass-auth-interceptor) may need to be implemented
54
+ * at the PersApiClient level or through a specialized auth client.
55
+ */
56
+ class AuthApi {
57
+ constructor(apiClient) {
58
+ this.apiClient = apiClient;
59
+ this.basePath = '/auth';
60
+ }
61
+ // ==========================================
62
+ // ADMIN AUTHENTICATION OPERATIONS
63
+ // ==========================================
64
+ /**
65
+ * ADMIN: Login tenant admin with JWT
66
+ * Note: JWT handling and auth bypass headers may need special implementation
67
+ */
68
+ async loginTenantAdmin(jwt) {
69
+ const body = {
70
+ authToken: jwt,
71
+ authType: persShared.AccountOwnerType.TENANT
72
+ };
73
+ return this.apiClient.post(`${this.basePath}/token`, body);
74
+ }
75
+ async loginUser(jwt) {
76
+ const body = {
77
+ authToken: jwt,
78
+ authType: persShared.AccountOwnerType.USER
79
+ };
80
+ return this.apiClient.post(`${this.basePath}/token`, body);
81
+ }
82
+ /**
83
+ * ADMIN: Refresh access token
84
+ * Note: Bypass header handling may need special implementation
85
+ */
86
+ async refreshAccessToken(refreshToken) {
87
+ return this.apiClient.post(`${this.basePath}/refresh`, { refreshToken });
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Platform-Agnostic Auth Admin Service
93
+ *
94
+ * Contains auth admin business logic and operations that work across platforms.
95
+ * No framework dependencies - pure TypeScript business logic.
96
+ *
97
+ * Focuses only on actual backend capabilities.
98
+ */
99
+ class AuthService {
100
+ constructor(authApi, authProvider) {
101
+ this.authApi = authApi;
102
+ this.authProvider = authProvider;
103
+ }
104
+ // ==========================================
105
+ // ADMIN AUTHENTICATION OPERATIONS
106
+ // ==========================================
107
+ /**
108
+ * ADMIN: Login tenant admin with JWT
109
+ * Automatically stores tokens if auth provider supports token storage
110
+ */
111
+ async loginTenantAdmin(jwt) {
112
+ const response = await this.authApi.loginTenantAdmin(jwt);
113
+ // Store tokens if auth provider supports it
114
+ if (this.authProvider && response.accessToken) {
115
+ await this.storeTokens(response.accessToken, response.refreshToken);
116
+ }
117
+ return response;
118
+ }
119
+ /**
120
+ * ADMIN: Login user with JWT
121
+ * Automatically stores tokens if auth provider supports token storage
122
+ */
123
+ async loginUser(jwt) {
124
+ const response = await this.authApi.loginUser(jwt);
125
+ // Store tokens if auth provider supports it
126
+ if (this.authProvider && response.accessToken) {
127
+ await this.storeTokens(response.accessToken, response.refreshToken);
128
+ }
129
+ return response;
130
+ }
131
+ /**
132
+ * ADMIN: Refresh access token
133
+ * Automatically stores new tokens if auth provider supports token storage
134
+ */
135
+ async refreshAccessToken(refreshToken) {
136
+ // Use provided refresh token or get from auth provider
137
+ const tokenToUse = refreshToken || (this.authProvider?.getRefreshToken ? await this.authProvider.getRefreshToken() : null);
138
+ if (!tokenToUse) {
139
+ throw new Error('No refresh token available for token refresh');
140
+ }
141
+ const response = await this.authApi.refreshAccessToken(tokenToUse);
142
+ // Store new tokens if auth provider supports it
143
+ if (this.authProvider && response.accessToken) {
144
+ await this.storeTokens(response.accessToken, response.refreshToken);
145
+ }
146
+ return response;
147
+ }
148
+ /**
149
+ * Automatic token refresh using stored refresh token
150
+ * Convenience method for 401 error handling
151
+ */
152
+ async autoRefreshToken() {
153
+ return this.refreshAccessToken(); // Uses stored refresh token
154
+ }
155
+ /**
156
+ * Clear stored tokens if auth provider supports it
157
+ */
158
+ async clearTokens() {
159
+ if (this.authProvider?.clearTokens) {
160
+ await this.authProvider.clearTokens();
161
+ }
162
+ }
163
+ /**
164
+ * Check if we have valid tokens for authentication
165
+ */
166
+ hasValidAuth() {
167
+ return this.authProvider?.hasValidToken?.() ?? false;
168
+ }
169
+ // ==========================================
170
+ // PRIVATE HELPERS
171
+ // ==========================================
172
+ /**
173
+ * Store tokens using auth provider if it supports token storage
174
+ */
175
+ async storeTokens(accessToken, refreshToken) {
176
+ if (!this.authProvider)
177
+ return;
178
+ try {
179
+ // Store access token
180
+ if (this.authProvider.setAccessToken) {
181
+ await this.authProvider.setAccessToken(accessToken);
182
+ }
183
+ // Store refresh token if provided and supported
184
+ if (refreshToken && this.authProvider.setRefreshToken) {
185
+ await this.authProvider.setRefreshToken(refreshToken);
186
+ }
187
+ }
188
+ catch (error) {
189
+ console.error('Failed to store tokens in auth provider:', error);
190
+ // Don't throw - token storage failure shouldn't break authentication
191
+ }
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Simple, self-contained authentication provider for SDK
197
+ *
198
+ * Manages tokens internally without external dependencies.
199
+ * Perfect for platform-agnostic usage.
200
+ */
201
+ class SimpleSdkAuthProvider {
202
+ constructor(projectKey) {
203
+ this.accessToken = null;
204
+ this.refreshToken = null;
205
+ this.projectKey = null;
206
+ this.authType = 'admin';
207
+ this.projectKey = projectKey || null;
208
+ // Try to load tokens from localStorage if available
209
+ this.loadTokensFromStorage();
210
+ }
211
+ async getToken() {
212
+ // Return stored access token
213
+ return this.accessToken;
214
+ }
215
+ async getProjectKey() {
216
+ return this.projectKey;
217
+ }
218
+ async onTokenExpired() {
219
+ console.log('SimpleSdkAuthProvider: Token expired, attempting refresh...');
220
+ if (!this.refreshToken) {
221
+ console.warn('SimpleSdkAuthProvider: No refresh token available');
222
+ throw new Error('No refresh token available for refresh');
223
+ }
224
+ // Note: Actual refresh logic would be handled by the SDK's AuthService
225
+ // This provider just manages token storage
226
+ console.warn('SimpleSdkAuthProvider: Token refresh should be handled by SDK AuthService');
227
+ }
228
+ // Token storage methods
229
+ async setAccessToken(token) {
230
+ this.accessToken = token;
231
+ this.saveTokenToStorage('pers_access_token', token);
232
+ }
233
+ async setRefreshToken(token) {
234
+ this.refreshToken = token;
235
+ this.saveTokenToStorage('pers_refresh_token', token);
236
+ }
237
+ async getRefreshToken() {
238
+ return this.refreshToken;
239
+ }
240
+ async clearTokens() {
241
+ this.accessToken = null;
242
+ this.refreshToken = null;
243
+ // Clear from storage
244
+ this.removeTokenFromStorage('pers_access_token');
245
+ this.removeTokenFromStorage('pers_refresh_token');
246
+ }
247
+ // Internal methods for token persistence
248
+ loadTokensFromStorage() {
249
+ if (typeof localStorage !== 'undefined') {
250
+ try {
251
+ this.accessToken = localStorage.getItem('pers_access_token');
252
+ this.refreshToken = localStorage.getItem('pers_refresh_token');
253
+ }
254
+ catch (error) {
255
+ console.error('Failed to load tokens from localStorage:', error);
256
+ }
257
+ }
258
+ else {
259
+ console.warn('localStorage is not available for token persistence');
260
+ }
261
+ }
262
+ saveTokenToStorage(key, value) {
263
+ if (typeof localStorage !== 'undefined') {
264
+ localStorage.setItem(key, value);
265
+ }
266
+ }
267
+ removeTokenFromStorage(key) {
268
+ if (typeof localStorage !== 'undefined') {
269
+ localStorage.removeItem(key);
270
+ }
271
+ }
272
+ // Utility methods for external integration
273
+ hasValidToken() {
274
+ const hasToken = !!this.accessToken;
275
+ return hasToken;
276
+ }
277
+ hasRefreshToken() {
278
+ return !!this.refreshToken;
279
+ }
280
+ }
281
+
47
282
  // packages/pers-sdk/src/core/pers-api-client.ts
48
283
  /**
49
284
  * PERS API Client - Core platform-agnostic client for PERS backend
@@ -54,8 +289,15 @@ class PersApiClient {
54
289
  this.config = config;
55
290
  // Merge user config with defaults (production + v2)
56
291
  this.mergedConfig = mergeWithDefaults(config);
292
+ // Auto-create auth provider if none provided
293
+ if (!this.mergedConfig.authProvider) {
294
+ this.mergedConfig.authProvider = new SimpleSdkAuthProvider(this.mergedConfig.apiProjectKey);
295
+ }
57
296
  // Build API root from merged environment and version
58
297
  this.apiRoot = buildApiRoot(this.mergedConfig.environment, this.mergedConfig.apiVersion);
298
+ // Initialize auth services for direct authentication
299
+ this.authApi = new AuthApi(this);
300
+ this.authService = new AuthService(this.authApi, this.mergedConfig.authProvider);
59
301
  }
60
302
  /**
61
303
  * Get request headers including auth token and project key
@@ -160,6 +402,45 @@ class PersApiClient {
160
402
  async delete(endpoint) {
161
403
  return this.request('DELETE', endpoint);
162
404
  }
405
+ // ==========================================
406
+ // AUTHENTICATION METHODS
407
+ // ==========================================
408
+ /**
409
+ * Login admin user with external JWT (e.g., Firebase)
410
+ * Automatically stores tokens in auth provider if available
411
+ */
412
+ async loginAdmin(externalJwt) {
413
+ return this.authService.loginTenantAdmin(externalJwt);
414
+ }
415
+ /**
416
+ * Login regular user with external JWT
417
+ * Automatically stores tokens in auth provider if available
418
+ */
419
+ async loginUser(externalJwt) {
420
+ return this.authService.loginUser(externalJwt);
421
+ }
422
+ /**
423
+ * Refresh access token using stored refresh token
424
+ */
425
+ async refreshToken() {
426
+ const refreshToken = await this.mergedConfig.authProvider?.getRefreshToken?.();
427
+ if (!refreshToken) {
428
+ throw new Error('No refresh token available');
429
+ }
430
+ return this.authService.refreshAccessToken(refreshToken);
431
+ }
432
+ /**
433
+ * Clear all stored authentication tokens
434
+ */
435
+ async clearAuth() {
436
+ return this.authService.clearTokens();
437
+ }
438
+ /**
439
+ * Check if user has valid authentication token
440
+ */
441
+ hasValidAuth() {
442
+ return this.mergedConfig.authProvider?.hasValidToken?.() || false;
443
+ }
163
444
  /**
164
445
  * Get current configuration (returns merged config)
165
446
  */
@@ -182,168 +463,48 @@ class PersApiError extends Error {
182
463
  this.name = 'PersApiError';
183
464
  }
184
465
  }
185
- /**
186
- * PERS API Client - Core platform-agnostic client for PERS backend
187
- */
188
- /*import { HttpClient, RequestOptions } from './abstractions/http-client';
189
- import { PersConfig, buildApiRoot, mergeWithDefaults } from './pers-config';
190
-
191
- export class PersApiClient {
192
- private readonly apiRoot: string;
193
- private readonly mergedConfig: ReturnType<typeof mergeWithDefaults>;
194
-
195
- constructor(
196
- private httpClient: HttpClient,
197
- private config: PersConfig
198
- ) {
199
- // Merge user config with defaults (production + v2)
200
- this.mergedConfig = mergeWithDefaults(config);
201
-
202
- // Build API root from merged environment and version
203
- this.apiRoot = buildApiRoot(this.mergedConfig.environment, this.mergedConfig.apiVersion);
204
- }
205
-
206
- /**
207
- * Get request headers including auth token and project key
208
- */
209
- /*private async getHeaders(): Promise<Record<string, string>> {
210
- const headers: Record<string, string> = {
211
- 'Content-Type': 'application/json',
212
- };
213
-
214
- // Add authentication token
215
- if (this.mergedConfig.authProvider) {
216
- const token = await this.mergedConfig.authProvider.getToken();
217
- if (token) {
218
- headers['Authorization'] = `Bearer ${token}`;
219
- }
220
- }
221
-
222
- // Add project key
223
- if (this.mergedConfig.authProvider) {
224
- const projectKey = await this.mergedConfig.authProvider.getProjectKey();
225
- if (projectKey) {
226
- headers['x-project-key'] = projectKey;
227
- }
228
- } else if(this.mergedConfig.apiProjectKey) {
229
- // Fallback to config project key if no auth provider
230
- headers['x-project-key'] = this.mergedConfig.apiProjectKey;
231
- }
232
-
233
- return headers;
234
- }
235
466
 
236
467
  /**
237
- * Make a request with proper headers, auth, and error handling
468
+ * Memory-based token storage (default)
238
469
  */
239
- /*private async request<T>(
240
- method: 'GET' | 'POST' | 'PUT' | 'DELETE',
241
- endpoint: string,
242
- body?: any,
243
- options?: { retryCount?: number }
244
- ): Promise<T> {
245
- const { retryCount = 0 } = options || {};
246
- const url = `${this.apiRoot}${endpoint}`;
247
-
248
- const requestOptions: RequestOptions = {
249
- headers: await this.getHeaders(),
250
- timeout: this.mergedConfig.timeout,
251
- };
252
-
253
- try {
254
- switch (method) {
255
- case 'GET':
256
- return await this.httpClient.get<T>(url, requestOptions);
257
- case 'POST':
258
- return await this.httpClient.post<T>(url, body, requestOptions);
259
- case 'PUT':
260
- return await this.httpClient.put<T>(url, body, requestOptions);
261
- case 'DELETE':
262
- return await this.httpClient.delete<T>(url, requestOptions);
263
- default:
264
- throw new Error(`Unsupported HTTP method: ${method}`);
265
- }
266
- } catch (error: any) {
267
- // Handle 401 errors with automatic token refresh
268
- if (error.status === 401 && retryCount === 0 && this.mergedConfig.authProvider?.onTokenExpired) {
269
- try {
270
- await this.mergedConfig.authProvider.onTokenExpired();
271
- // Retry once with refreshed token
272
- return this.request<T>(method, endpoint, body, { ...options, retryCount: 1 });
273
- } catch (refreshError) {
274
- throw new PersApiError(
275
- `Authentication refresh failed: ${refreshError}`,
276
- endpoint,
277
- method,
278
- 401
279
- );
280
- }
470
+ class MemoryTokenStorage {
471
+ constructor() {
472
+ this.storage = new Map();
473
+ }
474
+ async setItem(key, value) {
475
+ this.storage.set(key, value);
476
+ }
477
+ async getItem(key) {
478
+ return this.storage.get(key) || null;
479
+ }
480
+ async removeItem(key) {
481
+ this.storage.delete(key);
281
482
  }
282
-
283
- throw new PersApiError(
284
- `PERS API request failed: ${error.message || error}`,
285
- endpoint,
286
- method,
287
- error.status
288
- );
289
- }
290
- }
291
-
292
- /**
293
- * Generic GET request
294
- */
295
- /*async get<T>(endpoint: string): Promise<T> {
296
- return this.request<T>('GET', endpoint);
297
- }
298
-
299
- /**
300
- * Generic POST request
301
- */
302
- /*async post<T>(endpoint: string, body?: any): Promise<T> {
303
- return this.request<T>('POST', endpoint, body);
304
- }
305
-
306
- /**
307
- * Generic PUT request
308
- */
309
- /*async put<T>(endpoint: string, body?: any): Promise<T> {
310
- return this.request<T>('PUT', endpoint, body);
311
- }
312
-
313
- /**
314
- * Generic DELETE request
315
- */
316
- /*async delete<T>(endpoint: string): Promise<T> {
317
- return this.request<T>('DELETE', endpoint);
318
- }
319
-
320
- /**
321
- * Get current configuration (returns merged config)
322
- */
323
- /*getConfig(): ReturnType<typeof mergeWithDefaults> {
324
- return this.mergedConfig;
325
483
  }
326
-
327
484
  /**
328
- * Get original user configuration
485
+ * localStorage-based token storage (browser only)
329
486
  */
330
- /*getOriginalConfig(): PersConfig {
331
- return this.config;
332
- }
333
- }
334
-
335
- export class PersApiError extends Error {
336
- constructor(
337
- message: string,
338
- public endpoint: string,
339
- public method: string,
340
- public statusCode?: number
341
- ) {
342
- super(message);
343
- this.name = 'PersApiError';
487
+ class LocalStorageTokenStorage {
488
+ async setItem(key, value) {
489
+ if (typeof localStorage !== 'undefined') {
490
+ localStorage.setItem(key, value);
491
+ }
492
+ else {
493
+ throw new Error('localStorage is not available in this environment');
494
+ }
495
+ }
496
+ async getItem(key) {
497
+ if (typeof localStorage !== 'undefined') {
498
+ return localStorage.getItem(key);
499
+ }
500
+ return null;
501
+ }
502
+ async removeItem(key) {
503
+ if (typeof localStorage !== 'undefined') {
504
+ localStorage.removeItem(key);
505
+ }
506
+ }
344
507
  }
345
- }*/
346
-
347
508
  /**
348
509
  * Creates a platform-agnostic AuthProvider from simple configuration
349
510
  *
@@ -354,12 +515,33 @@ constructor(
354
515
  * - Token caching with refresh support
355
516
  * - Automatic token refresh on expiration
356
517
  * - Configurable token providers
357
- * - Platform-independent (no localStorage assumptions)
518
+ * - Token storage (memory, localStorage, custom)
519
+ * - Platform-independent
358
520
  *
359
521
  * @param config - Simple auth configuration
360
522
  * @returns AuthProvider implementation
361
523
  */
362
524
  function createAuthProvider(config) {
525
+ // Initialize token storage
526
+ let tokenStorage;
527
+ switch (config.tokenStorage) {
528
+ case 'localStorage':
529
+ tokenStorage = new LocalStorageTokenStorage();
530
+ break;
531
+ case 'custom':
532
+ if (!config.customTokenStorage) {
533
+ throw new Error('Custom token storage configuration is required when tokenStorage is "custom"');
534
+ }
535
+ tokenStorage = config.customTokenStorage;
536
+ break;
537
+ case 'memory':
538
+ default:
539
+ tokenStorage = new MemoryTokenStorage();
540
+ break;
541
+ }
542
+ // Token storage keys
543
+ const ACCESS_TOKEN_KEY = `pers_access_token_${config.authType || 'user'}`;
544
+ const REFRESH_TOKEN_KEY = `pers_refresh_token_${config.authType || 'user'}`;
363
545
  // Store current token for refresh scenarios and caching
364
546
  let currentToken = config.token || null;
365
547
  let isRefreshing = false; // Prevent concurrent refresh attempts
@@ -376,6 +558,17 @@ function createAuthProvider(config) {
376
558
  if (currentToken) {
377
559
  return currentToken;
378
560
  }
561
+ // Try to get token from storage
562
+ try {
563
+ const storedToken = await tokenStorage.getItem(ACCESS_TOKEN_KEY);
564
+ if (storedToken) {
565
+ currentToken = storedToken;
566
+ return storedToken;
567
+ }
568
+ }
569
+ catch (error) {
570
+ console.warn('Failed to retrieve token from storage:', error);
571
+ }
379
572
  // Custom token provider function (always fresh)
380
573
  if (config.tokenProvider) {
381
574
  const token = await config.tokenProvider();
@@ -388,6 +581,48 @@ function createAuthProvider(config) {
388
581
  async getProjectKey() {
389
582
  return config.projectKey || null;
390
583
  },
584
+ // Token storage methods
585
+ async setAccessToken(token) {
586
+ currentToken = token;
587
+ try {
588
+ await tokenStorage.setItem(ACCESS_TOKEN_KEY, token);
589
+ }
590
+ catch (error) {
591
+ console.error('Failed to store access token:', error);
592
+ throw error;
593
+ }
594
+ },
595
+ async setRefreshToken(token) {
596
+ try {
597
+ await tokenStorage.setItem(REFRESH_TOKEN_KEY, token);
598
+ }
599
+ catch (error) {
600
+ console.error('Failed to store refresh token:', error);
601
+ throw error;
602
+ }
603
+ },
604
+ async getRefreshToken() {
605
+ try {
606
+ return await tokenStorage.getItem(REFRESH_TOKEN_KEY);
607
+ }
608
+ catch (error) {
609
+ console.warn('Failed to retrieve refresh token from storage:', error);
610
+ return null;
611
+ }
612
+ },
613
+ async clearTokens() {
614
+ currentToken = null;
615
+ try {
616
+ await Promise.all([
617
+ tokenStorage.removeItem(ACCESS_TOKEN_KEY),
618
+ tokenStorage.removeItem(REFRESH_TOKEN_KEY)
619
+ ]);
620
+ }
621
+ catch (error) {
622
+ console.error('Failed to clear tokens from storage:', error);
623
+ throw error;
624
+ }
625
+ },
391
626
  async onTokenExpired() {
392
627
  // Prevent concurrent refresh attempts
393
628
  if (isRefreshing) {
@@ -399,7 +634,13 @@ function createAuthProvider(config) {
399
634
  // No refresh logic provided
400
635
  if (!config.onTokenExpired) {
401
636
  console.warn('Token expired but no refresh logic provided');
402
- currentToken = null; // Clear expired token
637
+ currentToken = null;
638
+ try {
639
+ await tokenStorage.removeItem(ACCESS_TOKEN_KEY);
640
+ }
641
+ catch (error) {
642
+ console.warn('Failed to clear expired token from storage:', error);
643
+ }
403
644
  return;
404
645
  }
405
646
  // Start refresh process
@@ -413,6 +654,13 @@ function createAuthProvider(config) {
413
654
  const newToken = await config.tokenProvider();
414
655
  if (newToken && newToken !== currentToken) {
415
656
  currentToken = newToken;
657
+ // Store the new token
658
+ try {
659
+ await tokenStorage.setItem(ACCESS_TOKEN_KEY, newToken);
660
+ }
661
+ catch (error) {
662
+ console.warn('Failed to store refreshed token:', error);
663
+ }
416
664
  // Notify about successful token refresh
417
665
  if (config.onTokenRefreshed) {
418
666
  config.onTokenRefreshed(newToken);
@@ -421,17 +669,35 @@ function createAuthProvider(config) {
421
669
  else {
422
670
  console.warn('Token refresh completed but no new token received');
423
671
  currentToken = null;
672
+ try {
673
+ await tokenStorage.removeItem(ACCESS_TOKEN_KEY);
674
+ }
675
+ catch (error) {
676
+ console.warn('Failed to clear token after failed refresh:', error);
677
+ }
424
678
  }
425
679
  }
426
680
  else {
427
681
  // For static token configs, clear the token since we can't refresh
428
682
  console.warn('Token expired for static token config - clearing token');
429
683
  currentToken = null;
684
+ try {
685
+ await tokenStorage.removeItem(ACCESS_TOKEN_KEY);
686
+ }
687
+ catch (error) {
688
+ console.warn('Failed to clear expired static token:', error);
689
+ }
430
690
  }
431
691
  }
432
692
  catch (error) {
433
693
  console.error('Token refresh failed:', error);
434
694
  currentToken = null; // Clear token on refresh failure
695
+ try {
696
+ await tokenStorage.removeItem(ACCESS_TOKEN_KEY);
697
+ }
698
+ catch (storageError) {
699
+ console.warn('Failed to clear token after refresh failure:', storageError);
700
+ }
435
701
  throw error; // Re-throw to let SDK handle the error
436
702
  }
437
703
  finally {
@@ -468,6 +734,44 @@ function createAuthProvider(config) {
468
734
  };
469
735
  } */
470
736
 
737
+ /**
738
+ * @explorins/pers-sdk/core/auth - Consolidated Auth Module
739
+ *
740
+ * All authentication functionality in one place:
741
+ * - Auth provider interfaces and implementations
742
+ * - Auth API and service logic
743
+ * - Token management
744
+ */
745
+ // Auth provider interfaces and implementations
746
+ /**
747
+ * Create a complete Auth SDK instance (for backward compatibility)
748
+ * Note: This is now handled directly by PersApiClient
749
+ *
750
+ * @param apiClient - Configured PERS API client
751
+ * @param authProvider - Optional auth provider. If not provided, uses SimpleSdkAuthProvider
752
+ * @returns Auth SDK with flattened structure for better DX
753
+ */
754
+ function createAuthSDK(apiClient, authProvider) {
755
+ // Use simple internal auth provider if none provided
756
+ const provider = authProvider || new SimpleSdkAuthProvider();
757
+ const authApi = new AuthApi(apiClient);
758
+ const authService = new AuthService(authApi, provider);
759
+ return {
760
+ // Direct access to service methods (primary interface)
761
+ // Admin authentication methods
762
+ loginTenantAdmin: (jwt) => authService.loginTenantAdmin(jwt),
763
+ loginUser: (jwt) => authService.loginUser(jwt),
764
+ refreshAccessToken: (refreshToken) => authService.refreshAccessToken(refreshToken),
765
+ clearTokens: () => authService.clearTokens(),
766
+ // Auth provider access for external integration
767
+ getAuthProvider: () => provider,
768
+ hasValidToken: () => provider.hasValidToken?.() || false,
769
+ // Advanced access for edge cases
770
+ api: authApi,
771
+ service: authService
772
+ };
773
+ }
774
+
471
775
  /**
472
776
  * PERS SDK - Minimal platform-agnostic client with built-in authentication
473
777
  * Authentication is now handled at the SDK core level for better scalability
@@ -1265,99 +1569,6 @@ function createAnalyticsSDK(apiClient) {
1265
1569
  };
1266
1570
  }
1267
1571
 
1268
- /**
1269
- * Platform-Agnostic Auth Admin API Client
1270
- *
1271
- * Handles authentication and authorization admin operations using the PERS backend.
1272
- * Uses @explorins/pers-shared DTOs for consistency with backend.
1273
- *
1274
- * Note: Special header handling (bypass-auth-interceptor) may need to be implemented
1275
- * at the PersApiClient level or through a specialized auth client.
1276
- */
1277
- class AuthAdminApi {
1278
- constructor(apiClient) {
1279
- this.apiClient = apiClient;
1280
- this.basePath = '/auth';
1281
- }
1282
- // ==========================================
1283
- // ADMIN AUTHENTICATION OPERATIONS
1284
- // ==========================================
1285
- /**
1286
- * ADMIN: Login tenant admin with JWT
1287
- * Note: JWT handling and auth bypass headers may need special implementation
1288
- */
1289
- async loginTenantAdmin(jwt) {
1290
- const body = {
1291
- authToken: jwt,
1292
- authType: persShared.AccountOwnerType.TENANT
1293
- };
1294
- return this.apiClient.post(`${this.basePath}/token`, body);
1295
- }
1296
- /**
1297
- * ADMIN: Refresh access token
1298
- * Note: Bypass header handling may need special implementation
1299
- */
1300
- async refreshAccessToken(refreshToken) {
1301
- return this.apiClient.post(`${this.basePath}/refresh`, { refreshToken });
1302
- }
1303
- }
1304
-
1305
- /**
1306
- * Platform-Agnostic Auth Admin Service
1307
- *
1308
- * Contains auth admin business logic and operations that work across platforms.
1309
- * No framework dependencies - pure TypeScript business logic.
1310
- *
1311
- * Focuses only on actual backend capabilities.
1312
- */
1313
- class AuthAdminService {
1314
- constructor(authAdminApi) {
1315
- this.authAdminApi = authAdminApi;
1316
- }
1317
- // ==========================================
1318
- // ADMIN AUTHENTICATION OPERATIONS
1319
- // ==========================================
1320
- /**
1321
- * ADMIN: Login tenant admin with JWT
1322
- */
1323
- async loginTenantAdmin(jwt) {
1324
- return this.authAdminApi.loginTenantAdmin(jwt);
1325
- }
1326
- /**
1327
- * ADMIN: Refresh access token
1328
- */
1329
- async refreshAccessToken(refreshToken) {
1330
- return this.authAdminApi.refreshAccessToken(refreshToken);
1331
- }
1332
- }
1333
-
1334
- /**
1335
- * @explorins/pers-sdk-auth-admin
1336
- *
1337
- * Platform-agnostic Auth Admin Domain SDK for PERS ecosystem
1338
- * Handles authentication and authorization admin operations
1339
- */
1340
- // API Layer
1341
- /**
1342
- * Create a complete Auth Admin SDK instance
1343
- *
1344
- * @param apiClient - Configured PERS API client
1345
- * @returns Auth Admin SDK with flattened structure for better DX
1346
- */
1347
- function createAuthAdminSDK(apiClient) {
1348
- const authAdminApi = new AuthAdminApi(apiClient);
1349
- const authAdminService = new AuthAdminService(authAdminApi);
1350
- return {
1351
- // Direct access to service methods (primary interface)
1352
- // Admin authentication methods
1353
- loginTenantAdmin: (jwt) => authAdminService.loginTenantAdmin(jwt),
1354
- refreshAccessToken: (refreshToken) => authAdminService.refreshAccessToken(refreshToken),
1355
- // Advanced access for edge cases
1356
- api: authAdminApi,
1357
- service: authAdminService
1358
- };
1359
- }
1360
-
1361
1572
  /**
1362
1573
  * Platform-Agnostic Campaign API Client (NEW - RESTful Design)
1363
1574
  *
@@ -2226,29 +2437,22 @@ class RedemptionApi {
2226
2437
  // PUBLIC OPERATIONS (Project Key)
2227
2438
  // ==========================================
2228
2439
  /**
2229
- * PUBLIC: Get redemptions (intelligent access)
2440
+ * UNIFIED: Get redemptions with intelligent access control
2230
2441
  *
2231
- * NEW: Intelligent endpoint that adapts based on authentication
2442
+ * Intelligent endpoint that adapts based on authentication:
2232
2443
  * - Public users: Get active redemptions only
2233
2444
  * - Admin users: Get all redemptions with optional filtering
2234
2445
  *
2235
- * Replaces: getActiveRedemptions() + getRedemptionsAsAdmin()
2446
+ * @param options.active - Filter by active status (undefined = all for admins/active for public)
2447
+ * @param options.adminAccess - Force admin access (requires admin auth)
2236
2448
  */
2237
- async getRedemptions(active) {
2449
+ async getRedemptions(options) {
2238
2450
  let url = `${this.basePath}`;
2239
- if (active !== undefined) {
2240
- url += `?active=${active}`;
2451
+ if (options?.active !== undefined) {
2452
+ url += `?active=${options.active}`;
2241
2453
  }
2242
2454
  return this.apiClient.get(url);
2243
2455
  }
2244
- /**
2245
- * PUBLIC: Get active redemptions
2246
- *
2247
- * Updated: Now uses unified endpoint (backward compatibility)
2248
- */
2249
- async getActiveRedemptions() {
2250
- return this.getRedemptions(); // Will return active only for public access
2251
- }
2252
2456
  /**
2253
2457
  * PUBLIC: Get redemption types
2254
2458
  *
@@ -2294,12 +2498,27 @@ class RedemptionApi {
2294
2498
  // REDEMPTION REDEEMS QUERIES (NEW ENDPOINTS)
2295
2499
  // ==========================================
2296
2500
  /**
2297
- * Get redemption redeems with filtering (unified endpoint)
2501
+ * UNIFIED: Get redemption redeems with filtering and intelligent access
2502
+ *
2503
+ * Role-based access with unified filtering:
2504
+ * - Users: See only their own redeems (userId/businessId filters ignored)
2505
+ * - Admins: Can filter by userId, businessId, or redemptionId
2298
2506
  *
2299
- * NEW: GET /redemption-redeems with query parameters
2300
- * Role-based access: Users see only their own, admins can filter by userId/businessId
2507
+ * @param filters.redemptionId - Filter by specific redemption
2508
+ * @param filters.userId - Admin only: Filter by user ID
2509
+ * @param filters.businessId - Admin only: Filter by business ID
2510
+ * @param filters.myRedeems - Force user's own redeems (uses /me endpoint)
2301
2511
  */
2302
2512
  async getRedemptionRedeems(filters) {
2513
+ // Use convenience endpoint for user's own redeems
2514
+ if (filters?.myRedeems) {
2515
+ let url = `${this.redeemsPath}/me`;
2516
+ if (filters.redemptionId) {
2517
+ url += `?redemptionId=${filters.redemptionId}`;
2518
+ }
2519
+ return this.apiClient.get(url);
2520
+ }
2521
+ // Use admin endpoint with filtering
2303
2522
  let url = this.redeemsPath;
2304
2523
  const params = new URLSearchParams();
2305
2524
  if (filters?.redemptionId)
@@ -2315,67 +2534,22 @@ class RedemptionApi {
2315
2534
  return this.apiClient.get(url);
2316
2535
  }
2317
2536
  /**
2318
- * Get specific redemption redeem by ID
2319
- *
2320
- * NEW: GET /redemption-redeems/:id
2537
+ * UNIFIED: Get specific redemption redeem by ID
2321
2538
  */
2322
2539
  async getRedemptionRedeemById(id) {
2323
2540
  return this.apiClient.get(`${this.redeemsPath}/${id}`);
2324
2541
  }
2325
- /**
2326
- * USER: Get my redemption redeems (convenience endpoint)
2327
- *
2328
- * NEW: GET /redemption-redeems/me with optional filtering
2329
- */
2330
- async getMyRedemptionRedeems(redemptionId) {
2331
- let url = `${this.redeemsPath}/me`;
2332
- if (redemptionId) {
2333
- url += `?redemptionId=${redemptionId}`;
2334
- }
2335
- return this.apiClient.get(url);
2336
- }
2337
- /**
2338
- * ADMIN: Get redemption redeems by user ID
2339
- *
2340
- * NEW: GET /redemption-redeems?userId=X
2341
- */
2342
- async getRedemptionRedeemsByUserId(userId, redemptionId) {
2343
- return this.getRedemptionRedeems({ userId, redemptionId });
2344
- }
2345
- /**
2346
- * ADMIN: Get redemption redeems by business ID
2347
- *
2348
- * NEW: GET /redemption-redeems?businessId=X
2349
- */
2350
- async getRedemptionRedeemsByBusinessId(businessId, redemptionId) {
2351
- return this.getRedemptionRedeems({ businessId, redemptionId });
2352
- }
2353
- /**
2354
- * ADMIN: Get redemption redeems by redemption ID
2355
- *
2356
- * NEW: GET /redemption-redeems?redemptionId=X
2357
- */
2358
- async getRedemptionRedeemsByRedemptionId(redemptionId) {
2359
- return this.getRedemptionRedeems({ redemptionId });
2360
- }
2361
2542
  // ==========================================
2362
2543
  // USER OPERATIONS (JWT + Project Key)
2363
2544
  // ==========================================
2364
2545
  /**
2365
- * USER: Get user redemption history
2546
+ * UNIFIED: Get user redemption history
2366
2547
  *
2367
- * Updated: Uses new convenience endpoint /redemption-redeems/me
2548
+ * Uses convenience endpoint /redemption-redeems/me with optional filtering
2549
+ * @param redemptionId - Optional filter by specific redemption
2368
2550
  */
2369
- async getUserRedemptionHistory() {
2370
- return this.getMyRedemptionRedeems();
2371
- }
2372
- /**
2373
- * USER: Get user redemptions (backward compatibility)
2374
- *
2375
- * Deprecated: Use getUserRedemptionHistory() instead
2376
- */
2377
- async getUserRedeems() {
2378
- return this.getUserRedemptionHistory();
2551
+ async getUserRedeems(redemptionId) {
2552
+ return this.getRedemptionRedeems({ myRedeems: true, redemptionId });
2379
2553
  }
2380
2554
  // ==========================================
2381
2555
  // ADMIN OPERATIONS (Tenant Admin JWT)
@@ -2387,7 +2561,7 @@ class RedemptionApi {
2387
2561
  * The unified endpoint will detect admin privileges and allow filtering
2388
2562
  */
2389
2563
  async getRedemptionsAsAdmin(active) {
2390
- return this.getRedemptions(active); // Uses intelligent endpoint
2564
+ return this.getRedemptions({ active, adminAccess: true });
2391
2565
  }
2392
2566
  /**
2393
2567
  * ADMIN: Create redemption
@@ -2406,21 +2580,13 @@ class RedemptionApi {
2406
2580
  return this.apiClient.put(`${this.basePath}/${id}`, redemptionCreateRequest);
2407
2581
  }
2408
2582
  /**
2409
- * ADMIN: Toggle redemption status
2583
+ * UNIFIED: Toggle redemption active status
2410
2584
  *
2411
2585
  * Updated: /redemption/admin/:id/toggle-active → /redemptions/:id/status
2412
2586
  * Following standard /status pattern used across domains
2413
2587
  */
2414
- async toggleRedemptionStatus(redemptionId) {
2415
- return this.apiClient.put(`${this.basePath}/${redemptionId}/status`, {});
2416
- }
2417
- /**
2418
- * ADMIN: Toggle redemption active (backward compatibility)
2419
- *
2420
- * Deprecated: Use toggleRedemptionStatus() instead
2421
- */
2422
2588
  async toggleRedemptionActive(redemptionId) {
2423
- return this.toggleRedemptionStatus(redemptionId);
2589
+ return this.apiClient.put(`${this.basePath}/${redemptionId}/status`, {});
2424
2590
  }
2425
2591
  /**
2426
2592
  * ADMIN: Delete redemption
@@ -2466,21 +2632,13 @@ class RedemptionApi {
2466
2632
  return this.apiClient.delete(`${this.basePath}/${redemptionId}/token-units/${redemptionTokenUnitId}`);
2467
2633
  }
2468
2634
  // ==========================================
2469
- // BACKWARD COMPATIBILITY METHODS
2635
+ // CONVENIENCE METHODS (Legacy Support)
2470
2636
  // ==========================================
2471
2637
  /**
2472
- * @deprecated Use getRedemptions() instead
2473
- * Backward compatibility for old admin endpoint
2638
+ * Convenience: Get active redemptions (public access)
2474
2639
  */
2475
- async getRedemptionsAdmin(active) {
2476
- return this.getRedemptionsAsAdmin(active);
2477
- }
2478
- /**
2479
- * @deprecated Use redeemRedemption() instead
2480
- * Backward compatibility for old redeem method
2481
- */
2482
- async redeem(redemptionId) {
2483
- return this.redeemRedemption(redemptionId);
2640
+ async getActiveRedemptions() {
2641
+ return this.getRedemptions({ active: true });
2484
2642
  }
2485
2643
  }
2486
2644
 
@@ -2500,7 +2658,15 @@ class RedemptionService {
2500
2658
  // PUBLIC OPERATIONS
2501
2659
  // ==========================================
2502
2660
  /**
2503
- * PUBLIC: Get active redemptions
2661
+ * UNIFIED: Get redemptions with intelligent access control
2662
+ * @param options.active - Filter by active status
2663
+ * @param options.adminAccess - Force admin access
2664
+ */
2665
+ async getRedemptions(options) {
2666
+ return this.redemptionApi.getRedemptions(options);
2667
+ }
2668
+ /**
2669
+ * Convenience: Get active redemptions (public)
2504
2670
  */
2505
2671
  async getActiveRedemptions() {
2506
2672
  return this.redemptionApi.getActiveRedemptions();
@@ -2521,7 +2687,13 @@ class RedemptionService {
2521
2687
  return this.redemptionApi.redeemRedemption(redemptionId);
2522
2688
  }
2523
2689
  /**
2524
- * AUTH: Get user redemptions
2690
+ * UNIFIED: Get redemption redeems with filtering
2691
+ */
2692
+ async getRedemptionRedeems(filters) {
2693
+ return this.redemptionApi.getRedemptionRedeems(filters);
2694
+ }
2695
+ /**
2696
+ * Convenience: Get user redemptions
2525
2697
  */
2526
2698
  async getUserRedeems() {
2527
2699
  return this.redemptionApi.getUserRedeems();
@@ -2530,7 +2702,7 @@ class RedemptionService {
2530
2702
  // ADMIN OPERATIONS
2531
2703
  // ==========================================
2532
2704
  /**
2533
- * ADMIN: Get redemptions with optional active filter
2705
+ * Convenience: Get redemptions as admin
2534
2706
  */
2535
2707
  async getRedemptionsAsAdmin(active) {
2536
2708
  return this.redemptionApi.getRedemptionsAsAdmin(active);
@@ -4684,10 +4856,14 @@ function createWeb3SDK(apiClient) {
4684
4856
  };
4685
4857
  }
4686
4858
 
4859
+ Object.defineProperty(exports, "AccountOwnerType", {
4860
+ enumerable: true,
4861
+ get: function () { return persShared.AccountOwnerType; }
4862
+ });
4687
4863
  exports.AnalyticsApi = AnalyticsApi;
4688
4864
  exports.AnalyticsService = AnalyticsService;
4689
- exports.AuthAdminApi = AuthAdminApi;
4690
- exports.AuthAdminService = AuthAdminService;
4865
+ exports.AuthApi = AuthApi;
4866
+ exports.AuthService = AuthService;
4691
4867
  exports.BaseTokenService = BaseTokenService;
4692
4868
  exports.BusinessApi = BusinessApi;
4693
4869
  exports.BusinessService = BusinessService;
@@ -4705,6 +4881,7 @@ exports.PersApiError = PersApiError;
4705
4881
  exports.PersSDK = PersSDK;
4706
4882
  exports.RedemptionApi = RedemptionApi;
4707
4883
  exports.RedemptionService = RedemptionService;
4884
+ exports.SimpleSdkAuthProvider = SimpleSdkAuthProvider;
4708
4885
  exports.TenantApi = TenantApi;
4709
4886
  exports.TenantService = TenantService;
4710
4887
  exports.TokenApi = TokenApi;
@@ -4723,8 +4900,8 @@ exports.Web3InfrastructureApi = Web3InfrastructureApi;
4723
4900
  exports.Web3ProviderService = Web3ProviderService;
4724
4901
  exports.buildApiRoot = buildApiRoot;
4725
4902
  exports.createAnalyticsSDK = createAnalyticsSDK;
4726
- exports.createAuthAdminSDK = createAuthAdminSDK;
4727
4903
  exports.createAuthProvider = createAuthProvider;
4904
+ exports.createAuthSDK = createAuthSDK;
4728
4905
  exports.createBusinessSDK = createBusinessSDK;
4729
4906
  exports.createCampaignSDK = createCampaignSDK;
4730
4907
  exports.createDonationSDK = createDonationSDK;