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 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type UserId } from './models/User.js';
|
|
2
|
+
export declare const UserIdShape: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnsafe<UserId>>;
|
|
3
|
+
export declare const UserShape: import("object-shape-tester").Shape<{
|
|
4
|
+
id: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnsafe<UserId>>;
|
|
5
|
+
createdAt: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnsafe<`${number}-${number}-${number}T${number}:${number}:${number}.${number}Z`>>;
|
|
6
|
+
updatedAt: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnsafe<`${number}-${number}-${number}T${number}:${number}:${number}.${number}Z`>>;
|
|
7
|
+
name: string;
|
|
8
|
+
}>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** AUTO-GENERATED FILE. DO NOT EDIT DIRECTLY. */
|
|
2
|
+
// @ts-nocheck
|
|
3
|
+
import { defineShape, typedStringShape } from 'object-shape-tester';
|
|
4
|
+
import { utcIsoStringShape } from 'date-vir';
|
|
5
|
+
export const UserIdShape = typedStringShape();
|
|
6
|
+
export const UserShape = defineShape({
|
|
7
|
+
id: UserIdShape,
|
|
8
|
+
createdAt: utcIsoStringShape(),
|
|
9
|
+
updatedAt: utcIsoStringShape(),
|
|
10
|
+
name: '',
|
|
11
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* All custom headers used by auth-vir.
|
|
3
|
+
*
|
|
4
|
+
* @category Internal
|
|
5
|
+
*/
|
|
6
|
+
export declare enum AuthHeaderName {
|
|
7
|
+
CsrfToken = "csrf-token",
|
|
8
|
+
AssumedUser = "assumed-user",
|
|
9
|
+
/**
|
|
10
|
+
* Used to track if the current user is signed in only with a sign-up cookie, which prevents us
|
|
11
|
+
* from prematurely wiping their CSRF token.
|
|
12
|
+
*/
|
|
13
|
+
IsSignUpAuth = "is-sign-up-auth"
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Merges multiple header values into a single array of header values.
|
|
17
|
+
*
|
|
18
|
+
* @category Internal
|
|
19
|
+
*/
|
|
20
|
+
export declare function mergeHeaderValues(...values: (string | string[] | undefined)[]): string[];
|
package/dist/headers.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { check } from '@augment-vir/assert';
|
|
2
|
+
/**
|
|
3
|
+
* All custom headers used by auth-vir.
|
|
4
|
+
*
|
|
5
|
+
* @category Internal
|
|
6
|
+
*/
|
|
7
|
+
export var AuthHeaderName;
|
|
8
|
+
(function (AuthHeaderName) {
|
|
9
|
+
AuthHeaderName["CsrfToken"] = "csrf-token";
|
|
10
|
+
AuthHeaderName["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
|
+
AuthHeaderName["IsSignUpAuth"] = "is-sign-up-auth";
|
|
16
|
+
})(AuthHeaderName || (AuthHeaderName = {}));
|
|
17
|
+
/**
|
|
18
|
+
* Merges multiple header values into a single array of header values.
|
|
19
|
+
*
|
|
20
|
+
* @category Internal
|
|
21
|
+
*/
|
|
22
|
+
export function mergeHeaderValues(...values) {
|
|
23
|
+
const finalHeaderValues = [];
|
|
24
|
+
values.forEach((value) => {
|
|
25
|
+
if (check.isArray(value)) {
|
|
26
|
+
finalHeaderValues.push(...value);
|
|
27
|
+
}
|
|
28
|
+
else if (check.isString(value)) {
|
|
29
|
+
finalHeaderValues.push(value);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return finalHeaderValues;
|
|
33
|
+
}
|
package/dist/index.d.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/dist/index.js
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/dist/jwt/jwt.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AnyObject, type PartialWithUndefined } from '@augment-vir/common';
|
|
2
|
-
import { type AnyDuration, type DateLike } from 'date-vir';
|
|
2
|
+
import { type AnyDuration, type DateLike, type FullDate, type UtcTimezone } from 'date-vir';
|
|
3
3
|
import { type JwtKeys } from './jwt-keys.js';
|
|
4
4
|
/**
|
|
5
5
|
* Params for {@link createJwt}.
|
|
@@ -74,10 +74,19 @@ data: JwtData, params: Readonly<CreateJwtParams>): Promise<string>;
|
|
|
74
74
|
* @category Internal
|
|
75
75
|
*/
|
|
76
76
|
export type ParseJwtParams = Readonly<Pick<CreateJwtParams, 'issuer' | 'audience' | 'jwtKeys'>>;
|
|
77
|
+
/**
|
|
78
|
+
* A fully parsed JWT with embedded data.
|
|
79
|
+
*
|
|
80
|
+
* @category Internal
|
|
81
|
+
*/
|
|
82
|
+
export type ParsedJwt<JwtData extends AnyObject> = {
|
|
83
|
+
data: JwtData;
|
|
84
|
+
jwtExpiration: FullDate<UtcTimezone>;
|
|
85
|
+
};
|
|
77
86
|
/**
|
|
78
87
|
* Parse and extract all data from an encrypted and signed JWT.
|
|
79
88
|
*
|
|
80
89
|
* @category Internal
|
|
81
90
|
* @throws Errors if the decryption, signature verification, or other JWT requirements fail
|
|
82
91
|
*/
|
|
83
|
-
export declare function parseJwt<JwtData extends AnyObject = AnyObject>(encryptedJwt: string, params: Readonly<ParseJwtParams>): Promise<JwtData
|
|
92
|
+
export declare function parseJwt<JwtData extends AnyObject = AnyObject>(encryptedJwt: string, params: Readonly<ParseJwtParams>): Promise<ParsedJwt<JwtData>>;
|
package/dist/jwt/jwt.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { check } from '@augment-vir/assert';
|
|
2
|
-
import { calculateRelativeDate, createFullDateInUserTimezone, getNowInUtcTimezone, toTimestamp, } from 'date-vir';
|
|
1
|
+
import { assertWrap, check } from '@augment-vir/assert';
|
|
2
|
+
import { calculateRelativeDate, createFullDateInUserTimezone, createUtcFullDate, getNowInUtcTimezone, isDateAfter, toTimestamp, } from 'date-vir';
|
|
3
3
|
import { EncryptJWT, jwtDecrypt, jwtVerify, SignJWT } from 'jose';
|
|
4
4
|
const encryptionProtectedHeader = { alg: 'dir', enc: 'A256GCM' };
|
|
5
5
|
const signingProtectedHeader = { alg: 'HS512' };
|
|
@@ -57,5 +57,16 @@ export async function parseJwt(encryptedJwt, params) {
|
|
|
57
57
|
if (!check.deepEquals(verifiedJwt.protectedHeader, signingProtectedHeader)) {
|
|
58
58
|
throw new Error('Invalid signing protected header.');
|
|
59
59
|
}
|
|
60
|
-
|
|
60
|
+
const expirationMs = assertWrap.isDefined(verifiedJwt.payload.exp, 'JWT has no expiration.');
|
|
61
|
+
const jwtExpiration = createUtcFullDate(expirationMs);
|
|
62
|
+
if (isDateAfter({
|
|
63
|
+
fullDate: getNowInUtcTimezone(),
|
|
64
|
+
relativeTo: jwtExpiration,
|
|
65
|
+
})) {
|
|
66
|
+
throw new Error('JWT expired.');
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
data: data,
|
|
70
|
+
jwtExpiration,
|
|
71
|
+
};
|
|
61
72
|
}
|
package/dist/jwt/user-jwt.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { type CreateJwtParams, type ParseJwtParams } from './jwt.js';
|
|
1
|
+
import { type CreateJwtParams, type ParsedJwt, type ParseJwtParams } from './jwt.js';
|
|
2
2
|
/**
|
|
3
|
-
* Shape definition and source of truth for {@link
|
|
3
|
+
* Shape definition and source of truth for {@link JwtUserData}.
|
|
4
4
|
*
|
|
5
5
|
* @category Internal
|
|
6
6
|
*/
|
|
7
7
|
export declare const userJwtDataShape: import("object-shape-tester").Shape<{
|
|
8
8
|
/** The id from your database of the user you're authenticating. */
|
|
9
|
-
userId:
|
|
9
|
+
userId: import("object-shape-tester").Shape<import("@sinclair/typebox").TUnion<(import("@sinclair/typebox").TString | import("@sinclair/typebox").TNumber)[]>>;
|
|
10
10
|
/**
|
|
11
11
|
* CSRF token. This can be any cryptographically secure randomized string.
|
|
12
12
|
*
|
|
@@ -19,20 +19,20 @@ export declare const userJwtDataShape: import("object-shape-tester").Shape<{
|
|
|
19
19
|
*
|
|
20
20
|
* @category Internal
|
|
21
21
|
*/
|
|
22
|
-
export type
|
|
22
|
+
export type JwtUserData = typeof userJwtDataShape.runtimeType;
|
|
23
23
|
/**
|
|
24
|
-
* Creates a new signed and encrypted {@link
|
|
24
|
+
* Creates a new signed and encrypted {@link JwtUserData} when a client (frontend) successfully
|
|
25
25
|
* authenticates with the host (backend). This is used by host (backend) code to establish a new
|
|
26
26
|
* user session. The output of this function should be sent to the client (frontend) for storage.
|
|
27
27
|
*
|
|
28
28
|
* @category Internal
|
|
29
29
|
*/
|
|
30
|
-
export declare function createUserJwt(data: Readonly<
|
|
30
|
+
export declare function createUserJwt(data: Readonly<JwtUserData>, params: Readonly<CreateJwtParams>): Promise<string>;
|
|
31
31
|
/**
|
|
32
|
-
* Parses a {@link
|
|
32
|
+
* Parses a {@link JwtUserData} generated from {@link createUserJwt}. This should be used on the host
|
|
33
33
|
* (backend) to a client (frontend) request. Do not use this function in client (frontend) code: it
|
|
34
34
|
* requires JWT signing keys which should not be shared with any client (frontend).
|
|
35
35
|
*
|
|
36
36
|
* @category Internal
|
|
37
37
|
*/
|
|
38
|
-
export declare function parseUserJwt(encryptedJwt: string, params: Readonly<ParseJwtParams>): Promise<
|
|
38
|
+
export declare function parseUserJwt(encryptedJwt: string, params: Readonly<ParseJwtParams>): Promise<ParsedJwt<JwtUserData> | undefined>;
|
package/dist/jwt/user-jwt.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { checkValidShape, defineShape } from 'object-shape-tester';
|
|
2
|
-
import { createJwt, parseJwt } from './jwt.js';
|
|
1
|
+
import { checkValidShape, defineShape, unionShape } from 'object-shape-tester';
|
|
2
|
+
import { createJwt, parseJwt, } from './jwt.js';
|
|
3
3
|
/**
|
|
4
|
-
* Shape definition and source of truth for {@link
|
|
4
|
+
* Shape definition and source of truth for {@link JwtUserData}.
|
|
5
5
|
*
|
|
6
6
|
* @category Internal
|
|
7
7
|
*/
|
|
8
8
|
export const userJwtDataShape = defineShape({
|
|
9
9
|
/** The id from your database of the user you're authenticating. */
|
|
10
|
-
userId: '',
|
|
10
|
+
userId: unionShape('', -1),
|
|
11
11
|
/**
|
|
12
12
|
* CSRF token. This can be any cryptographically secure randomized string.
|
|
13
13
|
*
|
|
@@ -16,7 +16,7 @@ export const userJwtDataShape = defineShape({
|
|
|
16
16
|
csrfToken: '',
|
|
17
17
|
});
|
|
18
18
|
/**
|
|
19
|
-
* Creates a new signed and encrypted {@link
|
|
19
|
+
* Creates a new signed and encrypted {@link JwtUserData} when a client (frontend) successfully
|
|
20
20
|
* authenticates with the host (backend). This is used by host (backend) code to establish a new
|
|
21
21
|
* user session. The output of this function should be sent to the client (frontend) for storage.
|
|
22
22
|
*
|
|
@@ -26,16 +26,19 @@ export async function createUserJwt(data, params) {
|
|
|
26
26
|
return await createJwt(data, params);
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
* Parses a {@link
|
|
29
|
+
* Parses a {@link JwtUserData} generated from {@link createUserJwt}. This should be used on the host
|
|
30
30
|
* (backend) to a client (frontend) request. Do not use this function in client (frontend) code: it
|
|
31
31
|
* requires JWT signing keys which should not be shared with any client (frontend).
|
|
32
32
|
*
|
|
33
33
|
* @category Internal
|
|
34
34
|
*/
|
|
35
35
|
export async function parseUserJwt(encryptedJwt, params) {
|
|
36
|
-
const
|
|
37
|
-
if (!checkValidShape(
|
|
36
|
+
const { data, jwtExpiration } = await parseJwt(encryptedJwt, params);
|
|
37
|
+
if (!checkValidShape(data, userJwtDataShape)) {
|
|
38
38
|
throw new TypeError('Verified jwt has wrong data.');
|
|
39
39
|
}
|
|
40
|
-
return
|
|
40
|
+
return {
|
|
41
|
+
data,
|
|
42
|
+
jwtExpiration,
|
|
43
|
+
};
|
|
41
44
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "auth-vir",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Auth made easy and secure via JWT cookies, CSRF tokens, and password hashing helpers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"auth",
|
|
@@ -33,15 +33,17 @@
|
|
|
33
33
|
"build": "virmator frontend build",
|
|
34
34
|
"compile": "virmator compile",
|
|
35
35
|
"docs": "virmator docs",
|
|
36
|
+
"init": "node -e \"require('fs').rmSync('src/generated', { recursive: true, force: true })\" && prisma generate --no-hints --schema test-files/schema.prisma",
|
|
36
37
|
"start": "virmator frontend",
|
|
37
|
-
"test": "
|
|
38
|
-
"test:coverage": "npm run test coverage",
|
|
38
|
+
"test": "runstorm --names web,node, \"npm run test:web\" \"npm run test:node\"",
|
|
39
39
|
"test:docs": "virmator docs check",
|
|
40
|
-
"test:
|
|
40
|
+
"test:node": "virmator test node 'src/**/*.test.node.ts'",
|
|
41
|
+
"test:update": "npm test update",
|
|
42
|
+
"test:web": "virmator test web"
|
|
41
43
|
},
|
|
42
44
|
"dependencies": {
|
|
43
|
-
"@augment-vir/assert": "^31.
|
|
44
|
-
"@augment-vir/common": "^31.
|
|
45
|
+
"@augment-vir/assert": "^31.41.0",
|
|
46
|
+
"@augment-vir/common": "^31.41.0",
|
|
45
47
|
"date-vir": "^8.0.0",
|
|
46
48
|
"hash-wasm": "^4.12.0",
|
|
47
49
|
"jose": "^6.1.0",
|
|
@@ -50,7 +52,9 @@
|
|
|
50
52
|
"url-vir": "^2.1.6"
|
|
51
53
|
},
|
|
52
54
|
"devDependencies": {
|
|
53
|
-
"@augment-vir/test": "^31.
|
|
55
|
+
"@augment-vir/test": "^31.41.0",
|
|
56
|
+
"@prisma/client": "^6.17.1",
|
|
57
|
+
"@types/node": "^24.8.1",
|
|
54
58
|
"@web/dev-server-esbuild": "^1.0.4",
|
|
55
59
|
"@web/test-runner": "^0.20.2",
|
|
56
60
|
"@web/test-runner-commands": "^0.9.0",
|
|
@@ -58,6 +62,7 @@
|
|
|
58
62
|
"@web/test-runner-visual-regression": "^0.10.0",
|
|
59
63
|
"istanbul-smart-text-reporter": "^1.1.5",
|
|
60
64
|
"markdown-code-example-inserter": "^3.0.3",
|
|
65
|
+
"prisma-vir": "^1.2.2",
|
|
61
66
|
"typedoc": "^0.28.14"
|
|
62
67
|
},
|
|
63
68
|
"engines": {
|