@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,87 @@
1
+ import { Infer, v } from "convex/values";
2
+ import { ActionCtx, Doc, MutationCtx } from "../types.js";
3
+ import {
4
+ isSignInRateLimited,
5
+ recordFailedSignIn,
6
+ resetSignInRateLimit,
7
+ } from "../rateLimit.js";
8
+ import * as Provider from "../provider.js";
9
+ import { LOG_LEVELS, logWithLevel, maybeRedact } from "../utils.js";
10
+ import { createAuthDb } from "../db.js";
11
+
12
+ export const retrieveAccountWithCredentialsArgs = v.object({
13
+ provider: v.string(),
14
+ account: v.object({ id: v.string(), secret: v.optional(v.string()) }),
15
+ });
16
+
17
+ type ReturnType =
18
+ | "InvalidAccountId"
19
+ | "TooManyFailedAttempts"
20
+ | "InvalidSecret"
21
+ | { account: Doc<"account">; user: Doc<"user"> };
22
+
23
+ export async function retrieveAccountWithCredentialsImpl(
24
+ ctx: MutationCtx,
25
+ args: Infer<typeof retrieveAccountWithCredentialsArgs>,
26
+ getProviderOrThrow: Provider.GetProviderOrThrowFunc,
27
+ config: Provider.Config,
28
+ ): Promise<ReturnType> {
29
+ const { provider: providerId, account } = args;
30
+ const authDb =
31
+ config.component !== undefined ? createAuthDb(ctx, config.component) : null;
32
+ logWithLevel(LOG_LEVELS.DEBUG, "retrieveAccountWithCredentialsImpl args:", {
33
+ provider: providerId,
34
+ account: {
35
+ id: account.id,
36
+ secret: maybeRedact(account.secret ?? ""),
37
+ },
38
+ });
39
+ const existingAccount =
40
+ authDb !== null
41
+ ? ((await authDb.accounts.get(providerId, account.id)) as Doc<"account"> | null)
42
+ : await ctx.db
43
+ .query("account")
44
+ .withIndex("providerAndAccountId", (q) =>
45
+ q.eq("provider", providerId).eq("providerAccountId", account.id),
46
+ )
47
+ .unique();
48
+ if (existingAccount === null) {
49
+ return "InvalidAccountId";
50
+ }
51
+ if (account.secret !== undefined) {
52
+ if (await isSignInRateLimited(ctx, existingAccount._id, config)) {
53
+ return "TooManyFailedAttempts";
54
+ }
55
+ if (
56
+ !(await Provider.verify(
57
+ getProviderOrThrow(providerId),
58
+ account.secret,
59
+ existingAccount.secret ?? "",
60
+ ))
61
+ ) {
62
+ await recordFailedSignIn(ctx, existingAccount._id, config);
63
+ return "InvalidSecret";
64
+ }
65
+ await resetSignInRateLimit(ctx, existingAccount._id, config);
66
+ }
67
+ return {
68
+ account: existingAccount,
69
+ // TODO: Ian removed this
70
+ user:
71
+ authDb !== null
72
+ ? ((await authDb.users.getById(existingAccount.userId)) as unknown as Doc<"user">)
73
+ : (await ctx.db.get(existingAccount.userId))!,
74
+ };
75
+ }
76
+
77
+ export const callRetreiveAccountWithCredentials = async (
78
+ ctx: ActionCtx,
79
+ args: Infer<typeof retrieveAccountWithCredentialsArgs>,
80
+ ): Promise<ReturnType> => {
81
+ return ctx.runMutation("auth:store" as any, {
82
+ args: {
83
+ type: "retrieveAccountWithCredentials",
84
+ ...args,
85
+ },
86
+ });
87
+ };
@@ -0,0 +1,51 @@
1
+ import { GenericId, Infer, v } from "convex/values";
2
+ import { ActionCtx, MutationCtx, SessionInfo } from "../types.js";
3
+ import * as Provider from "../provider.js";
4
+ import {
5
+ createNewAndDeleteExistingSession,
6
+ maybeGenerateTokensForSession,
7
+ } from "../sessions.js";
8
+ import { LOG_LEVELS, logWithLevel } from "../utils.js";
9
+
10
+ export const signInArgs = v.object({
11
+ userId: v.string(),
12
+ sessionId: v.optional(v.string()),
13
+ generateTokens: v.boolean(),
14
+ });
15
+
16
+ type ReturnType = SessionInfo;
17
+
18
+ export async function signInImpl(
19
+ ctx: MutationCtx,
20
+ args: Infer<typeof signInArgs>,
21
+ config: Provider.Config,
22
+ ): Promise<ReturnType> {
23
+ logWithLevel(LOG_LEVELS.DEBUG, "signInImpl args:", args);
24
+ const { userId, sessionId: existingSessionId, generateTokens } = args;
25
+ const typedUserId = userId as GenericId<"user">;
26
+ const typedExistingSessionId = existingSessionId as
27
+ | GenericId<"session">
28
+ | undefined;
29
+ const sessionId =
30
+ typedExistingSessionId ??
31
+ (await createNewAndDeleteExistingSession(ctx, config, typedUserId));
32
+ return await maybeGenerateTokensForSession(
33
+ ctx,
34
+ config,
35
+ typedUserId,
36
+ sessionId,
37
+ generateTokens,
38
+ );
39
+ }
40
+
41
+ export const callSignIn = async (
42
+ ctx: ActionCtx,
43
+ args: Infer<typeof signInArgs>,
44
+ ): Promise<ReturnType> => {
45
+ return ctx.runMutation("auth:store" as any, {
46
+ args: {
47
+ type: "signIn",
48
+ ...args,
49
+ },
50
+ });
51
+ };
@@ -0,0 +1,38 @@
1
+ import { GenericId } from "convex/values";
2
+ import { ActionCtx, MutationCtx } from "../types.js";
3
+ import { deleteSession, getAuthSessionId } from "../sessions.js";
4
+ import * as Provider from "../provider.js";
5
+ import { createAuthDb } from "../db.js";
6
+
7
+ type ReturnType = {
8
+ userId: GenericId<"user">;
9
+ sessionId: GenericId<"session">;
10
+ } | null;
11
+
12
+ export async function signOutImpl(
13
+ ctx: MutationCtx,
14
+ config: Provider.Config,
15
+ ): Promise<ReturnType> {
16
+ const authDb =
17
+ config.component !== undefined ? createAuthDb(ctx, config.component) : null;
18
+ const sessionId = await getAuthSessionId(ctx);
19
+ if (sessionId !== null) {
20
+ const session =
21
+ authDb !== null
22
+ ? await authDb.sessions.getById(sessionId)
23
+ : await ctx.db.get(sessionId);
24
+ if (session !== null) {
25
+ await deleteSession(ctx, session, config);
26
+ return { userId: session.userId, sessionId: session._id };
27
+ }
28
+ }
29
+ return null;
30
+ }
31
+
32
+ export const callSignOut = async (ctx: ActionCtx): Promise<void> => {
33
+ return ctx.runMutation("auth:store" as any, {
34
+ args: {
35
+ type: "signOut",
36
+ },
37
+ });
38
+ };
@@ -0,0 +1,112 @@
1
+ import { Infer, v } from "convex/values";
2
+ import { ActionCtx, MutationCtx } from "../types.js";
3
+ import * as Provider from "../provider.js";
4
+ import { OAuthConfig } from "@auth/core/providers/oauth.js";
5
+ import { upsertUserAndAccount } from "../users.js";
6
+ import { generateRandomString, logWithLevel, sha256 } from "../utils.js";
7
+ import { createAuthDb } from "../db.js";
8
+
9
+ const OAUTH_SIGN_IN_EXPIRATION_MS = 1000 * 60 * 2; // 2 minutes
10
+
11
+ export const userOAuthArgs = v.object({
12
+ provider: v.string(),
13
+ providerAccountId: v.string(),
14
+ profile: v.any(),
15
+ signature: v.string(),
16
+ });
17
+
18
+ type ReturnType = string;
19
+
20
+ export async function userOAuthImpl(
21
+ ctx: MutationCtx,
22
+ args: Infer<typeof userOAuthArgs>,
23
+ getProviderOrThrow: Provider.GetProviderOrThrowFunc,
24
+ config: Provider.Config,
25
+ ): Promise<ReturnType> {
26
+ logWithLevel("DEBUG", "userOAuthImpl args:", args);
27
+ const { profile, provider, providerAccountId, signature } = args;
28
+ const authDb =
29
+ config.component !== undefined ? createAuthDb(ctx, config.component) : null;
30
+ const providerConfig = getProviderOrThrow(provider) as OAuthConfig<any>;
31
+ const existingAccount =
32
+ authDb !== null
33
+ ? await authDb.accounts.get(provider, providerAccountId)
34
+ : await ctx.db
35
+ .query("account")
36
+ .withIndex("providerAndAccountId", (q) =>
37
+ q.eq("provider", provider).eq("providerAccountId", providerAccountId),
38
+ )
39
+ .unique();
40
+
41
+ const verifier =
42
+ authDb !== null
43
+ ? await authDb.verifiers.getBySignature(signature)
44
+ : await ctx.db
45
+ .query("verifier")
46
+ .withIndex("signature", (q) => q.eq("signature", signature))
47
+ .unique();
48
+ if (verifier === null) {
49
+ throw new Error("Invalid state");
50
+ }
51
+
52
+ const { accountId } = await upsertUserAndAccount(
53
+ ctx,
54
+ verifier.sessionId ?? null,
55
+ existingAccount !== null ? { existingAccount } : { providerAccountId },
56
+ { type: "oauth", provider: providerConfig, profile },
57
+ config,
58
+ );
59
+
60
+ const code = generateRandomString(8, "0123456789");
61
+ if (authDb !== null) {
62
+ await authDb.verifiers.delete(verifier._id);
63
+ } else {
64
+ await ctx.db.delete(verifier._id);
65
+ }
66
+ const existingVerificationCode =
67
+ authDb !== null
68
+ ? await authDb.verificationCodes.getByAccountId(accountId)
69
+ : await ctx.db
70
+ .query("verification")
71
+ .withIndex("accountId", (q) => q.eq("accountId", accountId))
72
+ .unique();
73
+ if (existingVerificationCode !== null) {
74
+ if (authDb !== null) {
75
+ await authDb.verificationCodes.delete(existingVerificationCode._id);
76
+ } else {
77
+ await ctx.db.delete(existingVerificationCode._id);
78
+ }
79
+ }
80
+ if (authDb !== null) {
81
+ await authDb.verificationCodes.create({
82
+ code: await sha256(code),
83
+ accountId,
84
+ provider,
85
+ expirationTime: Date.now() + OAUTH_SIGN_IN_EXPIRATION_MS,
86
+ verifier: verifier._id,
87
+ });
88
+ } else {
89
+ await ctx.db.insert("verification", {
90
+ code: await sha256(code),
91
+ accountId,
92
+ provider,
93
+ expirationTime: Date.now() + OAUTH_SIGN_IN_EXPIRATION_MS,
94
+ // The use of a verifier means we don't need an identifier
95
+ // during verification.
96
+ verifier: verifier._id,
97
+ });
98
+ }
99
+ return code;
100
+ }
101
+
102
+ export const callUserOAuth = async (
103
+ ctx: ActionCtx,
104
+ args: Infer<typeof userOAuthArgs>,
105
+ ): Promise<ReturnType> => {
106
+ return ctx.runMutation("auth:store" as any, {
107
+ args: {
108
+ type: "userOAuth",
109
+ ...args,
110
+ },
111
+ });
112
+ };
@@ -0,0 +1,29 @@
1
+ import { GenericId } from "convex/values";
2
+ import { ActionCtx, MutationCtx } from "../types.js";
3
+ import { getAuthSessionId } from "../sessions.js";
4
+ import * as Provider from "../provider.js";
5
+ import { createAuthDb } from "../db.js";
6
+
7
+ type ReturnType = GenericId<"verifier">;
8
+
9
+ export async function verifierImpl(
10
+ ctx: MutationCtx,
11
+ config: Provider.Config,
12
+ ): Promise<ReturnType> {
13
+ const sessionId = (await getAuthSessionId(ctx)) ?? undefined;
14
+ if (config.component !== undefined) {
15
+ return (await createAuthDb(ctx, config.component).verifiers.create(sessionId)) as
16
+ ReturnType;
17
+ }
18
+ return await ctx.db.insert("verifier", {
19
+ sessionId,
20
+ });
21
+ }
22
+
23
+ export const callVerifier = async (ctx: ActionCtx): Promise<ReturnType> => {
24
+ return ctx.runMutation("auth:store" as any, {
25
+ args: {
26
+ type: "verifier",
27
+ },
28
+ });
29
+ };
@@ -0,0 +1,44 @@
1
+ import { GenericId, Infer, v } from "convex/values";
2
+ import { ActionCtx, MutationCtx } from "../types.js";
3
+ import * as Provider from "../provider.js";
4
+ import { createAuthDb } from "../db.js";
5
+
6
+ export const verifierSignatureArgs = v.object({
7
+ verifier: v.string(),
8
+ signature: v.string(),
9
+ });
10
+
11
+ type ReturnType = void;
12
+
13
+ export async function verifierSignatureImpl(
14
+ ctx: MutationCtx,
15
+ args: Infer<typeof verifierSignatureArgs>,
16
+ config: Provider.Config,
17
+ ): Promise<ReturnType> {
18
+ const { verifier, signature } = args;
19
+ const authDb =
20
+ config.component !== undefined ? createAuthDb(ctx, config.component) : null;
21
+ const verifierDoc =
22
+ authDb !== null
23
+ ? await authDb.verifiers.getById(verifier as GenericId<"verifier">)
24
+ : await ctx.db.get(verifier as GenericId<"verifier">);
25
+ if (verifierDoc === null) {
26
+ throw new Error("Invalid verifier");
27
+ }
28
+ if (authDb !== null) {
29
+ return await authDb.verifiers.patch(verifierDoc._id, { signature });
30
+ }
31
+ return await ctx.db.patch(verifierDoc._id, { signature });
32
+ }
33
+
34
+ export const callVerifierSignature = async (
35
+ ctx: ActionCtx,
36
+ args: Infer<typeof verifierSignatureArgs>,
37
+ ): Promise<void> => {
38
+ return ctx.runMutation("auth:store" as any, {
39
+ args: {
40
+ type: "verifierSignature",
41
+ ...args,
42
+ },
43
+ });
44
+ };
@@ -0,0 +1,205 @@
1
+ import { GenericId, Infer, v } from "convex/values";
2
+ import { ActionCtx, MutationCtx, SessionInfo } from "../types.js";
3
+ import {
4
+ isSignInRateLimited,
5
+ recordFailedSignIn,
6
+ resetSignInRateLimit,
7
+ } from "../rateLimit.js";
8
+ import * as Provider from "../provider.js";
9
+ import {
10
+ createNewAndDeleteExistingSession,
11
+ getAuthSessionId,
12
+ maybeGenerateTokensForSession,
13
+ } from "../sessions.js";
14
+ import { ConvexAuthConfig } from "../../types.js";
15
+ import { LOG_LEVELS, logWithLevel, sha256 } from "../utils.js";
16
+ import { upsertUserAndAccount } from "../users.js";
17
+ import { createAuthDb } from "../db.js";
18
+
19
+ export const verifyCodeAndSignInArgs = v.object({
20
+ params: v.any(),
21
+ provider: v.optional(v.string()),
22
+ verifier: v.optional(v.string()),
23
+ generateTokens: v.boolean(),
24
+ allowExtraProviders: v.boolean(),
25
+ });
26
+
27
+ type ReturnType = null | SessionInfo;
28
+
29
+ export async function verifyCodeAndSignInImpl(
30
+ ctx: MutationCtx,
31
+ args: Infer<typeof verifyCodeAndSignInArgs>,
32
+ getProviderOrThrow: Provider.GetProviderOrThrowFunc,
33
+ config: Provider.Config,
34
+ ): Promise<ReturnType> {
35
+ logWithLevel(LOG_LEVELS.DEBUG, "verifyCodeAndSignInImpl args:", {
36
+ params: { email: args.params.email, phone: args.params.phone },
37
+ provider: args.provider,
38
+ verifier: args.verifier,
39
+ generateTokens: args.generateTokens,
40
+ allowExtraProviders: args.allowExtraProviders,
41
+ });
42
+ const { generateTokens, provider, allowExtraProviders } = args;
43
+ const identifier = args.params.email ?? args.params.phone;
44
+ if (identifier !== undefined) {
45
+ if (await isSignInRateLimited(ctx, identifier, config)) {
46
+ logWithLevel(
47
+ LOG_LEVELS.ERROR,
48
+ "Too many failed attempts to verify code for this email",
49
+ );
50
+ return null;
51
+ }
52
+ }
53
+ const verifyResult = await verifyCodeOnly(
54
+ ctx,
55
+ args,
56
+ provider ?? null,
57
+ getProviderOrThrow,
58
+ allowExtraProviders,
59
+ config,
60
+ await getAuthSessionId(ctx),
61
+ );
62
+ if (verifyResult === null) {
63
+ if (identifier !== undefined) {
64
+ await recordFailedSignIn(ctx, identifier, config);
65
+ }
66
+ return null;
67
+ }
68
+ if (identifier !== undefined) {
69
+ await resetSignInRateLimit(ctx, identifier, config);
70
+ }
71
+ const { userId } = verifyResult;
72
+ const sessionId = await createNewAndDeleteExistingSession(
73
+ ctx,
74
+ config,
75
+ userId,
76
+ );
77
+ return await maybeGenerateTokensForSession(
78
+ ctx,
79
+ config,
80
+ userId,
81
+ sessionId,
82
+ generateTokens,
83
+ );
84
+ }
85
+
86
+ export const callVerifyCodeAndSignIn = async (
87
+ ctx: ActionCtx,
88
+ args: Infer<typeof verifyCodeAndSignInArgs>,
89
+ ): Promise<ReturnType> => {
90
+ return ctx.runMutation("auth:store" as any, {
91
+ args: {
92
+ type: "verifyCodeAndSignIn",
93
+ ...args,
94
+ },
95
+ });
96
+ };
97
+
98
+ async function verifyCodeOnly(
99
+ ctx: MutationCtx,
100
+ args: {
101
+ params: any;
102
+ verifier?: string;
103
+ identifier?: string;
104
+ },
105
+ /**
106
+ * There are two providers at play:
107
+ * 1. the provider that generated the code
108
+ * 2. the provider the account is tied to.
109
+ * This is because we allow signing into an account
110
+ * via another provider, see {@link signInViaProvider}.
111
+ * This is the first provider.
112
+ */
113
+ methodProviderId: string | null,
114
+ getProviderOrThrow: Provider.GetProviderOrThrowFunc,
115
+ allowExtraProviders: boolean,
116
+ config: ConvexAuthConfig,
117
+ sessionId: GenericId<"session"> | null,
118
+ ) {
119
+ const authDb =
120
+ config.component !== undefined ? createAuthDb(ctx, config.component) : null;
121
+ const { params, verifier } = args;
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();
130
+ if (verificationCode === null) {
131
+ logWithLevel(LOG_LEVELS.ERROR, "Invalid verification code");
132
+ return null;
133
+ }
134
+ if (authDb !== null) {
135
+ await authDb.verificationCodes.delete(verificationCode._id);
136
+ } else {
137
+ await ctx.db.delete(verificationCode._id);
138
+ }
139
+ if (verificationCode.verifier !== verifier) {
140
+ logWithLevel(LOG_LEVELS.ERROR, "Invalid verifier");
141
+ return null;
142
+ }
143
+ if (verificationCode.expirationTime < Date.now()) {
144
+ logWithLevel(LOG_LEVELS.ERROR, "Expired verification code");
145
+ return null;
146
+ }
147
+ const { accountId, emailVerified, phoneVerified } = verificationCode;
148
+ const account =
149
+ authDb !== null ? await authDb.accounts.getById(accountId) : await ctx.db.get(accountId);
150
+ if (account === null) {
151
+ logWithLevel(
152
+ LOG_LEVELS.ERROR,
153
+ "Account associated with this email has been deleted",
154
+ );
155
+ return null;
156
+ }
157
+ if (
158
+ methodProviderId !== null &&
159
+ verificationCode.provider !== methodProviderId
160
+ ) {
161
+ logWithLevel(
162
+ LOG_LEVELS.ERROR,
163
+ `Invalid provider "${methodProviderId}" for given \`code\`, ` +
164
+ `which was generated by provider "${verificationCode.provider}"`,
165
+ );
166
+ return null;
167
+ }
168
+ // OTP providers perform an additional check against the provided
169
+ // params.
170
+ const methodProvider = getProviderOrThrow(
171
+ verificationCode.provider,
172
+ allowExtraProviders,
173
+ );
174
+ if (
175
+ methodProvider !== null &&
176
+ (methodProvider.type === "email" || methodProvider.type === "phone") &&
177
+ methodProvider.authorize !== undefined
178
+ ) {
179
+ await methodProvider.authorize(args.params, account);
180
+ }
181
+ let userId = account.userId;
182
+ const provider = getProviderOrThrow(account.provider);
183
+ if (!(provider.type === "oauth" || provider.type === "oidc")) {
184
+ ({ userId } = await upsertUserAndAccount(
185
+ ctx,
186
+ sessionId,
187
+ { existingAccount: account },
188
+ {
189
+ type: "verification",
190
+ provider,
191
+ profile: {
192
+ ...(emailVerified !== undefined
193
+ ? { email: emailVerified, emailVerified: true }
194
+ : {}),
195
+ ...(phoneVerified !== undefined
196
+ ? { phone: phoneVerified, phoneVerified: true }
197
+ : {}),
198
+ },
199
+ },
200
+ config,
201
+ ));
202
+ }
203
+
204
+ return { providerAccountId: account.providerAccountId, userId };
205
+ }
@@ -0,0 +1,38 @@
1
+ import { AuthProviderMaterializedConfig } from "../types.js";
2
+
3
+ export async function hash(provider: any, secret: string) {
4
+ if (provider.type !== "credentials") {
5
+ throw new Error(`Provider ${provider.id} is not a credentials provider`);
6
+ }
7
+ const hashSecretFn = provider.crypto?.hashSecret;
8
+ if (hashSecretFn === undefined) {
9
+ throw new Error(
10
+ `Provider ${provider.id} does not have a \`crypto.hashSecret\` function`,
11
+ );
12
+ }
13
+ return await hashSecretFn(secret);
14
+ }
15
+
16
+ export async function verify(
17
+ provider: AuthProviderMaterializedConfig,
18
+ secret: string,
19
+ hash: string,
20
+ ) {
21
+ if (provider.type !== "credentials") {
22
+ throw new Error(`Provider ${provider.id} is not a credentials provider`);
23
+ }
24
+ const verifySecretFn = provider.crypto?.verifySecret;
25
+ if (verifySecretFn === undefined) {
26
+ throw new Error(
27
+ `Provider ${provider.id} does not have a \`crypto.verifySecret\` function`,
28
+ );
29
+ }
30
+ return await verifySecretFn(secret, hash);
31
+ }
32
+
33
+ export type GetProviderOrThrowFunc = (
34
+ provider: string,
35
+ allowExtraProviders?: boolean,
36
+ ) => AuthProviderMaterializedConfig;
37
+
38
+ export type Config = any;