@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,108 @@
1
+ /**
2
+ * Configure {@link ConvexCredentials} provider given a {@link ConvexCredentialsUserConfig}.
3
+ *
4
+ * This is for a very custom authentication implementation, often you can
5
+ * use the [`Password`](https://labs.convex.dev/auth/api_reference/providers/Password) provider instead.
6
+ *
7
+ * ```ts
8
+ * import ConvexCredentials from "@robelest/convex-auth/providers/ConvexCredentials";
9
+ * import { convexAuth } from "@robelest/convex-auth/component";
10
+ *
11
+ * export const { auth, signIn, signOut, store } = convexAuth({
12
+ * providers: [
13
+ * ConvexCredentials({
14
+ * authorize: async (credentials, ctx) => {
15
+ * // Your custom logic here...
16
+ * },
17
+ * }),
18
+ * ],
19
+ * });
20
+ * ```
21
+ *
22
+ * @module
23
+ */
24
+
25
+ import {
26
+ AuthProviderConfig,
27
+ ConvexCredentialsConfig,
28
+ GenericActionCtxWithAuthConfig,
29
+ } from "@robelest/convex-auth/component";
30
+ import { GenericDataModel } from "convex/server";
31
+ import { GenericId, Value } from "convex/values";
32
+
33
+ /**
34
+ * The available options to a {@link ConvexCredentials} provider for Convex Auth.
35
+ */
36
+ export interface ConvexCredentialsUserConfig<
37
+ DataModel extends GenericDataModel = GenericDataModel,
38
+ > {
39
+ /**
40
+ * Uniquely identifies the provider, allowing to use
41
+ * multiple different {@link ConvexCredentials} providers.
42
+ */
43
+ id?: string;
44
+ /**
45
+ * Gives full control over how you handle the credentials received from the user
46
+ * via the client-side `signIn` function.
47
+ *
48
+ * @returns This method expects a user ID to be returned for a successful login.
49
+ * A session ID can be also returned and that session will be used.
50
+ * If an error is thrown or `null` is returned, the sign-in will fail.
51
+ */
52
+ authorize: (
53
+ /**
54
+ * The available keys are determined by your call to `signIn()` on the client.
55
+ *
56
+ * You can add basic validation depending on your use case,
57
+ * or you can use a popular library like [Zod](https://zod.dev) for validating
58
+ * the input.
59
+ */
60
+ credentials: Partial<Record<string, Value | undefined>>,
61
+ ctx: GenericActionCtxWithAuthConfig<DataModel>,
62
+ ) => Promise<{
63
+ userId: GenericId<"user">;
64
+ sessionId?: GenericId<"session">;
65
+ } | null>;
66
+ /**
67
+ * Provide hashing and verification functions if you're
68
+ * storing account secrets and want to control
69
+ * how they're hashed.
70
+ *
71
+ * These functions will be called during
72
+ * the `createAccount` and `retrieveAccount` execution when the
73
+ * `secret` option is used.
74
+ */
75
+ crypto?: {
76
+ /**
77
+ * Function used to hash the secret.
78
+ */
79
+ hashSecret: (secret: string) => Promise<string>;
80
+ /**
81
+ * Function used to verify that the secret
82
+ * matches the stored hash.
83
+ */
84
+ verifySecret: (secret: string, hash: string) => Promise<boolean>;
85
+ };
86
+ /**
87
+ * Register extra providers used in the implementation of the credentials
88
+ * provider. They will only be available to the `signInViaProvider`
89
+ * function, and not to the `signIn` function exposed to clients.
90
+ */
91
+ extraProviders?: (AuthProviderConfig | undefined)[];
92
+ }
93
+
94
+ /**
95
+ * The Credentials provider allows you to handle signing in with arbitrary credentials,
96
+ * such as a username and password, domain, or two factor authentication or hardware device (e.g. YubiKey U2F / FIDO).
97
+ */
98
+ export default function convexCredentials<DataModel extends GenericDataModel>(
99
+ config: ConvexCredentialsUserConfig<DataModel>,
100
+ ): ConvexCredentialsConfig {
101
+ return {
102
+ id: "credentials",
103
+ type: "credentials",
104
+ authorize: async () => null,
105
+ // @ts-expect-error Internal
106
+ options: config,
107
+ };
108
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Simplifies creating custom email providers, such as for sending OTPs.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import { GenericDataModel } from "convex/server";
8
+ import { EmailConfig, EmailUserConfig } from "../server/types.js";
9
+
10
+ /**
11
+ * Email providers send a token to the user's email address
12
+ * for sign-in.
13
+ *
14
+ * When you use this function to create your config, by default it
15
+ * checks that there is an `email` field during token verification
16
+ * that matches the `email` used during the initial `signIn` call.
17
+ *
18
+ * If you want the "magic link behavior", where only the token is needed,
19
+ * you can override the `authorize` method to skip the check:
20
+ *
21
+ * ```ts
22
+ * import Email from "@robelest/convex-auth/providers/Email";
23
+ * import { convexAuth } from "@robelest/convex-auth/component";
24
+ *
25
+ * export const { auth, signIn, signOut, store } = convexAuth({
26
+ * providers: [
27
+ * Email({ authorize: undefined }),
28
+ * ],
29
+ * });
30
+ * ```
31
+ *
32
+ * Make sure the token has high enough entropy to be secure.
33
+ */
34
+ export function Email<DataModel extends GenericDataModel>(
35
+ config: EmailUserConfig<DataModel> &
36
+ Pick<EmailConfig, "sendVerificationRequest">,
37
+ ): EmailConfig<DataModel> {
38
+ return {
39
+ id: "email",
40
+ type: "email",
41
+ name: "Email",
42
+ from: "Auth.js <no-reply@authjs.dev>",
43
+ maxAge: 60 * 60, // 1 hour
44
+ authorize: async (params, account) => {
45
+ if (typeof params.email !== "string") {
46
+ throw new Error(
47
+ "Token verification requires an `email` in params of `signIn`.",
48
+ );
49
+ }
50
+ if (account.providerAccountId !== params.email) {
51
+ throw new Error(
52
+ "Short verification code requires a matching `email` " +
53
+ "in params of `signIn`.",
54
+ );
55
+ }
56
+ },
57
+ sendVerificationRequest: config.sendVerificationRequest,
58
+ options: config,
59
+ };
60
+ }
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Configure {@link Password} provider given a {@link PasswordConfig}.
3
+ *
4
+ * The `Password` provider supports the following flows, determined
5
+ * by the `flow` parameter:
6
+ *
7
+ * - `"signUp"`: Create a new account with a password.
8
+ * - `"signIn"`: Sign in with an existing account and password.
9
+ * - `"reset"`: Request a password reset.
10
+ * - `"reset-verification"`: Verify a password reset code and change password.
11
+ * - `"email-verification"`: If email verification is enabled and `code` is
12
+ * included in params, verify an OTP.
13
+ *
14
+ * ```ts
15
+ * import Password from "@robelest/convex-auth/providers/Password";
16
+ * import { convexAuth } from "@robelest/convex-auth/component";
17
+ *
18
+ * export const { auth, signIn, signOut, store } = convexAuth({
19
+ * providers: [Password],
20
+ * });
21
+ * ```
22
+ *
23
+ * @module
24
+ */
25
+
26
+ import convexCredentials, {
27
+ ConvexCredentialsUserConfig,
28
+ } from "@robelest/convex-auth/providers/ConvexCredentials";
29
+ import {
30
+ EmailConfig,
31
+ GenericActionCtxWithAuthConfig,
32
+ GenericDoc,
33
+ createAccount,
34
+ invalidateSessions,
35
+ modifyAccountCredentials,
36
+ retrieveAccount,
37
+ signInViaProvider,
38
+ } from "@robelest/convex-auth/component";
39
+ import {
40
+ DocumentByName,
41
+ GenericDataModel,
42
+ WithoutSystemFields,
43
+ } from "convex/server";
44
+ import { Value } from "convex/values";
45
+ import { Scrypt } from "lucia";
46
+
47
+ /**
48
+ * The available options to a {@link Password} provider for Convex Auth.
49
+ */
50
+ export interface PasswordConfig<DataModel extends GenericDataModel> {
51
+ /**
52
+ * Uniquely identifies the provider, allowing to use
53
+ * multiple different {@link Password} providers.
54
+ */
55
+ id?: string;
56
+ /**
57
+ * Perform checks on provided params and customize the user
58
+ * information stored after sign up, including email normalization.
59
+ *
60
+ * Called for every flow ("signUp", "signIn", "reset",
61
+ * "reset-verification" and "email-verification").
62
+ */
63
+ profile?: (
64
+ /**
65
+ * The values passed to the `signIn` function.
66
+ */
67
+ params: Record<string, Value | undefined>,
68
+ /**
69
+ * Convex ActionCtx in case you want to read from or write to
70
+ * the database.
71
+ */
72
+ ctx: GenericActionCtxWithAuthConfig<DataModel>,
73
+ ) => WithoutSystemFields<DocumentByName<DataModel, "user">> & {
74
+ email: string;
75
+ };
76
+ /**
77
+ * Performs custom validation on password provided during sign up or reset.
78
+ *
79
+ * Otherwise the default validation is used (password is not empty and
80
+ * at least 8 characters in length).
81
+ *
82
+ * If the provided password is invalid, implementations must throw an Error.
83
+ *
84
+ * @param password the password supplied during "signUp" or
85
+ * "reset-verification" flows.
86
+ */
87
+ validatePasswordRequirements?: (password: string) => void;
88
+ /**
89
+ * Provide hashing and verification functions if you want to control
90
+ * how passwords are hashed.
91
+ */
92
+ crypto?: ConvexCredentialsUserConfig["crypto"];
93
+ /**
94
+ * An Auth.js email provider used to require verification
95
+ * before password reset.
96
+ */
97
+ reset?: EmailConfig | ((...args: any) => EmailConfig);
98
+ /**
99
+ * An Auth.js email provider used to require verification
100
+ * before sign up / sign in.
101
+ */
102
+ verify?: EmailConfig | ((...args: any) => EmailConfig);
103
+ }
104
+
105
+ /**
106
+ * Email and password authentication provider.
107
+ *
108
+ * Passwords are by default hashed using Scrypt from Lucia.
109
+ * You can customize the hashing via the `crypto` option.
110
+ *
111
+ * Email verification is not required unless you pass
112
+ * an email provider to the `verify` option.
113
+ */
114
+ export default function password<DataModel extends GenericDataModel>(
115
+ config: PasswordConfig<DataModel> = {},
116
+ ) {
117
+ const provider = config.id ?? "password";
118
+ return convexCredentials<DataModel>({
119
+ id: "password",
120
+ authorize: async (params, ctx) => {
121
+ const flow = params.flow as string;
122
+ const passwordToValidate =
123
+ flow === "signUp"
124
+ ? (params.password as string)
125
+ : flow === "reset-verification"
126
+ ? (params.newPassword as string)
127
+ : null;
128
+ if (passwordToValidate !== null) {
129
+ if (config.validatePasswordRequirements !== undefined) {
130
+ config.validatePasswordRequirements(passwordToValidate);
131
+ } else {
132
+ validateDefaultPasswordRequirements(passwordToValidate);
133
+ }
134
+ }
135
+ const profile = config.profile?.(params, ctx) ?? defaultProfile(params);
136
+ const { email } = profile;
137
+ const secret = params.password as string;
138
+ let account: GenericDoc<DataModel, "account">;
139
+ let user: GenericDoc<DataModel, "user">;
140
+ if (flow === "signUp") {
141
+ if (secret === undefined) {
142
+ throw new Error("Missing `password` param for `signUp` flow");
143
+ }
144
+ const created = await createAccount(ctx, {
145
+ provider,
146
+ account: { id: email, secret },
147
+ profile: profile as any,
148
+ shouldLinkViaEmail: config.verify !== undefined,
149
+ shouldLinkViaPhone: false,
150
+ });
151
+ ({ account, user } = created);
152
+ } else if (flow === "signIn") {
153
+ if (secret === undefined) {
154
+ throw new Error("Missing `password` param for `signIn` flow");
155
+ }
156
+ const retrieved = await retrieveAccount(ctx, {
157
+ provider,
158
+ account: { id: email, secret },
159
+ });
160
+ if (retrieved === null) {
161
+ throw new Error("Invalid credentials");
162
+ }
163
+ ({ account, user } = retrieved);
164
+ // START: Optional, support password reset
165
+ } else if (flow === "reset") {
166
+ if (!config.reset) {
167
+ throw new Error(`Password reset is not enabled for ${provider}`);
168
+ }
169
+ const { account } = await retrieveAccount(ctx, {
170
+ provider,
171
+ account: { id: email },
172
+ });
173
+ return await signInViaProvider(ctx, config.reset, {
174
+ accountId: account._id,
175
+ params,
176
+ });
177
+ } else if (flow === "reset-verification") {
178
+ if (!config.reset) {
179
+ throw new Error(`Password reset is not enabled for ${provider}`);
180
+ }
181
+ if (params.newPassword === undefined) {
182
+ throw new Error(
183
+ "Missing `newPassword` param for `reset-verification` flow",
184
+ );
185
+ }
186
+ const result = await signInViaProvider(ctx, config.reset, { params });
187
+ if (result === null) {
188
+ throw new Error("Invalid code");
189
+ }
190
+ const { userId, sessionId } = result;
191
+ const secret = params.newPassword as string;
192
+ await modifyAccountCredentials(ctx, {
193
+ provider,
194
+ account: { id: email, secret },
195
+ });
196
+ await invalidateSessions(ctx, { userId, except: [sessionId] });
197
+ return { userId, sessionId };
198
+ // END
199
+ // START: Optional, email verification during sign in
200
+ } else if (flow === "email-verification") {
201
+ if (!config.verify) {
202
+ throw new Error(`Email verification is not enabled for ${provider}`);
203
+ }
204
+ const { account } = await retrieveAccount(ctx, {
205
+ provider,
206
+ account: { id: email },
207
+ });
208
+ return await signInViaProvider(ctx, config.verify, {
209
+ accountId: account._id,
210
+ params,
211
+ });
212
+ // END
213
+ } else {
214
+ throw new Error(
215
+ "Missing `flow` param, it must be one of " +
216
+ '"signUp", "signIn", "reset", "reset-verification" or ' +
217
+ '"email-verification"!',
218
+ );
219
+ }
220
+ // START: Optional, email verification during sign in
221
+ if (config.verify && !account.emailVerified) {
222
+ return await signInViaProvider(ctx, config.verify, {
223
+ accountId: account._id,
224
+ params,
225
+ });
226
+ }
227
+ // END
228
+ return { userId: user._id };
229
+ },
230
+ crypto: {
231
+ async hashSecret(password: string) {
232
+ return await new Scrypt().hash(password);
233
+ },
234
+ async verifySecret(password: string, hash: string) {
235
+ return await new Scrypt().verify(hash, password);
236
+ },
237
+ },
238
+ extraProviders: [config.reset, config.verify],
239
+ ...config,
240
+ });
241
+ }
242
+
243
+ function validateDefaultPasswordRequirements(password: string) {
244
+ if (!password || password.length < 8) {
245
+ throw new Error("Invalid password");
246
+ }
247
+ }
248
+
249
+ function defaultProfile(params: Record<string, unknown>) {
250
+ return {
251
+ email: params.email as string,
252
+ };
253
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Configure {@link Phone} provider given a {@link PhoneUserConfig}.
3
+ *
4
+ * Simplifies creating phone providers.
5
+ *
6
+ * By default checks that there is an `phone` field during token verification
7
+ * that matches the `phone` used during the initial `signIn` call.
8
+ *
9
+ * @module
10
+ */
11
+
12
+ import { GenericDataModel } from "convex/server";
13
+ import { PhoneConfig, PhoneUserConfig } from "../server/types.js";
14
+
15
+ /**
16
+ * Phone providers send a token to the user's phone number
17
+ * for sign-in.
18
+ *
19
+ * When you use this function to create your config, it
20
+ * checks that there is a `phone` field during token verification
21
+ * that matches the `phone` used during the initial `signIn` call.
22
+ */
23
+ export default function phone<DataModel extends GenericDataModel>(
24
+ config: PhoneUserConfig & Pick<PhoneConfig, "sendVerificationRequest">,
25
+ ): PhoneConfig<DataModel> {
26
+ return {
27
+ id: "phone",
28
+ type: "phone",
29
+ maxAge: 60 * 20, // 20 minutes
30
+ authorize: async (params, account) => {
31
+ if (typeof params.phone !== "string") {
32
+ throw new Error(
33
+ "Token verification requires an `phone` in params of `signIn`.",
34
+ );
35
+ }
36
+ if (account.providerAccountId !== params.phone) {
37
+ throw new Error(
38
+ "Short verification code requires a matching `phone` " +
39
+ "in params of `signIn`.",
40
+ );
41
+ }
42
+ },
43
+ sendVerificationRequest: config.sendVerificationRequest,
44
+ options: config,
45
+ };
46
+ }
@@ -0,0 +1,55 @@
1
+ import {
2
+ DocumentByName,
3
+ FunctionReference,
4
+ GenericDataModel,
5
+ RegisteredAction,
6
+ RegisteredMutation,
7
+ RegisteredQuery,
8
+ TableNamesInDataModel,
9
+ } from "convex/server";
10
+ import { GenericId } from "convex/values";
11
+
12
+ /**
13
+ * Convex document from a given table.
14
+ */
15
+ export type GenericDoc<
16
+ DataModel extends GenericDataModel,
17
+ TableName extends TableNamesInDataModel<DataModel>,
18
+ > = DocumentByName<DataModel, TableName> & {
19
+ _id: GenericId<TableName>;
20
+ _creationTime: number;
21
+ };
22
+
23
+ /**
24
+ * @internal
25
+ */
26
+ export type FunctionReferenceFromExport<Export> =
27
+ Export extends RegisteredQuery<infer Visibility, infer Args, infer Output>
28
+ ? FunctionReference<"query", Visibility, Args, ConvertReturnType<Output>>
29
+ : Export extends RegisteredMutation<
30
+ infer Visibility,
31
+ infer Args,
32
+ infer Output
33
+ >
34
+ ? FunctionReference<
35
+ "mutation",
36
+ Visibility,
37
+ Args,
38
+ ConvertReturnType<Output>
39
+ >
40
+ : Export extends RegisteredAction<
41
+ infer Visibility,
42
+ infer Args,
43
+ infer Output
44
+ >
45
+ ? FunctionReference<
46
+ "action",
47
+ Visibility,
48
+ Args,
49
+ ConvertReturnType<Output>
50
+ >
51
+ : never;
52
+
53
+ type ConvertReturnType<T> = UndefinedToNull<Awaited<T>>;
54
+
55
+ type UndefinedToNull<T> = T extends void ? null : T;
@@ -0,0 +1,42 @@
1
+ import { isLocalHost } from "./utils.js";
2
+
3
+ export const SHARED_COOKIE_OPTIONS = {
4
+ httpOnly: true,
5
+ sameSite: "none" as const,
6
+ secure: true,
7
+ path: "/",
8
+ partitioned: true,
9
+ };
10
+
11
+ const REDIRECT_MAX_AGE = 60 * 15; // 15 minutes in seconds
12
+ export function redirectToParamCookie(providerId: string, redirectTo: string) {
13
+ return {
14
+ name: redirectToParamCookieName(providerId),
15
+ value: redirectTo,
16
+ options: { ...SHARED_COOKIE_OPTIONS, maxAge: REDIRECT_MAX_AGE },
17
+ };
18
+ }
19
+
20
+ export function useRedirectToParam(
21
+ providerId: string,
22
+ cookies: Record<string, string | undefined>,
23
+ ) {
24
+ const cookieName = redirectToParamCookieName(providerId);
25
+ const redirectTo = cookies[cookieName];
26
+ if (redirectTo === undefined) {
27
+ return null;
28
+ }
29
+
30
+ // Clear the cookie
31
+ const updatedCookie = {
32
+ name: cookieName,
33
+ value: "",
34
+ options: { ...SHARED_COOKIE_OPTIONS, maxAge: 0 },
35
+ };
36
+
37
+ return { redirectTo, updatedCookie };
38
+ }
39
+
40
+ function redirectToParamCookieName(providerId: string) {
41
+ return (!isLocalHost(process.env.CONVEX_SITE_URL) ? "__Host-" : "") + providerId + "RedirectTo";
42
+ }
@@ -0,0 +1,125 @@
1
+ import { GenericActionCtx, GenericDataModel, GenericMutationCtx } from "convex/server";
2
+ import { AuthComponentApi } from "../types.js";
3
+
4
+ type MutationCtxLike = Pick<GenericMutationCtx<GenericDataModel>, "runQuery" | "runMutation">;
5
+ type ActionCtxLike = Pick<
6
+ GenericActionCtx<GenericDataModel>,
7
+ "runQuery" | "runMutation" | "runAction"
8
+ >;
9
+
10
+ type CtxLike = MutationCtxLike | ActionCtxLike;
11
+
12
+ export type AuthDb = ReturnType<typeof createAuthDb>;
13
+
14
+ export function createAuthDb(ctx: CtxLike, component: AuthComponentApi) {
15
+ return {
16
+ users: {
17
+ getById: (userId: string) =>
18
+ ctx.runQuery(component.public.userGetById, { userId }),
19
+ findByVerifiedEmail: (email: string) =>
20
+ ctx.runQuery(component.public.userFindByVerifiedEmail, { email }),
21
+ findByVerifiedPhone: (phone: string) =>
22
+ ctx.runQuery(component.public.userFindByVerifiedPhone, { phone }),
23
+ insert: (data: Record<string, unknown>) =>
24
+ ctx.runMutation(component.public.userInsert, { data }) as Promise<string>,
25
+ patch: (userId: string, data: Record<string, unknown>) =>
26
+ ctx.runMutation(component.public.userPatch, { userId, data }),
27
+ upsert: (userId: string | undefined, data: Record<string, unknown>) =>
28
+ ctx.runMutation(component.public.userUpsert, { userId, data }) as Promise<string>,
29
+ },
30
+ accounts: {
31
+ get: (provider: string, providerAccountId: string) =>
32
+ ctx.runQuery(component.public.accountGet, { provider, providerAccountId }),
33
+ getById: (accountId: string) =>
34
+ ctx.runQuery(component.public.accountGetById, { accountId }),
35
+ create: (args: {
36
+ userId: string;
37
+ provider: string;
38
+ providerAccountId: string;
39
+ secret?: string;
40
+ }) => ctx.runMutation(component.public.accountInsert, args) as Promise<string>,
41
+ patch: (accountId: string, data: Record<string, unknown>) =>
42
+ ctx.runMutation(component.public.accountPatch, { accountId, data }),
43
+ delete: (accountId: string) =>
44
+ ctx.runMutation(component.public.accountDelete, { accountId }),
45
+ },
46
+ sessions: {
47
+ create: (userId: string, expirationTime: number) =>
48
+ ctx.runMutation(component.public.sessionCreate, { userId, expirationTime }) as Promise<string>,
49
+ getById: (sessionId: string) =>
50
+ ctx.runQuery(component.public.sessionGetById, { sessionId }),
51
+ delete: (sessionId: string) =>
52
+ ctx.runMutation(component.public.sessionDelete, { sessionId }),
53
+ listByUser: (userId: string) =>
54
+ ctx.runQuery(component.public.sessionListByUser, { userId }),
55
+ },
56
+ verifiers: {
57
+ create: (sessionId?: string) =>
58
+ ctx.runMutation(component.public.verifierCreate, { sessionId }) as Promise<string>,
59
+ getById: (verifierId: string) =>
60
+ ctx.runQuery(component.public.verifierGetById, { verifierId }),
61
+ getBySignature: (signature: string) =>
62
+ ctx.runQuery(component.public.verifierGetBySignature, { signature }),
63
+ patch: (verifierId: string, data: Record<string, unknown>) =>
64
+ ctx.runMutation(component.public.verifierPatch, { verifierId, data }),
65
+ delete: (verifierId: string) =>
66
+ ctx.runMutation(component.public.verifierDelete, { verifierId }),
67
+ },
68
+ verificationCodes: {
69
+ getByAccountId: (accountId: string) =>
70
+ ctx.runQuery(component.public.verificationCodeGetByAccountId, { accountId }),
71
+ getByCode: (code: string) =>
72
+ ctx.runQuery(component.public.verificationCodeGetByCode, { code }),
73
+ create: (args: {
74
+ accountId: string;
75
+ provider: string;
76
+ code: string;
77
+ expirationTime: number;
78
+ verifier?: string;
79
+ emailVerified?: string;
80
+ phoneVerified?: string;
81
+ }) =>
82
+ ctx.runMutation(component.public.verificationCodeCreate, args),
83
+ delete: (verificationCodeId: string) =>
84
+ ctx.runMutation(component.public.verificationCodeDelete, {
85
+ verificationCodeId,
86
+ }),
87
+ },
88
+ refreshTokens: {
89
+ create: (args: {
90
+ sessionId: string;
91
+ expirationTime: number;
92
+ parentRefreshTokenId?: string;
93
+ }) =>
94
+ ctx.runMutation(component.public.refreshTokenCreate, args) as Promise<string>,
95
+ getById: (refreshTokenId: string) =>
96
+ ctx.runQuery(component.public.refreshTokenGetById, { refreshTokenId }),
97
+ patch: (refreshTokenId: string, data: Record<string, unknown>) =>
98
+ ctx.runMutation(component.public.refreshTokenPatch, { refreshTokenId, data }),
99
+ getChildren: (sessionId: string, parentRefreshTokenId: string) =>
100
+ ctx.runQuery(component.public.refreshTokenGetChildren, {
101
+ sessionId,
102
+ parentRefreshTokenId,
103
+ }),
104
+ listBySession: (sessionId: string) =>
105
+ ctx.runQuery(component.public.refreshTokenListBySession, { sessionId }),
106
+ deleteAll: (sessionId: string) =>
107
+ ctx.runMutation(component.public.refreshTokenDeleteAll, { sessionId }),
108
+ getActive: (sessionId: string) =>
109
+ ctx.runQuery(component.public.refreshTokenGetActive, { sessionId }),
110
+ },
111
+ rateLimits: {
112
+ get: (identifier: string) =>
113
+ ctx.runQuery(component.public.rateLimitGet, { identifier }),
114
+ create: (args: {
115
+ identifier: string;
116
+ attemptsLeft: number;
117
+ lastAttemptTime: number;
118
+ }) => ctx.runMutation(component.public.rateLimitCreate, args),
119
+ patch: (rateLimitId: string, data: Record<string, unknown>) =>
120
+ ctx.runMutation(component.public.rateLimitPatch, { rateLimitId, data }),
121
+ delete: (rateLimitId: string) =>
122
+ ctx.runMutation(component.public.rateLimitDelete, { rateLimitId }),
123
+ },
124
+ };
125
+ }