@robelest/convex-auth 0.0.2-preview.0 → 0.0.2-preview.2

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 (166) hide show
  1. package/dist/bin.cjs +17 -15
  2. package/dist/client/index.d.ts +84 -30
  3. package/dist/client/index.d.ts.map +1 -1
  4. package/dist/client/index.js +259 -59
  5. package/dist/client/index.js.map +1 -1
  6. package/dist/component/_generated/component.d.ts +46 -120
  7. package/dist/component/_generated/component.d.ts.map +1 -1
  8. package/dist/component/index.d.ts +2 -4
  9. package/dist/component/index.d.ts.map +1 -1
  10. package/dist/component/index.js +2 -4
  11. package/dist/component/index.js.map +1 -1
  12. package/dist/component/public.d.ts +233 -167
  13. package/dist/component/public.d.ts.map +1 -1
  14. package/dist/component/public.js +328 -155
  15. package/dist/component/public.js.map +1 -1
  16. package/dist/component/schema.d.ts +127 -12
  17. package/dist/component/schema.d.ts.map +1 -1
  18. package/dist/component/schema.js +136 -10
  19. package/dist/component/schema.js.map +1 -1
  20. package/dist/providers/{Anonymous.d.ts → anonymous.d.ts} +8 -8
  21. package/dist/providers/{Anonymous.d.ts.map → anonymous.d.ts.map} +1 -1
  22. package/dist/providers/{Anonymous.js → anonymous.js} +9 -10
  23. package/dist/providers/anonymous.js.map +1 -0
  24. package/dist/providers/{ConvexCredentials.d.ts → credentials.d.ts} +11 -11
  25. package/dist/providers/credentials.d.ts.map +1 -0
  26. package/dist/providers/{ConvexCredentials.js → credentials.js} +8 -8
  27. package/dist/providers/credentials.js.map +1 -0
  28. package/dist/providers/{Email.d.ts → email.d.ts} +6 -6
  29. package/dist/providers/email.d.ts.map +1 -0
  30. package/dist/providers/{Email.js → email.js} +6 -6
  31. package/dist/providers/email.js.map +1 -0
  32. package/dist/providers/{Password.d.ts → password.d.ts} +10 -10
  33. package/dist/providers/{Password.d.ts.map → password.d.ts.map} +1 -1
  34. package/dist/providers/{Password.js → password.js} +19 -20
  35. package/dist/providers/password.js.map +1 -0
  36. package/dist/providers/{Phone.d.ts → phone.d.ts} +3 -3
  37. package/dist/providers/{Phone.d.ts.map → phone.d.ts.map} +1 -1
  38. package/dist/providers/{Phone.js → phone.js} +3 -3
  39. package/dist/providers/{Phone.js.map → phone.js.map} +1 -1
  40. package/dist/server/implementation/db.d.ts +5 -2
  41. package/dist/server/implementation/db.d.ts.map +1 -1
  42. package/dist/server/implementation/db.js +2 -1
  43. package/dist/server/implementation/db.js.map +1 -1
  44. package/dist/server/implementation/index.d.ts +285 -180
  45. package/dist/server/implementation/index.d.ts.map +1 -1
  46. package/dist/server/implementation/index.js +280 -173
  47. package/dist/server/implementation/index.js.map +1 -1
  48. package/dist/server/implementation/mutations/createAccountFromCredentials.d.ts.map +1 -1
  49. package/dist/server/implementation/mutations/createAccountFromCredentials.js +8 -18
  50. package/dist/server/implementation/mutations/createAccountFromCredentials.js.map +1 -1
  51. package/dist/server/implementation/mutations/createVerificationCode.d.ts.map +1 -1
  52. package/dist/server/implementation/mutations/createVerificationCode.js +16 -44
  53. package/dist/server/implementation/mutations/createVerificationCode.js.map +1 -1
  54. package/dist/server/implementation/mutations/invalidateSessions.d.ts.map +1 -1
  55. package/dist/server/implementation/mutations/invalidateSessions.js +4 -8
  56. package/dist/server/implementation/mutations/invalidateSessions.js.map +1 -1
  57. package/dist/server/implementation/mutations/modifyAccount.d.ts.map +1 -1
  58. package/dist/server/implementation/mutations/modifyAccount.js +8 -19
  59. package/dist/server/implementation/mutations/modifyAccount.js.map +1 -1
  60. package/dist/server/implementation/mutations/refreshSession.d.ts.map +1 -1
  61. package/dist/server/implementation/mutations/refreshSession.js +9 -23
  62. package/dist/server/implementation/mutations/refreshSession.js.map +1 -1
  63. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.d.ts.map +1 -1
  64. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.js +6 -12
  65. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.js.map +1 -1
  66. package/dist/server/implementation/mutations/signIn.d.ts.map +1 -1
  67. package/dist/server/implementation/mutations/signIn.js +2 -1
  68. package/dist/server/implementation/mutations/signIn.js.map +1 -1
  69. package/dist/server/implementation/mutations/signOut.d.ts.map +1 -1
  70. package/dist/server/implementation/mutations/signOut.js +5 -6
  71. package/dist/server/implementation/mutations/signOut.js.map +1 -1
  72. package/dist/server/implementation/mutations/storeRef.d.ts +8 -0
  73. package/dist/server/implementation/mutations/storeRef.d.ts.map +1 -0
  74. package/dist/server/implementation/mutations/storeRef.js +8 -0
  75. package/dist/server/implementation/mutations/storeRef.js.map +1 -0
  76. package/dist/server/implementation/mutations/userOAuth.d.ts.map +1 -1
  77. package/dist/server/implementation/mutations/userOAuth.js +16 -53
  78. package/dist/server/implementation/mutations/userOAuth.js.map +1 -1
  79. package/dist/server/implementation/mutations/verifier.d.ts.map +1 -1
  80. package/dist/server/implementation/mutations/verifier.js +4 -8
  81. package/dist/server/implementation/mutations/verifier.js.map +1 -1
  82. package/dist/server/implementation/mutations/verifierSignature.d.ts.map +1 -1
  83. package/dist/server/implementation/mutations/verifierSignature.js +6 -10
  84. package/dist/server/implementation/mutations/verifierSignature.js.map +1 -1
  85. package/dist/server/implementation/mutations/verifyCodeAndSignIn.d.ts.map +1 -1
  86. package/dist/server/implementation/mutations/verifyCodeAndSignIn.js +7 -16
  87. package/dist/server/implementation/mutations/verifyCodeAndSignIn.js.map +1 -1
  88. package/dist/server/implementation/provider.d.ts +2 -1
  89. package/dist/server/implementation/provider.d.ts.map +1 -1
  90. package/dist/server/implementation/provider.js.map +1 -1
  91. package/dist/server/implementation/rateLimit.d.ts.map +1 -1
  92. package/dist/server/implementation/rateLimit.js +13 -39
  93. package/dist/server/implementation/rateLimit.js.map +1 -1
  94. package/dist/server/implementation/refreshTokens.d.ts +1 -8
  95. package/dist/server/implementation/refreshTokens.d.ts.map +1 -1
  96. package/dist/server/implementation/refreshTokens.js +14 -58
  97. package/dist/server/implementation/refreshTokens.js.map +1 -1
  98. package/dist/server/implementation/sessions.d.ts +2 -20
  99. package/dist/server/implementation/sessions.d.ts.map +1 -1
  100. package/dist/server/implementation/sessions.js +8 -35
  101. package/dist/server/implementation/sessions.js.map +1 -1
  102. package/dist/server/implementation/types.d.ts +11 -267
  103. package/dist/server/implementation/types.d.ts.map +1 -1
  104. package/dist/server/implementation/types.js +1 -181
  105. package/dist/server/implementation/types.js.map +1 -1
  106. package/dist/server/implementation/users.d.ts.map +1 -1
  107. package/dist/server/implementation/users.js +19 -67
  108. package/dist/server/implementation/users.js.map +1 -1
  109. package/dist/server/index.d.ts +18 -0
  110. package/dist/server/index.d.ts.map +1 -1
  111. package/dist/server/index.js +255 -0
  112. package/dist/server/index.js.map +1 -1
  113. package/dist/server/provider_utils.d.ts +1 -1
  114. package/dist/server/provider_utils.d.ts.map +1 -1
  115. package/dist/server/provider_utils.js +2 -2
  116. package/dist/server/provider_utils.js.map +1 -1
  117. package/dist/server/types.d.ts +91 -52
  118. package/dist/server/types.d.ts.map +1 -1
  119. package/package.json +3 -6
  120. package/src/cli/index.ts +20 -19
  121. package/src/client/index.ts +347 -110
  122. package/src/component/_generated/component.ts +55 -214
  123. package/src/component/index.ts +1 -11
  124. package/src/component/public.ts +366 -178
  125. package/src/component/schema.ts +150 -19
  126. package/src/providers/{Anonymous.ts → anonymous.ts} +10 -11
  127. package/src/providers/{ConvexCredentials.ts → credentials.ts} +11 -11
  128. package/src/providers/{Email.ts → email.ts} +5 -5
  129. package/src/providers/{Password.ts → password.ts} +22 -27
  130. package/src/providers/{Phone.ts → phone.ts} +2 -2
  131. package/src/server/implementation/db.ts +5 -2
  132. package/src/server/implementation/index.ts +368 -313
  133. package/src/server/implementation/mutations/createAccountFromCredentials.ts +11 -25
  134. package/src/server/implementation/mutations/createVerificationCode.ts +16 -47
  135. package/src/server/implementation/mutations/invalidateSessions.ts +4 -9
  136. package/src/server/implementation/mutations/modifyAccount.ts +8 -22
  137. package/src/server/implementation/mutations/refreshSession.ts +11 -24
  138. package/src/server/implementation/mutations/retrieveAccountWithCredentials.ts +9 -17
  139. package/src/server/implementation/mutations/signIn.ts +2 -1
  140. package/src/server/implementation/mutations/signOut.ts +5 -8
  141. package/src/server/implementation/mutations/storeRef.ts +7 -0
  142. package/src/server/implementation/mutations/userOAuth.ts +10 -50
  143. package/src/server/implementation/mutations/verifier.ts +4 -9
  144. package/src/server/implementation/mutations/verifierSignature.ts +6 -12
  145. package/src/server/implementation/mutations/verifyCodeAndSignIn.ts +7 -18
  146. package/src/server/implementation/provider.ts +2 -1
  147. package/src/server/implementation/rateLimit.ts +15 -41
  148. package/src/server/implementation/refreshTokens.ts +26 -76
  149. package/src/server/implementation/sessions.ts +8 -39
  150. package/src/server/implementation/types.ts +16 -191
  151. package/src/server/implementation/users.ts +19 -66
  152. package/src/server/index.ts +373 -0
  153. package/src/server/provider_utils.ts +2 -2
  154. package/src/server/types.ts +116 -51
  155. package/dist/providers/Anonymous.js.map +0 -1
  156. package/dist/providers/ConvexCredentials.d.ts.map +0 -1
  157. package/dist/providers/ConvexCredentials.js.map +0 -1
  158. package/dist/providers/Email.d.ts.map +0 -1
  159. package/dist/providers/Email.js.map +0 -1
  160. package/dist/providers/Password.js.map +0 -1
  161. package/providers/Anonymous/package.json +0 -6
  162. package/providers/ConvexCredentials/package.json +0 -6
  163. package/providers/Email/package.json +0 -6
  164. package/providers/Password/package.json +0 -6
  165. package/providers/Phone/package.json +0 -6
  166. package/server/package.json +0 -6
@@ -14,7 +14,8 @@ import {
14
14
  import { ConvexAuthConfig } from "../../types.js";
15
15
  import { LOG_LEVELS, logWithLevel, sha256 } from "../utils.js";
16
16
  import { upsertUserAndAccount } from "../users.js";
17
- import { createAuthDb } from "../db.js";
17
+ import { authDb } from "../db.js";
18
+ import { AUTH_STORE_REF } from "./storeRef.js";
18
19
 
19
20
  export const verifyCodeAndSignInArgs = v.object({
20
21
  params: v.any(),
@@ -87,7 +88,7 @@ export const callVerifyCodeAndSignIn = async (
87
88
  ctx: ActionCtx,
88
89
  args: Infer<typeof verifyCodeAndSignInArgs>,
89
90
  ): Promise<ReturnType> => {
90
- return ctx.runMutation("auth:store" as any, {
91
+ return ctx.runMutation(AUTH_STORE_REF, {
91
92
  args: {
92
93
  type: "verifyCodeAndSignIn",
93
94
  ...args,
@@ -116,26 +117,15 @@ async function verifyCodeOnly(
116
117
  config: ConvexAuthConfig,
117
118
  sessionId: GenericId<"session"> | null,
118
119
  ) {
119
- const authDb =
120
- config.component !== undefined ? createAuthDb(ctx, config.component) : null;
120
+ const db = authDb(ctx, config);
121
121
  const { params, verifier } = args;
122
122
  const codeHash = await sha256(params.code);
123
- const verificationCode =
124
- authDb !== null
125
- ? await authDb.verificationCodes.getByCode(codeHash)
126
- : await ctx.db
127
- .query("verification")
128
- .withIndex("code", (q) => q.eq("code", codeHash))
129
- .unique();
123
+ const verificationCode = await db.verificationCodes.getByCode(codeHash);
130
124
  if (verificationCode === null) {
131
125
  logWithLevel(LOG_LEVELS.ERROR, "Invalid verification code");
132
126
  return null;
133
127
  }
134
- if (authDb !== null) {
135
- await authDb.verificationCodes.delete(verificationCode._id);
136
- } else {
137
- await ctx.db.delete(verificationCode._id);
138
- }
128
+ await db.verificationCodes.delete(verificationCode._id);
139
129
  if (verificationCode.verifier !== verifier) {
140
130
  logWithLevel(LOG_LEVELS.ERROR, "Invalid verifier");
141
131
  return null;
@@ -145,8 +135,7 @@ async function verifyCodeOnly(
145
135
  return null;
146
136
  }
147
137
  const { accountId, emailVerified, phoneVerified } = verificationCode;
148
- const account =
149
- authDb !== null ? await authDb.accounts.getById(accountId) : await ctx.db.get(accountId);
138
+ const account = await db.accounts.getById(accountId);
150
139
  if (account === null) {
151
140
  logWithLevel(
152
141
  LOG_LEVELS.ERROR,
@@ -1,4 +1,5 @@
1
1
  import { AuthProviderMaterializedConfig } from "../types.js";
2
+ import { ConvexAuthMaterializedConfig } from "../types.js";
2
3
 
3
4
  export async function hash(provider: any, secret: string) {
4
5
  if (provider.type !== "credentials") {
@@ -35,4 +36,4 @@ export type GetProviderOrThrowFunc = (
35
36
  allowExtraProviders?: boolean,
36
37
  ) => AuthProviderMaterializedConfig;
37
38
 
38
- export type Config = any;
39
+ export type Config = ConvexAuthMaterializedConfig;
@@ -1,6 +1,6 @@
1
1
  import { ConvexAuthConfig } from "../types.js";
2
2
  import { Doc, MutationCtx } from "./types.js";
3
- import { createAuthDb } from "./db.js";
3
+ import { authDb } from "./db.js";
4
4
 
5
5
  const DEFAULT_MAX_SIGN_IN_ATTEMPTS_PER_HOUR = 10;
6
6
 
@@ -21,34 +21,20 @@ export async function recordFailedSignIn(
21
21
  identifier: string,
22
22
  config: ConvexAuthConfig,
23
23
  ) {
24
+ const db = authDb(ctx, config);
24
25
  const state = await getRateLimitState(ctx, identifier, config);
25
26
  if (state !== null) {
26
- if (config.component !== undefined) {
27
- await createAuthDb(ctx, config.component).rateLimits.patch(state.limit._id, {
28
- attemptsLeft: state.attempsLeft - 1,
29
- lastAttemptTime: Date.now(),
30
- });
31
- } else {
32
- await ctx.db.patch(state.limit._id, {
33
- attemptsLeft: state.attempsLeft - 1,
34
- lastAttemptTime: Date.now(),
35
- });
36
- }
27
+ await db.rateLimits.patch(state.limit._id, {
28
+ attemptsLeft: state.attempsLeft - 1,
29
+ lastAttemptTime: Date.now(),
30
+ });
37
31
  } else {
38
32
  const maxAttempsPerHour = configuredMaxAttempsPerHour(config);
39
- if (config.component !== undefined) {
40
- await createAuthDb(ctx, config.component).rateLimits.create({
41
- identifier,
42
- attemptsLeft: maxAttempsPerHour - 1,
43
- lastAttemptTime: Date.now(),
44
- });
45
- } else {
46
- await ctx.db.insert("limit", {
47
- identifier,
48
- attemptsLeft: maxAttempsPerHour - 1,
49
- lastAttemptTime: Date.now(),
50
- });
51
- }
33
+ await db.rateLimits.create({
34
+ identifier,
35
+ attemptsLeft: maxAttempsPerHour - 1,
36
+ lastAttemptTime: Date.now(),
37
+ });
52
38
  }
53
39
  }
54
40
 
@@ -59,13 +45,7 @@ export async function resetSignInRateLimit(
59
45
  ) {
60
46
  const existingState = await getRateLimitState(ctx, identifier, config);
61
47
  if (existingState !== null) {
62
- if (config.component !== undefined) {
63
- await createAuthDb(ctx, config.component).rateLimits.delete(
64
- existingState.limit._id,
65
- );
66
- } else {
67
- await ctx.db.delete(existingState.limit._id);
68
- }
48
+ await authDb(ctx, config).rateLimits.delete(existingState.limit._id);
69
49
  }
70
50
  }
71
51
 
@@ -76,15 +56,9 @@ async function getRateLimitState(
76
56
  ) {
77
57
  const now = Date.now();
78
58
  const maxAttempsPerHour = configuredMaxAttempsPerHour(config);
79
- const limit =
80
- config.component !== undefined
81
- ? ((await createAuthDb(ctx, config.component).rateLimits.get(identifier)) as
82
- | Doc<"limit">
83
- | null)
84
- : await ctx.db
85
- .query("limit")
86
- .withIndex("identifier", (q) => q.eq("identifier", identifier))
87
- .unique();
59
+ const limit = (await authDb(ctx, config).rateLimits.get(identifier)) as
60
+ | Doc<"limit">
61
+ | null;
88
62
  if (limit === null) {
89
63
  return null;
90
64
  }
@@ -8,7 +8,7 @@ import {
8
8
  maybeRedact,
9
9
  stringToNumber,
10
10
  } from "./utils.js";
11
- import { createAuthDb } from "./db.js";
11
+ import { authDb } from "./db.js";
12
12
 
13
13
  const DEFAULT_SESSION_INACTIVE_DURATION_MS = 1000 * 60 * 60 * 24 * 30; // 30 days
14
14
  export const REFRESH_TOKEN_REUSE_WINDOW_MS = 10 * 1000; // 10 seconds
@@ -17,24 +17,18 @@ export async function createRefreshToken(
17
17
  config: ConvexAuthConfig,
18
18
  sessionId: GenericId<"session">,
19
19
  parentRefreshTokenId: GenericId<"token"> | null,
20
- ) {
20
+ ): Promise<GenericId<"token">> {
21
+ const db = authDb(ctx, config);
21
22
  const expirationTime =
22
23
  Date.now() +
23
24
  (config.session?.inactiveDurationMs ??
24
25
  stringToNumber(process.env.AUTH_SESSION_INACTIVE_DURATION_MS) ??
25
26
  DEFAULT_SESSION_INACTIVE_DURATION_MS);
26
- if (config.component !== undefined) {
27
- return (await createAuthDb(ctx, config.component).refreshTokens.create({
28
- sessionId,
29
- expirationTime,
30
- parentRefreshTokenId: parentRefreshTokenId ?? undefined,
31
- })) as GenericId<"token">;
32
- }
33
- const newRefreshTokenId = await ctx.db.insert("token", {
27
+ const newRefreshTokenId = (await db.refreshTokens.create({
34
28
  sessionId,
35
29
  expirationTime,
36
30
  parentRefreshTokenId: parentRefreshTokenId ?? undefined,
37
- });
31
+ })) as GenericId<"token">;
38
32
  return newRefreshTokenId;
39
33
  }
40
34
 
@@ -74,27 +68,16 @@ export async function invalidateRefreshTokensInSubtree(
74
68
  refreshToken: Doc<"token">,
75
69
  config: ConvexAuthConfig,
76
70
  ) {
77
- const authDb =
78
- config.component !== undefined ? createAuthDb(ctx, config.component) : null;
71
+ const db = authDb(ctx, config);
79
72
  const tokensToInvalidate = [refreshToken];
80
- let frontier = [refreshToken._id];
73
+ let frontier: GenericId<"token">[] = [refreshToken._id];
81
74
  while (frontier.length > 0) {
82
- const nextFrontier = [];
75
+ const nextFrontier: GenericId<"token">[] = [];
83
76
  for (const currentTokenId of frontier) {
84
- const children =
85
- authDb !== null
86
- ? ((await authDb.refreshTokens.getChildren(
87
- refreshToken.sessionId,
88
- currentTokenId,
89
- )) as Doc<"token">[])
90
- : await ctx.db
91
- .query("token")
92
- .withIndex("sessionIdAndParentRefreshTokenId", (q) =>
93
- q
94
- .eq("sessionId", refreshToken.sessionId)
95
- .eq("parentRefreshTokenId", currentTokenId),
96
- )
97
- .collect();
77
+ const children = (await db.refreshTokens.getChildren(
78
+ refreshToken.sessionId,
79
+ currentTokenId,
80
+ )) as Doc<"token">[];
98
81
  tokensToInvalidate.push(...children);
99
82
  nextFrontier.push(...children.map((child) => child._id));
100
83
  }
@@ -106,15 +89,9 @@ export async function invalidateRefreshTokensInSubtree(
106
89
  token.firstUsedTime === undefined ||
107
90
  token.firstUsedTime > Date.now() - REFRESH_TOKEN_REUSE_WINDOW_MS
108
91
  ) {
109
- if (authDb !== null) {
110
- await authDb.refreshTokens.patch(token._id, {
111
- firstUsedTime: Date.now() - REFRESH_TOKEN_REUSE_WINDOW_MS,
112
- });
113
- } else {
114
- await ctx.db.patch(token._id, {
115
- firstUsedTime: Date.now() - REFRESH_TOKEN_REUSE_WINDOW_MS,
116
- });
117
- }
92
+ await db.refreshTokens.patch(token._id, {
93
+ firstUsedTime: Date.now() - REFRESH_TOKEN_REUSE_WINDOW_MS,
94
+ });
118
95
  }
119
96
  }
120
97
  return tokensToInvalidate;
@@ -125,19 +102,7 @@ export async function deleteAllRefreshTokens(
125
102
  sessionId: GenericId<"session">,
126
103
  config: ConvexAuthConfig,
127
104
  ) {
128
- if (config.component !== undefined) {
129
- await createAuthDb(ctx, config.component).refreshTokens.deleteAll(sessionId);
130
- return;
131
- }
132
- const existingRefreshTokens = await ctx.db
133
- .query("token")
134
- .withIndex("sessionIdAndParentRefreshTokenId", (q) =>
135
- q.eq("sessionId", sessionId),
136
- )
137
- .collect();
138
- for (const refreshTokenDoc of existingRefreshTokens) {
139
- await ctx.db.delete(refreshTokenDoc._id);
140
- }
105
+ await authDb(ctx, config).refreshTokens.deleteAll(sessionId);
141
106
  }
142
107
 
143
108
  export async function refreshTokenIfValid(
@@ -146,16 +111,12 @@ export async function refreshTokenIfValid(
146
111
  tokenSessionId: string,
147
112
  config: ConvexAuthConfig,
148
113
  ) {
149
- const authDb =
150
- config.component !== undefined ? createAuthDb(ctx, config.component) : null;
114
+ const db = authDb(ctx, config);
151
115
  let refreshTokenDoc: Doc<"token"> | null;
152
116
  try {
153
- refreshTokenDoc =
154
- authDb !== null
155
- ? ((await authDb.refreshTokens.getById(
156
- refreshTokenId as GenericId<"token">,
157
- )) as Doc<"token"> | null)
158
- : await ctx.db.get(refreshTokenId as GenericId<"token">);
117
+ refreshTokenDoc = (await db.refreshTokens.getById(
118
+ refreshTokenId as GenericId<"token">,
119
+ )) as Doc<"token"> | null;
159
120
  } catch {
160
121
  logWithLevel(LOG_LEVELS.ERROR, "Invalid refresh token format");
161
122
  return null;
@@ -175,12 +136,9 @@ export async function refreshTokenIfValid(
175
136
  }
176
137
  let session: Doc<"session"> | null;
177
138
  try {
178
- session =
179
- authDb !== null
180
- ? ((await authDb.sessions.getById(refreshTokenDoc.sessionId)) as
181
- | Doc<"session">
182
- | null)
183
- : await ctx.db.get(refreshTokenDoc.sessionId);
139
+ session = (await db.sessions.getById(refreshTokenDoc.sessionId)) as
140
+ | Doc<"session">
141
+ | null;
184
142
  } catch {
185
143
  logWithLevel(LOG_LEVELS.ERROR, "Invalid refresh token session format");
186
144
  return null;
@@ -207,15 +165,7 @@ export async function loadActiveRefreshToken(
207
165
  sessionId: GenericId<"session">,
208
166
  config: ConvexAuthConfig,
209
167
  ) {
210
- if (config.component !== undefined) {
211
- return (await createAuthDb(ctx, config.component).refreshTokens.getActive(
212
- sessionId,
213
- )) as Doc<"token"> | null;
214
- }
215
- return ctx.db
216
- .query("token")
217
- .withIndex("sessionId", (q) => q.eq("sessionId", sessionId))
218
- .filter((q) => q.eq(q.field("firstUsedTime"), undefined))
219
- .order("desc")
220
- .first();
168
+ return (await authDb(ctx, config).refreshTokens.getActive(sessionId)) as
169
+ | Doc<"token">
170
+ | null;
221
171
  }
@@ -15,7 +15,7 @@ import {
15
15
  formatRefreshToken,
16
16
  deleteAllRefreshTokens,
17
17
  } from "./refreshTokens.js";
18
- import { createAuthDb } from "./db.js";
18
+ import { authDb } from "./db.js";
19
19
 
20
20
  const DEFAULT_SESSION_TOTAL_DURATION_MS = 1000 * 60 * 60 * 24 * 30; // 30 days
21
21
 
@@ -45,14 +45,10 @@ export async function createNewAndDeleteExistingSession(
45
45
  config: ConvexAuthConfig,
46
46
  userId: GenericId<"user">,
47
47
  ) {
48
- const authDb =
49
- config.component !== undefined ? createAuthDb(ctx, config.component) : null;
48
+ const db = authDb(ctx, config);
50
49
  const existingSessionId = await getAuthSessionId(ctx);
51
50
  if (existingSessionId !== null) {
52
- const existingSession =
53
- authDb !== null
54
- ? await authDb.sessions.getById(existingSessionId)
55
- : await ctx.db.get(existingSessionId);
51
+ const existingSession = await db.sessions.getById(existingSessionId);
56
52
  if (existingSession !== null) {
57
53
  await deleteSession(ctx, existingSession, config);
58
54
  }
@@ -95,18 +91,13 @@ async function createSession(
95
91
  userId: GenericId<"user">,
96
92
  config: ConvexAuthConfig,
97
93
  ) {
94
+ const db = authDb(ctx, config);
98
95
  const expirationTime =
99
96
  Date.now() +
100
97
  (config.session?.totalDurationMs ??
101
98
  stringToNumber(process.env.AUTH_SESSION_TOTAL_DURATION_MS) ??
102
99
  DEFAULT_SESSION_TOTAL_DURATION_MS);
103
- if (config.component !== undefined) {
104
- return (await createAuthDb(ctx, config.component).sessions.create(
105
- userId,
106
- expirationTime,
107
- )) as GenericId<"session">;
108
- }
109
- return await ctx.db.insert("session", { expirationTime, userId });
100
+ return (await db.sessions.create(userId, expirationTime)) as GenericId<"session">;
110
101
  }
111
102
 
112
103
  export async function deleteSession(
@@ -114,36 +105,14 @@ export async function deleteSession(
114
105
  session: Doc<"session">,
115
106
  config: ConvexAuthConfig,
116
107
  ) {
117
- if (config.component !== undefined) {
118
- await createAuthDb(ctx, config.component).sessions.delete(session._id);
119
- } else {
120
- await ctx.db.delete(session._id);
121
- }
108
+ await authDb(ctx, config).sessions.delete(session._id);
122
109
  await deleteAllRefreshTokens(ctx, session._id, config);
123
110
  }
124
111
 
125
112
  /**
126
- * Return the current session ID.
127
- *
128
- * ```ts filename="convex/myFunctions.tsx"
129
- * import { mutation } from "./_generated/server";
130
- * import { getAuthSessionId } from "@robelest/convex-auth/component";
131
- *
132
- * export const doSomething = mutation({
133
- * args: {/* ... *\/},
134
- * handler: async (ctx, args) => {
135
- * const sessionId = await getAuthSessionId(ctx);
136
- * if (sessionId === null) {
137
- * throw new Error("Client is not authenticated!")
138
- * }
139
- * const session = await ctx.db.get(sessionId);
140
- * // ...
141
- * },
142
- * });
143
- * ```
113
+ * Return the current session ID from the auth identity subject.
144
114
  *
145
- * @param ctx query, mutation or action `ctx`
146
- * @returns the session ID or `null` if the client isn't authenticated
115
+ * Internal helper used by auth runtime internals and `auth.session.current`.
147
116
  */
148
117
  export async function getAuthSessionId(ctx: { auth: Auth }) {
149
118
  const identity = await ctx.auth.getUserIdentity();
@@ -4,215 +4,40 @@ import {
4
4
  GenericMutationCtx,
5
5
  GenericQueryCtx,
6
6
  TableNamesInDataModel,
7
- defineSchema,
8
- defineTable,
9
7
  } from "convex/server";
10
- import { GenericId, v } from "convex/values";
8
+ import { GenericId } from "convex/values";
11
9
  import { GenericDoc } from "../convex_types.js";
10
+ import schema from "../../component/schema.js";
12
11
 
13
- /**
14
- * The table definitions required by the library.
15
- *
16
- * Your schema must include these so that the indexes
17
- * are set up:
18
- *
19
- *
20
- * ```ts filename="convex/schema.ts"
21
- * import { defineSchema } from "convex/server";
22
- * import { authTables } from "@robelest/convex-auth/component";
23
- *
24
- * const schema = defineSchema({
25
- * ...authTables,
26
- * });
27
- *
28
- * export default schema;
29
- * ```
30
- *
31
- * You can inline the table definitions into your schema
32
- * and extend them with additional optional and required
33
- * fields. See https://labs.convex.dev/auth/setup/schema
34
- * for more details.
35
- */
36
- export const authTables = {
37
- /**
38
- * Users.
39
- */
40
- user: defineTable({
41
- name: v.optional(v.string()),
42
- image: v.optional(v.string()),
43
- email: v.optional(v.string()),
44
- emailVerificationTime: v.optional(v.number()),
45
- phone: v.optional(v.string()),
46
- phoneVerificationTime: v.optional(v.number()),
47
- isAnonymous: v.optional(v.boolean()),
48
- })
49
- .index("email", ["email"])
50
- .index("phone", ["phone"]),
51
- /**
52
- * Sessions.
53
- * A single user can have multiple active sessions.
54
- * See [Session document lifecycle](https://labs.convex.dev/auth/advanced#session-document-lifecycle).
55
- */
56
- session: defineTable({
57
- userId: v.id("user"),
58
- expirationTime: v.number(),
59
- }).index("userId", ["userId"]),
60
- /**
61
- * Accounts. An account corresponds to
62
- * a single authentication provider.
63
- * A single user can have multiple accounts linked.
64
- */
65
- account: defineTable({
66
- userId: v.id("user"),
67
- provider: v.string(),
68
- providerAccountId: v.string(),
69
- secret: v.optional(v.string()),
70
- emailVerified: v.optional(v.string()),
71
- phoneVerified: v.optional(v.string()),
72
- })
73
- .index("userIdAndProvider", ["userId", "provider"])
74
- .index("providerAndAccountId", ["provider", "providerAccountId"]),
75
- /**
76
- * Refresh tokens.
77
- * Refresh tokens are generally meant to be used once, to be exchanged for another
78
- * refresh token and a JWT access token, but with a few exceptions:
79
- * - The "active refresh token" is the most recently created refresh token that has
80
- * not been used yet. The parent of the active refresh token can always be used to
81
- * obtain the active refresh token.
82
- * - A refresh token can be used within a 10 second window ("reuse window") to
83
- * obtain a new refresh token.
84
- * - On any invalid use of a refresh token, the token itself and all its descendants
85
- * are invalidated.
86
- */
87
- token: defineTable({
88
- sessionId: v.id("session"),
89
- expirationTime: v.number(),
90
- firstUsedTime: v.optional(v.number()),
91
- // This is the ID of the refresh token that was exchanged to create this one.
92
- parentRefreshTokenId: v.optional(v.id("token")),
93
- })
94
- // Sort by creationTime
95
- .index("sessionId", ["sessionId"])
96
- .index("sessionIdAndParentRefreshTokenId", [
97
- "sessionId",
98
- "parentRefreshTokenId",
99
- ]),
100
- /**
101
- * Verification codes:
102
- * - OTP tokens
103
- * - magic link tokens
104
- * - OAuth codes
105
- */
106
- verification: defineTable({
107
- accountId: v.id("account"),
108
- provider: v.string(),
109
- code: v.string(),
110
- expirationTime: v.number(),
111
- verifier: v.optional(v.string()),
112
- emailVerified: v.optional(v.string()),
113
- phoneVerified: v.optional(v.string()),
114
- })
115
- .index("accountId", ["accountId"])
116
- .index("code", ["code"]),
117
- /**
118
- * PKCE verifiers for OAuth.
119
- */
120
- verifier: defineTable({
121
- sessionId: v.optional(v.id("session")),
122
- signature: v.optional(v.string()),
123
- }).index("signature", ["signature"]),
124
- /**
125
- * Rate limits for OTP and password sign-in.
126
- */
127
- limit: defineTable({
128
- identifier: v.string(),
129
- lastAttemptTime: v.number(),
130
- attemptsLeft: v.number(),
131
- }).index("identifier", ["identifier"]),
12
+ /** Data model derived from the component schema. */
13
+ export type AuthDataModel = DataModelFromSchemaDefinition<typeof schema>;
132
14
 
133
- organization: defineTable({
134
- name: v.string(),
135
- slug: v.optional(v.string()),
136
- ownerUserId: v.optional(v.id("user")),
137
- parentOrganizationId: v.optional(v.id("organization")),
138
- metadata: v.optional(v.any()),
139
- })
140
- .index("slug", ["slug"])
141
- .index("ownerUserId", ["ownerUserId"])
142
- .index("parentOrganizationId", ["parentOrganizationId"]),
143
- team: defineTable({
144
- organizationId: v.id("organization"),
145
- name: v.string(),
146
- slug: v.optional(v.string()),
147
- parentTeamId: v.optional(v.id("team")),
148
- metadata: v.optional(v.any()),
149
- })
150
- .index("organizationId", ["organizationId"])
151
- .index("organizationIdAndSlug", ["organizationId", "slug"])
152
- .index("parentTeamId", ["parentTeamId"]),
153
- teamRelation: defineTable({
154
- organizationId: v.id("organization"),
155
- parentTeamId: v.id("team"),
156
- childTeamId: v.id("team"),
157
- relation: v.optional(v.string()),
158
- })
159
- .index("organizationId", ["organizationId"])
160
- .index("organizationIdAndParentTeamId", ["organizationId", "parentTeamId"])
161
- .index("organizationIdAndChildTeamId", ["organizationId", "childTeamId"]),
162
- member: defineTable({
163
- organizationId: v.id("organization"),
164
- userId: v.id("user"),
165
- teamId: v.optional(v.id("team")),
166
- role: v.optional(v.string()),
167
- status: v.optional(v.string()),
168
- metadata: v.optional(v.any()),
169
- })
170
- .index("organizationId", ["organizationId"])
171
- .index("organizationIdAndUserId", ["organizationId", "userId"])
172
- .index("teamId", ["teamId"])
173
- .index("userId", ["userId"]),
174
- invite: defineTable({
175
- organizationId: v.optional(v.id("organization")),
176
- teamId: v.optional(v.id("team")),
177
- invitedByUserId: v.id("user"),
178
- email: v.string(),
179
- tokenHash: v.string(),
180
- role: v.optional(v.string()),
181
- status: v.union(
182
- v.literal("pending"),
183
- v.literal("accepted"),
184
- v.literal("revoked"),
185
- v.literal("expired"),
186
- ),
187
- expiresTime: v.number(),
188
- acceptedByUserId: v.optional(v.id("user")),
189
- acceptedTime: v.optional(v.number()),
190
- metadata: v.optional(v.any()),
191
- })
192
- .index("tokenHash", ["tokenHash"])
193
- .index("emailAndStatus", ["email", "status"])
194
- .index("invitedByUserIdAndStatus", ["invitedByUserId", "status"])
195
- .index("organizationId", ["organizationId"])
196
- .index("organizationIdAndStatus", ["organizationId", "status"]),
197
- };
198
-
199
- type DefaultSchema = ReturnType<typeof defineSchema<typeof authTables>>;
200
-
201
- export type AuthDataModel = DataModelFromSchemaDefinition<DefaultSchema>;
15
+ /** Action context typed to the auth component's data model. */
202
16
  export type ActionCtx = GenericActionCtx<AuthDataModel>;
17
+
18
+ /** Mutation context typed to the auth component's data model. */
203
19
  export type MutationCtx = GenericMutationCtx<AuthDataModel>;
20
+
21
+ /** Query context typed to the auth component's data model. */
204
22
  export type QueryCtx = GenericQueryCtx<AuthDataModel>;
23
+
24
+ /** A document from any table in the auth component schema. */
205
25
  export type Doc<T extends TableNamesInDataModel<AuthDataModel>> = GenericDoc<
206
26
  AuthDataModel,
207
27
  T
208
28
  >;
209
29
 
30
+ /** A pair of JWT access token and refresh token. */
210
31
  export type Tokens = { token: string; refreshToken: string };
32
+
33
+ /** Session information returned after authentication. */
211
34
  export type SessionInfo = {
212
35
  userId: GenericId<"user">;
213
36
  sessionId: GenericId<"session">;
214
37
  tokens: Tokens | null;
215
38
  };
39
+
40
+ /** Session information with guaranteed non-null tokens. */
216
41
  export type SessionInfoWithTokens = {
217
42
  userId: GenericId<"user">;
218
43
  sessionId: GenericId<"session">;