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.
Files changed (59) hide show
  1. package/README.md +27 -18
  2. package/dist/auth-client/backend-auth.client.d.ts +177 -0
  3. package/dist/auth-client/backend-auth.client.js +232 -0
  4. package/dist/auth-client/frontend-auth.client.d.ts +64 -0
  5. package/dist/auth-client/frontend-auth.client.js +107 -0
  6. package/dist/auth.d.ts +32 -46
  7. package/dist/auth.js +36 -36
  8. package/dist/cookie.d.ts +15 -4
  9. package/dist/cookie.js +16 -4
  10. package/dist/csrf-token.d.ts +113 -3
  11. package/dist/csrf-token.js +101 -5
  12. package/dist/generated/browser.d.ts +9 -0
  13. package/dist/generated/browser.js +16 -0
  14. package/dist/generated/client.d.ts +26 -0
  15. package/dist/generated/client.js +31 -0
  16. package/dist/generated/commonInputTypes.d.ts +122 -0
  17. package/dist/generated/commonInputTypes.js +1 -0
  18. package/dist/generated/enums.d.ts +1 -0
  19. package/dist/generated/enums.js +9 -0
  20. package/dist/generated/internal/class.d.ts +126 -0
  21. package/dist/generated/internal/class.js +84 -0
  22. package/dist/generated/internal/prismaNamespace.d.ts +544 -0
  23. package/dist/generated/internal/prismaNamespace.js +101 -0
  24. package/dist/generated/internal/prismaNamespaceBrowser.d.ts +75 -0
  25. package/dist/generated/internal/prismaNamespaceBrowser.js +69 -0
  26. package/dist/generated/models/User.d.ts +983 -0
  27. package/dist/generated/models/User.js +1 -0
  28. package/dist/generated/models.d.ts +2 -0
  29. package/dist/generated/models.js +1 -0
  30. package/dist/generated/shapes.gen.d.ts +8 -0
  31. package/dist/generated/shapes.gen.js +11 -0
  32. package/dist/headers.d.ts +20 -0
  33. package/dist/headers.js +33 -0
  34. package/dist/index.d.ts +2 -0
  35. package/dist/index.js +2 -0
  36. package/dist/jwt/jwt.d.ts +11 -2
  37. package/dist/jwt/jwt.js +14 -3
  38. package/dist/jwt/user-jwt.d.ts +8 -8
  39. package/dist/jwt/user-jwt.js +12 -9
  40. package/package.json +12 -7
  41. package/src/auth-client/backend-auth.client.ts +500 -0
  42. package/src/auth-client/frontend-auth.client.ts +182 -0
  43. package/src/auth.ts +99 -77
  44. package/src/cookie.ts +20 -8
  45. package/src/csrf-token.ts +196 -5
  46. package/src/generated/browser.ts +23 -0
  47. package/src/generated/client.ts +47 -0
  48. package/src/generated/commonInputTypes.ts +147 -0
  49. package/src/generated/enums.ts +14 -0
  50. package/src/generated/internal/class.ts +236 -0
  51. package/src/generated/internal/prismaNamespace.ts +761 -0
  52. package/src/generated/internal/prismaNamespaceBrowser.ts +102 -0
  53. package/src/generated/models/User.ts +1135 -0
  54. package/src/generated/models.ts +11 -0
  55. package/src/generated/shapes.gen.ts +15 -0
  56. package/src/headers.ts +35 -0
  57. package/src/index.ts +2 -0
  58. package/src/jwt/jwt.ts +34 -5
  59. package/src/jwt/user-jwt.ts +21 -12
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export type * from './models/User.js';
2
+ export type * from './commonInputTypes.js';
@@ -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[];
@@ -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
- return data;
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
  }
@@ -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 UserJwtData}.
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: string;
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 UserJwtData = typeof userJwtDataShape.runtimeType;
22
+ export type JwtUserData = typeof userJwtDataShape.runtimeType;
23
23
  /**
24
- * Creates a new signed and encrypted {@link UserJwtData} when a client (frontend) successfully
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<UserJwtData>, params: Readonly<CreateJwtParams>): Promise<string>;
30
+ export declare function createUserJwt(data: Readonly<JwtUserData>, params: Readonly<CreateJwtParams>): Promise<string>;
31
31
  /**
32
- * Parses a {@link UserJwtData} generated from {@link createUserJwt}. This should be used on the host
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<UserJwtData | undefined>;
38
+ export declare function parseUserJwt(encryptedJwt: string, params: Readonly<ParseJwtParams>): Promise<ParsedJwt<JwtUserData> | undefined>;
@@ -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 UserJwtData}.
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 UserJwtData} when a client (frontend) successfully
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 UserJwtData} generated from {@link createUserJwt}. This should be used on the host
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 parsed = await parseJwt(encryptedJwt, params);
37
- if (!checkValidShape(parsed, userJwtDataShape)) {
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 parsed;
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": "1.3.2",
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": "virmator test web",
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:update": "npm test update"
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.40.0",
44
- "@augment-vir/common": "^31.40.0",
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.40.0",
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": {