auth-vir 4.0.0 → 5.0.0

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/src/auth.ts CHANGED
@@ -1,20 +1,15 @@
1
- import {type PartialWithUndefined} from '@augment-vir/common';
1
+ import {type SelectFrom} from '@augment-vir/common';
2
2
  import {type FullDate, type UtcTimezone} from 'date-vir';
3
3
  import {
4
- AuthCookieName,
4
+ AuthCookie,
5
5
  clearAuthCookie,
6
+ clearCsrfCookie,
6
7
  type CookieParams,
7
8
  extractCookieJwt,
8
9
  generateAuthCookie,
10
+ generateCsrfCookie,
9
11
  } from './cookie.js';
10
- import {type CsrfTokenStore} from './csrf-token-store.js';
11
- import {
12
- type CsrfHeaderNameOption,
13
- extractCsrfTokenHeader,
14
- generateCsrfToken,
15
- resolveCsrfHeaderName,
16
- storeCsrfToken,
17
- } from './csrf-token.js';
12
+ import {type CsrfHeaderNameOption, generateCsrfToken, resolveCsrfHeaderName} from './csrf-token.js';
18
13
  import {type ParseJwtParams} from './jwt/jwt.js';
19
14
  import {type JwtUserData} from './jwt/user-jwt.js';
20
15
 
@@ -51,7 +46,7 @@ export type UserIdResult<UserId extends string | number> = {
51
46
  jwtExpiration: FullDate<UtcTimezone>;
52
47
  /** When the JWT was issued (`iat` claim). */
53
48
  jwtIssuedAt: FullDate<UtcTimezone>;
54
- cookieName: string;
49
+ cookieName: AuthCookie;
55
50
  /** The CSRF token embedded in the JWT. */
56
51
  csrfToken: string;
57
52
  /**
@@ -80,7 +75,7 @@ export async function extractUserIdFromRequestHeaders<UserId extends string | nu
80
75
  headers: HeaderContainer,
81
76
  jwtParams: Readonly<ParseJwtParams>,
82
77
  csrfHeaderNameOption: Readonly<CsrfHeaderNameOption>,
83
- cookieName: string = AuthCookieName.Auth,
78
+ cookieName: AuthCookie = AuthCookie.Auth,
84
79
  ): Promise<Readonly<UserIdResult<UserId>> | undefined> {
85
80
  try {
86
81
  const csrfToken = readCsrfTokenHeader(headers, csrfHeaderNameOption);
@@ -120,7 +115,7 @@ export async function extractUserIdFromRequestHeaders<UserId extends string | nu
120
115
  export async function insecureExtractUserIdFromCookieAlone<UserId extends string | number>(
121
116
  headers: HeaderContainer,
122
117
  jwtParams: Readonly<ParseJwtParams>,
123
- cookieName: string = AuthCookieName.Auth,
118
+ cookieName: AuthCookie,
124
119
  ): Promise<Readonly<UserIdResult<UserId>> | undefined> {
125
120
  try {
126
121
  const cookie = readHeader(headers, 'cookie');
@@ -149,7 +144,9 @@ export async function insecureExtractUserIdFromCookieAlone<UserId extends string
149
144
  }
150
145
 
151
146
  /**
152
- * Used by host (backend) code to set headers on a response object.
147
+ * Used by host (backend) code to set headers on a response object. Sets both the auth JWT cookie
148
+ * and the CSRF token cookie. The CSRF cookie is not `HttpOnly` so that frontend JavaScript can read
149
+ * it and inject the value as a request header.
153
150
  *
154
151
  * @category Auth : Host
155
152
  */
@@ -157,17 +154,15 @@ export async function generateSuccessfulLoginHeaders(
157
154
  /** The id from your database of the user you're authenticating. */
158
155
  userId: string | number,
159
156
  cookieConfig: Readonly<CookieParams>,
160
- csrfHeaderNameOption: Readonly<CsrfHeaderNameOption>,
161
157
  /**
162
158
  * The timestamp (in seconds) when the session originally started. If not provided, the current
163
159
  * time will be used (for new sessions).
164
160
  */
165
161
  sessionStartedAt?: number | undefined,
166
- ): Promise<Record<string, string>> {
162
+ ): Promise<Record<string, string[]>> {
167
163
  const csrfToken = generateCsrfToken();
168
- const csrfHeaderName = resolveCsrfHeaderName(csrfHeaderNameOption);
169
164
 
170
- const {cookie} = await generateAuthCookie(
165
+ const authCookie = await generateAuthCookie(
171
166
  {
172
167
  csrfToken,
173
168
  userId,
@@ -176,9 +171,13 @@ export async function generateSuccessfulLoginHeaders(
176
171
  cookieConfig,
177
172
  );
178
173
 
174
+ const csrfCookie = generateCsrfCookie(csrfToken, cookieConfig);
175
+
179
176
  return {
180
- 'set-cookie': cookie,
181
- [csrfHeaderName]: csrfToken,
177
+ 'set-cookie': [
178
+ authCookie,
179
+ csrfCookie,
180
+ ],
182
181
  };
183
182
  }
184
183
 
@@ -189,43 +188,12 @@ export async function generateSuccessfulLoginHeaders(
189
188
  * @category Auth : Host
190
189
  */
191
190
  export function generateLogoutHeaders(
192
- cookieConfig: Readonly<Pick<CookieParams, 'cookieName' | 'hostOrigin' | 'isDev'>>,
193
- ): Record<string, string> {
191
+ cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}>>,
192
+ ): Record<string, string[]> {
194
193
  return {
195
- 'set-cookie': clearAuthCookie(cookieConfig),
194
+ 'set-cookie': [
195
+ clearAuthCookie(cookieConfig),
196
+ clearCsrfCookie(cookieConfig),
197
+ ],
196
198
  };
197
199
  }
198
-
199
- /**
200
- * Store auth data on a client (frontend) after receiving an auth response from the host (backend).
201
- * Specifically, this stores the CSRF token into IndexedDB (which doesn't need to be a secret).
202
- * Alternatively, if the given response failed, this will wipe the existing (if any) stored CSRF
203
- * token.
204
- *
205
- * @category Auth : Client
206
- * @throws Error if no CSRF token header is found.
207
- */
208
- export async function handleAuthResponse(
209
- response: Readonly<Pick<Response, 'ok' | 'headers'>>,
210
- options: Readonly<CsrfHeaderNameOption> &
211
- PartialWithUndefined<{
212
- /**
213
- * Allows mocking or overriding the default CSRF token store.
214
- *
215
- * @default getDefaultCsrfTokenStore()
216
- */
217
- csrfTokenStore: CsrfTokenStore;
218
- }>,
219
- ): Promise<void> {
220
- if (!response.ok) {
221
- return;
222
- }
223
-
224
- const csrfToken = extractCsrfTokenHeader(response, options);
225
-
226
- if (!csrfToken) {
227
- throw new Error('Did not receive any CSRF token.');
228
- }
229
-
230
- await storeCsrfToken(csrfToken, options);
231
- }
package/src/cookie.ts CHANGED
@@ -1,13 +1,11 @@
1
1
  import {check} from '@augment-vir/assert';
2
- import {escapeStringForRegExp, safeMatch, type PartialWithUndefined} from '@augment-vir/common';
3
2
  import {
4
- calculateRelativeDate,
5
- convertDuration,
6
- getNowInUtcTimezone,
7
- type AnyDuration,
8
- type FullDate,
9
- type UtcTimezone,
10
- } from 'date-vir';
3
+ escapeStringForRegExp,
4
+ safeMatch,
5
+ type PartialWithUndefined,
6
+ type SelectFrom,
7
+ } from '@augment-vir/common';
8
+ import {convertDuration, type AnyDuration} from 'date-vir';
11
9
  import {type Primitive} from 'type-fest';
12
10
  import {parseUrl} from 'url-vir';
13
11
  import {type CreateJwtParams, type ParseJwtParams, type ParsedJwt} from './jwt/jwt.js';
@@ -18,11 +16,13 @@ import {createUserJwt, parseUserJwt, type JwtUserData} from './jwt/user-jwt.js';
18
16
  *
19
17
  * @category Internal
20
18
  */
21
- export enum AuthCookieName {
19
+ export enum AuthCookie {
22
20
  /** Used for a full user login auth. */
23
21
  Auth = 'auth',
24
22
  /** Use for a temporary "just signed up" auth. */
25
23
  SignUp = 'sign-up',
24
+ /** Used for storing the CSRF token. Not `HttpOnly` so that frontend JS can read it. */
25
+ Csrf = 'auth-vir-csrf',
26
26
  }
27
27
 
28
28
  /**
@@ -49,8 +49,13 @@ export type CookieParams = {
49
49
  * client, etc.
50
50
  */
51
51
  jwtParams: Readonly<CreateJwtParams>;
52
- cookieName?: string;
53
52
  } & PartialWithUndefined<{
53
+ /**
54
+ * Which auth cookie name to use.
55
+ *
56
+ * @default AuthCookie.Auth
57
+ */
58
+ authCookie: AuthCookie;
54
59
  /**
55
60
  * Is set to `true` (which should only be done in development environments), the cookie will be
56
61
  * allowed in insecure requests (non HTTPS requests).
@@ -60,15 +65,32 @@ export type CookieParams = {
60
65
  isDev: boolean;
61
66
  }>;
62
67
 
63
- /**
64
- * Output from {@link generateAuthCookie}.
65
- *
66
- * @category Internal
67
- */
68
- export type GenerateAuthCookieResult = {
69
- cookie: string;
70
- expiration: FullDate<UtcTimezone>;
71
- };
68
+ function generateSetCookie({
69
+ name,
70
+ value,
71
+ httpOnly,
72
+ cookieConfig,
73
+ }: {
74
+ name: string;
75
+ value: string;
76
+ httpOnly: boolean;
77
+ cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}>> &
78
+ PartialWithUndefined<SelectFrom<CookieParams, {cookieDuration: true}>>;
79
+ }): string {
80
+ return generateCookie({
81
+ [name]: value,
82
+ Domain: parseUrl(cookieConfig.hostOrigin).hostname,
83
+ HttpOnly: httpOnly,
84
+ Path: '/',
85
+ SameSite: 'Strict',
86
+ 'MAX-AGE': cookieConfig.cookieDuration
87
+ ? convertDuration(cookieConfig.cookieDuration, {
88
+ seconds: true,
89
+ }).seconds
90
+ : 0,
91
+ Secure: !cookieConfig.isDev,
92
+ });
93
+ }
72
94
 
73
95
  /**
74
96
  * Generate a secure cookie that stores the user JWT data. Used in host (backend) code.
@@ -78,26 +100,41 @@ export type GenerateAuthCookieResult = {
78
100
  export async function generateAuthCookie(
79
101
  userJwtData: Readonly<JwtUserData>,
80
102
  cookieConfig: Readonly<CookieParams>,
81
- ): Promise<GenerateAuthCookieResult> {
82
- const expiration = calculateRelativeDate(getNowInUtcTimezone(), cookieConfig.cookieDuration);
103
+ ): Promise<string> {
104
+ return generateSetCookie({
105
+ name: cookieConfig.authCookie || AuthCookie.Auth,
106
+ value: await createUserJwt(userJwtData, cookieConfig.jwtParams),
107
+ httpOnly: true,
108
+ cookieConfig,
109
+ });
110
+ }
83
111
 
84
- return {
85
- cookie: generateCookie({
86
- [cookieConfig.cookieName || 'auth']: await createUserJwt(
87
- userJwtData,
88
- cookieConfig.jwtParams,
89
- ),
90
- Domain: parseUrl(cookieConfig.hostOrigin).hostname,
91
- HttpOnly: true,
92
- Path: '/',
93
- SameSite: 'Strict',
94
- 'MAX-AGE': convertDuration(cookieConfig.cookieDuration, {
95
- seconds: true,
96
- }).seconds,
97
- Secure: !cookieConfig.isDev,
98
- }),
99
- expiration,
100
- };
112
+ /**
113
+ * Generate a CSRF token cookie. This cookie is intentionally not `HttpOnly` so that frontend
114
+ * JavaScript can read it and inject the value as a request header for double-submit verification.
115
+ *
116
+ * The CSRF cookie uses a fixed 400-day MAX-AGE rather than matching the auth cookie duration. 400
117
+ * days is the cross-browser safe maximum (Chrome caps cookie lifetimes at 400 days; other browsers
118
+ * accept it as-is). The CSRF token is only meaningful when paired with a valid JWT, so it doesn't
119
+ * need its own expiration management. It gets regenerated on every fresh login.
120
+ *
121
+ * @category Internal
122
+ */
123
+ export function generateCsrfCookie(
124
+ csrfToken: string,
125
+ cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}>>,
126
+ ): string {
127
+ return generateSetCookie({
128
+ name: AuthCookie.Csrf,
129
+ value: csrfToken,
130
+ httpOnly: false,
131
+ cookieConfig: {
132
+ ...cookieConfig,
133
+ cookieDuration: {
134
+ days: 400,
135
+ },
136
+ },
137
+ });
101
138
  }
102
139
 
103
140
  /**
@@ -106,16 +143,30 @@ export async function generateAuthCookie(
106
143
  * @category Internal
107
144
  */
108
145
  export function clearAuthCookie(
109
- cookieConfig: Readonly<Pick<CookieParams, 'cookieName' | 'hostOrigin' | 'isDev'>>,
146
+ cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}>> &
147
+ PartialWithUndefined<{authCookie: AuthCookie}>,
110
148
  ) {
111
- return generateCookie({
112
- [cookieConfig.cookieName || 'auth']: 'redacted',
113
- Domain: parseUrl(cookieConfig.hostOrigin).hostname,
114
- HttpOnly: true,
115
- Path: '/',
116
- SameSite: 'Strict',
117
- 'MAX-AGE': 0,
118
- Secure: !cookieConfig.isDev,
149
+ return generateSetCookie({
150
+ name: cookieConfig.authCookie || AuthCookie.Auth,
151
+ value: 'redacted',
152
+ httpOnly: true,
153
+ cookieConfig,
154
+ });
155
+ }
156
+
157
+ /**
158
+ * Generate a cookie value that will clear the CSRF token cookie. Use this when signing out.
159
+ *
160
+ * @category Internal
161
+ */
162
+ export function clearCsrfCookie(
163
+ cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}>>,
164
+ ) {
165
+ return generateSetCookie({
166
+ name: AuthCookie.Csrf,
167
+ value: 'redacted',
168
+ httpOnly: false,
169
+ cookieConfig,
119
170
  });
120
171
  }
121
172
 
@@ -158,7 +209,7 @@ export function generateCookie(
158
209
  export async function extractCookieJwt(
159
210
  rawCookie: string,
160
211
  jwtParams: Readonly<ParseJwtParams>,
161
- cookieName: string = AuthCookieName.Auth,
212
+ cookieName: AuthCookie,
162
213
  ): Promise<undefined | ParsedJwt<JwtUserData>> {
163
214
  const cookieRegExp = new RegExp(`${escapeStringForRegExp(cookieName)}=[^;]+(?:;|$)`);
164
215
 
package/src/csrf-token.ts CHANGED
@@ -1,18 +1,7 @@
1
- import {randomString, type PartialWithUndefined, type SelectFrom} from '@augment-vir/common';
2
- import {type AnyDuration} from 'date-vir';
1
+ import {check} from '@augment-vir/assert';
2
+ import {escapeStringForRegExp, randomString, safeMatch} from '@augment-vir/common';
3
3
  import {type RequireExactlyOne} from 'type-fest';
4
- import {getDefaultCsrfTokenStore, type CsrfTokenStore} from './csrf-token-store.js';
5
-
6
- /**
7
- * Default allowed clock skew for JWT expiration checks. Accounts for differences between server and
8
- * client clocks.
9
- *
10
- * @category Internal
11
- * @default {minutes: 5}
12
- */
13
- export const defaultAllowedClockSkew: Readonly<AnyDuration> = {
14
- minutes: 5,
15
- };
4
+ import {AuthCookie} from './cookie.js';
16
5
 
17
6
  /**
18
7
  * Generates a random, cryptographically secure CSRF token string.
@@ -42,91 +31,31 @@ export type CsrfHeaderNameOption = RequireExactlyOne<{
42
31
  * @category Auth : Client
43
32
  * @category Auth : Host
44
33
  */
45
- export function resolveCsrfHeaderName(option: Readonly<CsrfHeaderNameOption>): string {
46
- if ('csrfHeaderName' in option && option.csrfHeaderName) {
47
- return option.csrfHeaderName;
34
+ export function resolveCsrfHeaderName(options: Readonly<CsrfHeaderNameOption>): string {
35
+ if ('csrfHeaderName' in options && options.csrfHeaderName) {
36
+ return options.csrfHeaderName;
48
37
  } else {
49
38
  return [
50
- option.csrfHeaderPrefix,
39
+ options.csrfHeaderPrefix,
51
40
  'auth-vir',
52
41
  'csrf-token',
53
- ].join('-');
42
+ ]
43
+ .filter(check.isTruthy)
44
+ .join('-');
54
45
  }
55
46
  }
56
47
 
57
48
  /**
58
- * Extract the CSRF token header from a response.
59
- *
60
- * @category Auth : Client
61
- */
62
- export function extractCsrfTokenHeader(
63
- response: Readonly<PartialWithUndefined<SelectFrom<Response, {headers: true}>>>,
64
- csrfHeaderNameOption: Readonly<CsrfHeaderNameOption>,
65
- ): string | undefined {
66
- const csrfTokenHeaderName = resolveCsrfHeaderName(csrfHeaderNameOption);
67
-
68
- return response.headers?.get(csrfTokenHeaderName) || undefined;
69
- }
70
-
71
- /**
72
- * Stores the given CSRF token into IndexedDB.
73
- *
74
- * @category Auth : Client
75
- */
76
- export async function storeCsrfToken(
77
- csrfToken: string,
78
- options: Readonly<CsrfHeaderNameOption> &
79
- PartialWithUndefined<{
80
- /**
81
- * Allows mocking or overriding the default CSRF token store.
82
- *
83
- * @default getDefaultCsrfTokenStore()
84
- */
85
- csrfTokenStore: CsrfTokenStore;
86
- }>,
87
- ): Promise<void> {
88
- await (options.csrfTokenStore || (await getDefaultCsrfTokenStore())).setCsrfToken(csrfToken);
89
- }
90
-
91
- /**
92
- * Used in client (frontend) code to retrieve the current CSRF token in order to send it with
93
- * requests to the host (backend).
94
- *
95
- * @category Auth : Client
96
- */
97
- export async function getCurrentCsrfToken(
98
- options: Readonly<CsrfHeaderNameOption> &
99
- PartialWithUndefined<{
100
- /**
101
- * Allows mocking or overriding the default CSRF token store.
102
- *
103
- * @default getDefaultCsrfTokenStore()
104
- */
105
- csrfTokenStore: CsrfTokenStore;
106
- }>,
107
- ): Promise<string | undefined> {
108
- return (
109
- (await (options.csrfTokenStore || (await getDefaultCsrfTokenStore())).getCsrfToken()) ||
110
- undefined
111
- );
112
- }
113
-
114
- /**
115
- * Wipes the current stored CSRF token. This should be used by client (frontend) code to react to a
116
- * session timeout.
49
+ * Used in client (frontend) code to retrieve the current CSRF token from the browser cookie in
50
+ * order to send it with requests to the host (backend).
117
51
  *
118
52
  * @category Auth : Client
119
53
  */
120
- export async function wipeCurrentCsrfToken(
121
- options: Readonly<CsrfHeaderNameOption> &
122
- PartialWithUndefined<{
123
- /**
124
- * Allows mocking or overriding the default CSRF token store.
125
- *
126
- * @default getDefaultCsrfTokenStore()
127
- */
128
- csrfTokenStore: CsrfTokenStore;
129
- }>,
130
- ): Promise<void> {
131
- await (options.csrfTokenStore || (await getDefaultCsrfTokenStore())).deleteCsrfToken();
54
+ export function getCurrentCsrfToken(): string | undefined {
55
+ const cookieRegExp = new RegExp(`${escapeStringForRegExp(AuthCookie.Csrf)}=([^;]+)`);
56
+ const [
57
+ ,
58
+ value,
59
+ ] = safeMatch(globalThis.document.cookie, cookieRegExp);
60
+ return value || undefined;
132
61
  }
package/src/index.ts CHANGED
@@ -3,11 +3,9 @@ export * from './auth-client/frontend-auth.client.js';
3
3
  export * from './auth-client/is-session-refresh-ready.js';
4
4
  export * from './auth.js';
5
5
  export * from './cookie.js';
6
- export * from './csrf-token-store.js';
7
6
  export * from './csrf-token.js';
8
7
  export * from './hash.js';
9
8
  export * from './headers.js';
10
9
  export * from './jwt/jwt-keys.js';
11
10
  export * from './jwt/jwt.js';
12
11
  export * from './jwt/user-jwt.js';
13
- export * from './mock-csrf-token-store.js';
package/src/jwt/jwt.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {assertWrap, check} from '@augment-vir/assert';
2
- import {type AnyObject, type PartialWithUndefined} from '@augment-vir/common';
2
+ import {type AnyObject, type PartialWithUndefined, type SelectFrom} from '@augment-vir/common';
3
3
  import {
4
4
  type AnyDuration,
5
5
  calculateRelativeDate,
@@ -13,7 +13,6 @@ import {
13
13
  type UtcTimezone,
14
14
  } from 'date-vir';
15
15
  import {EncryptJWT, jwtDecrypt, jwtVerify, SignJWT} from 'jose';
16
- import {defaultAllowedClockSkew} from '../csrf-token.js';
17
16
  import {type JwtKeys} from './jwt-keys.js';
18
17
 
19
18
  const encryptionProtectedHeader = {
@@ -24,6 +23,17 @@ const signingProtectedHeader = {
24
23
  alg: 'HS512',
25
24
  };
26
25
 
26
+ /**
27
+ * Default allowed clock skew for JWT expiration checks. Accounts for differences between server and
28
+ * client clocks.
29
+ *
30
+ * @category Internal
31
+ * @default {minutes: 5}
32
+ */
33
+ export const defaultAllowedClockSkew: Readonly<AnyDuration> = {
34
+ minutes: 5,
35
+ };
36
+
27
37
  /**
28
38
  * Params for {@link createJwt}.
29
39
  *
@@ -148,7 +158,9 @@ export async function createJwt<JwtData extends AnyObject = AnyObject>(
148
158
  *
149
159
  * @category Internal
150
160
  */
151
- export type ParseJwtParams = Readonly<Pick<CreateJwtParams, 'issuer' | 'audience' | 'jwtKeys'>> &
161
+ export type ParseJwtParams = Readonly<
162
+ SelectFrom<CreateJwtParams, {issuer: true; audience: true; jwtKeys: true}>
163
+ > &
152
164
  PartialWithUndefined<{
153
165
  /**
154
166
  * Allowed clock skew tolerance for JWT expiration and timestamp checks. Accounts for
@@ -1,21 +0,0 @@
1
- /**
2
- * The interface used for overriding the default CSRF token store in storage functions.
3
- *
4
- * @category Internal
5
- */
6
- export type CsrfTokenStore = {
7
- /** Retrieves the stored CSRF token, if any. */
8
- getCsrfToken(): Promise<string | undefined>;
9
- /** Stores a CSRF token. */
10
- setCsrfToken(value: string): Promise<void>;
11
- /** Deletes the stored CSRF token. */
12
- deleteCsrfToken(): Promise<void>;
13
- };
14
- /**
15
- * The default {@link LocalDbClient} instance used for storing CSRF tokens. This uses a dedicated
16
- * store name to avoid collisions with other storage. Lazily initialized to avoid crashes in Node.js
17
- * environments where IndexedDB is not available.
18
- *
19
- * @category Internal
20
- */
21
- export declare function getDefaultCsrfTokenStore(): Promise<CsrfTokenStore>;
@@ -1,35 +0,0 @@
1
- import { LocalDbClient } from 'local-db-client';
2
- import { defineShape } from 'object-shape-tester';
3
- const csrfTokenDbShapes = {
4
- csrfToken: defineShape(''),
5
- };
6
- async function createDefaultCsrfTokenStore() {
7
- const client = await LocalDbClient.createClient(csrfTokenDbShapes, {
8
- storeName: 'auth-vir-csrf',
9
- });
10
- return {
11
- async getCsrfToken() {
12
- return (await client.load.csrfToken()) || undefined;
13
- },
14
- async setCsrfToken(value) {
15
- await client.set.csrfToken(value);
16
- },
17
- async deleteCsrfToken() {
18
- await client.delete.csrfToken();
19
- },
20
- };
21
- }
22
- /**
23
- * The default {@link LocalDbClient} instance used for storing CSRF tokens. This uses a dedicated
24
- * store name to avoid collisions with other storage. Lazily initialized to avoid crashes in Node.js
25
- * environments where IndexedDB is not available.
26
- *
27
- * @category Internal
28
- */
29
- export async function getDefaultCsrfTokenStore() {
30
- if (!cachedStorePromise) {
31
- cachedStorePromise = createDefaultCsrfTokenStore();
32
- }
33
- return cachedStorePromise;
34
- }
35
- let cachedStorePromise;
@@ -1,64 +0,0 @@
1
- import { type CsrfTokenStore } from './csrf-token-store.js';
2
- /**
3
- * `accessRecord` type for {@link createMockLocalStorage}'s output.
4
- *
5
- * @category Internal
6
- */
7
- export type MockLocalStorageAccessRecord = {
8
- getItem: string[];
9
- removeItem: string[];
10
- setItem: {
11
- key: string;
12
- value: string;
13
- }[];
14
- key: number[];
15
- };
16
- /**
17
- * Create an empty `accessRecord` object, this is to be used in conjunction with
18
- * {@link createMockLocalStorage}.
19
- *
20
- * @category Mock
21
- */
22
- export declare function createEmptyMockLocalStorageAccessRecord(): MockLocalStorageAccessRecord;
23
- /**
24
- * Create a LocalStorage mock.
25
- *
26
- * @category Mock
27
- */
28
- export declare function createMockLocalStorage(
29
- /** Set values in here to initialize the mocked localStorage data store contents. */
30
- init?: Record<string, string>): {
31
- localStorage: Storage;
32
- store: Record<string, string>;
33
- accessRecord: MockLocalStorageAccessRecord;
34
- };
35
- /**
36
- * `accessRecord` type for {@link createMockCsrfTokenStore}'s output.
37
- *
38
- * @category Internal
39
- */
40
- export type MockCsrfTokenStoreAccessRecord = {
41
- getCsrfToken: number;
42
- setCsrfToken: string[];
43
- deleteCsrfToken: number;
44
- };
45
- /**
46
- * Create an empty `accessRecord` object, this is to be used in conjunction with
47
- * {@link createMockCsrfTokenStore}.
48
- *
49
- * @category Mock
50
- */
51
- export declare function createEmptyMockCsrfTokenStoreAccessRecord(): MockCsrfTokenStoreAccessRecord;
52
- /**
53
- * Create a mock {@link CsrfTokenStore} backed by a simple in-memory object, for use in tests.
54
- *
55
- * @category Mock
56
- */
57
- export declare function createMockCsrfTokenStore(
58
- /** Set an initial value to initialize the mocked store contents. */
59
- init?: string | undefined): {
60
- csrfTokenStore: CsrfTokenStore;
61
- /** The current value held in the mock store. */
62
- readonly storedValue: string | undefined;
63
- accessRecord: MockCsrfTokenStoreAccessRecord;
64
- };