auth-vir 5.0.0 → 5.0.2

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 (49) hide show
  1. package/package.json +8 -8
  2. package/src/auth-client/backend-auth.client.ts +8 -0
  3. package/src/auth.ts +9 -1
  4. package/dist/auth-client/backend-auth.client.d.ts +0 -263
  5. package/dist/auth-client/backend-auth.client.js +0 -391
  6. package/dist/auth-client/frontend-auth.client.d.ts +0 -113
  7. package/dist/auth-client/frontend-auth.client.js +0 -131
  8. package/dist/auth-client/is-session-refresh-ready.d.ts +0 -23
  9. package/dist/auth-client/is-session-refresh-ready.js +0 -21
  10. package/dist/auth.d.ts +0 -74
  11. package/dist/auth.js +0 -128
  12. package/dist/cookie.d.ts +0 -111
  13. package/dist/cookie.js +0 -137
  14. package/dist/csrf-token.d.ts +0 -33
  15. package/dist/csrf-token.js +0 -42
  16. package/dist/generated/browser.d.ts +0 -9
  17. package/dist/generated/browser.js +0 -17
  18. package/dist/generated/client.d.ts +0 -26
  19. package/dist/generated/client.js +0 -32
  20. package/dist/generated/commonInputTypes.d.ts +0 -122
  21. package/dist/generated/commonInputTypes.js +0 -1
  22. package/dist/generated/enums.d.ts +0 -1
  23. package/dist/generated/enums.js +0 -10
  24. package/dist/generated/internal/class.d.ts +0 -126
  25. package/dist/generated/internal/class.js +0 -85
  26. package/dist/generated/internal/prismaNamespace.d.ts +0 -545
  27. package/dist/generated/internal/prismaNamespace.js +0 -102
  28. package/dist/generated/internal/prismaNamespaceBrowser.d.ts +0 -75
  29. package/dist/generated/internal/prismaNamespaceBrowser.js +0 -70
  30. package/dist/generated/models/User.d.ts +0 -980
  31. package/dist/generated/models/User.js +0 -1
  32. package/dist/generated/models.d.ts +0 -2
  33. package/dist/generated/models.js +0 -1
  34. package/dist/generated/shapes.gen.d.ts +0 -8
  35. package/dist/generated/shapes.gen.js +0 -11
  36. package/dist/hash.d.ts +0 -42
  37. package/dist/hash.js +0 -52
  38. package/dist/headers.d.ts +0 -19
  39. package/dist/headers.js +0 -32
  40. package/dist/index.d.ts +0 -11
  41. package/dist/index.js +0 -11
  42. package/dist/jwt/jwt-keys.d.ts +0 -44
  43. package/dist/jwt/jwt-keys.js +0 -57
  44. package/dist/jwt/jwt-keys.script.d.ts +0 -1
  45. package/dist/jwt/jwt-keys.script.js +0 -3
  46. package/dist/jwt/jwt.d.ts +0 -126
  47. package/dist/jwt/jwt.js +0 -109
  48. package/dist/jwt/user-jwt.d.ts +0 -44
  49. package/dist/jwt/user-jwt.js +0 -53
@@ -1,391 +0,0 @@
1
- import { ensureArray, } from '@augment-vir/common';
2
- import { calculateRelativeDate, createUtcFullDate, getNowInUtcTimezone, isDateAfter, } from 'date-vir';
3
- import { extractUserIdFromRequestHeaders, generateLogoutHeaders, generateSuccessfulLoginHeaders, insecureExtractUserIdFromCookieAlone, } from '../auth.js';
4
- import { AuthCookie, generateAuthCookie, generateCsrfCookie } from '../cookie.js';
5
- import { AuthHeaderName, mergeHeaderValues } from '../headers.js';
6
- import { parseJwtKeys } from '../jwt/jwt-keys.js';
7
- import { defaultAllowedClockSkew } from '../jwt/jwt.js';
8
- import { isSessionRefreshReady } from './is-session-refresh-ready.js';
9
- const defaultSessionIdleTimeout = {
10
- minutes: 20,
11
- };
12
- const defaultSessionRefreshStartTime = {
13
- minutes: 2,
14
- };
15
- const defaultMaxSessionDuration = {
16
- days: 1.5,
17
- };
18
- /**
19
- * An auth client for creating and validating JWTs embedded in cookies. This should only be used in
20
- * a backend environment as it accesses native Node packages.
21
- *
22
- * @category Auth : Host
23
- * @category Clients
24
- */
25
- export class BackendAuthClient {
26
- config;
27
- cachedParsedJwtKeys = {};
28
- constructor(config) {
29
- this.config = config;
30
- }
31
- /** Conditionally logs a message if logging is enabled for the given user context. */
32
- logForUser(params, message, extra) {
33
- if (this.config.enableLogging?.(params)) {
34
- const extraData = {
35
- userId: params.userId,
36
- ...extra,
37
- };
38
- if (this.config.log) {
39
- this.config.log(message, extraData);
40
- }
41
- else {
42
- console.info(`[auth-vir] ${message}`, extraData);
43
- }
44
- }
45
- }
46
- /** Get all the parameters used for cookie generation. */
47
- async getCookieParams({ isSignUpCookie, requestHeaders, }) {
48
- const serviceOrigin = requestHeaders
49
- ? await this.config.generateServiceOrigin?.({
50
- requestHeaders,
51
- })
52
- : undefined;
53
- return {
54
- cookieDuration: this.config.userSessionIdleTimeout || defaultSessionIdleTimeout,
55
- hostOrigin: serviceOrigin || this.config.serviceOrigin,
56
- jwtParams: await this.getJwtParams(),
57
- isDev: this.config.isDev,
58
- authCookie: isSignUpCookie ? AuthCookie.SignUp : AuthCookie.Auth,
59
- };
60
- }
61
- /** Calls the provided `getUserFromDatabase` config. */
62
- async getDatabaseUser({ isSignUpCookie, userId, assumingUser, requestHeaders, }) {
63
- if (!userId) {
64
- return undefined;
65
- }
66
- const authenticatedUser = await this.config.getUserFromDatabase({
67
- assumingUser,
68
- userId,
69
- isSignUpCookie,
70
- requestHeaders,
71
- });
72
- if (!authenticatedUser) {
73
- this.logForUser({
74
- user: undefined,
75
- userId,
76
- assumedUserParams: assumingUser,
77
- }, 'getUserFromDatabase returned no user', {
78
- isSignUpCookie,
79
- });
80
- return undefined;
81
- }
82
- return authenticatedUser;
83
- }
84
- /** Creates a `'cookie-set'` header to refresh the user's session cookie. */
85
- async createCookieRefreshHeaders({ userIdResult, requestHeaders, }) {
86
- const now = getNowInUtcTimezone();
87
- const clockSkew = this.config.allowedClockSkew || defaultAllowedClockSkew;
88
- /** Double check that the JWT hasn't already expired (with clock skew tolerance). */
89
- const isExpiredAlready = isDateAfter({
90
- fullDate: now,
91
- relativeTo: calculateRelativeDate(userIdResult.jwtExpiration, clockSkew),
92
- });
93
- if (isExpiredAlready) {
94
- this.logForUser({
95
- user: undefined,
96
- userId: userIdResult.userId,
97
- assumedUserParams: undefined,
98
- }, 'Session refresh denied: JWT already expired (even with clock skew tolerance)', {
99
- jwtExpiration: userIdResult.jwtExpiration,
100
- now: JSON.stringify(now),
101
- });
102
- return undefined;
103
- }
104
- /**
105
- * Check if the session has exceeded the max session duration. If so, don't refresh the
106
- * session and let it expire naturally.
107
- */
108
- const maxSessionDuration = this.config.maxSessionDuration || defaultMaxSessionDuration;
109
- if (userIdResult.sessionStartedAt) {
110
- const sessionStartDate = createUtcFullDate(userIdResult.sessionStartedAt);
111
- const maxSessionEndDate = calculateRelativeDate(sessionStartDate, maxSessionDuration);
112
- const isSessionExpired = isDateAfter({
113
- fullDate: now,
114
- relativeTo: maxSessionEndDate,
115
- });
116
- if (isSessionExpired) {
117
- this.logForUser({
118
- user: undefined,
119
- userId: userIdResult.userId,
120
- assumedUserParams: undefined,
121
- }, 'Session refresh denied: max session duration exceeded', {
122
- sessionStartedAt: userIdResult.sessionStartedAt,
123
- maxSessionEndDate: JSON.stringify(maxSessionEndDate),
124
- now: JSON.stringify(now),
125
- });
126
- return undefined;
127
- }
128
- }
129
- const sessionRefreshStartTime = this.config.sessionRefreshStartTime || defaultSessionRefreshStartTime;
130
- const isRefreshReady = isSessionRefreshReady({
131
- now,
132
- jwtIssuedAt: userIdResult.jwtIssuedAt,
133
- sessionRefreshStartTime,
134
- });
135
- if (isRefreshReady) {
136
- const isSignUpCookie = userIdResult.cookieName === AuthCookie.SignUp;
137
- const cookieParams = await this.getCookieParams({
138
- isSignUpCookie,
139
- requestHeaders,
140
- });
141
- const authCookie = await generateAuthCookie({
142
- csrfToken: userIdResult.csrfToken,
143
- userId: userIdResult.userId,
144
- sessionStartedAt: userIdResult.sessionStartedAt || Date.now(),
145
- }, cookieParams);
146
- const csrfCookie = generateCsrfCookie(userIdResult.csrfToken, cookieParams);
147
- return {
148
- 'set-cookie': [
149
- authCookie,
150
- csrfCookie,
151
- ],
152
- };
153
- }
154
- else {
155
- this.logForUser({
156
- user: undefined,
157
- userId: userIdResult.userId,
158
- assumedUserParams: undefined,
159
- }, 'Session refresh skipped: not yet ready for refresh', {
160
- jwtIssuedAt: userIdResult.jwtIssuedAt,
161
- sessionRefreshStartTime,
162
- });
163
- return undefined;
164
- }
165
- }
166
- /** Reads the user's assumed user headers and, if configured, gets the assumed user. */
167
- async getAssumedUser({ requestHeaders, user, }) {
168
- if (!this.config.assumeUser || !(await this.config.assumeUser.canAssumeUser(user))) {
169
- return undefined;
170
- }
171
- const assumedUserHeader = ensureArray(requestHeaders[this.config.assumedUserHeaderName || AuthHeaderName.AssumedUser])[0];
172
- if (!assumedUserHeader) {
173
- return undefined;
174
- }
175
- const parsedAssumedUserData = await this.config.assumeUser.parseAssumedUserHeaderValue(assumedUserHeader);
176
- if (!parsedAssumedUserData || !parsedAssumedUserData.userId) {
177
- return undefined;
178
- }
179
- const assumedUser = await this.getDatabaseUser({
180
- isSignUpCookie: false,
181
- userId: parsedAssumedUserData.userId,
182
- assumingUser: parsedAssumedUserData.assumedUserParams,
183
- requestHeaders,
184
- });
185
- return assumedUser;
186
- }
187
- /** Securely extract a user from their request headers. */
188
- async getSecureUser({ requestHeaders, isSignUpCookie, allowUserAuthRefresh, }) {
189
- const userIdResult = await extractUserIdFromRequestHeaders(requestHeaders, await this.getJwtParams(), this.config.csrf, isSignUpCookie ? AuthCookie.SignUp : AuthCookie.Auth);
190
- if (!userIdResult) {
191
- this.logForUser({
192
- user: undefined,
193
- userId: undefined,
194
- assumedUserParams: undefined,
195
- }, 'getSecureUser: failed to extract user ID from request headers (invalid JWT, missing cookie, or CSRF mismatch)', {
196
- isSignUpCookie,
197
- });
198
- return undefined;
199
- }
200
- const user = await this.getDatabaseUser({
201
- userId: userIdResult.userId,
202
- assumingUser: undefined,
203
- isSignUpCookie,
204
- requestHeaders,
205
- });
206
- if (!user) {
207
- this.logForUser({
208
- user: undefined,
209
- userId: userIdResult.userId,
210
- assumedUserParams: undefined,
211
- }, 'getSecureUser: user not found in database', {
212
- isSignUpCookie,
213
- });
214
- return undefined;
215
- }
216
- const assumedUser = await this.getAssumedUser({
217
- requestHeaders,
218
- user,
219
- });
220
- const cookieRefreshHeaders = allowUserAuthRefresh
221
- ? await this.createCookieRefreshHeaders({
222
- userIdResult,
223
- requestHeaders,
224
- })
225
- : undefined;
226
- /**
227
- * Always include the CSRF cookie so it gets re-established if the browser clears it. When
228
- * session refresh fires, its headers already include a CSRF cookie.
229
- */
230
- const csrfCookie = generateCsrfCookie(userIdResult.csrfToken, {
231
- hostOrigin: (await this.config.generateServiceOrigin?.({
232
- requestHeaders,
233
- })) || this.config.serviceOrigin,
234
- isDev: this.config.isDev,
235
- });
236
- return {
237
- user: assumedUser || user,
238
- isAssumed: !!assumedUser,
239
- responseHeaders: {
240
- 'set-cookie': mergeHeaderValues(cookieRefreshHeaders?.['set-cookie'], csrfCookie),
241
- },
242
- };
243
- }
244
- /**
245
- * Get all the JWT params used when creating the auth cookie, in case you need them for
246
- * something else too.
247
- */
248
- async getJwtParams() {
249
- const rawJwtKeys = await this.config.getJwtKeys();
250
- const cacheKey = JSON.stringify(rawJwtKeys);
251
- const cachedParsedKeys = this.cachedParsedJwtKeys[cacheKey];
252
- const parsedKeys = cachedParsedKeys || (await parseJwtKeys(rawJwtKeys));
253
- if (!cachedParsedKeys) {
254
- this.cachedParsedJwtKeys = {
255
- [cacheKey]: parsedKeys,
256
- };
257
- }
258
- return {
259
- jwtKeys: parsedKeys,
260
- audience: 'server-context',
261
- issuer: 'server-auth',
262
- jwtDuration: this.config.userSessionIdleTimeout || defaultSessionIdleTimeout,
263
- allowedClockSkew: this.config.allowedClockSkew || defaultAllowedClockSkew,
264
- };
265
- }
266
- /** Use these headers to log out the user. */
267
- async createLogoutHeaders(params) {
268
- const signUpCookieHeaders = params.allCookies || params.isSignUpCookie
269
- ? generateLogoutHeaders(await this.getCookieParams({
270
- isSignUpCookie: true,
271
- requestHeaders: undefined,
272
- }))
273
- : undefined;
274
- const authCookieHeaders = params.allCookies || !params.isSignUpCookie
275
- ? generateLogoutHeaders(await this.getCookieParams({
276
- isSignUpCookie: false,
277
- requestHeaders: undefined,
278
- }))
279
- : undefined;
280
- return {
281
- 'set-cookie': mergeHeaderValues(signUpCookieHeaders?.['set-cookie'], authCookieHeaders?.['set-cookie']),
282
- };
283
- }
284
- /**
285
- * Refreshes a login session by reissuing the auth cookie with the same CSRF token instead of
286
- * generating a new one.
287
- */
288
- async refreshLoginHeaders({ userId, cookieParams, existingUserIdResult, }) {
289
- const authCookie = await generateAuthCookie({
290
- csrfToken: existingUserIdResult.csrfToken,
291
- userId,
292
- sessionStartedAt: existingUserIdResult.sessionStartedAt,
293
- }, cookieParams);
294
- const csrfCookie = generateCsrfCookie(existingUserIdResult.csrfToken, cookieParams);
295
- return {
296
- 'set-cookie': [
297
- authCookie,
298
- csrfCookie,
299
- ],
300
- };
301
- }
302
- /** Use these headers to log a user in. */
303
- async createLoginHeaders({ userId, requestHeaders, isSignUpCookie, }) {
304
- const oppositeCookieName = isSignUpCookie ? AuthCookie.Auth : AuthCookie.SignUp;
305
- const hasExistingOppositeCookie = requestHeaders.cookie?.includes(`${oppositeCookieName}=`);
306
- const discardOppositeCookieHeaders = hasExistingOppositeCookie
307
- ? generateLogoutHeaders(await this.getCookieParams({
308
- isSignUpCookie: !isSignUpCookie,
309
- requestHeaders,
310
- }))
311
- : undefined;
312
- const existingUserIdResult = await extractUserIdFromRequestHeaders(requestHeaders, await this.getJwtParams(), this.config.csrf, isSignUpCookie ? AuthCookie.SignUp : AuthCookie.Auth);
313
- const cookieParams = await this.getCookieParams({
314
- isSignUpCookie,
315
- requestHeaders,
316
- });
317
- const newCookieHeaders = existingUserIdResult
318
- ? await this.refreshLoginHeaders({
319
- userId,
320
- cookieParams,
321
- existingUserIdResult,
322
- })
323
- : await generateSuccessfulLoginHeaders(userId, cookieParams);
324
- return {
325
- ...newCookieHeaders,
326
- 'set-cookie': mergeHeaderValues(newCookieHeaders['set-cookie'], discardOppositeCookieHeaders?.['set-cookie']),
327
- ...(isSignUpCookie
328
- ? {
329
- [AuthHeaderName.IsSignUpAuth]: 'true',
330
- }
331
- : {}),
332
- };
333
- }
334
- /** Combines `.getInsecureUser()` and `.getSecureUser()` into one method. */
335
- async getInsecureOrSecureUser(params) {
336
- const secureUser = await this.getSecureUser(params);
337
- if (secureUser) {
338
- return {
339
- secureUser,
340
- };
341
- }
342
- // eslint-disable-next-line @typescript-eslint/no-deprecated
343
- const insecureUser = await this.getInsecureUser(params);
344
- return insecureUser
345
- ? {
346
- insecureUser,
347
- }
348
- : {};
349
- }
350
- /**
351
- * @deprecated This only half authenticates the user. It should only be used in circumstances
352
- * where JavaScript cannot be used to attach the CSRF token header to the request (like when
353
- * opening a PDF file). Use `.getSecureUser()` instead, whenever possible.
354
- */
355
- async getInsecureUser({ requestHeaders, allowUserAuthRefresh, }) {
356
- // eslint-disable-next-line @typescript-eslint/no-deprecated
357
- const userIdResult = await insecureExtractUserIdFromCookieAlone(requestHeaders, await this.getJwtParams(), AuthCookie.Auth);
358
- if (!userIdResult) {
359
- this.logForUser({
360
- user: undefined,
361
- userId: undefined,
362
- assumedUserParams: undefined,
363
- }, 'getInsecureUser: failed to extract user ID from cookie (invalid JWT or missing cookie)');
364
- return undefined;
365
- }
366
- const user = await this.getDatabaseUser({
367
- isSignUpCookie: false,
368
- userId: userIdResult.userId,
369
- assumingUser: undefined,
370
- requestHeaders,
371
- });
372
- if (!user) {
373
- this.logForUser({
374
- user: undefined,
375
- userId: userIdResult.userId,
376
- assumedUserParams: undefined,
377
- }, 'getInsecureUser: user not found in database');
378
- return undefined;
379
- }
380
- const refreshHeaders = allowUserAuthRefresh &&
381
- (await this.createCookieRefreshHeaders({
382
- userIdResult,
383
- requestHeaders,
384
- }));
385
- return {
386
- user,
387
- isAssumed: false,
388
- responseHeaders: refreshHeaders || {},
389
- };
390
- }
391
- }
@@ -1,113 +0,0 @@
1
- import { type createBlockingInterval, type JsonCompatibleObject, type MaybePromise, type PartialWithUndefined, type SelectFrom } from '@augment-vir/common';
2
- import { type AnyDuration } from 'date-vir';
3
- import { type EmptyObject } from 'type-fest';
4
- import { type CsrfHeaderNameOption } from '../csrf-token.js';
5
- /**
6
- * Config for {@link FrontendAuthClient}.
7
- *
8
- * @category Internal
9
- */
10
- export type FrontendAuthClientConfig = Readonly<{
11
- csrf: Readonly<CsrfHeaderNameOption>;
12
- }> & PartialWithUndefined<{
13
- /**
14
- * Determine if the current user can assume the identity of another user. If this is not
15
- * defined, all users will be blocked from assuming other user identities.
16
- */
17
- canAssumeUser: () => MaybePromise<boolean>;
18
- /** Called whenever the current user becomes unauthorized and their CSRF token is wiped. */
19
- authClearedCallback: () => MaybePromise<void>;
20
- /**
21
- * Performs automatic checks on an interval to see if the user is still authenticated. Omit
22
- * this to turn off automatic checks.
23
- */
24
- checkUser: {
25
- /**
26
- * Get a response from the backend to see if the user is still authenticated. If the
27
- * response returns a non-authorized status, the user is wiped. Any other status is
28
- * ignored.
29
- *
30
- * If the user is not currently authorized, this should return `undefined` to prevent
31
- * unnecessary network traffic.
32
- *
33
- * This will be called any time the user interacts with the page, debounced by the
34
- * adjacent `debounce` property.
35
- */
36
- performCheck: () => MaybePromise<SelectFrom<Response, {
37
- status: true;
38
- }> | undefined>;
39
- /**
40
- * Debounce for firing `performCheck`.
41
- *
42
- * @default {minutes: 1}
43
- */
44
- debounce?: AnyDuration | undefined;
45
- };
46
- /**
47
- * Overwrite the header name used for tracking is an admin is assuming the identity of
48
- * another user.
49
- */
50
- assumedUserHeaderName: string;
51
- overrides: PartialWithUndefined<{
52
- localStorage: SelectFrom<Storage, {
53
- setItem: true;
54
- removeItem: true;
55
- getItem: true;
56
- }>;
57
- }>;
58
- }>;
59
- /**
60
- * An auth client for sending and validating client requests to a backend. This should only be used
61
- * in a frontend environment as it accesses native browser APIs.
62
- *
63
- * @category Auth : Client
64
- * @category Clients
65
- */
66
- export declare class FrontendAuthClient<AssumedUserParams extends JsonCompatibleObject = EmptyObject> {
67
- protected readonly config: FrontendAuthClientConfig;
68
- protected userCheckInterval: undefined | ReturnType<typeof createBlockingInterval>;
69
- /** Used to clean up the activity listener on `.destroy()`. */
70
- protected removeActivityListener: VoidFunction | undefined;
71
- constructor(config: FrontendAuthClientConfig);
72
- /**
73
- * Destroys the client and performs all necessary cleanup (like clearing the user check
74
- * interval).
75
- */
76
- destroy(): void;
77
- /**
78
- * Assume the given user. Pass `undefined` to wipe the currently assumed user.
79
- *
80
- * @returns Whether the assumed user setting or clearing succeeded or not.
81
- */
82
- assumeUser(assumedUserParams: Readonly<AssumedUserParams> | undefined): Promise<boolean>;
83
- /** Gets the assumed user params stored in local storage, if any. */
84
- getAssumedUser(): AssumedUserParams | undefined;
85
- /**
86
- * Creates a `RequestInit` object for the `fetch` API. If you have other request init options,
87
- * use [`mergeDeep` from
88
- * `@augment-vir/common`](https://electrovir.github.io/augment-vir/functions/mergeDeep.html) to
89
- * combine them with these.
90
- */
91
- createAuthenticatedRequestInit(): RequestInit;
92
- /** Wipes the current user auth. */
93
- logout(): Promise<void>;
94
- /**
95
- * Use to handle a login response. The CSRF token cookie is automatically stored by the browser
96
- * from the `Set-Cookie` response header.
97
- *
98
- * @throws Error if the login response failed.
99
- */
100
- handleLoginResponse(response: Readonly<SelectFrom<Response, {
101
- ok: true;
102
- }>>): Promise<void>;
103
- /**
104
- * Use to verify _all_ responses received from the backend. Immediately logs the user out once
105
- * an unauthorized response is detected.
106
- *
107
- * @returns `true` if the auth is okay, `false` otherwise.
108
- */
109
- verifyResponseAuth(response: Readonly<PartialWithUndefined<SelectFrom<Response, {
110
- status: true;
111
- headers: true;
112
- }>>>): Promise<boolean>;
113
- }
@@ -1,131 +0,0 @@
1
- import { HttpStatus, } from '@augment-vir/common';
2
- import { listenToActivity } from 'detect-activity';
3
- import { getCurrentCsrfToken, resolveCsrfHeaderName, } from '../csrf-token.js';
4
- import { AuthHeaderName } from '../headers.js';
5
- /**
6
- * An auth client for sending and validating client requests to a backend. This should only be used
7
- * in a frontend environment as it accesses native browser APIs.
8
- *
9
- * @category Auth : Client
10
- * @category Clients
11
- */
12
- export class FrontendAuthClient {
13
- config;
14
- userCheckInterval;
15
- /** Used to clean up the activity listener on `.destroy()`. */
16
- removeActivityListener;
17
- constructor(config) {
18
- this.config = config;
19
- if (config.checkUser) {
20
- this.removeActivityListener = listenToActivity({
21
- listener: async () => {
22
- const response = await config.checkUser?.performCheck();
23
- if (response) {
24
- await this.verifyResponseAuth({
25
- status: response.status,
26
- });
27
- }
28
- },
29
- debounce: config.checkUser.debounce || {
30
- minutes: 1,
31
- },
32
- fireImmediately: false,
33
- });
34
- }
35
- }
36
- /**
37
- * Destroys the client and performs all necessary cleanup (like clearing the user check
38
- * interval).
39
- */
40
- destroy() {
41
- this.userCheckInterval?.clearInterval();
42
- this.removeActivityListener?.();
43
- }
44
- /**
45
- * Assume the given user. Pass `undefined` to wipe the currently assumed user.
46
- *
47
- * @returns Whether the assumed user setting or clearing succeeded or not.
48
- */
49
- async assumeUser(assumedUserParams) {
50
- const localStorage = this.config.overrides?.localStorage || globalThis.localStorage;
51
- const storageKey = this.config.assumedUserHeaderName || AuthHeaderName.AssumedUser;
52
- if (!assumedUserParams) {
53
- localStorage.removeItem(storageKey);
54
- return true;
55
- }
56
- else if (!(await this.config.canAssumeUser?.())) {
57
- return false;
58
- }
59
- localStorage.setItem(storageKey, JSON.stringify(assumedUserParams));
60
- return true;
61
- }
62
- /** Gets the assumed user params stored in local storage, if any. */
63
- getAssumedUser() {
64
- const rawValue = (this.config.overrides?.localStorage || globalThis.localStorage).getItem(this.config.assumedUserHeaderName || AuthHeaderName.AssumedUser);
65
- if (!rawValue) {
66
- return undefined;
67
- }
68
- try {
69
- return JSON.parse(rawValue);
70
- }
71
- catch {
72
- return undefined;
73
- }
74
- }
75
- /**
76
- * Creates a `RequestInit` object for the `fetch` API. If you have other request init options,
77
- * use [`mergeDeep` from
78
- * `@augment-vir/common`](https://electrovir.github.io/augment-vir/functions/mergeDeep.html) to
79
- * combine them with these.
80
- */
81
- createAuthenticatedRequestInit() {
82
- const csrfToken = getCurrentCsrfToken();
83
- const assumedUser = this.getAssumedUser();
84
- const headers = {
85
- ...(csrfToken
86
- ? {
87
- [resolveCsrfHeaderName(this.config.csrf)]: csrfToken,
88
- }
89
- : {}),
90
- ...(assumedUser
91
- ? {
92
- [this.config.assumedUserHeaderName || AuthHeaderName.AssumedUser]: JSON.stringify(assumedUser),
93
- }
94
- : {}),
95
- };
96
- return {
97
- headers,
98
- credentials: 'include',
99
- };
100
- }
101
- /** Wipes the current user auth. */
102
- async logout() {
103
- await this.config.authClearedCallback?.();
104
- }
105
- /**
106
- * Use to handle a login response. The CSRF token cookie is automatically stored by the browser
107
- * from the `Set-Cookie` response header.
108
- *
109
- * @throws Error if the login response failed.
110
- */
111
- async handleLoginResponse(response) {
112
- if (!response.ok) {
113
- await this.logout();
114
- throw new Error('Login response failed.');
115
- }
116
- }
117
- /**
118
- * Use to verify _all_ responses received from the backend. Immediately logs the user out once
119
- * an unauthorized response is detected.
120
- *
121
- * @returns `true` if the auth is okay, `false` otherwise.
122
- */
123
- async verifyResponseAuth(response) {
124
- if (response.status === HttpStatus.Unauthorized &&
125
- !response.headers?.get(AuthHeaderName.IsSignUpAuth)) {
126
- await this.logout();
127
- return false;
128
- }
129
- return true;
130
- }
131
- }
@@ -1,23 +0,0 @@
1
- import { type AnyDuration, type FullDate, type UtcTimezone } from 'date-vir';
2
- /**
3
- * Determines if enough time has passed since the JWT was issued to start refreshing the session.
4
- *
5
- * Visually, this check looks like this:
6
- *
7
- * I====R===========E
8
- *
9
- * - I = JWT issued time (from the JWT's `iat` claim)
10
- * - R = session refreshing is available now (I + sessionRefreshStartTime)
11
- * - E = JWT expiration
12
- * - `=` between R and E = the time frame in which the return value is `true`.
13
- *
14
- * @category Auth : Host
15
- */
16
- export declare function isSessionRefreshReady({ now, jwtIssuedAt, sessionRefreshStartTime, }: {
17
- /** The current time. */
18
- now?: Readonly<FullDate<UtcTimezone>> | undefined;
19
- /** When the JWT was issued (`iat` claim). */
20
- jwtIssuedAt: Readonly<FullDate<UtcTimezone>>;
21
- /** How long after JWT issuance before refreshing is available. */
22
- sessionRefreshStartTime: Readonly<AnyDuration>;
23
- }): boolean;
@@ -1,21 +0,0 @@
1
- import { calculateRelativeDate, getNowInUtcTimezone, isDateAfter, } from 'date-vir';
2
- /**
3
- * Determines if enough time has passed since the JWT was issued to start refreshing the session.
4
- *
5
- * Visually, this check looks like this:
6
- *
7
- * I====R===========E
8
- *
9
- * - I = JWT issued time (from the JWT's `iat` claim)
10
- * - R = session refreshing is available now (I + sessionRefreshStartTime)
11
- * - E = JWT expiration
12
- * - `=` between R and E = the time frame in which the return value is `true`.
13
- *
14
- * @category Auth : Host
15
- */
16
- export function isSessionRefreshReady({ now = getNowInUtcTimezone(), jwtIssuedAt, sessionRefreshStartTime, }) {
17
- return isDateAfter({
18
- fullDate: now,
19
- relativeTo: calculateRelativeDate(jwtIssuedAt, sessionRefreshStartTime),
20
- });
21
- }