auth-vir 1.3.2 → 2.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/README.md +27 -18
- package/dist/auth-client/backend-auth.client.d.ts +177 -0
- package/dist/auth-client/backend-auth.client.js +232 -0
- package/dist/auth-client/frontend-auth.client.d.ts +64 -0
- package/dist/auth-client/frontend-auth.client.js +107 -0
- package/dist/auth.d.ts +32 -46
- package/dist/auth.js +36 -36
- package/dist/cookie.d.ts +15 -4
- package/dist/cookie.js +16 -4
- package/dist/csrf-token.d.ts +113 -3
- package/dist/csrf-token.js +101 -5
- package/dist/generated/browser.d.ts +9 -0
- package/dist/generated/browser.js +16 -0
- package/dist/generated/client.d.ts +26 -0
- package/dist/generated/client.js +31 -0
- package/dist/generated/commonInputTypes.d.ts +122 -0
- package/dist/generated/commonInputTypes.js +1 -0
- package/dist/generated/enums.d.ts +1 -0
- package/dist/generated/enums.js +9 -0
- package/dist/generated/internal/class.d.ts +126 -0
- package/dist/generated/internal/class.js +84 -0
- package/dist/generated/internal/prismaNamespace.d.ts +544 -0
- package/dist/generated/internal/prismaNamespace.js +101 -0
- package/dist/generated/internal/prismaNamespaceBrowser.d.ts +75 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +69 -0
- package/dist/generated/models/User.d.ts +983 -0
- package/dist/generated/models/User.js +1 -0
- package/dist/generated/models.d.ts +2 -0
- package/dist/generated/models.js +1 -0
- package/dist/generated/shapes.gen.d.ts +8 -0
- package/dist/generated/shapes.gen.js +11 -0
- package/dist/headers.d.ts +20 -0
- package/dist/headers.js +33 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/jwt/jwt.d.ts +11 -2
- package/dist/jwt/jwt.js +14 -3
- package/dist/jwt/user-jwt.d.ts +8 -8
- package/dist/jwt/user-jwt.js +12 -9
- package/package.json +12 -7
- package/src/auth-client/backend-auth.client.ts +500 -0
- package/src/auth-client/frontend-auth.client.ts +182 -0
- package/src/auth.ts +99 -77
- package/src/cookie.ts +20 -8
- package/src/csrf-token.ts +196 -5
- package/src/generated/browser.ts +23 -0
- package/src/generated/client.ts +47 -0
- package/src/generated/commonInputTypes.ts +147 -0
- package/src/generated/enums.ts +14 -0
- package/src/generated/internal/class.ts +236 -0
- package/src/generated/internal/prismaNamespace.ts +761 -0
- package/src/generated/internal/prismaNamespaceBrowser.ts +102 -0
- package/src/generated/models/User.ts +1135 -0
- package/src/generated/models.ts +11 -0
- package/src/generated/shapes.gen.ts +15 -0
- package/src/headers.ts +35 -0
- package/src/index.ts +2 -0
- package/src/jwt/jwt.ts +34 -5
- package/src/jwt/user-jwt.ts +21 -12
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
/* !!! This is code generated by Prisma. Do not edit directly. !!! */
|
|
3
|
+
/* eslint-disable */
|
|
4
|
+
// @ts-nocheck
|
|
5
|
+
/*
|
|
6
|
+
* This is a barrel export file for all models and their related types.
|
|
7
|
+
*
|
|
8
|
+
* 🟢 You can import this file directly.
|
|
9
|
+
*/
|
|
10
|
+
export type * from './models/User.js'
|
|
11
|
+
export type * from './commonInputTypes.js'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** AUTO-GENERATED FILE. DO NOT EDIT DIRECTLY. */
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import {defineShape, enumShape, unionShape, unknownShape, typedStringShape} from 'object-shape-tester';
|
|
4
|
+
import {JsonValue} from '@prisma/client/runtime/client.js';
|
|
5
|
+
import {utcIsoStringShape} from 'date-vir';
|
|
6
|
+
import {type UserId} from './models/User.js';
|
|
7
|
+
|
|
8
|
+
export const UserIdShape = typedStringShape<UserId>();
|
|
9
|
+
|
|
10
|
+
export const UserShape = defineShape({
|
|
11
|
+
id: UserIdShape,
|
|
12
|
+
createdAt: utcIsoStringShape(),
|
|
13
|
+
updatedAt: utcIsoStringShape(),
|
|
14
|
+
name: '',
|
|
15
|
+
});
|
package/src/headers.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {check} from '@augment-vir/assert';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* All custom headers used by auth-vir.
|
|
5
|
+
*
|
|
6
|
+
* @category Internal
|
|
7
|
+
*/
|
|
8
|
+
export enum AuthHeaderName {
|
|
9
|
+
CsrfToken = 'csrf-token',
|
|
10
|
+
AssumedUser = 'assumed-user',
|
|
11
|
+
/**
|
|
12
|
+
* Used to track if the current user is signed in only with a sign-up cookie, which prevents us
|
|
13
|
+
* from prematurely wiping their CSRF token.
|
|
14
|
+
*/
|
|
15
|
+
IsSignUpAuth = 'is-sign-up-auth',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Merges multiple header values into a single array of header values.
|
|
20
|
+
*
|
|
21
|
+
* @category Internal
|
|
22
|
+
*/
|
|
23
|
+
export function mergeHeaderValues(...values: (string | string[] | undefined)[]): string[] {
|
|
24
|
+
const finalHeaderValues: string[] = [];
|
|
25
|
+
|
|
26
|
+
values.forEach((value) => {
|
|
27
|
+
if (check.isArray(value)) {
|
|
28
|
+
finalHeaderValues.push(...value);
|
|
29
|
+
} else if (check.isString(value)) {
|
|
30
|
+
finalHeaderValues.push(value);
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
return finalHeaderValues;
|
|
35
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
export * from './auth-client/backend-auth.client.js';
|
|
1
2
|
export * from './auth.js';
|
|
2
3
|
export * from './cookie.js';
|
|
3
4
|
export * from './csrf-token.js';
|
|
4
5
|
export * from './hash.js';
|
|
6
|
+
export * from './headers.js';
|
|
5
7
|
export * from './jwt/jwt-keys.js';
|
|
6
8
|
export * from './jwt/jwt.js';
|
|
7
9
|
export * from './jwt/user-jwt.js';
|
package/src/jwt/jwt.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import {check} from '@augment-vir/assert';
|
|
1
|
+
import {assertWrap, check} from '@augment-vir/assert';
|
|
2
2
|
import {type AnyObject, type PartialWithUndefined} from '@augment-vir/common';
|
|
3
3
|
import {
|
|
4
|
+
type AnyDuration,
|
|
4
5
|
calculateRelativeDate,
|
|
5
6
|
createFullDateInUserTimezone,
|
|
7
|
+
createUtcFullDate,
|
|
8
|
+
type DateLike,
|
|
9
|
+
type FullDate,
|
|
6
10
|
getNowInUtcTimezone,
|
|
11
|
+
isDateAfter,
|
|
7
12
|
toTimestamp,
|
|
8
|
-
type
|
|
9
|
-
type DateLike,
|
|
13
|
+
type UtcTimezone,
|
|
10
14
|
} from 'date-vir';
|
|
11
15
|
import {EncryptJWT, jwtDecrypt, jwtVerify, SignJWT} from 'jose';
|
|
12
16
|
import {type JwtKeys} from './jwt-keys.js';
|
|
@@ -118,6 +122,16 @@ export async function createJwt<JwtData extends AnyObject = AnyObject>(
|
|
|
118
122
|
*/
|
|
119
123
|
export type ParseJwtParams = Readonly<Pick<CreateJwtParams, 'issuer' | 'audience' | 'jwtKeys'>>;
|
|
120
124
|
|
|
125
|
+
/**
|
|
126
|
+
* A fully parsed JWT with embedded data.
|
|
127
|
+
*
|
|
128
|
+
* @category Internal
|
|
129
|
+
*/
|
|
130
|
+
export type ParsedJwt<JwtData extends AnyObject> = {
|
|
131
|
+
data: JwtData;
|
|
132
|
+
jwtExpiration: FullDate<UtcTimezone>;
|
|
133
|
+
};
|
|
134
|
+
|
|
121
135
|
/**
|
|
122
136
|
* Parse and extract all data from an encrypted and signed JWT.
|
|
123
137
|
*
|
|
@@ -127,7 +141,7 @@ export type ParseJwtParams = Readonly<Pick<CreateJwtParams, 'issuer' | 'audience
|
|
|
127
141
|
export async function parseJwt<JwtData extends AnyObject = AnyObject>(
|
|
128
142
|
encryptedJwt: string,
|
|
129
143
|
params: Readonly<ParseJwtParams>,
|
|
130
|
-
): Promise<JwtData
|
|
144
|
+
): Promise<ParsedJwt<JwtData>> {
|
|
131
145
|
const decryptedJwt = await jwtDecrypt(encryptedJwt, params.jwtKeys.encryptionKey);
|
|
132
146
|
|
|
133
147
|
if (!check.deepEquals(decryptedJwt.protectedHeader, encryptionProtectedHeader)) {
|
|
@@ -156,5 +170,20 @@ export async function parseJwt<JwtData extends AnyObject = AnyObject>(
|
|
|
156
170
|
throw new Error('Invalid signing protected header.');
|
|
157
171
|
}
|
|
158
172
|
|
|
159
|
-
|
|
173
|
+
const expirationMs = assertWrap.isDefined(verifiedJwt.payload.exp, 'JWT has no expiration.');
|
|
174
|
+
const jwtExpiration: FullDate<UtcTimezone> = createUtcFullDate(expirationMs);
|
|
175
|
+
|
|
176
|
+
if (
|
|
177
|
+
isDateAfter({
|
|
178
|
+
fullDate: getNowInUtcTimezone(),
|
|
179
|
+
relativeTo: jwtExpiration,
|
|
180
|
+
})
|
|
181
|
+
) {
|
|
182
|
+
throw new Error('JWT expired.');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
data: data as JwtData,
|
|
187
|
+
jwtExpiration,
|
|
188
|
+
};
|
|
160
189
|
}
|
package/src/jwt/user-jwt.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
|
-
import {checkValidShape, defineShape} from 'object-shape-tester';
|
|
1
|
+
import {checkValidShape, defineShape, unionShape} from 'object-shape-tester';
|
|
2
2
|
import {type generateCsrfToken} from '../csrf-token.js';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createJwt,
|
|
5
|
+
type CreateJwtParams,
|
|
6
|
+
type ParsedJwt,
|
|
7
|
+
parseJwt,
|
|
8
|
+
type ParseJwtParams,
|
|
9
|
+
} from './jwt.js';
|
|
4
10
|
|
|
5
11
|
/**
|
|
6
|
-
* Shape definition and source of truth for {@link
|
|
12
|
+
* Shape definition and source of truth for {@link JwtUserData}.
|
|
7
13
|
*
|
|
8
14
|
* @category Internal
|
|
9
15
|
*/
|
|
10
16
|
export const userJwtDataShape = defineShape({
|
|
11
17
|
/** The id from your database of the user you're authenticating. */
|
|
12
|
-
userId: '',
|
|
18
|
+
userId: unionShape('', -1),
|
|
13
19
|
/**
|
|
14
20
|
* CSRF token. This can be any cryptographically secure randomized string.
|
|
15
21
|
*
|
|
@@ -23,24 +29,24 @@ export const userJwtDataShape = defineShape({
|
|
|
23
29
|
*
|
|
24
30
|
* @category Internal
|
|
25
31
|
*/
|
|
26
|
-
export type
|
|
32
|
+
export type JwtUserData = typeof userJwtDataShape.runtimeType;
|
|
27
33
|
|
|
28
34
|
/**
|
|
29
|
-
* Creates a new signed and encrypted {@link
|
|
35
|
+
* Creates a new signed and encrypted {@link JwtUserData} when a client (frontend) successfully
|
|
30
36
|
* authenticates with the host (backend). This is used by host (backend) code to establish a new
|
|
31
37
|
* user session. The output of this function should be sent to the client (frontend) for storage.
|
|
32
38
|
*
|
|
33
39
|
* @category Internal
|
|
34
40
|
*/
|
|
35
41
|
export async function createUserJwt(
|
|
36
|
-
data: Readonly<
|
|
42
|
+
data: Readonly<JwtUserData>,
|
|
37
43
|
params: Readonly<CreateJwtParams>,
|
|
38
44
|
): Promise<string> {
|
|
39
45
|
return await createJwt(data, params);
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
/**
|
|
43
|
-
* Parses a {@link
|
|
49
|
+
* Parses a {@link JwtUserData} generated from {@link createUserJwt}. This should be used on the host
|
|
44
50
|
* (backend) to a client (frontend) request. Do not use this function in client (frontend) code: it
|
|
45
51
|
* requires JWT signing keys which should not be shared with any client (frontend).
|
|
46
52
|
*
|
|
@@ -49,12 +55,15 @@ export async function createUserJwt(
|
|
|
49
55
|
export async function parseUserJwt(
|
|
50
56
|
encryptedJwt: string,
|
|
51
57
|
params: Readonly<ParseJwtParams>,
|
|
52
|
-
): Promise<
|
|
53
|
-
const
|
|
58
|
+
): Promise<ParsedJwt<JwtUserData> | undefined> {
|
|
59
|
+
const {data, jwtExpiration} = await parseJwt(encryptedJwt, params);
|
|
54
60
|
|
|
55
|
-
if (!checkValidShape(
|
|
61
|
+
if (!checkValidShape(data, userJwtDataShape)) {
|
|
56
62
|
throw new TypeError('Verified jwt has wrong data.');
|
|
57
63
|
}
|
|
58
64
|
|
|
59
|
-
return
|
|
65
|
+
return {
|
|
66
|
+
data,
|
|
67
|
+
jwtExpiration,
|
|
68
|
+
};
|
|
60
69
|
}
|