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,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 AnyDuration,
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
- return data as JwtData;
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
  }
@@ -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 {createJwt, type CreateJwtParams, parseJwt, type ParseJwtParams} from './jwt.js';
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 UserJwtData}.
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 UserJwtData = typeof userJwtDataShape.runtimeType;
32
+ export type JwtUserData = typeof userJwtDataShape.runtimeType;
27
33
 
28
34
  /**
29
- * Creates a new signed and encrypted {@link UserJwtData} when a client (frontend) successfully
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<UserJwtData>,
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 UserJwtData} generated from {@link createUserJwt}. This should be used on the host
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<UserJwtData | undefined> {
53
- const parsed = await parseJwt(encryptedJwt, params);
58
+ ): Promise<ParsedJwt<JwtUserData> | undefined> {
59
+ const {data, jwtExpiration} = await parseJwt(encryptedJwt, params);
54
60
 
55
- if (!checkValidShape(parsed, userJwtDataShape)) {
61
+ if (!checkValidShape(data, userJwtDataShape)) {
56
62
  throw new TypeError('Verified jwt has wrong data.');
57
63
  }
58
64
 
59
- return parsed;
65
+ return {
66
+ data,
67
+ jwtExpiration,
68
+ };
60
69
  }