@tstdl/base 0.92.158 → 0.92.160
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.
|
@@ -27,6 +27,7 @@ export declare class AuthenticationClientService<AdditionalTokenPayload extends
|
|
|
27
27
|
private readonly lock;
|
|
28
28
|
private readonly logger;
|
|
29
29
|
private readonly disposeToken;
|
|
30
|
+
private clockOffset;
|
|
30
31
|
/**
|
|
31
32
|
* Observable for authentication errors.
|
|
32
33
|
* Emits when a refresh fails.
|
|
@@ -83,7 +84,7 @@ export declare class AuthenticationClientService<AdditionalTokenPayload extends
|
|
|
83
84
|
get definedSessionId(): string;
|
|
84
85
|
/** Whether a valid token is available (not undefined and not expired) */
|
|
85
86
|
get hasValidToken(): boolean;
|
|
86
|
-
constructor(
|
|
87
|
+
constructor();
|
|
87
88
|
/** @internal */
|
|
88
89
|
[afterResolve](): void;
|
|
89
90
|
/**
|
|
@@ -113,53 +114,64 @@ export declare class AuthenticationClientService<AdditionalTokenPayload extends
|
|
|
113
114
|
*/
|
|
114
115
|
login(subject: string, secret: string, data?: AuthenticationData): Promise<void>;
|
|
115
116
|
/**
|
|
116
|
-
* Logout
|
|
117
|
+
* Logout from the current session.
|
|
118
|
+
* This will attempt to end the session on the server and then clear local credentials.
|
|
117
119
|
*/
|
|
118
120
|
logout(): Promise<void>;
|
|
119
121
|
/**
|
|
120
|
-
* Force
|
|
122
|
+
* Force an immediate refresh of the token.
|
|
121
123
|
* @param data Additional authentication data
|
|
122
124
|
*/
|
|
123
125
|
requestRefresh(data?: AuthenticationData): void;
|
|
124
126
|
/**
|
|
125
|
-
* Refresh the token
|
|
127
|
+
* Refresh the token.
|
|
126
128
|
* @param data Additional authentication data
|
|
127
129
|
*/
|
|
128
130
|
refresh(data?: AuthenticationData): Promise<void>;
|
|
129
131
|
/**
|
|
130
|
-
* Impersonate a subject
|
|
132
|
+
* Impersonate a subject.
|
|
131
133
|
* @param subject The subject to impersonate
|
|
132
|
-
* @param data Additional authentication data
|
|
134
|
+
* @param data Additional authentication data for the impersonated session
|
|
133
135
|
*/
|
|
134
136
|
impersonate(subject: string, data?: AuthenticationData): Promise<void>;
|
|
135
137
|
/**
|
|
136
|
-
*
|
|
138
|
+
* End impersonation and return to the original user session.
|
|
137
139
|
* @param data Additional authentication data. If not provided, the data from before impersonation is used.
|
|
138
140
|
*/
|
|
139
141
|
unimpersonate(data?: AuthenticationData): Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Change the secret for a subject.
|
|
144
|
+
* @param subject The subject to change the secret for
|
|
145
|
+
* @param currentSecret The current secret
|
|
146
|
+
* @param newSecret The new secret
|
|
147
|
+
*/
|
|
140
148
|
changeSecret(subject: string, currentSecret: string, newSecret: string): Promise<void>;
|
|
141
149
|
/**
|
|
142
|
-
* Initialize a secret reset
|
|
150
|
+
* Initialize a secret reset.
|
|
143
151
|
* @param subject The subject to reset the secret for
|
|
144
152
|
* @param data Additional data for secret reset
|
|
145
153
|
*/
|
|
146
154
|
initResetSecret(subject: string, data: AdditionalInitSecretResetData): Promise<void>;
|
|
147
155
|
/**
|
|
148
|
-
* Reset a secret
|
|
156
|
+
* Reset a secret using a reset token.
|
|
149
157
|
* @param token The secret reset token
|
|
150
158
|
* @param newSecret The new secret
|
|
151
159
|
*/
|
|
152
160
|
resetSecret(token: string, newSecret: string): Promise<void>;
|
|
153
161
|
/**
|
|
154
|
-
* Check a secret for requirements
|
|
162
|
+
* Check a secret for requirements.
|
|
155
163
|
* @param secret The secret to check
|
|
156
164
|
* @returns The result of the check
|
|
157
165
|
*/
|
|
158
166
|
checkSecret(secret: string): Promise<SecretCheckResult>;
|
|
159
|
-
private saveToken;
|
|
160
|
-
private loadToken;
|
|
161
167
|
private setNewToken;
|
|
162
168
|
private refreshLoop;
|
|
163
169
|
private refreshLoopIteration;
|
|
164
170
|
private handleRefreshError;
|
|
171
|
+
private estimatedServerTimestampSeconds;
|
|
172
|
+
private syncClock;
|
|
173
|
+
private saveToken;
|
|
174
|
+
private loadToken;
|
|
175
|
+
private readFromStorage;
|
|
176
|
+
private writeToStorage;
|
|
165
177
|
}
|
|
@@ -7,9 +7,6 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
-
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
-
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
-
};
|
|
13
10
|
import { Subject, filter, firstValueFrom, race, timer } from 'rxjs';
|
|
14
11
|
import { CancellationToken } from '../../cancellation/token.js';
|
|
15
12
|
import { disposeAsync } from '../../disposable/index.js';
|
|
@@ -19,14 +16,16 @@ import { InvalidTokenError } from '../../errors/invalid-token.error.js';
|
|
|
19
16
|
import { NotFoundError } from '../../errors/not-found.error.js';
|
|
20
17
|
import { NotSupportedError } from '../../errors/not-supported.error.js';
|
|
21
18
|
import { UnauthorizedError } from '../../errors/unauthorized.error.js';
|
|
22
|
-
import {
|
|
19
|
+
import { Singleton, afterResolve, inject } from '../../injector/index.js';
|
|
23
20
|
import { Lock } from '../../lock/index.js';
|
|
24
21
|
import { Logger } from '../../logger/index.js';
|
|
25
22
|
import { MessageBus } from '../../message-bus/index.js';
|
|
26
23
|
import { computed, signal, toObservable } from '../../signals/api.js';
|
|
27
24
|
import { currentTimestampSeconds } from '../../utils/date-time.js';
|
|
25
|
+
import { formatError } from '../../utils/format-error.js';
|
|
28
26
|
import { timeout } from '../../utils/timing.js';
|
|
29
|
-
import { assertDefinedPass, isDefined, isNullOrUndefined,
|
|
27
|
+
import { assertDefinedPass, isDefined, isNullOrUndefined, isUndefined } from '../../utils/type-guards.js';
|
|
28
|
+
import { millisecondsPerSecond } from '../../utils/units.js';
|
|
30
29
|
import { AUTHENTICATION_API_CLIENT, INITIAL_AUTHENTICATION_DATA } from './tokens.js';
|
|
31
30
|
const tokenStorageKey = 'AuthenticationService:token';
|
|
32
31
|
const authenticationDataStorageKey = 'AuthenticationService:authentication-data';
|
|
@@ -35,6 +34,17 @@ const tokenUpdateBusName = 'AuthenticationService:tokenUpdate';
|
|
|
35
34
|
const loggedOutBusName = 'AuthenticationService:loggedOut';
|
|
36
35
|
const refreshLockResource = 'AuthenticationService:refresh';
|
|
37
36
|
const localStorage = globalThis.localStorage;
|
|
37
|
+
const refreshBufferSeconds = 15;
|
|
38
|
+
const lockTimeout = 10000;
|
|
39
|
+
const logoutTimeout = 150;
|
|
40
|
+
const unrecoverableErrors = [
|
|
41
|
+
InvalidTokenError,
|
|
42
|
+
NotFoundError,
|
|
43
|
+
BadRequestError,
|
|
44
|
+
ForbiddenError,
|
|
45
|
+
NotSupportedError,
|
|
46
|
+
UnauthorizedError,
|
|
47
|
+
];
|
|
38
48
|
/**
|
|
39
49
|
* Handles authentication on client side.
|
|
40
50
|
*
|
|
@@ -58,6 +68,7 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
58
68
|
lock = inject(Lock, refreshLockResource);
|
|
59
69
|
logger = inject(Logger, 'AuthenticationService');
|
|
60
70
|
disposeToken = new CancellationToken();
|
|
71
|
+
clockOffset = 0;
|
|
61
72
|
/**
|
|
62
73
|
* Observable for authentication errors.
|
|
63
74
|
* Emits when a refresh fails.
|
|
@@ -80,7 +91,7 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
80
91
|
/** Emits when token is available (not undefined) */
|
|
81
92
|
definedToken$ = this.token$.pipe(filter(isDefined));
|
|
82
93
|
/** Emits when a valid token is available (not undefined and not expired) */
|
|
83
|
-
validToken$ = this.definedToken$.pipe(filter((token) => token.exp >
|
|
94
|
+
validToken$ = this.definedToken$.pipe(filter((token) => token.exp > this.estimatedServerTimestampSeconds()));
|
|
84
95
|
/** Current subject */
|
|
85
96
|
subject$ = toObservable(this.subject);
|
|
86
97
|
/** Emits when subject is available */
|
|
@@ -94,30 +105,16 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
94
105
|
/** Emits when the user logs out */
|
|
95
106
|
loggedOut$ = this.loggedOutBus.allMessages$;
|
|
96
107
|
get authenticationData() {
|
|
97
|
-
|
|
98
|
-
return isNullOrUndefined(data) ? undefined : JSON.parse(data);
|
|
108
|
+
return this.readFromStorage(authenticationDataStorageKey);
|
|
99
109
|
}
|
|
100
110
|
set authenticationData(data) {
|
|
101
|
-
|
|
102
|
-
localStorage?.removeItem(authenticationDataStorageKey);
|
|
103
|
-
}
|
|
104
|
-
else {
|
|
105
|
-
const json = JSON.stringify(data);
|
|
106
|
-
localStorage?.setItem(authenticationDataStorageKey, json);
|
|
107
|
-
}
|
|
111
|
+
this.writeToStorage(authenticationDataStorageKey, data);
|
|
108
112
|
}
|
|
109
113
|
get impersonatorAuthenticationData() {
|
|
110
|
-
|
|
111
|
-
return isNullOrUndefined(data) ? undefined : JSON.parse(data);
|
|
114
|
+
return this.readFromStorage(impersonatorAuthenticationDataStorageKey);
|
|
112
115
|
}
|
|
113
116
|
set impersonatorAuthenticationData(data) {
|
|
114
|
-
|
|
115
|
-
localStorage?.removeItem(impersonatorAuthenticationDataStorageKey);
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
const json = JSON.stringify(data);
|
|
119
|
-
localStorage?.setItem(impersonatorAuthenticationDataStorageKey, json);
|
|
120
|
-
}
|
|
117
|
+
this.writeToStorage(impersonatorAuthenticationDataStorageKey, data);
|
|
121
118
|
}
|
|
122
119
|
/**
|
|
123
120
|
* Get current token or throw if not available
|
|
@@ -142,11 +139,11 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
142
139
|
}
|
|
143
140
|
/** Whether a valid token is available (not undefined and not expired) */
|
|
144
141
|
get hasValidToken() {
|
|
145
|
-
return (this.token()?.exp ?? 0) >
|
|
142
|
+
return (this.token()?.exp ?? 0) > this.estimatedServerTimestampSeconds();
|
|
146
143
|
}
|
|
147
|
-
constructor(
|
|
144
|
+
constructor() {
|
|
148
145
|
if (isUndefined(this.authenticationData)) {
|
|
149
|
-
this.authenticationData =
|
|
146
|
+
this.authenticationData = inject(INITIAL_AUTHENTICATION_DATA, undefined, { optional: true });
|
|
150
147
|
}
|
|
151
148
|
}
|
|
152
149
|
/** @internal */
|
|
@@ -195,26 +192,31 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
195
192
|
if (isDefined(data)) {
|
|
196
193
|
this.setAdditionalData(data);
|
|
197
194
|
}
|
|
198
|
-
const token = await
|
|
195
|
+
const [token] = await Promise.all([
|
|
196
|
+
this.client.login({ subject, secret, data: this.authenticationData }),
|
|
197
|
+
this.syncClock(),
|
|
198
|
+
]);
|
|
199
199
|
this.setNewToken(token);
|
|
200
200
|
}
|
|
201
201
|
/**
|
|
202
|
-
* Logout
|
|
202
|
+
* Logout from the current session.
|
|
203
|
+
* This will attempt to end the session on the server and then clear local credentials.
|
|
203
204
|
*/
|
|
204
205
|
async logout() {
|
|
205
206
|
try {
|
|
206
207
|
await Promise.race([
|
|
207
208
|
this.client.endSession(),
|
|
208
|
-
timeout(
|
|
209
|
+
timeout(logoutTimeout),
|
|
209
210
|
]).catch((error) => this.logger.error(error));
|
|
210
211
|
}
|
|
211
212
|
finally {
|
|
213
|
+
// Always clear the local token, even if the server call fails.
|
|
212
214
|
this.setNewToken(undefined);
|
|
213
215
|
this.loggedOutBus.publishAndForget();
|
|
214
216
|
}
|
|
215
217
|
}
|
|
216
218
|
/**
|
|
217
|
-
* Force
|
|
219
|
+
* Force an immediate refresh of the token.
|
|
218
220
|
* @param data Additional authentication data
|
|
219
221
|
*/
|
|
220
222
|
requestRefresh(data) {
|
|
@@ -224,7 +226,7 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
224
226
|
this.forceRefreshToken.set();
|
|
225
227
|
}
|
|
226
228
|
/**
|
|
227
|
-
* Refresh the token
|
|
229
|
+
* Refresh the token.
|
|
228
230
|
* @param data Additional authentication data
|
|
229
231
|
*/
|
|
230
232
|
async refresh(data) {
|
|
@@ -232,7 +234,10 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
232
234
|
this.setAdditionalData(data);
|
|
233
235
|
}
|
|
234
236
|
try {
|
|
235
|
-
const token = await
|
|
237
|
+
const [token] = await Promise.all([
|
|
238
|
+
this.client.refresh({ data: this.authenticationData }),
|
|
239
|
+
this.syncClock(),
|
|
240
|
+
]);
|
|
236
241
|
this.setNewToken(token);
|
|
237
242
|
}
|
|
238
243
|
catch (error) {
|
|
@@ -241,12 +246,15 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
241
246
|
}
|
|
242
247
|
}
|
|
243
248
|
/**
|
|
244
|
-
* Impersonate a subject
|
|
249
|
+
* Impersonate a subject.
|
|
245
250
|
* @param subject The subject to impersonate
|
|
246
|
-
* @param data Additional authentication data
|
|
251
|
+
* @param data Additional authentication data for the impersonated session
|
|
247
252
|
*/
|
|
248
253
|
async impersonate(subject, data) {
|
|
249
|
-
|
|
254
|
+
if (this.impersonated()) {
|
|
255
|
+
throw new Error('Already impersonating. Please unimpersonate first.');
|
|
256
|
+
}
|
|
257
|
+
await this.lock.use(lockTimeout, true, async () => {
|
|
250
258
|
this.impersonatorAuthenticationData = this.authenticationData;
|
|
251
259
|
this.authenticationData = data;
|
|
252
260
|
try {
|
|
@@ -254,17 +262,20 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
254
262
|
this.setNewToken(token);
|
|
255
263
|
}
|
|
256
264
|
catch (error) {
|
|
265
|
+
// Rollback authentication data on failure
|
|
266
|
+
this.authenticationData = this.impersonatorAuthenticationData;
|
|
267
|
+
this.impersonatorAuthenticationData = undefined;
|
|
257
268
|
await this.handleRefreshError(error);
|
|
258
269
|
throw error;
|
|
259
270
|
}
|
|
260
271
|
});
|
|
261
272
|
}
|
|
262
273
|
/**
|
|
263
|
-
*
|
|
274
|
+
* End impersonation and return to the original user session.
|
|
264
275
|
* @param data Additional authentication data. If not provided, the data from before impersonation is used.
|
|
265
276
|
*/
|
|
266
277
|
async unimpersonate(data) {
|
|
267
|
-
await this.lock.use(
|
|
278
|
+
await this.lock.use(lockTimeout, true, async () => {
|
|
268
279
|
const newData = data ?? this.impersonatorAuthenticationData;
|
|
269
280
|
try {
|
|
270
281
|
const token = await this.client.unimpersonate({ data: newData });
|
|
@@ -278,11 +289,17 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
278
289
|
}
|
|
279
290
|
});
|
|
280
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* Change the secret for a subject.
|
|
294
|
+
* @param subject The subject to change the secret for
|
|
295
|
+
* @param currentSecret The current secret
|
|
296
|
+
* @param newSecret The new secret
|
|
297
|
+
*/
|
|
281
298
|
async changeSecret(subject, currentSecret, newSecret) {
|
|
282
299
|
await this.client.changeSecret({ subject, currentSecret, newSecret });
|
|
283
300
|
}
|
|
284
301
|
/**
|
|
285
|
-
* Initialize a secret reset
|
|
302
|
+
* Initialize a secret reset.
|
|
286
303
|
* @param subject The subject to reset the secret for
|
|
287
304
|
* @param data Additional data for secret reset
|
|
288
305
|
*/
|
|
@@ -290,7 +307,7 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
290
307
|
await this.client.initSecretReset({ subject, data });
|
|
291
308
|
}
|
|
292
309
|
/**
|
|
293
|
-
* Reset a secret
|
|
310
|
+
* Reset a secret using a reset token.
|
|
294
311
|
* @param token The secret reset token
|
|
295
312
|
* @param newSecret The new secret
|
|
296
313
|
*/
|
|
@@ -298,39 +315,30 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
298
315
|
await this.client.resetSecret({ token, newSecret });
|
|
299
316
|
}
|
|
300
317
|
/**
|
|
301
|
-
* Check a secret for requirements
|
|
318
|
+
* Check a secret for requirements.
|
|
302
319
|
* @param secret The secret to check
|
|
303
320
|
* @returns The result of the check
|
|
304
321
|
*/
|
|
305
322
|
async checkSecret(secret) {
|
|
306
323
|
return await this.client.checkSecret({ secret });
|
|
307
324
|
}
|
|
308
|
-
saveToken(token) {
|
|
309
|
-
if (isNullOrUndefined(token)) {
|
|
310
|
-
localStorage?.removeItem(tokenStorageKey);
|
|
311
|
-
}
|
|
312
|
-
else {
|
|
313
|
-
const serialized = JSON.stringify(token);
|
|
314
|
-
localStorage?.setItem(tokenStorageKey, serialized);
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
loadToken() {
|
|
318
|
-
const existingSerializedToken = localStorage?.getItem(tokenStorageKey);
|
|
319
|
-
const token = isString(existingSerializedToken)
|
|
320
|
-
? JSON.parse(existingSerializedToken)
|
|
321
|
-
: undefined;
|
|
322
|
-
this.token.set(token);
|
|
323
|
-
}
|
|
324
325
|
setNewToken(token) {
|
|
325
326
|
this.saveToken(token);
|
|
326
327
|
this.token.set(token);
|
|
327
328
|
this.tokenUpdateBus.publishAndForget(token);
|
|
328
329
|
}
|
|
329
330
|
async refreshLoop() {
|
|
331
|
+
if (this.isLoggedIn()) {
|
|
332
|
+
await this.syncClock();
|
|
333
|
+
}
|
|
330
334
|
while (this.disposeToken.isUnset) {
|
|
331
335
|
try {
|
|
336
|
+
// Use a non-blocking lock to ensure only one tab/instance runs the refresh logic at a time.
|
|
332
337
|
await this.lock.use(0, false, async () => await this.refreshLoopIteration());
|
|
333
|
-
|
|
338
|
+
// Calculate delay until the next refresh check.
|
|
339
|
+
// The buffer ensures we refresh *before* the token actually expires.
|
|
340
|
+
const delay = ((this.token()?.exp ?? 0) - this.estimatedServerTimestampSeconds() - refreshBufferSeconds) * millisecondsPerSecond;
|
|
341
|
+
await firstValueFrom(race([timer(delay), this.disposeToken, this.forceRefreshToken]));
|
|
334
342
|
}
|
|
335
343
|
catch {
|
|
336
344
|
await firstValueFrom(race([timer(5000), this.disposeToken, this.forceRefreshToken]));
|
|
@@ -338,27 +346,74 @@ let AuthenticationClientService = class AuthenticationClientService {
|
|
|
338
346
|
}
|
|
339
347
|
}
|
|
340
348
|
async refreshLoopIteration() {
|
|
349
|
+
// Wait for a token to be available or for the service to be disposed.
|
|
341
350
|
const token = await firstValueFrom(race([this.definedToken$, this.disposeToken]));
|
|
342
351
|
if (isUndefined(token)) {
|
|
343
352
|
return;
|
|
344
353
|
}
|
|
345
|
-
|
|
354
|
+
const needsRefresh = this.estimatedServerTimestampSeconds() >= (token.exp - refreshBufferSeconds);
|
|
355
|
+
if (this.forceRefreshToken.isSet || needsRefresh) {
|
|
346
356
|
this.forceRefreshToken.unset();
|
|
347
|
-
await this.refresh();
|
|
357
|
+
await this.refresh(); // Errors are caught by the outer loop
|
|
348
358
|
}
|
|
349
359
|
}
|
|
350
360
|
async handleRefreshError(error) {
|
|
351
361
|
this.logger.error(error);
|
|
352
362
|
this.errorSubject.next(error);
|
|
353
|
-
if ((
|
|
363
|
+
if (unrecoverableErrors.some((errorType) => error instanceof errorType)) {
|
|
354
364
|
await this.logout();
|
|
355
365
|
}
|
|
356
366
|
}
|
|
367
|
+
estimatedServerTimestampSeconds() {
|
|
368
|
+
return currentTimestampSeconds() + this.clockOffset;
|
|
369
|
+
}
|
|
370
|
+
async syncClock() {
|
|
371
|
+
try {
|
|
372
|
+
const serverTimestamp = await this.client.timestamp();
|
|
373
|
+
this.clockOffset = serverTimestamp - currentTimestampSeconds();
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
this.logger.warn(`Failed to synchronize clock with server: ${formatError(error)}`);
|
|
377
|
+
this.clockOffset = 0;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
saveToken(token) {
|
|
381
|
+
this.writeToStorage(tokenStorageKey, token);
|
|
382
|
+
}
|
|
383
|
+
loadToken() {
|
|
384
|
+
const token = this.readFromStorage(tokenStorageKey);
|
|
385
|
+
this.token.set(token);
|
|
386
|
+
}
|
|
387
|
+
readFromStorage(key) {
|
|
388
|
+
try {
|
|
389
|
+
const serialized = localStorage?.getItem(key);
|
|
390
|
+
if (isNullOrUndefined(serialized)) {
|
|
391
|
+
return undefined;
|
|
392
|
+
}
|
|
393
|
+
return JSON.parse(serialized);
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
this.logger.warn(`Failed to read and parse from localStorage key "${key}": ${formatError(error)}`);
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
writeToStorage(key, value) {
|
|
401
|
+
try {
|
|
402
|
+
if (isUndefined(value)) {
|
|
403
|
+
localStorage?.removeItem(key);
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
const serialized = JSON.stringify(value);
|
|
407
|
+
localStorage?.setItem(key, serialized);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
this.logger.warn(`Failed to write to localStorage key "${key}": ${formatError(error)}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
357
414
|
};
|
|
358
415
|
AuthenticationClientService = __decorate([
|
|
359
416
|
Singleton(),
|
|
360
|
-
|
|
361
|
-
__param(0, Optional()),
|
|
362
|
-
__metadata("design:paramtypes", [Object])
|
|
417
|
+
__metadata("design:paramtypes", [])
|
|
363
418
|
], AuthenticationClientService);
|
|
364
419
|
export { AuthenticationClientService };
|
|
@@ -69,7 +69,7 @@ export declare class AuthenticationApiController<AdditionalTokenPayload extends
|
|
|
69
69
|
checkSecret({ parameters }: ApiRequestContext<AuthenticationApiDefinition<AdditionalTokenPayload, AuthenticationData, AdditionalInitSecretResetData>, 'checkSecret'>): Promise<ApiServerResult<AuthenticationApiDefinition<AdditionalTokenPayload, AuthenticationData, AdditionalInitSecretResetData>, 'checkSecret'>>;
|
|
70
70
|
/**
|
|
71
71
|
* Get the current server timestamp.
|
|
72
|
-
* @returns The current server timestamp.
|
|
72
|
+
* @returns The current server timestamp in seconds.
|
|
73
73
|
*/
|
|
74
74
|
timestamp(): ApiServerResult<AuthenticationApiDefinition<AdditionalTokenPayload, AuthenticationData, AdditionalInitSecretResetData>, 'timestamp'>;
|
|
75
75
|
protected getTokenResponse({ token, jsonToken, refreshToken, omitImpersonatorRefreshToken, impersonatorRefreshToken, impersonatorRefreshTokenExpiration }: TokenResult<AdditionalTokenPayload>): HttpServerResponse;
|
|
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
};
|
|
10
10
|
import { apiController } from '../../api/server/index.js';
|
|
11
11
|
import { HttpServerResponse } from '../../http/server/index.js';
|
|
12
|
-
import {
|
|
12
|
+
import { currentTimestampSeconds } from '../../utils/date-time.js';
|
|
13
13
|
import { assertDefinedPass, isDefined } from '../../utils/type-guards.js';
|
|
14
14
|
import { authenticationApiDefinition, getAuthenticationApiDefinition } from '../authentication.api.js';
|
|
15
15
|
import { AuthenticationService } from './authentication.service.js';
|
|
@@ -138,10 +138,10 @@ let AuthenticationApiController = class AuthenticationApiController {
|
|
|
138
138
|
}
|
|
139
139
|
/**
|
|
140
140
|
* Get the current server timestamp.
|
|
141
|
-
* @returns The current server timestamp.
|
|
141
|
+
* @returns The current server timestamp in seconds.
|
|
142
142
|
*/
|
|
143
143
|
timestamp() {
|
|
144
|
-
return
|
|
144
|
+
return currentTimestampSeconds();
|
|
145
145
|
}
|
|
146
146
|
getTokenResponse({ token, jsonToken, refreshToken, omitImpersonatorRefreshToken, impersonatorRefreshToken, impersonatorRefreshTokenExpiration }) {
|
|
147
147
|
const result = jsonToken.payload;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tstdl/base",
|
|
3
|
-
"version": "0.92.
|
|
3
|
+
"version": "0.92.160",
|
|
4
4
|
"author": "Patrick Hein",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
"./jsx": "./jsx/index.js",
|
|
81
81
|
"./key-value-store": "./key-value-store/index.js",
|
|
82
82
|
"./key-value-store/mongo": "./key-value-store/mongo/index.js",
|
|
83
|
+
"./key-value-store/postgres": "./key-value-store/postgres/index.js",
|
|
83
84
|
"./lock": "./lock/index.js",
|
|
84
85
|
"./lock/mongo": "./lock/mongo/index.js",
|
|
85
86
|
"./lock/web": "./lock/web/index.js",
|