@robelest/convex-auth 0.0.1

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 (280) hide show
  1. package/README.md +6 -0
  2. package/dist/bin.cjs +27733 -0
  3. package/dist/client/index.d.ts +49 -0
  4. package/dist/client/index.d.ts.map +1 -0
  5. package/dist/client/index.js +283 -0
  6. package/dist/client/index.js.map +1 -0
  7. package/dist/component/_generated/api.d.ts +36 -0
  8. package/dist/component/_generated/api.d.ts.map +1 -0
  9. package/dist/component/_generated/api.js +31 -0
  10. package/dist/component/_generated/api.js.map +1 -0
  11. package/dist/component/_generated/component.d.ts +295 -0
  12. package/dist/component/_generated/component.d.ts.map +1 -0
  13. package/dist/component/_generated/component.js +11 -0
  14. package/dist/component/_generated/component.js.map +1 -0
  15. package/dist/component/_generated/dataModel.d.ts +46 -0
  16. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  17. package/dist/component/_generated/dataModel.js +11 -0
  18. package/dist/component/_generated/dataModel.js.map +1 -0
  19. package/dist/component/_generated/server.d.ts +121 -0
  20. package/dist/component/_generated/server.d.ts.map +1 -0
  21. package/dist/component/_generated/server.js +78 -0
  22. package/dist/component/_generated/server.js.map +1 -0
  23. package/dist/component/convex.config.d.ts +3 -0
  24. package/dist/component/convex.config.d.ts.map +1 -0
  25. package/dist/component/convex.config.js +4 -0
  26. package/dist/component/convex.config.js.map +1 -0
  27. package/dist/component/index.d.ts +15 -0
  28. package/dist/component/index.d.ts.map +1 -0
  29. package/dist/component/index.js +13 -0
  30. package/dist/component/index.js.map +1 -0
  31. package/dist/component/public.d.ts +450 -0
  32. package/dist/component/public.d.ts.map +1 -0
  33. package/dist/component/public.js +528 -0
  34. package/dist/component/public.js.map +1 -0
  35. package/dist/component/schema.d.ts +107 -0
  36. package/dist/component/schema.d.ts.map +1 -0
  37. package/dist/component/schema.js +26 -0
  38. package/dist/component/schema.js.map +1 -0
  39. package/dist/providers/Anonymous.d.ts +50 -0
  40. package/dist/providers/Anonymous.d.ts.map +1 -0
  41. package/dist/providers/Anonymous.js +39 -0
  42. package/dist/providers/Anonymous.js.map +1 -0
  43. package/dist/providers/ConvexCredentials.d.ts +88 -0
  44. package/dist/providers/ConvexCredentials.d.ts.map +1 -0
  45. package/dist/providers/ConvexCredentials.js +37 -0
  46. package/dist/providers/ConvexCredentials.js.map +1 -0
  47. package/dist/providers/Email.d.ts +33 -0
  48. package/dist/providers/Email.d.ts.map +1 -0
  49. package/dist/providers/Email.js +50 -0
  50. package/dist/providers/Email.js.map +1 -0
  51. package/dist/providers/Password.d.ts +95 -0
  52. package/dist/providers/Password.d.ts.map +1 -0
  53. package/dist/providers/Password.js +174 -0
  54. package/dist/providers/Password.js.map +1 -0
  55. package/dist/providers/Phone.d.ts +22 -0
  56. package/dist/providers/Phone.d.ts.map +1 -0
  57. package/dist/providers/Phone.js +37 -0
  58. package/dist/providers/Phone.js.map +1 -0
  59. package/dist/server/convex_types.d.ts +17 -0
  60. package/dist/server/convex_types.d.ts.map +1 -0
  61. package/dist/server/convex_types.js +2 -0
  62. package/dist/server/convex_types.js.map +1 -0
  63. package/dist/server/cookies.d.ts +35 -0
  64. package/dist/server/cookies.d.ts.map +1 -0
  65. package/dist/server/cookies.js +34 -0
  66. package/dist/server/cookies.js.map +1 -0
  67. package/dist/server/implementation/db.d.ts +80 -0
  68. package/dist/server/implementation/db.d.ts.map +1 -0
  69. package/dist/server/implementation/db.js +59 -0
  70. package/dist/server/implementation/db.js.map +1 -0
  71. package/dist/server/implementation/index.d.ts +370 -0
  72. package/dist/server/implementation/index.d.ts.map +1 -0
  73. package/dist/server/implementation/index.js +521 -0
  74. package/dist/server/implementation/index.js.map +1 -0
  75. package/dist/server/implementation/mutations/createAccountFromCredentials.d.ts +33 -0
  76. package/dist/server/implementation/mutations/createAccountFromCredentials.d.ts.map +1 -0
  77. package/dist/server/implementation/mutations/createAccountFromCredentials.js +71 -0
  78. package/dist/server/implementation/mutations/createAccountFromCredentials.js.map +1 -0
  79. package/dist/server/implementation/mutations/createVerificationCode.d.ts +25 -0
  80. package/dist/server/implementation/mutations/createVerificationCode.d.ts.map +1 -0
  81. package/dist/server/implementation/mutations/createVerificationCode.js +84 -0
  82. package/dist/server/implementation/mutations/createVerificationCode.js.map +1 -0
  83. package/dist/server/implementation/mutations/index.d.ts +304 -0
  84. package/dist/server/implementation/mutations/index.d.ts.map +1 -0
  85. package/dist/server/implementation/mutations/index.js +108 -0
  86. package/dist/server/implementation/mutations/index.js.map +1 -0
  87. package/dist/server/implementation/mutations/invalidateSessions.d.ts +13 -0
  88. package/dist/server/implementation/mutations/invalidateSessions.d.ts.map +1 -0
  89. package/dist/server/implementation/mutations/invalidateSessions.js +35 -0
  90. package/dist/server/implementation/mutations/invalidateSessions.js.map +1 -0
  91. package/dist/server/implementation/mutations/modifyAccount.d.ts +23 -0
  92. package/dist/server/implementation/mutations/modifyAccount.d.ts.map +1 -0
  93. package/dist/server/implementation/mutations/modifyAccount.js +48 -0
  94. package/dist/server/implementation/mutations/modifyAccount.js.map +1 -0
  95. package/dist/server/implementation/mutations/refreshSession.d.ts +16 -0
  96. package/dist/server/implementation/mutations/refreshSession.d.ts.map +1 -0
  97. package/dist/server/implementation/mutations/refreshSession.js +116 -0
  98. package/dist/server/implementation/mutations/refreshSession.js.map +1 -0
  99. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.d.ts +27 -0
  100. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.d.ts.map +1 -0
  101. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.js +55 -0
  102. package/dist/server/implementation/mutations/retrieveAccountWithCredentials.js.map +1 -0
  103. package/dist/server/implementation/mutations/signIn.d.ts +17 -0
  104. package/dist/server/implementation/mutations/signIn.d.ts.map +1 -0
  105. package/dist/server/implementation/mutations/signIn.js +26 -0
  106. package/dist/server/implementation/mutations/signIn.js.map +1 -0
  107. package/dist/server/implementation/mutations/signOut.d.ts +11 -0
  108. package/dist/server/implementation/mutations/signOut.d.ts.map +1 -0
  109. package/dist/server/implementation/mutations/signOut.js +24 -0
  110. package/dist/server/implementation/mutations/signOut.js.map +1 -0
  111. package/dist/server/implementation/mutations/userOAuth.d.ts +19 -0
  112. package/dist/server/implementation/mutations/userOAuth.d.ts.map +1 -0
  113. package/dist/server/implementation/mutations/userOAuth.js +84 -0
  114. package/dist/server/implementation/mutations/userOAuth.js.map +1 -0
  115. package/dist/server/implementation/mutations/verifier.d.ts +8 -0
  116. package/dist/server/implementation/mutations/verifier.d.ts.map +1 -0
  117. package/dist/server/implementation/mutations/verifier.js +19 -0
  118. package/dist/server/implementation/mutations/verifier.js.map +1 -0
  119. package/dist/server/implementation/mutations/verifierSignature.d.ts +15 -0
  120. package/dist/server/implementation/mutations/verifierSignature.d.ts.map +1 -0
  121. package/dist/server/implementation/mutations/verifierSignature.js +29 -0
  122. package/dist/server/implementation/mutations/verifierSignature.js.map +1 -0
  123. package/dist/server/implementation/mutations/verifyCodeAndSignIn.d.ts +21 -0
  124. package/dist/server/implementation/mutations/verifyCodeAndSignIn.d.ts.map +1 -0
  125. package/dist/server/implementation/mutations/verifyCodeAndSignIn.js +127 -0
  126. package/dist/server/implementation/mutations/verifyCodeAndSignIn.js.map +1 -0
  127. package/dist/server/implementation/provider.d.ts +6 -0
  128. package/dist/server/implementation/provider.d.ts.map +1 -0
  129. package/dist/server/implementation/provider.js +21 -0
  130. package/dist/server/implementation/provider.js.map +1 -0
  131. package/dist/server/implementation/rateLimit.d.ts +6 -0
  132. package/dist/server/implementation/rateLimit.d.ts.map +1 -0
  133. package/dist/server/implementation/rateLimit.js +76 -0
  134. package/dist/server/implementation/rateLimit.js.map +1 -0
  135. package/dist/server/implementation/redirects.d.ts +6 -0
  136. package/dist/server/implementation/redirects.d.ts.map +1 -0
  137. package/dist/server/implementation/redirects.js +40 -0
  138. package/dist/server/implementation/redirects.js.map +1 -0
  139. package/dist/server/implementation/refreshTokens.d.ts +40 -0
  140. package/dist/server/implementation/refreshTokens.d.ts.map +1 -0
  141. package/dist/server/implementation/refreshTokens.js +160 -0
  142. package/dist/server/implementation/refreshTokens.js.map +1 -0
  143. package/dist/server/implementation/sessions.d.ts +43 -0
  144. package/dist/server/implementation/sessions.d.ts.map +1 -0
  145. package/dist/server/implementation/sessions.js +94 -0
  146. package/dist/server/implementation/sessions.js.map +1 -0
  147. package/dist/server/implementation/signIn.d.ts +31 -0
  148. package/dist/server/implementation/signIn.d.ts.map +1 -0
  149. package/dist/server/implementation/signIn.js +148 -0
  150. package/dist/server/implementation/signIn.js.map +1 -0
  151. package/dist/server/implementation/tokens.d.ts +7 -0
  152. package/dist/server/implementation/tokens.d.ts.map +1 -0
  153. package/dist/server/implementation/tokens.js +18 -0
  154. package/dist/server/implementation/tokens.js.map +1 -0
  155. package/dist/server/implementation/types.d.ts +288 -0
  156. package/dist/server/implementation/types.d.ts.map +1 -0
  157. package/dist/server/implementation/types.js +182 -0
  158. package/dist/server/implementation/types.js.map +1 -0
  159. package/dist/server/implementation/users.d.ts +27 -0
  160. package/dist/server/implementation/users.d.ts.map +1 -0
  161. package/dist/server/implementation/users.js +181 -0
  162. package/dist/server/implementation/users.js.map +1 -0
  163. package/dist/server/implementation/utils.d.ts +17 -0
  164. package/dist/server/implementation/utils.d.ts.map +1 -0
  165. package/dist/server/implementation/utils.js +72 -0
  166. package/dist/server/implementation/utils.js.map +1 -0
  167. package/dist/server/index.d.ts +17 -0
  168. package/dist/server/index.d.ts.map +1 -0
  169. package/dist/server/index.js +54 -0
  170. package/dist/server/index.js.map +1 -0
  171. package/dist/server/oauth/authorizationUrl.d.ts +13 -0
  172. package/dist/server/oauth/authorizationUrl.d.ts.map +1 -0
  173. package/dist/server/oauth/authorizationUrl.js +91 -0
  174. package/dist/server/oauth/authorizationUrl.js.map +1 -0
  175. package/dist/server/oauth/callback.d.ts +19 -0
  176. package/dist/server/oauth/callback.d.ts.map +1 -0
  177. package/dist/server/oauth/callback.js +173 -0
  178. package/dist/server/oauth/callback.js.map +1 -0
  179. package/dist/server/oauth/checks.d.ts +52 -0
  180. package/dist/server/oauth/checks.d.ts.map +1 -0
  181. package/dist/server/oauth/checks.js +106 -0
  182. package/dist/server/oauth/checks.js.map +1 -0
  183. package/dist/server/oauth/convexAuth.d.ts +12 -0
  184. package/dist/server/oauth/convexAuth.d.ts.map +1 -0
  185. package/dist/server/oauth/convexAuth.js +137 -0
  186. package/dist/server/oauth/convexAuth.js.map +1 -0
  187. package/dist/server/oauth/lib/utils/customFetch.d.ts +9 -0
  188. package/dist/server/oauth/lib/utils/customFetch.d.ts.map +1 -0
  189. package/dist/server/oauth/lib/utils/customFetch.js +11 -0
  190. package/dist/server/oauth/lib/utils/customFetch.js.map +1 -0
  191. package/dist/server/oauth/lib/utils/providers.d.ts +3 -0
  192. package/dist/server/oauth/lib/utils/providers.d.ts.map +1 -0
  193. package/dist/server/oauth/lib/utils/providers.js +7 -0
  194. package/dist/server/oauth/lib/utils/providers.js.map +1 -0
  195. package/dist/server/oauth/providers/oauth.d.ts +43 -0
  196. package/dist/server/oauth/providers/oauth.d.ts.map +1 -0
  197. package/dist/server/oauth/providers/oauth.js +3 -0
  198. package/dist/server/oauth/providers/oauth.js.map +1 -0
  199. package/dist/server/oauth/types.d.ts +24 -0
  200. package/dist/server/oauth/types.d.ts.map +1 -0
  201. package/dist/server/oauth/types.js +5 -0
  202. package/dist/server/oauth/types.js.map +1 -0
  203. package/dist/server/provider_utils.d.ts +76 -0
  204. package/dist/server/provider_utils.d.ts.map +1 -0
  205. package/dist/server/provider_utils.js +177 -0
  206. package/dist/server/provider_utils.js.map +1 -0
  207. package/dist/server/types.d.ts +412 -0
  208. package/dist/server/types.d.ts.map +1 -0
  209. package/dist/server/types.js +2 -0
  210. package/dist/server/types.js.map +1 -0
  211. package/dist/server/utils.d.ts +3 -0
  212. package/dist/server/utils.d.ts.map +1 -0
  213. package/dist/server/utils.js +11 -0
  214. package/dist/server/utils.js.map +1 -0
  215. package/package.json +126 -0
  216. package/providers/Anonymous/package.json +6 -0
  217. package/providers/ConvexCredentials/package.json +6 -0
  218. package/providers/Email/package.json +6 -0
  219. package/providers/Password/package.json +6 -0
  220. package/providers/Phone/package.json +6 -0
  221. package/server/package.json +6 -0
  222. package/src/cli/command.ts +69 -0
  223. package/src/cli/generateKeys.ts +20 -0
  224. package/src/cli/index.ts +840 -0
  225. package/src/client/index.ts +415 -0
  226. package/src/component/_generated/api.ts +52 -0
  227. package/src/component/_generated/component.ts +586 -0
  228. package/src/component/_generated/dataModel.ts +60 -0
  229. package/src/component/_generated/server.ts +156 -0
  230. package/src/component/convex.config.ts +5 -0
  231. package/src/component/index.ts +40 -0
  232. package/src/component/public.ts +607 -0
  233. package/src/component/schema.ts +35 -0
  234. package/src/providers/Anonymous.ts +79 -0
  235. package/src/providers/ConvexCredentials.ts +108 -0
  236. package/src/providers/Email.ts +60 -0
  237. package/src/providers/Password.ts +253 -0
  238. package/src/providers/Phone.ts +46 -0
  239. package/src/server/convex_types.ts +55 -0
  240. package/src/server/cookies.ts +42 -0
  241. package/src/server/implementation/db.ts +125 -0
  242. package/src/server/implementation/index.ts +815 -0
  243. package/src/server/implementation/mutations/createAccountFromCredentials.ts +113 -0
  244. package/src/server/implementation/mutations/createVerificationCode.ts +139 -0
  245. package/src/server/implementation/mutations/index.ts +157 -0
  246. package/src/server/implementation/mutations/invalidateSessions.ts +47 -0
  247. package/src/server/implementation/mutations/modifyAccount.ts +65 -0
  248. package/src/server/implementation/mutations/refreshSession.ts +188 -0
  249. package/src/server/implementation/mutations/retrieveAccountWithCredentials.ts +87 -0
  250. package/src/server/implementation/mutations/signIn.ts +51 -0
  251. package/src/server/implementation/mutations/signOut.ts +38 -0
  252. package/src/server/implementation/mutations/userOAuth.ts +112 -0
  253. package/src/server/implementation/mutations/verifier.ts +29 -0
  254. package/src/server/implementation/mutations/verifierSignature.ts +44 -0
  255. package/src/server/implementation/mutations/verifyCodeAndSignIn.ts +205 -0
  256. package/src/server/implementation/provider.ts +38 -0
  257. package/src/server/implementation/rateLimit.ts +105 -0
  258. package/src/server/implementation/redirects.ts +58 -0
  259. package/src/server/implementation/refreshTokens.ts +221 -0
  260. package/src/server/implementation/sessions.ts +155 -0
  261. package/src/server/implementation/signIn.ts +253 -0
  262. package/src/server/implementation/tokens.ts +29 -0
  263. package/src/server/implementation/types.ts +220 -0
  264. package/src/server/implementation/users.ts +286 -0
  265. package/src/server/implementation/utils.ts +91 -0
  266. package/src/server/index.ts +74 -0
  267. package/src/server/oauth/NOTICE.txt +21 -0
  268. package/src/server/oauth/README.md +7 -0
  269. package/src/server/oauth/authorizationUrl.ts +113 -0
  270. package/src/server/oauth/callback.ts +243 -0
  271. package/src/server/oauth/checks.ts +136 -0
  272. package/src/server/oauth/convexAuth.ts +168 -0
  273. package/src/server/oauth/lib/utils/customFetch.ts +18 -0
  274. package/src/server/oauth/lib/utils/providers.ts +12 -0
  275. package/src/server/oauth/providers/oauth.ts +56 -0
  276. package/src/server/oauth/types.ts +60 -0
  277. package/src/server/provider_utils.ts +222 -0
  278. package/src/server/types.ts +470 -0
  279. package/src/server/utils.ts +12 -0
  280. package/src/test.ts +24 -0
@@ -0,0 +1,113 @@
1
+ import { Infer, v } from "convex/values";
2
+ import { ActionCtx, Doc, MutationCtx } from "../types.js";
3
+ import * as Provider from "../provider.js";
4
+ import { ConvexCredentialsConfig } from "../../types.js";
5
+ import { upsertUserAndAccount } from "../users.js";
6
+ import { getAuthSessionId } from "../sessions.js";
7
+ import { LOG_LEVELS, logWithLevel, maybeRedact } from "../utils.js";
8
+ import { createAuthDb } from "../db.js";
9
+
10
+ export const createAccountFromCredentialsArgs = v.object({
11
+ provider: v.string(),
12
+ account: v.object({ id: v.string(), secret: v.optional(v.string()) }),
13
+ profile: v.any(),
14
+ shouldLinkViaEmail: v.optional(v.boolean()),
15
+ shouldLinkViaPhone: v.optional(v.boolean()),
16
+ });
17
+
18
+ type ReturnType = { account: Doc<"account">; user: Doc<"user"> };
19
+
20
+ export async function createAccountFromCredentialsImpl(
21
+ ctx: MutationCtx,
22
+ args: Infer<typeof createAccountFromCredentialsArgs>,
23
+ getProviderOrThrow: Provider.GetProviderOrThrowFunc,
24
+ config: Provider.Config,
25
+ ): Promise<ReturnType> {
26
+ logWithLevel(LOG_LEVELS.DEBUG, "createAccountFromCredentialsImpl args:", {
27
+ provider: args.provider,
28
+ account: {
29
+ id: args.account.id,
30
+ secret: maybeRedact(args.account.secret ?? ""),
31
+ },
32
+ });
33
+ const {
34
+ provider: providerId,
35
+ account,
36
+ profile,
37
+ shouldLinkViaEmail,
38
+ shouldLinkViaPhone,
39
+ } = args;
40
+ const authDb =
41
+ config.component !== undefined ? createAuthDb(ctx, config.component) : null;
42
+ const provider = getProviderOrThrow(providerId) as ConvexCredentialsConfig;
43
+ const existingAccount =
44
+ authDb !== null
45
+ ? ((await authDb.accounts.get(provider.id, account.id)) as Doc<"account"> | null)
46
+ : await ctx.db
47
+ .query("account")
48
+ .withIndex("providerAndAccountId", (q) =>
49
+ q.eq("provider", provider.id).eq("providerAccountId", account.id),
50
+ )
51
+ .unique();
52
+ if (existingAccount !== null) {
53
+ if (
54
+ account.secret !== undefined &&
55
+ !(await Provider.verify(
56
+ provider,
57
+ account.secret,
58
+ existingAccount.secret ?? "",
59
+ ))
60
+ ) {
61
+ throw new Error(`Account ${account.id} already exists`);
62
+ }
63
+ return {
64
+ account: existingAccount,
65
+ // TODO: Ian removed this,
66
+ user:
67
+ authDb !== null
68
+ ? ((await authDb.users.getById(existingAccount.userId)) as unknown as Doc<"user">)
69
+ : (await ctx.db.get(existingAccount.userId))!,
70
+ };
71
+ }
72
+
73
+ const secret =
74
+ account.secret !== undefined
75
+ ? await Provider.hash(provider, account.secret)
76
+ : undefined;
77
+ const { userId, accountId } = await upsertUserAndAccount(
78
+ ctx,
79
+ await getAuthSessionId(ctx),
80
+ { providerAccountId: account.id, secret },
81
+ {
82
+ type: "credentials",
83
+ provider,
84
+ profile,
85
+ shouldLinkViaEmail,
86
+ shouldLinkViaPhone,
87
+ },
88
+ config,
89
+ );
90
+
91
+ return {
92
+ account:
93
+ authDb !== null
94
+ ? ((await authDb.accounts.getById(accountId)) as Doc<"account">)
95
+ : (await ctx.db.get(accountId))!,
96
+ user:
97
+ authDb !== null
98
+ ? ((await authDb.users.getById(userId)) as unknown as Doc<"user">)
99
+ : (await ctx.db.get(userId))!,
100
+ };
101
+ }
102
+
103
+ export const callCreateAccountFromCredentials = async (
104
+ ctx: ActionCtx,
105
+ args: Infer<typeof createAccountFromCredentialsArgs>,
106
+ ): Promise<ReturnType> => {
107
+ return ctx.runMutation("auth:store" as any, {
108
+ args: {
109
+ type: "createAccountFromCredentials",
110
+ ...args,
111
+ },
112
+ });
113
+ };
@@ -0,0 +1,139 @@
1
+ import { GenericId, Infer, v } from "convex/values";
2
+ import { ActionCtx, MutationCtx } from "../types.js";
3
+ import * as Provider from "../provider.js";
4
+ import { EmailConfig, PhoneConfig } from "../../types.js";
5
+ import { getAccountOrThrow, upsertUserAndAccount } from "../users.js";
6
+ import { getAuthSessionId } from "../sessions.js";
7
+ import { LOG_LEVELS, logWithLevel, sha256 } from "../utils.js";
8
+ import { createAuthDb } from "../db.js";
9
+
10
+ export const createVerificationCodeArgs = v.object({
11
+ accountId: v.optional(v.string()),
12
+ provider: v.string(),
13
+ email: v.optional(v.string()),
14
+ phone: v.optional(v.string()),
15
+ code: v.string(),
16
+ expirationTime: v.number(),
17
+ allowExtraProviders: v.boolean(),
18
+ });
19
+
20
+ type ReturnType = string;
21
+
22
+ export async function createVerificationCodeImpl(
23
+ ctx: MutationCtx,
24
+ args: Infer<typeof createVerificationCodeArgs>,
25
+ getProviderOrThrow: Provider.GetProviderOrThrowFunc,
26
+ config: Provider.Config,
27
+ ): Promise<ReturnType> {
28
+ logWithLevel(LOG_LEVELS.DEBUG, "createVerificationCodeImpl args:", args);
29
+ const {
30
+ email,
31
+ phone,
32
+ code,
33
+ expirationTime,
34
+ provider: providerId,
35
+ accountId: existingAccountId,
36
+ allowExtraProviders,
37
+ } = args;
38
+ const authDb =
39
+ config.component !== undefined ? createAuthDb(ctx, config.component) : null;
40
+ const typedExistingAccountId = existingAccountId as
41
+ | GenericId<"account">
42
+ | undefined;
43
+ const existingAccount =
44
+ typedExistingAccountId !== undefined
45
+ ? await getAccountOrThrow(ctx, typedExistingAccountId, config)
46
+ : authDb !== null
47
+ ? await authDb.accounts.get(providerId, email ?? phone!)
48
+ : await ctx.db
49
+ .query("account")
50
+ .withIndex("providerAndAccountId", (q) =>
51
+ q
52
+ .eq("provider", providerId)
53
+ .eq("providerAccountId", email ?? phone!),
54
+ )
55
+ .unique();
56
+
57
+ const provider = getProviderOrThrow(providerId, allowExtraProviders) as
58
+ | EmailConfig
59
+ | PhoneConfig;
60
+ const { accountId } = await upsertUserAndAccount(
61
+ ctx,
62
+ await getAuthSessionId(ctx),
63
+ existingAccount !== null
64
+ ? { existingAccount }
65
+ : { providerAccountId: email ?? phone! },
66
+ provider.type === "email"
67
+ ? { type: "email", provider, profile: { email: email! } }
68
+ : { type: "phone", provider, profile: { phone: phone! } },
69
+ config,
70
+ );
71
+ await generateUniqueVerificationCode(
72
+ ctx,
73
+ accountId,
74
+ providerId,
75
+ code,
76
+ expirationTime,
77
+ { email, phone },
78
+ config,
79
+ );
80
+ return email ?? phone!;
81
+ }
82
+
83
+ export const callCreateVerificationCode = async (
84
+ ctx: ActionCtx,
85
+ args: Infer<typeof createVerificationCodeArgs>,
86
+ ): Promise<ReturnType> => {
87
+ return ctx.runMutation("auth:store" as any, {
88
+ args: {
89
+ type: "createVerificationCode",
90
+ ...args,
91
+ },
92
+ });
93
+ };
94
+
95
+ async function generateUniqueVerificationCode(
96
+ ctx: MutationCtx,
97
+ accountId: GenericId<"account">,
98
+ provider: string,
99
+ code: string,
100
+ expirationTime: number,
101
+ { email, phone }: { email?: string; phone?: string },
102
+ config: Provider.Config,
103
+ ) {
104
+ const authDb =
105
+ config.component !== undefined ? createAuthDb(ctx, config.component) : null;
106
+ const existingCode =
107
+ authDb !== null
108
+ ? await authDb.verificationCodes.getByAccountId(accountId)
109
+ : await ctx.db
110
+ .query("verification")
111
+ .withIndex("accountId", (q) => q.eq("accountId", accountId))
112
+ .unique();
113
+ if (existingCode !== null) {
114
+ if (authDb !== null) {
115
+ await authDb.verificationCodes.delete(existingCode._id);
116
+ } else {
117
+ await ctx.db.delete(existingCode._id);
118
+ }
119
+ }
120
+ if (authDb !== null) {
121
+ await authDb.verificationCodes.create({
122
+ accountId,
123
+ provider,
124
+ code: await sha256(code),
125
+ expirationTime,
126
+ emailVerified: email,
127
+ phoneVerified: phone,
128
+ });
129
+ } else {
130
+ await ctx.db.insert("verification", {
131
+ accountId,
132
+ provider,
133
+ code: await sha256(code),
134
+ expirationTime,
135
+ emailVerified: email,
136
+ phoneVerified: phone,
137
+ });
138
+ }
139
+ }
@@ -0,0 +1,157 @@
1
+ import { Infer, v } from "convex/values";
2
+ import { MutationCtx } from "../types.js";
3
+ import { signInArgs, signInImpl } from "./signIn.js";
4
+ import { signOutImpl } from "./signOut.js";
5
+ import { refreshSessionArgs, refreshSessionImpl } from "./refreshSession.js";
6
+ import {
7
+ verifyCodeAndSignInArgs,
8
+ verifyCodeAndSignInImpl,
9
+ } from "./verifyCodeAndSignIn.js";
10
+ import {
11
+ verifierSignatureArgs,
12
+ verifierSignatureImpl,
13
+ } from "./verifierSignature.js";
14
+ import { userOAuthArgs, userOAuthImpl } from "./userOAuth.js";
15
+ import {
16
+ createVerificationCodeArgs,
17
+ createVerificationCodeImpl,
18
+ } from "./createVerificationCode.js";
19
+ import {
20
+ createAccountFromCredentialsArgs,
21
+ createAccountFromCredentialsImpl,
22
+ } from "./createAccountFromCredentials.js";
23
+ import {
24
+ retrieveAccountWithCredentialsArgs,
25
+ retrieveAccountWithCredentialsImpl,
26
+ } from "./retrieveAccountWithCredentials.js";
27
+ import { modifyAccountArgs, modifyAccountImpl } from "./modifyAccount.js";
28
+ import {
29
+ invalidateSessionsArgs,
30
+ invalidateSessionsImpl,
31
+ } from "./invalidateSessions.js";
32
+ import * as Provider from "../provider.js";
33
+ import { verifierImpl } from "./verifier.js";
34
+ import { LOG_LEVELS, logWithLevel } from "../utils.js";
35
+ export { callInvalidateSessions } from "./invalidateSessions.js";
36
+ export { callModifyAccount } from "./modifyAccount.js";
37
+ export { callRetreiveAccountWithCredentials } from "./retrieveAccountWithCredentials.js";
38
+ export { callCreateAccountFromCredentials } from "./createAccountFromCredentials.js";
39
+ export { callCreateVerificationCode } from "./createVerificationCode.js";
40
+ export { callUserOAuth } from "./userOAuth.js";
41
+ export { callVerifierSignature } from "./verifierSignature.js";
42
+ export { callVerifyCodeAndSignIn } from "./verifyCodeAndSignIn.js";
43
+ export { callVerifier } from "./verifier.js";
44
+ export { callRefreshSession } from "./refreshSession.js";
45
+ export { callSignOut } from "./signOut.js";
46
+ export { callSignIn } from "./signIn.js";
47
+
48
+ export const storeArgs = v.object({
49
+ args: v.union(
50
+ v.object({
51
+ type: v.literal("signIn"),
52
+ ...signInArgs.fields,
53
+ }),
54
+ v.object({
55
+ type: v.literal("signOut"),
56
+ }),
57
+ v.object({
58
+ type: v.literal("refreshSession"),
59
+ ...refreshSessionArgs.fields,
60
+ }),
61
+ v.object({
62
+ type: v.literal("verifyCodeAndSignIn"),
63
+ ...verifyCodeAndSignInArgs.fields,
64
+ }),
65
+ v.object({
66
+ type: v.literal("verifier"),
67
+ }),
68
+ v.object({
69
+ type: v.literal("verifierSignature"),
70
+ ...verifierSignatureArgs.fields,
71
+ }),
72
+ v.object({
73
+ type: v.literal("userOAuth"),
74
+ ...userOAuthArgs.fields,
75
+ }),
76
+ v.object({
77
+ type: v.literal("createVerificationCode"),
78
+ ...createVerificationCodeArgs.fields,
79
+ }),
80
+ v.object({
81
+ type: v.literal("createAccountFromCredentials"),
82
+ ...createAccountFromCredentialsArgs.fields,
83
+ }),
84
+ v.object({
85
+ type: v.literal("retrieveAccountWithCredentials"),
86
+ ...retrieveAccountWithCredentialsArgs.fields,
87
+ }),
88
+ v.object({
89
+ type: v.literal("modifyAccount"),
90
+ ...modifyAccountArgs.fields,
91
+ }),
92
+ v.object({
93
+ type: v.literal("invalidateSessions"),
94
+ ...invalidateSessionsArgs.fields,
95
+ }),
96
+ ),
97
+ });
98
+
99
+ export const storeImpl = async (
100
+ ctx: MutationCtx,
101
+ fnArgs: Infer<typeof storeArgs>,
102
+ getProviderOrThrow: Provider.GetProviderOrThrowFunc,
103
+ config: Provider.Config,
104
+ ) => {
105
+ const args = fnArgs.args;
106
+ logWithLevel(LOG_LEVELS.INFO, `\`auth:store\` type: ${args.type}`);
107
+ switch (args.type) {
108
+ case "signIn": {
109
+ return signInImpl(ctx, args, config);
110
+ }
111
+ case "signOut": {
112
+ return signOutImpl(ctx, config);
113
+ }
114
+ case "refreshSession": {
115
+ return refreshSessionImpl(ctx, args, getProviderOrThrow, config);
116
+ }
117
+ case "verifyCodeAndSignIn": {
118
+ return verifyCodeAndSignInImpl(ctx, args, getProviderOrThrow, config);
119
+ }
120
+ case "verifier": {
121
+ return verifierImpl(ctx, config);
122
+ }
123
+ case "verifierSignature": {
124
+ return verifierSignatureImpl(ctx, args, config);
125
+ }
126
+ case "userOAuth": {
127
+ return userOAuthImpl(ctx, args, getProviderOrThrow, config);
128
+ }
129
+ case "createVerificationCode": {
130
+ return createVerificationCodeImpl(ctx, args, getProviderOrThrow, config);
131
+ }
132
+ case "createAccountFromCredentials": {
133
+ return createAccountFromCredentialsImpl(
134
+ ctx,
135
+ args,
136
+ getProviderOrThrow,
137
+ config,
138
+ );
139
+ }
140
+ case "retrieveAccountWithCredentials": {
141
+ return retrieveAccountWithCredentialsImpl(
142
+ ctx,
143
+ args,
144
+ getProviderOrThrow,
145
+ config,
146
+ );
147
+ }
148
+ case "modifyAccount": {
149
+ return modifyAccountImpl(ctx, args, getProviderOrThrow, config);
150
+ }
151
+ case "invalidateSessions": {
152
+ return invalidateSessionsImpl(ctx, args, config);
153
+ }
154
+ default:
155
+ args satisfies never;
156
+ }
157
+ };
@@ -0,0 +1,47 @@
1
+ import { GenericId, Infer, v } from "convex/values";
2
+ import { deleteSession } from "../sessions.js";
3
+ import { ActionCtx, MutationCtx } from "../types.js";
4
+ import { LOG_LEVELS, logWithLevel } from "../utils.js";
5
+ import * as Provider from "../provider.js";
6
+ import { createAuthDb } from "../db.js";
7
+
8
+ export const invalidateSessionsArgs = v.object({
9
+ userId: v.string(),
10
+ except: v.optional(v.array(v.string())),
11
+ });
12
+
13
+ export const callInvalidateSessions = async (
14
+ ctx: ActionCtx,
15
+ args: Infer<typeof invalidateSessionsArgs>,
16
+ ): Promise<void> => {
17
+ return ctx.runMutation("auth:store" as any, {
18
+ args: {
19
+ type: "invalidateSessions",
20
+ ...args,
21
+ },
22
+ });
23
+ };
24
+
25
+ export const invalidateSessionsImpl = async (
26
+ ctx: MutationCtx,
27
+ args: Infer<typeof invalidateSessionsArgs>,
28
+ config: Provider.Config,
29
+ ): Promise<void> => {
30
+ logWithLevel(LOG_LEVELS.DEBUG, "invalidateSessionsImpl args:", args);
31
+ const { userId, except } = args;
32
+ const exceptSet = new Set(except ?? []);
33
+ const typedUserId = userId as GenericId<"user">;
34
+ const sessions =
35
+ config.component !== undefined
36
+ ? await createAuthDb(ctx, config.component).sessions.listByUser(typedUserId)
37
+ : await ctx.db
38
+ .query("session")
39
+ .withIndex("userId", (q) => q.eq("userId", typedUserId))
40
+ .collect();
41
+ for (const session of sessions) {
42
+ if (!exceptSet.has(session._id)) {
43
+ await deleteSession(ctx, session, config);
44
+ }
45
+ }
46
+ return;
47
+ };
@@ -0,0 +1,65 @@
1
+ import { Infer, v } from "convex/values";
2
+ import { ActionCtx, MutationCtx } from "../types.js";
3
+ import { GetProviderOrThrowFunc, hash } from "../provider.js";
4
+ import { LOG_LEVELS, logWithLevel, maybeRedact } from "../utils.js";
5
+ import * as Provider from "../provider.js";
6
+ import { createAuthDb } from "../db.js";
7
+
8
+ export const modifyAccountArgs = v.object({
9
+ provider: v.string(),
10
+ account: v.object({ id: v.string(), secret: v.string() }),
11
+ });
12
+
13
+ export async function modifyAccountImpl(
14
+ ctx: MutationCtx,
15
+ args: Infer<typeof modifyAccountArgs>,
16
+ getProviderOrThrow: GetProviderOrThrowFunc,
17
+ config: Provider.Config,
18
+ ): Promise<void> {
19
+ const { provider, account } = args;
20
+ const authDb =
21
+ config.component !== undefined ? createAuthDb(ctx, config.component) : null;
22
+ logWithLevel(LOG_LEVELS.DEBUG, "retrieveAccountWithCredentialsImpl args:", {
23
+ provider: provider,
24
+ account: {
25
+ id: account.id,
26
+ secret: maybeRedact(account.secret ?? ""),
27
+ },
28
+ });
29
+ const existingAccount =
30
+ authDb !== null
31
+ ? await authDb.accounts.get(provider, account.id)
32
+ : await ctx.db
33
+ .query("account")
34
+ .withIndex("providerAndAccountId", (q) =>
35
+ q.eq("provider", provider).eq("providerAccountId", account.id),
36
+ )
37
+ .unique();
38
+ if (existingAccount === null) {
39
+ throw new Error(
40
+ `Cannot modify account with ID ${account.id} because it does not exist`,
41
+ );
42
+ }
43
+ if (authDb !== null) {
44
+ await authDb.accounts.patch(existingAccount._id, {
45
+ secret: await hash(getProviderOrThrow(provider), account.secret),
46
+ });
47
+ } else {
48
+ await ctx.db.patch(existingAccount._id, {
49
+ secret: await hash(getProviderOrThrow(provider), account.secret),
50
+ });
51
+ }
52
+ return;
53
+ }
54
+
55
+ export const callModifyAccount = async (
56
+ ctx: ActionCtx,
57
+ args: Infer<typeof modifyAccountArgs>,
58
+ ): Promise<void> => {
59
+ return ctx.runMutation("auth:store" as any, {
60
+ args: {
61
+ type: "modifyAccount",
62
+ ...args,
63
+ },
64
+ });
65
+ };
@@ -0,0 +1,188 @@
1
+ import { Infer, v } from "convex/values";
2
+ import { ActionCtx, MutationCtx } from "../types.js";
3
+ import * as Provider from "../provider.js";
4
+ import { logWithLevel, maybeRedact } from "../utils.js";
5
+ import {
6
+ deleteAllRefreshTokens,
7
+ invalidateRefreshTokensInSubtree,
8
+ loadActiveRefreshToken,
9
+ parseRefreshToken,
10
+ REFRESH_TOKEN_REUSE_WINDOW_MS,
11
+ refreshTokenIfValid,
12
+ } from "../refreshTokens.js";
13
+ import { generateTokensForSession } from "../sessions.js";
14
+ import { createAuthDb } from "../db.js";
15
+
16
+ export const refreshSessionArgs = v.object({
17
+ refreshToken: v.string(),
18
+ });
19
+
20
+ type ReturnType = null | {
21
+ token: string;
22
+ refreshToken: string;
23
+ };
24
+
25
+ export async function refreshSessionImpl(
26
+ ctx: MutationCtx,
27
+ args: Infer<typeof refreshSessionArgs>,
28
+ getProviderOrThrow: Provider.GetProviderOrThrowFunc,
29
+ config: Provider.Config,
30
+ ): Promise<ReturnType> {
31
+ const authDb =
32
+ config.component !== undefined ? createAuthDb(ctx, config.component) : null;
33
+ const { refreshToken } = args;
34
+ const { refreshTokenId, sessionId: tokenSessionId } =
35
+ parseRefreshToken(refreshToken);
36
+ logWithLevel(
37
+ "DEBUG",
38
+ `refreshSessionImpl args: Token ID: ${maybeRedact(refreshTokenId)} Session ID: ${maybeRedact(
39
+ tokenSessionId,
40
+ )}`,
41
+ );
42
+ const validationResult = await refreshTokenIfValid(
43
+ ctx,
44
+ refreshTokenId,
45
+ tokenSessionId,
46
+ config,
47
+ );
48
+
49
+ if (validationResult === null) {
50
+ // Replicating `deleteSession` but ensuring that we delete both the session
51
+ // and the refresh token, even if one of them is missing.
52
+ let session = null;
53
+ try {
54
+ session =
55
+ authDb !== null
56
+ ? await authDb.sessions.getById(tokenSessionId)
57
+ : await ctx.db.get(tokenSessionId);
58
+ } catch {
59
+ logWithLevel("DEBUG", "Skipping invalid session id during refresh cleanup");
60
+ }
61
+ if (session !== null) {
62
+ if (authDb !== null) {
63
+ await authDb.sessions.delete(session._id);
64
+ } else {
65
+ await ctx.db.delete(session._id);
66
+ }
67
+ }
68
+ try {
69
+ await deleteAllRefreshTokens(ctx, tokenSessionId, config);
70
+ } catch {
71
+ logWithLevel(
72
+ "DEBUG",
73
+ "Skipping invalid token session id during refresh token cleanup",
74
+ );
75
+ }
76
+ return null;
77
+ }
78
+ const { session } = validationResult;
79
+ const sessionId = session._id;
80
+ const userId = session.userId;
81
+
82
+ const tokenFirstUsed = validationResult.refreshTokenDoc.firstUsedTime;
83
+
84
+ // First use -- mark as used and generate new refresh token
85
+ if (tokenFirstUsed === undefined) {
86
+ if (authDb !== null) {
87
+ await authDb.refreshTokens.patch(refreshTokenId, {
88
+ firstUsedTime: Date.now(),
89
+ });
90
+ } else {
91
+ await ctx.db.patch(refreshTokenId, {
92
+ firstUsedTime: Date.now(),
93
+ });
94
+ }
95
+ const result = await generateTokensForSession(ctx, config, {
96
+ userId,
97
+ sessionId,
98
+ issuedRefreshTokenId: null,
99
+ parentRefreshTokenId: refreshTokenId,
100
+ });
101
+ const { refreshTokenId: newRefreshTokenId } = parseRefreshToken(
102
+ result.refreshToken,
103
+ );
104
+ logWithLevel(
105
+ "DEBUG",
106
+ `Exchanged ${maybeRedact(validationResult.refreshTokenDoc._id)} (first use) for new refresh token ${maybeRedact(newRefreshTokenId)}`,
107
+ );
108
+ return result;
109
+ }
110
+
111
+ // Token has been used before
112
+ // Check if parent of active refresh token
113
+ const activeRefreshToken = await loadActiveRefreshToken(
114
+ ctx,
115
+ tokenSessionId,
116
+ config,
117
+ );
118
+ logWithLevel(
119
+ "DEBUG",
120
+ `Active refresh token: ${maybeRedact(activeRefreshToken?._id ?? "(none)")}, parent ${maybeRedact(activeRefreshToken?.parentRefreshTokenId ?? "(none)")}`,
121
+ );
122
+ if (
123
+ activeRefreshToken !== null &&
124
+ activeRefreshToken.parentRefreshTokenId === refreshTokenId
125
+ ) {
126
+ logWithLevel(
127
+ "DEBUG",
128
+ `Token ${maybeRedact(validationResult.refreshTokenDoc._id)} is parent of active refresh token ${maybeRedact(activeRefreshToken._id)}, so returning that token`,
129
+ );
130
+
131
+ const result = await generateTokensForSession(ctx, config, {
132
+ userId,
133
+ sessionId,
134
+ issuedRefreshTokenId: activeRefreshToken._id,
135
+ parentRefreshTokenId: refreshTokenId,
136
+ });
137
+ return result;
138
+ }
139
+
140
+ // Check if within reuse window
141
+ if (tokenFirstUsed + REFRESH_TOKEN_REUSE_WINDOW_MS > Date.now()) {
142
+ const result = await generateTokensForSession(ctx, config, {
143
+ userId,
144
+ sessionId,
145
+ issuedRefreshTokenId: null,
146
+ parentRefreshTokenId: refreshTokenId,
147
+ });
148
+ const { refreshTokenId: newRefreshTokenId } = parseRefreshToken(
149
+ result.refreshToken,
150
+ );
151
+ logWithLevel(
152
+ "DEBUG",
153
+ `Exchanged ${maybeRedact(validationResult.refreshTokenDoc._id)} (reuse) for new refresh token ${maybeRedact(newRefreshTokenId)}`,
154
+ );
155
+ return result;
156
+ } else {
157
+ // Outside of reuse window -- invalidate all refresh tokens in subtree
158
+ logWithLevel("ERROR", "Refresh token used outside of reuse window");
159
+ logWithLevel(
160
+ "DEBUG",
161
+ `Token ${maybeRedact(validationResult.refreshTokenDoc._id)} being used outside of reuse window, so invalidating all refresh tokens in subtree`,
162
+ );
163
+ const tokensToInvalidate = await invalidateRefreshTokensInSubtree(
164
+ ctx,
165
+ validationResult.refreshTokenDoc,
166
+ config,
167
+ );
168
+ logWithLevel(
169
+ "DEBUG",
170
+ `Invalidated ${tokensToInvalidate.length} refresh tokens in subtree: ${tokensToInvalidate
171
+ .map((token) => maybeRedact(token._id))
172
+ .join(", ")}`,
173
+ );
174
+ return null;
175
+ }
176
+ }
177
+
178
+ export const callRefreshSession = async (
179
+ ctx: ActionCtx,
180
+ args: Infer<typeof refreshSessionArgs>,
181
+ ): Promise<ReturnType> => {
182
+ return ctx.runMutation("auth:store" as any, {
183
+ args: {
184
+ type: "refreshSession",
185
+ ...args,
186
+ },
187
+ });
188
+ };