auth-vir 5.1.0 → 5.2.1
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 +8 -8
- package/dist/auth-client/backend-auth.client.d.ts +7 -2
- package/dist/auth-client/backend-auth.client.js +29 -5
- package/dist/auth-client/frontend-auth.client.d.ts +6 -2
- package/dist/auth-client/frontend-auth.client.js +6 -4
- package/dist/auth.d.ts +22 -9
- package/dist/auth.js +15 -5
- package/dist/cookie.d.ts +28 -5
- package/dist/cookie.js +22 -7
- package/dist/csrf-token.d.ts +2 -2
- package/dist/csrf-token.js +5 -4
- package/dist/generated/client.d.ts +4 -2
- package/dist/generated/client.js +5 -3
- package/dist/generated/internal/class.d.ts +17 -11
- package/dist/generated/internal/class.js +19 -52
- package/dist/generated/internal/prismaNamespace.d.ts +105 -51
- package/dist/generated/internal/prismaNamespace.js +10 -10
- package/dist/generated/internal/prismaNamespaceBrowser.d.ts +6 -21
- package/dist/generated/internal/prismaNamespaceBrowser.js +6 -6
- package/dist/generated/models/User.d.ts +6 -2
- package/dist/jwt/jwt-keys.js +13 -11
- package/package.json +20 -21
- package/src/auth-client/backend-auth.client.ts +43 -19
- package/src/auth-client/frontend-auth.client.ts +12 -7
- package/src/auth.ts +50 -24
- package/src/cookie.ts +55 -15
- package/src/csrf-token.ts +10 -5
- package/src/generated/client.ts +6 -6
- package/src/generated/internal/class.ts +35 -65
- package/src/generated/internal/prismaNamespace.ts +112 -41
- package/src/generated/internal/prismaNamespaceBrowser.ts +8 -6
- package/src/generated/models/User.ts +6 -1
- package/src/jwt/jwt-keys.ts +21 -16
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureArray,
|
|
3
3
|
type AnyObject,
|
|
4
|
+
type EmptyObject,
|
|
4
5
|
type JsonCompatibleObject,
|
|
5
6
|
type MaybePromise,
|
|
6
7
|
type PartialWithUndefined,
|
|
8
|
+
type RequireExactlyOne,
|
|
9
|
+
type RequireOneOrNone,
|
|
7
10
|
} from '@augment-vir/common';
|
|
8
11
|
import {
|
|
9
12
|
calculateRelativeDate,
|
|
@@ -13,7 +16,6 @@ import {
|
|
|
13
16
|
type AnyDuration,
|
|
14
17
|
} from 'date-vir';
|
|
15
18
|
import {type IncomingHttpHeaders, type OutgoingHttpHeaders} from 'node:http';
|
|
16
|
-
import {type EmptyObject, type RequireExactlyOne, type RequireOneOrNone} from 'type-fest';
|
|
17
19
|
import {
|
|
18
20
|
extractUserIdFromRequestHeaders,
|
|
19
21
|
generateLogoutHeaders,
|
|
@@ -25,6 +27,7 @@ import {
|
|
|
25
27
|
clearCsrfCookie,
|
|
26
28
|
generateAuthCookie,
|
|
27
29
|
generateCsrfCookie,
|
|
30
|
+
resolveCookieName,
|
|
28
31
|
type CookieParams,
|
|
29
32
|
} from '../cookie.js';
|
|
30
33
|
import {generateCsrfToken, type CsrfHeaderNameOption} from '../csrf-token.js';
|
|
@@ -186,6 +189,12 @@ export type BackendAuthClientConfig<
|
|
|
186
189
|
* JWT embedded in the `HttpOnly` auth cookie.
|
|
187
190
|
*/
|
|
188
191
|
csrfCookieOrigin: string;
|
|
192
|
+
/**
|
|
193
|
+
* Optional suffix appended to cookie names (e.g., `'staging'` produces `auth-staging`,
|
|
194
|
+
* `auth-vir-csrf-staging`). When `undefined`, cookie names are unchanged. Useful for
|
|
195
|
+
* running multiple environments on the same domain without cookie collisions.
|
|
196
|
+
*/
|
|
197
|
+
cookieNameSuffix: string;
|
|
189
198
|
}>
|
|
190
199
|
>;
|
|
191
200
|
|
|
@@ -277,6 +286,7 @@ export class BackendAuthClient<
|
|
|
277
286
|
jwtParams: await this.getJwtParams(),
|
|
278
287
|
isDev: this.config.isDev,
|
|
279
288
|
authCookie: isSignUpCookie ? AuthCookie.SignUp : AuthCookie.Auth,
|
|
289
|
+
cookieNameSuffix: this.config.cookieNameSuffix,
|
|
280
290
|
};
|
|
281
291
|
}
|
|
282
292
|
|
|
@@ -412,6 +422,7 @@ export class BackendAuthClient<
|
|
|
412
422
|
const csrfCookie = generateCsrfCookie(userIdResult.csrfToken, {
|
|
413
423
|
...cookieParams,
|
|
414
424
|
hostOrigin: this.resolveCsrfCookieOrigin(cookieParams.hostOrigin),
|
|
425
|
+
cookieNameSuffix: this.config.cookieNameSuffix,
|
|
415
426
|
});
|
|
416
427
|
|
|
417
428
|
return {
|
|
@@ -489,12 +500,13 @@ export class BackendAuthClient<
|
|
|
489
500
|
*/
|
|
490
501
|
allowUserAuthRefresh: boolean;
|
|
491
502
|
}): Promise<GetUserResult<DatabaseUser> | undefined> {
|
|
492
|
-
const userIdResult = await extractUserIdFromRequestHeaders<UserId>(
|
|
493
|
-
requestHeaders,
|
|
494
|
-
await this.getJwtParams(),
|
|
495
|
-
this.config.csrf,
|
|
496
|
-
isSignUpCookie ? AuthCookie.SignUp : AuthCookie.Auth,
|
|
497
|
-
|
|
503
|
+
const userIdResult = await extractUserIdFromRequestHeaders<UserId>({
|
|
504
|
+
headers: requestHeaders,
|
|
505
|
+
jwtParams: await this.getJwtParams(),
|
|
506
|
+
csrfHeaderNameOption: this.config.csrf,
|
|
507
|
+
cookieName: isSignUpCookie ? AuthCookie.SignUp : AuthCookie.Auth,
|
|
508
|
+
cookieNameSuffix: this.config.cookieNameSuffix,
|
|
509
|
+
});
|
|
498
510
|
if (!userIdResult) {
|
|
499
511
|
this.logForUser(
|
|
500
512
|
{
|
|
@@ -556,6 +568,7 @@ export class BackendAuthClient<
|
|
|
556
568
|
const csrfCookie = generateCsrfCookie(userIdResult.csrfToken, {
|
|
557
569
|
hostOrigin: this.resolveCsrfCookieOrigin(authCookieOrigin),
|
|
558
570
|
isDev: this.config.isDev,
|
|
571
|
+
cookieNameSuffix: this.config.cookieNameSuffix,
|
|
559
572
|
});
|
|
560
573
|
|
|
561
574
|
return {
|
|
@@ -645,6 +658,7 @@ export class BackendAuthClient<
|
|
|
645
658
|
? clearCsrfCookie({
|
|
646
659
|
hostOrigin: this.config.csrfCookieOrigin,
|
|
647
660
|
isDev: this.config.isDev,
|
|
661
|
+
cookieNameSuffix: this.config.cookieNameSuffix,
|
|
648
662
|
})
|
|
649
663
|
: undefined;
|
|
650
664
|
|
|
@@ -682,6 +696,7 @@ export class BackendAuthClient<
|
|
|
682
696
|
const csrfCookie = generateCsrfCookie(existingUserIdResult.csrfToken, {
|
|
683
697
|
...cookieParams,
|
|
684
698
|
hostOrigin: this.resolveCsrfCookieOrigin(cookieParams.hostOrigin),
|
|
699
|
+
cookieNameSuffix: this.config.cookieNameSuffix,
|
|
685
700
|
});
|
|
686
701
|
|
|
687
702
|
return {
|
|
@@ -711,6 +726,7 @@ export class BackendAuthClient<
|
|
|
711
726
|
const csrfCookie = generateCsrfCookie(csrfToken, {
|
|
712
727
|
...cookieParams,
|
|
713
728
|
hostOrigin: this.resolveCsrfCookieOrigin(cookieParams.hostOrigin),
|
|
729
|
+
cookieNameSuffix: this.config.cookieNameSuffix,
|
|
714
730
|
});
|
|
715
731
|
|
|
716
732
|
return {
|
|
@@ -732,7 +748,13 @@ export class BackendAuthClient<
|
|
|
732
748
|
isSignUpCookie: boolean;
|
|
733
749
|
}): Promise<OutgoingHttpHeaders> {
|
|
734
750
|
const oppositeCookieName = isSignUpCookie ? AuthCookie.Auth : AuthCookie.SignUp;
|
|
735
|
-
const
|
|
751
|
+
const resolvedOppositeCookieName = resolveCookieName(
|
|
752
|
+
oppositeCookieName,
|
|
753
|
+
this.config.cookieNameSuffix,
|
|
754
|
+
);
|
|
755
|
+
const hasExistingOppositeCookie = requestHeaders.cookie?.includes(
|
|
756
|
+
`${resolvedOppositeCookieName}=`,
|
|
757
|
+
);
|
|
736
758
|
|
|
737
759
|
const discardOppositeCookieHeaders = hasExistingOppositeCookie
|
|
738
760
|
? generateLogoutHeaders(
|
|
@@ -746,12 +768,13 @@ export class BackendAuthClient<
|
|
|
746
768
|
)
|
|
747
769
|
: undefined;
|
|
748
770
|
|
|
749
|
-
const existingUserIdResult = await extractUserIdFromRequestHeaders<UserId>(
|
|
750
|
-
requestHeaders,
|
|
751
|
-
await this.getJwtParams(),
|
|
752
|
-
this.config.csrf,
|
|
753
|
-
isSignUpCookie ? AuthCookie.SignUp : AuthCookie.Auth,
|
|
754
|
-
|
|
771
|
+
const existingUserIdResult = await extractUserIdFromRequestHeaders<UserId>({
|
|
772
|
+
headers: requestHeaders,
|
|
773
|
+
jwtParams: await this.getJwtParams(),
|
|
774
|
+
csrfHeaderNameOption: this.config.csrf,
|
|
775
|
+
cookieName: isSignUpCookie ? AuthCookie.SignUp : AuthCookie.Auth,
|
|
776
|
+
cookieNameSuffix: this.config.cookieNameSuffix,
|
|
777
|
+
});
|
|
755
778
|
|
|
756
779
|
const cookieParams = await this.getCookieParams({
|
|
757
780
|
isSignUpCookie,
|
|
@@ -840,11 +863,12 @@ export class BackendAuthClient<
|
|
|
840
863
|
allowUserAuthRefresh: boolean;
|
|
841
864
|
}): Promise<GetUserResult<DatabaseUser> | undefined> {
|
|
842
865
|
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
843
|
-
const userIdResult = await insecureExtractUserIdFromCookieAlone<UserId>(
|
|
844
|
-
requestHeaders,
|
|
845
|
-
await this.getJwtParams(),
|
|
846
|
-
AuthCookie.Auth,
|
|
847
|
-
|
|
866
|
+
const userIdResult = await insecureExtractUserIdFromCookieAlone<UserId>({
|
|
867
|
+
headers: requestHeaders,
|
|
868
|
+
jwtParams: await this.getJwtParams(),
|
|
869
|
+
cookieName: AuthCookie.Auth,
|
|
870
|
+
cookieNameSuffix: this.config.cookieNameSuffix,
|
|
871
|
+
});
|
|
848
872
|
|
|
849
873
|
if (!userIdResult) {
|
|
850
874
|
this.logForUser(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type createBlockingInterval,
|
|
3
|
+
type EmptyObject,
|
|
3
4
|
HttpStatus,
|
|
4
5
|
type JsonCompatibleObject,
|
|
5
6
|
type MaybePromise,
|
|
@@ -8,7 +9,6 @@ import {
|
|
|
8
9
|
} from '@augment-vir/common';
|
|
9
10
|
import {type AnyDuration} from 'date-vir';
|
|
10
11
|
import {listenToActivity} from 'detect-activity';
|
|
11
|
-
import {type EmptyObject} from 'type-fest';
|
|
12
12
|
import {
|
|
13
13
|
type CsrfHeaderNameOption,
|
|
14
14
|
getCurrentCsrfToken,
|
|
@@ -25,6 +25,11 @@ export type FrontendAuthClientConfig = Readonly<{
|
|
|
25
25
|
csrf: Readonly<CsrfHeaderNameOption>;
|
|
26
26
|
}> &
|
|
27
27
|
PartialWithUndefined<{
|
|
28
|
+
/**
|
|
29
|
+
* Optional suffix appended to cookie names (e.g., `'staging'` produces
|
|
30
|
+
* `auth-vir-csrf-staging`). When `undefined`, cookie names are unchanged.
|
|
31
|
+
*/
|
|
32
|
+
cookieNameSuffix: string;
|
|
28
33
|
/**
|
|
29
34
|
* Determine if the current user can assume the identity of another user. If this is not
|
|
30
35
|
* defined, all users will be blocked from assuming other user identities.
|
|
@@ -131,13 +136,13 @@ export class FrontendAuthClient<AssumedUserParams extends JsonCompatibleObject =
|
|
|
131
136
|
if (!assumedUserParams) {
|
|
132
137
|
localStorage.removeItem(storageKey);
|
|
133
138
|
return true;
|
|
134
|
-
} else if (
|
|
139
|
+
} else if (await this.config.canAssumeUser?.()) {
|
|
140
|
+
localStorage.setItem(storageKey, JSON.stringify(assumedUserParams));
|
|
141
|
+
|
|
142
|
+
return true;
|
|
143
|
+
} else {
|
|
135
144
|
return false;
|
|
136
145
|
}
|
|
137
|
-
|
|
138
|
-
localStorage.setItem(storageKey, JSON.stringify(assumedUserParams));
|
|
139
|
-
|
|
140
|
-
return true;
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
/** Gets the assumed user params stored in local storage, if any. */
|
|
@@ -163,7 +168,7 @@ export class FrontendAuthClient<AssumedUserParams extends JsonCompatibleObject =
|
|
|
163
168
|
* combine them with these.
|
|
164
169
|
*/
|
|
165
170
|
public createAuthenticatedRequestInit(): RequestInit {
|
|
166
|
-
const csrfToken = getCurrentCsrfToken();
|
|
171
|
+
const csrfToken = getCurrentCsrfToken(this.config.cookieNameSuffix);
|
|
167
172
|
|
|
168
173
|
const assumedUser = this.getAssumedUser();
|
|
169
174
|
const headers: HeadersInit = {
|
package/src/auth.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {type SelectFrom} from '@augment-vir/common';
|
|
1
|
+
import {type PartialWithUndefined, type SelectFrom} from '@augment-vir/common';
|
|
2
2
|
import {type FullDate, type UtcTimezone} from 'date-vir';
|
|
3
3
|
import {
|
|
4
|
-
AuthCookie,
|
|
4
|
+
type AuthCookie,
|
|
5
5
|
clearAuthCookie,
|
|
6
6
|
clearCsrfCookie,
|
|
7
7
|
type CookieParams,
|
|
@@ -71,12 +71,19 @@ function readCsrfTokenHeader(
|
|
|
71
71
|
* @category Auth : Host
|
|
72
72
|
* @returns The extracted user id or `undefined` if no valid auth headers exist.
|
|
73
73
|
*/
|
|
74
|
-
export async function extractUserIdFromRequestHeaders<UserId extends string | number>(
|
|
75
|
-
headers
|
|
76
|
-
jwtParams
|
|
77
|
-
csrfHeaderNameOption
|
|
78
|
-
cookieName
|
|
79
|
-
|
|
74
|
+
export async function extractUserIdFromRequestHeaders<UserId extends string | number>({
|
|
75
|
+
headers,
|
|
76
|
+
jwtParams,
|
|
77
|
+
csrfHeaderNameOption,
|
|
78
|
+
cookieName,
|
|
79
|
+
cookieNameSuffix,
|
|
80
|
+
}: Readonly<{
|
|
81
|
+
headers: HeaderContainer;
|
|
82
|
+
jwtParams: Readonly<ParseJwtParams>;
|
|
83
|
+
csrfHeaderNameOption: Readonly<CsrfHeaderNameOption>;
|
|
84
|
+
cookieName: AuthCookie;
|
|
85
|
+
cookieNameSuffix?: string | undefined;
|
|
86
|
+
}>): Promise<Readonly<UserIdResult<UserId>> | undefined> {
|
|
80
87
|
try {
|
|
81
88
|
const csrfToken = readCsrfTokenHeader(headers, csrfHeaderNameOption);
|
|
82
89
|
const cookie = readHeader(headers, 'cookie');
|
|
@@ -85,7 +92,12 @@ export async function extractUserIdFromRequestHeaders<UserId extends string | nu
|
|
|
85
92
|
return undefined;
|
|
86
93
|
}
|
|
87
94
|
|
|
88
|
-
const jwt = await extractCookieJwt(
|
|
95
|
+
const jwt = await extractCookieJwt({
|
|
96
|
+
rawCookie: cookie,
|
|
97
|
+
jwtParams,
|
|
98
|
+
cookieName,
|
|
99
|
+
cookieNameSuffix,
|
|
100
|
+
});
|
|
89
101
|
|
|
90
102
|
if (!jwt || jwt.data.csrfToken !== csrfToken) {
|
|
91
103
|
return undefined;
|
|
@@ -112,11 +124,17 @@ export async function extractUserIdFromRequestHeaders<UserId extends string | nu
|
|
|
112
124
|
* @deprecated Prefer {@link extractUserIdFromRequestHeaders} instead: it is more secure.
|
|
113
125
|
* @category Auth : Host
|
|
114
126
|
*/
|
|
115
|
-
export async function insecureExtractUserIdFromCookieAlone<UserId extends string | number>(
|
|
116
|
-
headers
|
|
117
|
-
jwtParams
|
|
118
|
-
cookieName
|
|
119
|
-
|
|
127
|
+
export async function insecureExtractUserIdFromCookieAlone<UserId extends string | number>({
|
|
128
|
+
headers,
|
|
129
|
+
jwtParams,
|
|
130
|
+
cookieName,
|
|
131
|
+
cookieNameSuffix,
|
|
132
|
+
}: Readonly<{
|
|
133
|
+
headers: HeaderContainer;
|
|
134
|
+
jwtParams: Readonly<ParseJwtParams>;
|
|
135
|
+
cookieName: AuthCookie;
|
|
136
|
+
cookieNameSuffix?: string | undefined;
|
|
137
|
+
}>): Promise<Readonly<UserIdResult<UserId>> | undefined> {
|
|
120
138
|
try {
|
|
121
139
|
const cookie = readHeader(headers, 'cookie');
|
|
122
140
|
|
|
@@ -124,7 +142,12 @@ export async function insecureExtractUserIdFromCookieAlone<UserId extends string
|
|
|
124
142
|
return undefined;
|
|
125
143
|
}
|
|
126
144
|
|
|
127
|
-
const jwt = await extractCookieJwt(
|
|
145
|
+
const jwt = await extractCookieJwt({
|
|
146
|
+
rawCookie: cookie,
|
|
147
|
+
jwtParams,
|
|
148
|
+
cookieName,
|
|
149
|
+
cookieNameSuffix,
|
|
150
|
+
});
|
|
128
151
|
|
|
129
152
|
if (!jwt) {
|
|
130
153
|
return undefined;
|
|
@@ -188,15 +211,18 @@ export async function generateSuccessfulLoginHeaders(
|
|
|
188
211
|
* @category Auth : Host
|
|
189
212
|
*/
|
|
190
213
|
export function generateLogoutHeaders(
|
|
191
|
-
cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
214
|
+
cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}>> &
|
|
215
|
+
PartialWithUndefined<{cookieNameSuffix: string}>,
|
|
216
|
+
options?: Readonly<
|
|
217
|
+
PartialWithUndefined<{
|
|
218
|
+
/**
|
|
219
|
+
* When `true`, the CSRF cookie is preserved (not cleared). Use this when clearing only
|
|
220
|
+
* one cookie type (e.g., the auth cookie) while keeping the other active session (e.g.,
|
|
221
|
+
* sign-up) that still needs its CSRF token.
|
|
222
|
+
*/
|
|
223
|
+
preserveCsrf: boolean;
|
|
224
|
+
}>
|
|
225
|
+
>,
|
|
200
226
|
): Record<string, string[]> {
|
|
201
227
|
return {
|
|
202
228
|
'set-cookie': [
|
package/src/cookie.ts
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
escapeStringForRegExp,
|
|
4
4
|
safeMatch,
|
|
5
5
|
type PartialWithUndefined,
|
|
6
|
+
type Primitive,
|
|
6
7
|
type SelectFrom,
|
|
7
8
|
} from '@augment-vir/common';
|
|
8
9
|
import {convertDuration, type AnyDuration} from 'date-vir';
|
|
9
|
-
import {type Primitive} from 'type-fest';
|
|
10
10
|
import {parseUrl} from 'url-vir';
|
|
11
11
|
import {type CreateJwtParams, type ParseJwtParams, type ParsedJwt} from './jwt/jwt.js';
|
|
12
12
|
import {createUserJwt, parseUserJwt, type JwtUserData} from './jwt/user-jwt.js';
|
|
@@ -25,6 +25,24 @@ export enum AuthCookie {
|
|
|
25
25
|
Csrf = 'auth-vir-csrf',
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Resolves a cookie name by appending a suffix when provided. When `cookieNameSuffix` is
|
|
30
|
+
* `undefined`, the base name is returned unchanged.
|
|
31
|
+
*
|
|
32
|
+
* @category Internal
|
|
33
|
+
*/
|
|
34
|
+
export function resolveCookieName(
|
|
35
|
+
baseCookieName: AuthCookie,
|
|
36
|
+
cookieNameSuffix?: string | undefined,
|
|
37
|
+
): string {
|
|
38
|
+
return [
|
|
39
|
+
baseCookieName,
|
|
40
|
+
cookieNameSuffix,
|
|
41
|
+
]
|
|
42
|
+
.filter(check.isTruthy)
|
|
43
|
+
.join('-');
|
|
44
|
+
}
|
|
45
|
+
|
|
28
46
|
/**
|
|
29
47
|
* Parameters for {@link generateAuthCookie}.
|
|
30
48
|
*
|
|
@@ -63,6 +81,12 @@ export type CookieParams = {
|
|
|
63
81
|
* @default false
|
|
64
82
|
*/
|
|
65
83
|
isDev: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Optional suffix appended to cookie names (e.g., `'staging'` produces `auth-staging`). When
|
|
86
|
+
* `undefined`, cookie names are unchanged. Useful for running multiple environments on the same
|
|
87
|
+
* domain without cookie collisions.
|
|
88
|
+
*/
|
|
89
|
+
cookieNameSuffix: string;
|
|
66
90
|
}>;
|
|
67
91
|
|
|
68
92
|
function generateSetCookie({
|
|
@@ -102,7 +126,10 @@ export async function generateAuthCookie(
|
|
|
102
126
|
cookieConfig: Readonly<CookieParams>,
|
|
103
127
|
): Promise<string> {
|
|
104
128
|
return generateSetCookie({
|
|
105
|
-
name:
|
|
129
|
+
name: resolveCookieName(
|
|
130
|
+
cookieConfig.authCookie || AuthCookie.Auth,
|
|
131
|
+
cookieConfig.cookieNameSuffix,
|
|
132
|
+
),
|
|
106
133
|
value: await createUserJwt(userJwtData, cookieConfig.jwtParams),
|
|
107
134
|
httpOnly: true,
|
|
108
135
|
cookieConfig,
|
|
@@ -122,10 +149,11 @@ export async function generateAuthCookie(
|
|
|
122
149
|
*/
|
|
123
150
|
export function generateCsrfCookie(
|
|
124
151
|
csrfToken: string,
|
|
125
|
-
cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}
|
|
152
|
+
cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}>> &
|
|
153
|
+
PartialWithUndefined<{cookieNameSuffix: string}>,
|
|
126
154
|
): string {
|
|
127
155
|
return generateSetCookie({
|
|
128
|
-
name: AuthCookie.Csrf,
|
|
156
|
+
name: resolveCookieName(AuthCookie.Csrf, cookieConfig.cookieNameSuffix),
|
|
129
157
|
value: csrfToken,
|
|
130
158
|
httpOnly: false,
|
|
131
159
|
cookieConfig: {
|
|
@@ -144,10 +172,13 @@ export function generateCsrfCookie(
|
|
|
144
172
|
*/
|
|
145
173
|
export function clearAuthCookie(
|
|
146
174
|
cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}>> &
|
|
147
|
-
PartialWithUndefined<{authCookie: AuthCookie}>,
|
|
175
|
+
PartialWithUndefined<{authCookie: AuthCookie; cookieNameSuffix: string}>,
|
|
148
176
|
) {
|
|
149
177
|
return generateSetCookie({
|
|
150
|
-
name:
|
|
178
|
+
name: resolveCookieName(
|
|
179
|
+
cookieConfig.authCookie || AuthCookie.Auth,
|
|
180
|
+
cookieConfig.cookieNameSuffix,
|
|
181
|
+
),
|
|
151
182
|
value: 'redacted',
|
|
152
183
|
httpOnly: true,
|
|
153
184
|
cookieConfig,
|
|
@@ -160,10 +191,11 @@ export function clearAuthCookie(
|
|
|
160
191
|
* @category Internal
|
|
161
192
|
*/
|
|
162
193
|
export function clearCsrfCookie(
|
|
163
|
-
cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}
|
|
194
|
+
cookieConfig: Readonly<SelectFrom<CookieParams, {hostOrigin: true; isDev: true}>> &
|
|
195
|
+
PartialWithUndefined<{cookieNameSuffix: string}>,
|
|
164
196
|
) {
|
|
165
197
|
return generateSetCookie({
|
|
166
|
-
name: AuthCookie.Csrf,
|
|
198
|
+
name: resolveCookieName(AuthCookie.Csrf, cookieConfig.cookieNameSuffix),
|
|
167
199
|
value: 'redacted',
|
|
168
200
|
httpOnly: false,
|
|
169
201
|
cookieConfig,
|
|
@@ -206,12 +238,20 @@ export function generateCookie(
|
|
|
206
238
|
* @category Internal
|
|
207
239
|
* @returns The extracted auth Cookie JWT data or `undefined` if no valid auth JWT data was found.
|
|
208
240
|
*/
|
|
209
|
-
export async function extractCookieJwt(
|
|
210
|
-
rawCookie
|
|
211
|
-
jwtParams
|
|
212
|
-
cookieName
|
|
213
|
-
|
|
214
|
-
|
|
241
|
+
export async function extractCookieJwt({
|
|
242
|
+
rawCookie,
|
|
243
|
+
jwtParams,
|
|
244
|
+
cookieName,
|
|
245
|
+
cookieNameSuffix,
|
|
246
|
+
}: {
|
|
247
|
+
rawCookie: string;
|
|
248
|
+
jwtParams: Readonly<ParseJwtParams>;
|
|
249
|
+
cookieName: AuthCookie;
|
|
250
|
+
} & PartialWithUndefined<{
|
|
251
|
+
cookieNameSuffix: string;
|
|
252
|
+
}>): Promise<undefined | ParsedJwt<JwtUserData>> {
|
|
253
|
+
const resolvedName = resolveCookieName(cookieName, cookieNameSuffix);
|
|
254
|
+
const cookieRegExp = new RegExp(`${escapeStringForRegExp(resolvedName)}=[^;]+(?:;|$)`);
|
|
215
255
|
|
|
216
256
|
const [cookieValue] = safeMatch(rawCookie, cookieRegExp);
|
|
217
257
|
|
|
@@ -219,7 +259,7 @@ export async function extractCookieJwt(
|
|
|
219
259
|
return undefined;
|
|
220
260
|
}
|
|
221
261
|
|
|
222
|
-
const rawJwt = cookieValue.replace(`${
|
|
262
|
+
const rawJwt = cookieValue.replace(`${resolvedName}=`, '').replace(';', '');
|
|
223
263
|
|
|
224
264
|
const jwt = await parseUserJwt(rawJwt, jwtParams);
|
|
225
265
|
|
package/src/csrf-token.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import {check} from '@augment-vir/assert';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
escapeStringForRegExp,
|
|
4
|
+
randomString,
|
|
5
|
+
safeMatch,
|
|
6
|
+
type RequireExactlyOne,
|
|
7
|
+
} from '@augment-vir/common';
|
|
8
|
+
import {AuthCookie, resolveCookieName} from './cookie.js';
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
11
|
* Generates a random, cryptographically secure CSRF token string.
|
|
@@ -51,8 +55,9 @@ export function resolveCsrfHeaderName(options: Readonly<CsrfHeaderNameOption>):
|
|
|
51
55
|
*
|
|
52
56
|
* @category Auth : Client
|
|
53
57
|
*/
|
|
54
|
-
export function getCurrentCsrfToken(): string | undefined {
|
|
55
|
-
const
|
|
58
|
+
export function getCurrentCsrfToken(cookieNameSuffix?: string | undefined): string | undefined {
|
|
59
|
+
const resolvedName = resolveCookieName(AuthCookie.Csrf, cookieNameSuffix);
|
|
60
|
+
const cookieRegExp = new RegExp(`${escapeStringForRegExp(resolvedName)}=([^;]+)`);
|
|
56
61
|
const [
|
|
57
62
|
,
|
|
58
63
|
value,
|
package/src/generated/client.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/*
|
|
7
7
|
* This file should be your main import to use Prisma. Through it you get access to all the models, enums, and input types.
|
|
8
8
|
* If you're looking for something you can import in the client-side of your application, please refer to the `browser.ts` file instead.
|
|
9
|
-
*
|
|
9
|
+
*
|
|
10
10
|
* 🟢 You can import this file directly.
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -28,19 +28,19 @@ export * from "./enums.js"
|
|
|
28
28
|
* Type-safe database client for TypeScript
|
|
29
29
|
* @example
|
|
30
30
|
* ```
|
|
31
|
-
* const prisma = new PrismaClient(
|
|
31
|
+
* const prisma = new PrismaClient({
|
|
32
|
+
* adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
|
|
33
|
+
* })
|
|
32
34
|
* // Fetch zero or more Users
|
|
33
35
|
* const users = await prisma.user.findMany()
|
|
34
36
|
* ```
|
|
35
37
|
*
|
|
36
|
-
* Read more in our [docs](https://
|
|
38
|
+
* Read more in our [docs](https://pris.ly/d/client).
|
|
37
39
|
*/
|
|
38
|
-
export const PrismaClient = $Class.getPrismaClientClass(
|
|
40
|
+
export const PrismaClient = $Class.getPrismaClientClass()
|
|
39
41
|
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
|
40
42
|
export { Prisma }
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
|
|
44
44
|
/**
|
|
45
45
|
* Model User
|
|
46
46
|
*
|