@explorins/pers-sdk 1.6.6 → 1.6.9
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.
- package/README.md +87 -369
- package/dist/chunks/{pers-sdk-eO4XUi8w.js → pers-sdk-Bd6BZHgt.js} +97 -242
- package/dist/chunks/pers-sdk-Bd6BZHgt.js.map +1 -0
- package/dist/chunks/{pers-sdk-CajYwGkL.cjs → pers-sdk-CmyPEhy7.cjs} +97 -242
- package/dist/chunks/pers-sdk-CmyPEhy7.cjs.map +1 -0
- package/dist/chunks/{web3-chain-service-BYkj61DN.cjs → web3-chain-service-D75TcHkh.cjs} +28 -21
- package/dist/chunks/web3-chain-service-D75TcHkh.cjs.map +1 -0
- package/dist/chunks/{web3-chain-service-DN6tJmvK.js → web3-chain-service-Dp5Z8p9I.js} +28 -22
- package/dist/chunks/web3-chain-service-Dp5Z8p9I.js.map +1 -0
- package/dist/core/auth/api/auth-api.d.ts +11 -14
- package/dist/core/auth/api/auth-api.d.ts.map +1 -1
- package/dist/core/auth/auth-provider.interface.d.ts +10 -5
- package/dist/core/auth/auth-provider.interface.d.ts.map +1 -1
- package/dist/core/auth/default-auth-provider.d.ts +3 -6
- package/dist/core/auth/default-auth-provider.d.ts.map +1 -1
- package/dist/core/auth/index.d.ts +2 -6
- package/dist/core/auth/index.d.ts.map +1 -1
- package/dist/core/auth/refresh-manager.d.ts +2 -5
- package/dist/core/auth/refresh-manager.d.ts.map +1 -1
- package/dist/core/auth/services/auth-service.d.ts +9 -20
- package/dist/core/auth/services/auth-service.d.ts.map +1 -1
- package/dist/core/auth/token-storage.d.ts +2 -4
- package/dist/core/auth/token-storage.d.ts.map +1 -1
- package/dist/core/environment.d.ts +2 -4
- package/dist/core/environment.d.ts.map +1 -1
- package/dist/core/index.d.ts +0 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/pers-api-client.d.ts +8 -34
- package/dist/core/pers-api-client.d.ts.map +1 -1
- package/dist/core/pers-config.d.ts +5 -9
- package/dist/core/pers-config.d.ts.map +1 -1
- package/dist/core/utils/jwt.function.d.ts +9 -0
- package/dist/core/utils/jwt.function.d.ts.map +1 -1
- package/dist/core.cjs +3 -4
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +3 -4
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +2 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/package.json +2 -3
- package/dist/web3-chain.cjs +1 -2
- package/dist/web3-chain.cjs.map +1 -1
- package/dist/web3-chain.js +1 -2
- package/dist/web3-chain.js.map +1 -1
- package/package.json +2 -3
- package/dist/chunks/pers-sdk-CajYwGkL.cjs.map +0 -1
- package/dist/chunks/pers-sdk-eO4XUi8w.js.map +0 -1
- package/dist/chunks/web3-chain-service-BYkj61DN.cjs.map +0 -1
- package/dist/chunks/web3-chain-service-DN6tJmvK.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var persShared = require('@explorins/pers-shared');
|
|
4
|
+
var web3ChainService = require('./web3-chain-service-D75TcHkh.cjs');
|
|
4
5
|
var userService = require('./user-service-D1Rn4U8u.cjs');
|
|
5
6
|
var userStatus = require('../user-status.cjs');
|
|
6
7
|
var tokenService = require('./token-service-BWScn8Qa.cjs');
|
|
@@ -13,7 +14,6 @@ var tenantService = require('./tenant-service-Ba7xrWED.cjs');
|
|
|
13
14
|
var analyticsService = require('./analytics-service-CitlimKJ.cjs');
|
|
14
15
|
var donationService = require('./donation-service-D-xFrONi.cjs');
|
|
15
16
|
var explorer_utils = require('./explorer.utils-GpskbLl1.cjs');
|
|
16
|
-
var web3ChainService = require('./web3-chain-service-BYkj61DN.cjs');
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* PERS SDK Configuration interfaces and utilities
|
|
@@ -59,25 +59,16 @@ function mergeWithDefaults(config) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
/**
|
|
62
|
-
* Platform-Agnostic Auth
|
|
63
|
-
*
|
|
64
|
-
* Handles authentication and authorization admin operations using the PERS backend.
|
|
65
|
-
* Uses @explorins/pers-shared DTOs for consistency with backend.
|
|
66
|
-
*
|
|
67
|
-
* Note: Special header handling (bypass-auth-interceptor) may need to be implemented
|
|
68
|
-
* at the PersApiClient level or through a specialized auth client.
|
|
62
|
+
* Platform-Agnostic Auth API Client
|
|
63
|
+
* Handles authentication operations using the PERS backend
|
|
69
64
|
*/
|
|
70
65
|
class AuthApi {
|
|
71
66
|
constructor(apiClient) {
|
|
72
67
|
this.apiClient = apiClient;
|
|
73
68
|
this.basePath = '/auth';
|
|
74
69
|
}
|
|
75
|
-
// ==========================================
|
|
76
|
-
// ADMIN AUTHENTICATION OPERATIONS
|
|
77
|
-
// ==========================================
|
|
78
70
|
/**
|
|
79
|
-
*
|
|
80
|
-
* Note: JWT handling and auth bypass headers may need special implementation
|
|
71
|
+
* Login tenant admin with JWT
|
|
81
72
|
*/
|
|
82
73
|
async loginTenantAdmin(jwt) {
|
|
83
74
|
const body = {
|
|
@@ -87,7 +78,7 @@ class AuthApi {
|
|
|
87
78
|
return this.apiClient.post(`${this.basePath}/token`, body, { bypassAuth: true });
|
|
88
79
|
}
|
|
89
80
|
/**
|
|
90
|
-
* Login user with JWT
|
|
81
|
+
* Login user with JWT
|
|
91
82
|
*/
|
|
92
83
|
async loginUser(jwt) {
|
|
93
84
|
const body = {
|
|
@@ -96,6 +87,9 @@ class AuthApi {
|
|
|
96
87
|
};
|
|
97
88
|
return this.apiClient.post(`${this.basePath}/token`, body, { bypassAuth: true });
|
|
98
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Login user with raw data (no external authentication)
|
|
92
|
+
*/
|
|
99
93
|
async loginUnAuthenticated(rawLoginData) {
|
|
100
94
|
const body = {
|
|
101
95
|
authToken: '',
|
|
@@ -105,21 +99,16 @@ class AuthApi {
|
|
|
105
99
|
return this.apiClient.post(`${this.basePath}/token`, body, { bypassAuth: true });
|
|
106
100
|
}
|
|
107
101
|
/**
|
|
108
|
-
* Refresh access token
|
|
102
|
+
* Refresh access token
|
|
109
103
|
*/
|
|
110
104
|
async refreshAccessToken(refreshToken) {
|
|
111
|
-
// Bypass auth headers for refresh calls to prevent circular dependency
|
|
112
105
|
return this.apiClient.post(`${this.basePath}/refresh`, { refreshToken }, { bypassAuth: true });
|
|
113
106
|
}
|
|
114
107
|
}
|
|
115
108
|
|
|
116
109
|
/**
|
|
117
|
-
* Platform-
|
|
118
|
-
*
|
|
119
|
-
* Contains auth admin business logic and operations that work across platforms.
|
|
120
|
-
* No framework dependencies - pure TypeScript business logic.
|
|
121
|
-
*
|
|
122
|
-
* Focuses only on actual backend capabilities.
|
|
110
|
+
* Platform-agnostic authentication service
|
|
111
|
+
* Handles login, token refresh, and storage operations
|
|
123
112
|
*/
|
|
124
113
|
class AuthService {
|
|
125
114
|
constructor(authApi, authProvider) {
|
|
@@ -128,110 +117,84 @@ class AuthService {
|
|
|
128
117
|
this.activeRefreshPromise = null;
|
|
129
118
|
}
|
|
130
119
|
// ==========================================
|
|
131
|
-
//
|
|
120
|
+
// AUTHENTICATION OPERATIONS
|
|
132
121
|
// ==========================================
|
|
133
122
|
/**
|
|
134
|
-
*
|
|
135
|
-
* Automatically stores tokens if auth provider supports token storage
|
|
123
|
+
* Login tenant admin with JWT
|
|
136
124
|
*/
|
|
137
125
|
async loginTenantAdmin(jwt) {
|
|
138
126
|
const response = await this.authApi.loginTenantAdmin(jwt);
|
|
139
|
-
// Store tokens if auth provider supports it
|
|
140
127
|
if (this.authProvider && response.accessToken) {
|
|
141
128
|
await this.storeTokens(response.accessToken, response.refreshToken, 'admin', jwt);
|
|
142
129
|
}
|
|
143
130
|
return response;
|
|
144
131
|
}
|
|
145
132
|
/**
|
|
146
|
-
*
|
|
133
|
+
* Login user with JWT
|
|
147
134
|
*/
|
|
148
135
|
async loginUser(jwt) {
|
|
149
136
|
const response = await this.authApi.loginUser(jwt);
|
|
150
|
-
// Store tokens if auth provider supports it
|
|
151
137
|
if (this.authProvider && response.accessToken) {
|
|
152
138
|
await this.storeTokens(response.accessToken, response.refreshToken, 'user', jwt);
|
|
153
139
|
}
|
|
154
140
|
return response;
|
|
155
141
|
}
|
|
156
142
|
/**
|
|
157
|
-
*
|
|
143
|
+
* Login user with raw data (no external auth)
|
|
158
144
|
*/
|
|
159
145
|
async loginUserWithRawData(rawLoginData) {
|
|
160
|
-
const
|
|
161
|
-
externalId: rawLoginData?.externalId,
|
|
162
|
-
email: rawLoginData?.email,
|
|
163
|
-
firstName: rawLoginData?.firstName,
|
|
164
|
-
lastName: rawLoginData?.lastName,
|
|
165
|
-
customData: rawLoginData?.customData
|
|
166
|
-
};
|
|
167
|
-
const response = await this.authApi.loginUnAuthenticated(loginData);
|
|
168
|
-
// Store tokens if auth provider supports it
|
|
146
|
+
const response = await this.authApi.loginUnAuthenticated(rawLoginData);
|
|
169
147
|
if (this.authProvider && response.accessToken) {
|
|
170
148
|
await this.storeTokens(response.accessToken, response.refreshToken, 'user');
|
|
171
149
|
}
|
|
172
150
|
return response;
|
|
173
151
|
}
|
|
174
152
|
/**
|
|
175
|
-
*
|
|
176
|
-
* Automatically stores new tokens if auth provider supports token storage
|
|
177
|
-
*
|
|
178
|
-
* Implements refresh deduplication to prevent race conditions when multiple
|
|
179
|
-
* requests trigger refresh simultaneously.
|
|
153
|
+
* Refresh access token with race condition protection
|
|
180
154
|
*/
|
|
181
155
|
async refreshAccessToken(refreshToken) {
|
|
182
|
-
// If there's already an active refresh, return that promise instead of starting a new one
|
|
183
156
|
if (this.activeRefreshPromise) {
|
|
184
|
-
console.log('[AuthService] Refresh already in progress, reusing existing promise');
|
|
185
157
|
try {
|
|
186
158
|
return await this.activeRefreshPromise;
|
|
187
159
|
}
|
|
188
160
|
catch (error) {
|
|
189
|
-
// If the active refresh failed, we'll try again with a fresh attempt
|
|
190
161
|
console.warn('[AuthService] Active refresh failed, attempting new refresh:', error);
|
|
191
162
|
this.activeRefreshPromise = null;
|
|
192
163
|
}
|
|
193
164
|
}
|
|
194
|
-
// Start a new refresh operation
|
|
195
165
|
this.activeRefreshPromise = this.performRefresh(refreshToken);
|
|
196
166
|
try {
|
|
197
|
-
|
|
198
|
-
return result;
|
|
167
|
+
return await this.activeRefreshPromise;
|
|
199
168
|
}
|
|
200
169
|
finally {
|
|
201
|
-
// Clear the active refresh promise when done (success or failure)
|
|
202
170
|
this.activeRefreshPromise = null;
|
|
203
171
|
}
|
|
204
172
|
}
|
|
205
173
|
/**
|
|
206
174
|
* Performs the actual refresh operation
|
|
207
|
-
* Separated from refreshAccessToken for cleaner promise management
|
|
208
175
|
*/
|
|
209
176
|
async performRefresh(refreshToken) {
|
|
210
|
-
// Use provided refresh token or get from auth provider
|
|
211
177
|
const tokenToUse = refreshToken || (this.authProvider ? await this.authProvider.getRefreshToken() : null);
|
|
212
178
|
if (!tokenToUse) {
|
|
213
179
|
throw new Error('No refresh token available for token refresh');
|
|
214
180
|
}
|
|
215
181
|
const response = await this.authApi.refreshAccessToken(tokenToUse);
|
|
216
|
-
// Store new tokens if auth provider supports it
|
|
217
182
|
if (this.authProvider && response.accessToken) {
|
|
218
183
|
await this.storeTokens(response.accessToken, response.refreshToken);
|
|
219
184
|
}
|
|
220
185
|
return response;
|
|
221
186
|
}
|
|
222
187
|
/**
|
|
223
|
-
* Clear stored tokens
|
|
224
|
-
* Also clears any active refresh operations
|
|
188
|
+
* Clear stored tokens and active refresh operations
|
|
225
189
|
*/
|
|
226
190
|
async clearTokens() {
|
|
227
|
-
// Clear any active refresh operation
|
|
228
191
|
this.activeRefreshPromise = null;
|
|
229
192
|
if (this.authProvider) {
|
|
230
193
|
await this.authProvider.clearTokens();
|
|
231
194
|
}
|
|
232
195
|
}
|
|
233
196
|
/**
|
|
234
|
-
* Check if we have valid tokens
|
|
197
|
+
* Check if we have valid authentication tokens
|
|
235
198
|
*/
|
|
236
199
|
async hasValidAuth() {
|
|
237
200
|
if (!this.authProvider)
|
|
@@ -243,43 +206,34 @@ class AuthService {
|
|
|
243
206
|
// PRIVATE HELPERS
|
|
244
207
|
// ==========================================
|
|
245
208
|
/**
|
|
246
|
-
* Store tokens using auth provider
|
|
209
|
+
* Store tokens using auth provider
|
|
247
210
|
*/
|
|
248
211
|
async storeTokens(accessToken, refreshToken, authType, providerToken) {
|
|
249
212
|
if (!this.authProvider)
|
|
250
213
|
return;
|
|
251
214
|
try {
|
|
252
|
-
|
|
253
|
-
if (
|
|
254
|
-
await this.authProvider.setAccessToken(accessToken);
|
|
255
|
-
}
|
|
256
|
-
// Store refresh token if provided and supported
|
|
257
|
-
if (refreshToken && this.authProvider) {
|
|
215
|
+
await this.authProvider.setAccessToken(accessToken);
|
|
216
|
+
if (refreshToken) {
|
|
258
217
|
await this.authProvider.setRefreshToken(refreshToken);
|
|
259
218
|
}
|
|
260
|
-
//
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
await
|
|
219
|
+
// Type-safe handling of extended token storage features
|
|
220
|
+
const extendedProvider = this.authProvider;
|
|
221
|
+
if (providerToken && extendedProvider.setProviderToken) {
|
|
222
|
+
await extendedProvider.setProviderToken(providerToken);
|
|
264
223
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
typeof this.authProvider.setAuthType === 'function') {
|
|
268
|
-
await this.authProvider.setAuthType(authType);
|
|
224
|
+
if (authType && extendedProvider.setAuthType) {
|
|
225
|
+
await extendedProvider.setAuthType(authType);
|
|
269
226
|
}
|
|
270
227
|
}
|
|
271
228
|
catch (error) {
|
|
272
|
-
// Log error for debugging but don't throw - token storage failure shouldn't break auth flow
|
|
273
229
|
console.warn('[AuthService] Failed to store tokens:', error);
|
|
274
230
|
}
|
|
275
231
|
}
|
|
276
232
|
}
|
|
277
233
|
|
|
278
234
|
/**
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
* Just moves the working refresh logic to a separate file for better organization.
|
|
282
|
-
* No overengineering - keeps the exact same simple logic.
|
|
235
|
+
* Token Refresh Manager
|
|
236
|
+
* Simple container for token refresh logic with better organization
|
|
283
237
|
*/
|
|
284
238
|
class TokenRefreshManager {
|
|
285
239
|
constructor(authService, authProvider) {
|
|
@@ -290,12 +244,7 @@ class TokenRefreshManager {
|
|
|
290
244
|
async ensureValidToken() {
|
|
291
245
|
try {
|
|
292
246
|
const token = await this.authProvider.getToken();
|
|
293
|
-
if (!token) {
|
|
294
|
-
await this.attemptInternalRefresh();
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
// Check if token is close to expiry (within 2 minutes)
|
|
298
|
-
if (this.isTokenExpiringSoon(token, this.tokenRefreshMarginSeconds)) {
|
|
247
|
+
if (!token || web3ChainService.isTokenExpired(token, this.tokenRefreshMarginSeconds)) {
|
|
299
248
|
await this.attemptInternalRefresh();
|
|
300
249
|
}
|
|
301
250
|
}
|
|
@@ -313,27 +262,11 @@ class TokenRefreshManager {
|
|
|
313
262
|
return false;
|
|
314
263
|
}
|
|
315
264
|
}
|
|
316
|
-
isTokenExpiringSoon(token, marginSeconds) {
|
|
317
|
-
try {
|
|
318
|
-
const parts = token.split('.');
|
|
319
|
-
if (parts.length !== 3)
|
|
320
|
-
return true;
|
|
321
|
-
const payload = JSON.parse(atob(parts[1]));
|
|
322
|
-
if (!payload.exp)
|
|
323
|
-
return false;
|
|
324
|
-
const timeUntilExpiry = payload.exp - Math.floor(Date.now() / 1000);
|
|
325
|
-
return timeUntilExpiry <= marginSeconds;
|
|
326
|
-
}
|
|
327
|
-
catch {
|
|
328
|
-
return true;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
265
|
}
|
|
332
266
|
|
|
333
267
|
/**
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
* Simple, focused token storage with clear separation of concerns.
|
|
268
|
+
* Token Storage System
|
|
269
|
+
* Simple, focused token storage with clear separation of concerns
|
|
337
270
|
*/
|
|
338
271
|
const AUTH_STORAGE_KEYS = {
|
|
339
272
|
ACCESS_TOKEN: 'pers_access_token',
|
|
@@ -346,26 +279,24 @@ const AUTH_STORAGE_KEYS = {
|
|
|
346
279
|
*/
|
|
347
280
|
class LocalStorageTokenStorage {
|
|
348
281
|
async get(key) {
|
|
349
|
-
|
|
350
|
-
return null;
|
|
351
|
-
return localStorage.getItem(key);
|
|
282
|
+
return typeof localStorage !== 'undefined' ? localStorage.getItem(key) : null;
|
|
352
283
|
}
|
|
353
284
|
async set(key, value) {
|
|
354
|
-
if (typeof localStorage
|
|
355
|
-
|
|
356
|
-
|
|
285
|
+
if (typeof localStorage !== 'undefined') {
|
|
286
|
+
localStorage.setItem(key, value);
|
|
287
|
+
}
|
|
357
288
|
}
|
|
358
289
|
async remove(key) {
|
|
359
|
-
if (typeof localStorage
|
|
360
|
-
|
|
361
|
-
|
|
290
|
+
if (typeof localStorage !== 'undefined') {
|
|
291
|
+
localStorage.removeItem(key);
|
|
292
|
+
}
|
|
362
293
|
}
|
|
363
294
|
async clear() {
|
|
364
|
-
if (typeof localStorage
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
}
|
|
295
|
+
if (typeof localStorage !== 'undefined') {
|
|
296
|
+
Object.values(AUTH_STORAGE_KEYS).forEach(key => {
|
|
297
|
+
localStorage.removeItem(key);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
369
300
|
}
|
|
370
301
|
}
|
|
371
302
|
/**
|
|
@@ -390,34 +321,29 @@ class MemoryTokenStorage {
|
|
|
390
321
|
}
|
|
391
322
|
/**
|
|
392
323
|
* Token manager for authentication tokens
|
|
393
|
-
* Clean, focused on auth tokens only
|
|
394
324
|
*/
|
|
395
325
|
class AuthTokenManager {
|
|
396
326
|
constructor(storage = new LocalStorageTokenStorage()) {
|
|
397
327
|
this.storage = storage;
|
|
398
328
|
}
|
|
399
|
-
// Access token methods
|
|
400
329
|
async getAccessToken() {
|
|
401
330
|
return this.storage.get(AUTH_STORAGE_KEYS.ACCESS_TOKEN);
|
|
402
331
|
}
|
|
403
332
|
async setAccessToken(token) {
|
|
404
333
|
await this.storage.set(AUTH_STORAGE_KEYS.ACCESS_TOKEN, token);
|
|
405
334
|
}
|
|
406
|
-
// Refresh token methods
|
|
407
335
|
async getRefreshToken() {
|
|
408
336
|
return this.storage.get(AUTH_STORAGE_KEYS.REFRESH_TOKEN);
|
|
409
337
|
}
|
|
410
338
|
async setRefreshToken(token) {
|
|
411
339
|
await this.storage.set(AUTH_STORAGE_KEYS.REFRESH_TOKEN, token);
|
|
412
340
|
}
|
|
413
|
-
// Provider token methods
|
|
414
341
|
async getProviderToken() {
|
|
415
342
|
return this.storage.get(AUTH_STORAGE_KEYS.PROVIDER_TOKEN);
|
|
416
343
|
}
|
|
417
344
|
async setProviderToken(token) {
|
|
418
345
|
await this.storage.set(AUTH_STORAGE_KEYS.PROVIDER_TOKEN, token);
|
|
419
346
|
}
|
|
420
|
-
// Auth type methods
|
|
421
347
|
async getAuthType() {
|
|
422
348
|
const authType = await this.storage.get(AUTH_STORAGE_KEYS.AUTH_TYPE);
|
|
423
349
|
return authType;
|
|
@@ -425,7 +351,6 @@ class AuthTokenManager {
|
|
|
425
351
|
async setAuthType(authType) {
|
|
426
352
|
await this.storage.set(AUTH_STORAGE_KEYS.AUTH_TYPE, authType);
|
|
427
353
|
}
|
|
428
|
-
// Bulk operations
|
|
429
354
|
async setTokens(accessToken, refreshToken, providerToken) {
|
|
430
355
|
await this.setAccessToken(accessToken);
|
|
431
356
|
if (refreshToken)
|
|
@@ -436,7 +361,6 @@ class AuthTokenManager {
|
|
|
436
361
|
async clearAllTokens() {
|
|
437
362
|
await this.storage.clear();
|
|
438
363
|
}
|
|
439
|
-
// Utility methods
|
|
440
364
|
async hasAccessToken() {
|
|
441
365
|
const token = await this.getAccessToken();
|
|
442
366
|
return !!token;
|
|
@@ -448,31 +372,24 @@ class AuthTokenManager {
|
|
|
448
372
|
}
|
|
449
373
|
|
|
450
374
|
/**
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
* Simple, focused implementation of PERS authentication
|
|
454
|
-
* with clear separation of concerns and minimal complexity.
|
|
375
|
+
* Default Authentication Provider
|
|
376
|
+
* Simple implementation of PERS authentication with token lifecycle management
|
|
455
377
|
*/
|
|
456
378
|
/**
|
|
457
|
-
*
|
|
458
|
-
* Handles token lifecycle with minimal complexity
|
|
379
|
+
* Simple authentication provider with token storage
|
|
459
380
|
*/
|
|
460
381
|
class DefaultAuthProvider {
|
|
461
382
|
constructor(config = {}) {
|
|
462
383
|
this.config = config;
|
|
463
384
|
this.authType = config.authType || 'user';
|
|
464
|
-
// Use custom storage if provided, otherwise auto-detect
|
|
465
385
|
const storage = config.storage || this.createStorage();
|
|
466
386
|
this.tokenManager = new AuthTokenManager(storage);
|
|
467
387
|
}
|
|
468
388
|
createStorage() {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}
|
|
473
|
-
return new LocalStorageTokenStorage();
|
|
389
|
+
return typeof window === 'undefined' || typeof localStorage === 'undefined'
|
|
390
|
+
? new MemoryTokenStorage()
|
|
391
|
+
: new LocalStorageTokenStorage();
|
|
474
392
|
}
|
|
475
|
-
// Core PersAuthProvider methods
|
|
476
393
|
getToken() {
|
|
477
394
|
return this.tokenManager.getAccessToken();
|
|
478
395
|
}
|
|
@@ -481,14 +398,10 @@ class DefaultAuthProvider {
|
|
|
481
398
|
}
|
|
482
399
|
async onTokenExpired() {
|
|
483
400
|
try {
|
|
484
|
-
// Note: Internal refresh token logic is now handled by PersApiClient.attemptInternalRefresh()
|
|
485
|
-
// This callback is only called when internal refresh fails or no refresh token is available
|
|
486
|
-
// Try custom expiration handler first
|
|
487
401
|
if (this.config.onTokenExpired) {
|
|
488
402
|
await this.config.onTokenExpired();
|
|
489
403
|
return;
|
|
490
404
|
}
|
|
491
|
-
// Try token provider (Firebase, Auth0, etc.) as fallback
|
|
492
405
|
if (this.config.tokenProvider) {
|
|
493
406
|
const newToken = await this.config.tokenProvider();
|
|
494
407
|
if (newToken) {
|
|
@@ -497,15 +410,12 @@ class DefaultAuthProvider {
|
|
|
497
410
|
return;
|
|
498
411
|
}
|
|
499
412
|
}
|
|
500
|
-
// Final fallback: clear all tokens (logout)
|
|
501
413
|
await this.tokenManager.clearAllTokens();
|
|
502
414
|
}
|
|
503
415
|
catch (error) {
|
|
504
|
-
// On any error, clear tokens to ensure clean state
|
|
505
416
|
await this.tokenManager.clearAllTokens();
|
|
506
417
|
}
|
|
507
418
|
}
|
|
508
|
-
// TokenStorageProvider methods
|
|
509
419
|
async setAccessToken(token) {
|
|
510
420
|
await this.tokenManager.setAccessToken(token);
|
|
511
421
|
}
|
|
@@ -518,7 +428,6 @@ class DefaultAuthProvider {
|
|
|
518
428
|
async clearTokens() {
|
|
519
429
|
await this.tokenManager.clearAllTokens();
|
|
520
430
|
}
|
|
521
|
-
// Utility methods
|
|
522
431
|
async setTokens(accessToken, refreshToken, providerToken) {
|
|
523
432
|
await this.tokenManager.setTokens(accessToken, refreshToken, providerToken);
|
|
524
433
|
}
|
|
@@ -620,39 +529,21 @@ class PersApiError extends Error {
|
|
|
620
529
|
|
|
621
530
|
// packages/pers-sdk/src/core/pers-api-client.ts
|
|
622
531
|
/**
|
|
623
|
-
* PERS API Client -
|
|
532
|
+
* PERS API Client - Platform-agnostic HTTP client with authentication
|
|
624
533
|
*
|
|
625
|
-
*
|
|
626
|
-
* proactive refresh, and comprehensive error handling.
|
|
627
|
-
*
|
|
628
|
-
* Features:
|
|
629
|
-
* - Automatic token refresh before expiry
|
|
630
|
-
* - Background refresh for optimal performance
|
|
631
|
-
* - Provider token fallback for seamless authentication
|
|
632
|
-
* - Configurable retry and timeout settings
|
|
633
|
-
* - Platform-agnostic design
|
|
534
|
+
* Features: automatic token refresh, background validation, configurable settings
|
|
634
535
|
*
|
|
635
536
|
* @example
|
|
636
537
|
* ```typescript
|
|
637
538
|
* const client = new PersApiClient(httpClient, {
|
|
638
539
|
* environment: 'production',
|
|
639
540
|
* apiProjectKey: 'your-project-key',
|
|
640
|
-
* authProvider: new DefaultAuthProvider(
|
|
641
|
-
* tokenProvider: () => getFirebaseToken()
|
|
642
|
-
* })
|
|
541
|
+
* authProvider: new DefaultAuthProvider()
|
|
643
542
|
* });
|
|
644
543
|
*
|
|
645
|
-
* // Make authenticated requests
|
|
646
544
|
* const data = await client.get('/users/me');
|
|
647
545
|
* ```
|
|
648
546
|
*/
|
|
649
|
-
// Auth constants
|
|
650
|
-
const AUTH_METHODS = {
|
|
651
|
-
GET: 'GET',
|
|
652
|
-
POST: 'POST',
|
|
653
|
-
PUT: 'PUT',
|
|
654
|
-
DELETE: 'DELETE',
|
|
655
|
-
};
|
|
656
547
|
class PersApiClient {
|
|
657
548
|
/**
|
|
658
549
|
* Creates a new PERS API Client instance
|
|
@@ -701,72 +592,36 @@ class PersApiClient {
|
|
|
701
592
|
async attemptInternalRefresh() {
|
|
702
593
|
return this.refreshManager.attemptInternalRefresh();
|
|
703
594
|
}
|
|
704
|
-
/**
|
|
705
|
-
* Get request headers including auth token and project key
|
|
706
|
-
*/
|
|
707
|
-
async getHeaders() {
|
|
708
|
-
const headers = {
|
|
709
|
-
'Content-Type': 'application/json',
|
|
710
|
-
};
|
|
711
|
-
// Add authentication token
|
|
712
|
-
if (this.mergedConfig.authProvider) {
|
|
713
|
-
const token = await this.mergedConfig.authProvider.getToken();
|
|
714
|
-
if (token) {
|
|
715
|
-
headers['Authorization'] = `Bearer ${token}`;
|
|
716
|
-
}
|
|
717
|
-
else {
|
|
718
|
-
console.warn('[PersApiClient] No token available from auth provider');
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
else {
|
|
722
|
-
console.warn('[PersApiClient] No auth provider configured');
|
|
723
|
-
}
|
|
724
|
-
// Add project key
|
|
725
|
-
if (this.mergedConfig.authProvider) {
|
|
726
|
-
const projectKey = await this.mergedConfig.authProvider.getProjectKey();
|
|
727
|
-
if (projectKey) {
|
|
728
|
-
headers['x-project-key'] = projectKey;
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
else if (this.mergedConfig.apiProjectKey) {
|
|
732
|
-
// Fallback to config project key if no auth provider
|
|
733
|
-
headers['x-project-key'] = this.mergedConfig.apiProjectKey;
|
|
734
|
-
}
|
|
735
|
-
return headers;
|
|
736
|
-
}
|
|
737
595
|
/**
|
|
738
596
|
* Make a request with proper headers, auth, and error handling
|
|
739
597
|
*/
|
|
740
598
|
async request(method, endpoint, body, options) {
|
|
741
599
|
const { retryCount = 0, responseType = 'json', bypassAuth = false } = options || {};
|
|
742
600
|
const url = `${this.apiRoot}${endpoint}`;
|
|
743
|
-
// Proactive token
|
|
744
|
-
// Non-blocking for optimal performance - request proceeds immediately
|
|
601
|
+
// Proactive token validation for optimal performance
|
|
745
602
|
if (!bypassAuth && this.mergedConfig.authProvider && retryCount === 0) {
|
|
746
603
|
this.ensureValidToken().catch(error => {
|
|
747
|
-
// Log but don't block request - 401 handling will catch any issues
|
|
748
604
|
console.debug('[PersApiClient] Background token validation failed:', error);
|
|
749
605
|
});
|
|
750
606
|
}
|
|
751
607
|
const requestOptions = {
|
|
752
|
-
headers:
|
|
608
|
+
headers: await this.getHeaders(!bypassAuth),
|
|
753
609
|
timeout: this.mergedConfig.timeout,
|
|
754
610
|
responseType
|
|
755
611
|
};
|
|
756
|
-
// const isCSVEndpoint = endpoint.includes('/export/csv');
|
|
757
612
|
try {
|
|
758
613
|
let result;
|
|
759
614
|
switch (method) {
|
|
760
|
-
case
|
|
615
|
+
case 'GET':
|
|
761
616
|
result = await this.httpClient.get(url, requestOptions);
|
|
762
617
|
break;
|
|
763
|
-
case
|
|
618
|
+
case 'POST':
|
|
764
619
|
result = await this.httpClient.post(url, body, requestOptions);
|
|
765
620
|
break;
|
|
766
|
-
case
|
|
621
|
+
case 'PUT':
|
|
767
622
|
result = await this.httpClient.put(url, body, requestOptions);
|
|
768
623
|
break;
|
|
769
|
-
case
|
|
624
|
+
case 'DELETE':
|
|
770
625
|
result = await this.httpClient.delete(url, requestOptions);
|
|
771
626
|
break;
|
|
772
627
|
default:
|
|
@@ -819,46 +674,51 @@ class PersApiClient {
|
|
|
819
674
|
* ```
|
|
820
675
|
*/
|
|
821
676
|
async get(endpoint, responseType) {
|
|
822
|
-
return this.request(
|
|
677
|
+
return this.request('GET', endpoint, undefined, { responseType });
|
|
823
678
|
}
|
|
824
679
|
/**
|
|
825
680
|
* Performs an authenticated POST request
|
|
826
|
-
*
|
|
827
|
-
* @template T - Expected response type
|
|
828
|
-
* @param endpoint - API endpoint path (without base URL)
|
|
829
|
-
* @param body - Request payload data
|
|
830
|
-
* @param options - Request options including auth bypass
|
|
831
|
-
* @returns Promise resolving to typed response data
|
|
832
|
-
*
|
|
833
|
-
* @example
|
|
834
|
-
* ```typescript
|
|
835
|
-
* const user = await client.post<User>('/users', userData);
|
|
836
|
-
* const publicData = await client.post('/public/contact', formData, { bypassAuth: true });
|
|
837
|
-
* ```
|
|
838
681
|
*/
|
|
839
682
|
async post(endpoint, body, options) {
|
|
840
|
-
return this.request(
|
|
683
|
+
return this.request('POST', endpoint, body, options);
|
|
841
684
|
}
|
|
842
685
|
/**
|
|
843
686
|
* Generic PUT request
|
|
844
687
|
*/
|
|
845
688
|
async put(endpoint, body) {
|
|
846
|
-
return this.request(
|
|
689
|
+
return this.request('PUT', endpoint, body);
|
|
847
690
|
}
|
|
848
691
|
/**
|
|
849
692
|
* Generic DELETE request
|
|
850
693
|
*/
|
|
851
694
|
async delete(endpoint) {
|
|
852
|
-
return this.request(
|
|
695
|
+
return this.request('DELETE', endpoint);
|
|
853
696
|
}
|
|
854
697
|
/**
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
698
|
+
* Get request headers with optional auth token and project key
|
|
699
|
+
*
|
|
700
|
+
* @param includeAuth - Whether to include the Authorization header (default: true)
|
|
701
|
+
*/
|
|
702
|
+
async getHeaders(includeAuth = true) {
|
|
858
703
|
const headers = {
|
|
859
704
|
'Content-Type': 'application/json',
|
|
860
705
|
};
|
|
861
|
-
|
|
706
|
+
if (includeAuth) {
|
|
707
|
+
// Add authentication token
|
|
708
|
+
if (this.mergedConfig.authProvider) {
|
|
709
|
+
const token = await this.mergedConfig.authProvider.getToken();
|
|
710
|
+
if (token) {
|
|
711
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
console.warn('[PersApiClient] No token available from auth provider');
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
else {
|
|
718
|
+
console.warn('[PersApiClient] No auth provider configured');
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
// Add project key
|
|
862
722
|
if (this.mergedConfig.authProvider) {
|
|
863
723
|
const projectKey = await this.mergedConfig.authProvider.getProjectKey();
|
|
864
724
|
if (projectKey) {
|
|
@@ -871,11 +731,10 @@ class PersApiClient {
|
|
|
871
731
|
return headers;
|
|
872
732
|
}
|
|
873
733
|
// ==========================================
|
|
874
|
-
// INTERNAL AUTH METHODS
|
|
734
|
+
// INTERNAL AUTH METHODS
|
|
875
735
|
// ==========================================
|
|
876
736
|
/**
|
|
877
737
|
* @internal - For AuthManager use only
|
|
878
|
-
* Direct access to auth service for manager layer
|
|
879
738
|
*/
|
|
880
739
|
getAuthService() {
|
|
881
740
|
return this.authService;
|
|
@@ -896,25 +755,21 @@ class PersApiClient {
|
|
|
896
755
|
|
|
897
756
|
/**
|
|
898
757
|
* Environment Detection Utility
|
|
899
|
-
*
|
|
900
|
-
* Detects the runtime environment to handle platform-specific behaviors
|
|
901
|
-
* for bundling and module resolution.
|
|
758
|
+
* Detects runtime environment for platform-specific behaviors
|
|
902
759
|
*/
|
|
903
760
|
/**
|
|
904
761
|
* Detects the current runtime environment
|
|
905
762
|
*/
|
|
906
763
|
function detectEnvironment() {
|
|
764
|
+
const global = globalThis;
|
|
907
765
|
const isNode = typeof process !== 'undefined' &&
|
|
908
|
-
process.versions != null &&
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
typeof globalThis.navigator.product === 'string' &&
|
|
915
|
-
globalThis.navigator.product.toLowerCase() === 'reactnative';
|
|
766
|
+
process.versions?.node != null &&
|
|
767
|
+
typeof global.window === 'undefined';
|
|
768
|
+
const isBrowser = typeof global.window !== 'undefined' &&
|
|
769
|
+
typeof global.document !== 'undefined';
|
|
770
|
+
const isReactNative = typeof global.navigator?.product === 'string' &&
|
|
771
|
+
global.navigator.product.toLowerCase() === 'reactnative';
|
|
916
772
|
const isWeb = isBrowser || isReactNative;
|
|
917
|
-
// Node.js streams: Only native in Node.js, need polyfills or avoidance in web
|
|
918
773
|
const needsNodeStreamPolyfills = isWeb;
|
|
919
774
|
return {
|
|
920
775
|
isNode,
|
|
@@ -929,7 +784,7 @@ function detectEnvironment() {
|
|
|
929
784
|
*/
|
|
930
785
|
const environment = detectEnvironment();
|
|
931
786
|
/**
|
|
932
|
-
* Warns if
|
|
787
|
+
* Warns if environment might have bundling issues
|
|
933
788
|
*/
|
|
934
789
|
function warnIfProblematicEnvironment(feature) {
|
|
935
790
|
if (environment.needsNodeStreamPolyfills) {
|
|
@@ -5847,4 +5702,4 @@ exports.detectEnvironment = detectEnvironment;
|
|
|
5847
5702
|
exports.environment = environment;
|
|
5848
5703
|
exports.mergeWithDefaults = mergeWithDefaults;
|
|
5849
5704
|
exports.warnIfProblematicEnvironment = warnIfProblematicEnvironment;
|
|
5850
|
-
//# sourceMappingURL=pers-sdk-
|
|
5705
|
+
//# sourceMappingURL=pers-sdk-CmyPEhy7.cjs.map
|