@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,253 @@
1
+ import { GenericId } from "convex/values";
2
+ import {
3
+ AuthProviderMaterializedConfig,
4
+ ConvexCredentialsConfig,
5
+ EmailConfig,
6
+ GenericActionCtxWithAuthConfig,
7
+ PhoneConfig,
8
+ } from "../types.js";
9
+ import {
10
+ AuthDataModel,
11
+ SessionInfo,
12
+ SessionInfoWithTokens,
13
+ Tokens,
14
+ } from "./types.js";
15
+ import {
16
+ callCreateVerificationCode,
17
+ callRefreshSession,
18
+ callSignIn,
19
+ callVerifier,
20
+ callVerifyCodeAndSignIn,
21
+ } from "./mutations/index.js";
22
+ import { redirectAbsoluteUrl, setURLSearchParam } from "./redirects.js";
23
+ import { requireEnv } from "../utils.js";
24
+ import { OAuth2Config, OIDCConfig } from "@auth/core/providers/oauth.js";
25
+ import { generateRandomString } from "./utils.js";
26
+
27
+ const DEFAULT_EMAIL_VERIFICATION_CODE_DURATION_S = 60 * 60 * 24; // 24 hours
28
+
29
+ type EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;
30
+
31
+ export async function signInImpl(
32
+ ctx: EnrichedActionCtx,
33
+ provider: AuthProviderMaterializedConfig | null,
34
+ args: {
35
+ accountId?: GenericId<"account">;
36
+ params?: Record<string, any>;
37
+ verifier?: string;
38
+ refreshToken?: string;
39
+ calledBy?: string;
40
+ },
41
+ options: {
42
+ generateTokens: boolean;
43
+ allowExtraProviders: boolean;
44
+ },
45
+ ): Promise<
46
+ | { kind: "signedIn"; signedIn: SessionInfo | null }
47
+ // refresh tokens
48
+ | { kind: "refreshTokens"; signedIn: { tokens: Tokens } }
49
+ // Multi-step flows like magic link + OTP
50
+ | { kind: "started"; started: true }
51
+ // OAuth2 and OIDC flows
52
+ | { kind: "redirect"; redirect: string; verifier: string }
53
+ > {
54
+ if (provider === null && args.refreshToken) {
55
+ const tokens: Tokens = (await callRefreshSession(ctx, {
56
+ refreshToken: args.refreshToken,
57
+ }))!;
58
+ return { kind: "refreshTokens", signedIn: { tokens } };
59
+ }
60
+ if (provider === null && args.params?.code !== undefined) {
61
+ const result = await callVerifyCodeAndSignIn(ctx, {
62
+ params: args.params,
63
+ verifier: args.verifier,
64
+ generateTokens: true,
65
+ allowExtraProviders: options.allowExtraProviders,
66
+ });
67
+ return {
68
+ kind: "signedIn",
69
+ signedIn: result,
70
+ };
71
+ }
72
+
73
+ if (provider === null) {
74
+ throw new Error(
75
+ "Cannot sign in: Missing `provider`, `params.code` or `refreshToken`",
76
+ );
77
+ }
78
+ if (provider.type === "email" || provider.type === "phone") {
79
+ return handleEmailAndPhoneProvider(ctx, provider, args, options);
80
+ }
81
+ if (provider.type === "credentials") {
82
+ return handleCredentials(ctx, provider, args, options);
83
+ }
84
+ if (provider.type === "oauth" || provider.type === "oidc") {
85
+ return handleOAuthProvider(ctx, provider, args, options);
86
+ }
87
+ const _typecheck: never = provider;
88
+ throw new Error(
89
+ `Provider type ${(provider as any).type} is not supported yet`,
90
+ );
91
+ }
92
+
93
+ async function handleEmailAndPhoneProvider(
94
+ ctx: EnrichedActionCtx,
95
+ provider: EmailConfig | PhoneConfig,
96
+ args: {
97
+ params?: Record<string, any>;
98
+ accountId?: GenericId<"account">;
99
+ },
100
+ options: {
101
+ generateTokens: boolean;
102
+ allowExtraProviders: boolean;
103
+ },
104
+ ): Promise<
105
+ | { kind: "started"; started: true }
106
+ | { kind: "signedIn"; signedIn: SessionInfoWithTokens }
107
+ > {
108
+ if (args.params?.code !== undefined) {
109
+ const result = await callVerifyCodeAndSignIn(ctx, {
110
+ params: args.params,
111
+ provider: provider.id,
112
+ generateTokens: options.generateTokens,
113
+ allowExtraProviders: options.allowExtraProviders,
114
+ });
115
+ if (result === null) {
116
+ throw new Error("Could not verify code");
117
+ }
118
+ return {
119
+ kind: "signedIn",
120
+ signedIn: result as SessionInfoWithTokens,
121
+ };
122
+ }
123
+
124
+ const alphabet =
125
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
126
+ const code = provider.generateVerificationToken
127
+ ? await provider.generateVerificationToken()
128
+ : generateRandomString(32, alphabet);
129
+ const expirationTime =
130
+ Date.now() +
131
+ (provider.maxAge ?? DEFAULT_EMAIL_VERIFICATION_CODE_DURATION_S) * 1000;
132
+
133
+ const identifier = await callCreateVerificationCode(ctx, {
134
+ provider: provider.id,
135
+ accountId: args.accountId,
136
+ email: args.params?.email,
137
+ phone: args.params?.phone,
138
+ code,
139
+ expirationTime,
140
+ allowExtraProviders: options.allowExtraProviders,
141
+ });
142
+ const destination = await redirectAbsoluteUrl(
143
+ ctx.auth.config,
144
+ (args.params ?? {}) as { redirectTo: unknown },
145
+ );
146
+ const verificationArgs = {
147
+ identifier,
148
+ url: setURLSearchParam(destination, "code", code),
149
+ token: code,
150
+ expires: new Date(expirationTime),
151
+ };
152
+ if (provider.type === "email") {
153
+ await provider.sendVerificationRequest(
154
+ {
155
+ ...verificationArgs,
156
+ provider: {
157
+ ...provider,
158
+ from:
159
+ // Simplifies demo configuration of Resend
160
+ provider.from === "Auth.js <no-reply@authjs.dev>" &&
161
+ provider.id === "resend"
162
+ ? "My App <onboarding@resend.dev>"
163
+ : provider.from,
164
+ },
165
+ request: new Request("http://localhost"), // TODO: Document
166
+ theme: ctx.auth.config.theme,
167
+ },
168
+ // @ts-expect-error Figure out typing for email providers so they can
169
+ // access ctx.
170
+ ctx,
171
+ );
172
+ } else if (provider.type === "phone") {
173
+ await provider.sendVerificationRequest(
174
+ { ...verificationArgs, provider },
175
+ ctx,
176
+ );
177
+ }
178
+ return { kind: "started", started: true };
179
+ }
180
+
181
+ async function handleCredentials(
182
+ ctx: EnrichedActionCtx,
183
+ provider: ConvexCredentialsConfig,
184
+ args: {
185
+ params?: Record<string, any>;
186
+ },
187
+ options: {
188
+ generateTokens: boolean;
189
+ },
190
+ ): Promise<{ kind: "signedIn"; signedIn: SessionInfo | null }> {
191
+ const result = await provider.authorize(args.params ?? {}, ctx);
192
+ if (result === null) {
193
+ return { kind: "signedIn", signedIn: null };
194
+ }
195
+ const idsAndTokens = await callSignIn(ctx, {
196
+ userId: result.userId,
197
+ sessionId: result.sessionId,
198
+ generateTokens: options.generateTokens,
199
+ });
200
+ return {
201
+ kind: "signedIn",
202
+ signedIn: idsAndTokens,
203
+ };
204
+ }
205
+
206
+ async function handleOAuthProvider(
207
+ ctx: EnrichedActionCtx,
208
+ provider: OAuth2Config<any> | OIDCConfig<any>,
209
+ args: {
210
+ params?: Record<string, any>;
211
+ verifier?: string;
212
+ },
213
+ options: {
214
+ allowExtraProviders: boolean;
215
+ },
216
+ ): Promise<
217
+ | { kind: "signedIn"; signedIn: SessionInfoWithTokens | null }
218
+ | { kind: "redirect"; redirect: string; verifier: string }
219
+ > {
220
+ // We have this action because:
221
+ // 1. We remember the current sessionId if any, so we can link accounts
222
+ // 2. The client doesn't need to know the HTTP Actions URL
223
+ // of the backend (this simplifies using local backend)
224
+ // 3. The client doesn't need to know which provider is of which type,
225
+ // and hence which provider requires client-side redirect
226
+ // 4. On mobile the client can complete the flow manually
227
+ if (args.params?.code !== undefined) {
228
+ const result = await callVerifyCodeAndSignIn(ctx, {
229
+ params: args.params,
230
+ verifier: args.verifier,
231
+ generateTokens: true,
232
+ allowExtraProviders: options.allowExtraProviders,
233
+ });
234
+ return {
235
+ kind: "signedIn",
236
+ signedIn: result as SessionInfoWithTokens | null,
237
+ };
238
+ }
239
+ const redirect = new URL(
240
+ (process.env.CUSTOM_AUTH_SITE_URL ?? requireEnv("CONVEX_SITE_URL")) + `/api/auth/signin/${provider.id}`,
241
+ );
242
+ const verifier = await callVerifier(ctx);
243
+ redirect.searchParams.set("code", verifier);
244
+ if (args.params?.redirectTo !== undefined) {
245
+ if (typeof args.params.redirectTo !== "string") {
246
+ throw new Error(
247
+ `Expected \`redirectTo\` to be a string, got ${args.params.redirectTo}`,
248
+ );
249
+ }
250
+ redirect.searchParams.set("redirectTo", args.params.redirectTo);
251
+ }
252
+ return { kind: "redirect", redirect: redirect.toString(), verifier };
253
+ }
@@ -0,0 +1,29 @@
1
+ import { GenericId } from "convex/values";
2
+ import { ConvexAuthConfig } from "../types.js";
3
+ import { SignJWT, importPKCS8 } from "jose";
4
+ import { requireEnv } from "../utils.js";
5
+ import { TOKEN_SUB_CLAIM_DIVIDER } from "./utils.js";
6
+
7
+ const DEFAULT_JWT_DURATION_MS = 1000 * 60 * 60; // 1 hour
8
+
9
+ export async function generateToken(
10
+ args: {
11
+ userId: GenericId<"user">;
12
+ sessionId: GenericId<"session">;
13
+ },
14
+ config: ConvexAuthConfig,
15
+ ) {
16
+ const privateKey = await importPKCS8(requireEnv("JWT_PRIVATE_KEY"), "RS256");
17
+ const expirationTime = new Date(
18
+ Date.now() + (config.jwt?.durationMs ?? DEFAULT_JWT_DURATION_MS),
19
+ );
20
+ return await new SignJWT({
21
+ sub: args.userId + TOKEN_SUB_CLAIM_DIVIDER + args.sessionId,
22
+ })
23
+ .setProtectedHeader({ alg: "RS256" })
24
+ .setIssuedAt()
25
+ .setIssuer(requireEnv("CONVEX_SITE_URL"))
26
+ .setAudience("convex")
27
+ .setExpirationTime(expirationTime)
28
+ .sign(privateKey);
29
+ }
@@ -0,0 +1,220 @@
1
+ import {
2
+ DataModelFromSchemaDefinition,
3
+ GenericActionCtx,
4
+ GenericMutationCtx,
5
+ GenericQueryCtx,
6
+ TableNamesInDataModel,
7
+ defineSchema,
8
+ defineTable,
9
+ } from "convex/server";
10
+ import { GenericId, v } from "convex/values";
11
+ import { GenericDoc } from "../convex_types.js";
12
+
13
+ /**
14
+ * The table definitions required by the library.
15
+ *
16
+ * Your schema must include these so that the indexes
17
+ * are set up:
18
+ *
19
+ *
20
+ * ```ts filename="convex/schema.ts"
21
+ * import { defineSchema } from "convex/server";
22
+ * import { authTables } from "@robelest/convex-auth/component";
23
+ *
24
+ * const schema = defineSchema({
25
+ * ...authTables,
26
+ * });
27
+ *
28
+ * export default schema;
29
+ * ```
30
+ *
31
+ * You can inline the table definitions into your schema
32
+ * and extend them with additional optional and required
33
+ * fields. See https://labs.convex.dev/auth/setup/schema
34
+ * for more details.
35
+ */
36
+ export const authTables = {
37
+ /**
38
+ * Users.
39
+ */
40
+ user: defineTable({
41
+ name: v.optional(v.string()),
42
+ image: v.optional(v.string()),
43
+ email: v.optional(v.string()),
44
+ emailVerificationTime: v.optional(v.number()),
45
+ phone: v.optional(v.string()),
46
+ phoneVerificationTime: v.optional(v.number()),
47
+ isAnonymous: v.optional(v.boolean()),
48
+ })
49
+ .index("email", ["email"])
50
+ .index("phone", ["phone"]),
51
+ /**
52
+ * Sessions.
53
+ * A single user can have multiple active sessions.
54
+ * See [Session document lifecycle](https://labs.convex.dev/auth/advanced#session-document-lifecycle).
55
+ */
56
+ session: defineTable({
57
+ userId: v.id("user"),
58
+ expirationTime: v.number(),
59
+ }).index("userId", ["userId"]),
60
+ /**
61
+ * Accounts. An account corresponds to
62
+ * a single authentication provider.
63
+ * A single user can have multiple accounts linked.
64
+ */
65
+ account: defineTable({
66
+ userId: v.id("user"),
67
+ provider: v.string(),
68
+ providerAccountId: v.string(),
69
+ secret: v.optional(v.string()),
70
+ emailVerified: v.optional(v.string()),
71
+ phoneVerified: v.optional(v.string()),
72
+ })
73
+ .index("userIdAndProvider", ["userId", "provider"])
74
+ .index("providerAndAccountId", ["provider", "providerAccountId"]),
75
+ /**
76
+ * Refresh tokens.
77
+ * Refresh tokens are generally meant to be used once, to be exchanged for another
78
+ * refresh token and a JWT access token, but with a few exceptions:
79
+ * - The "active refresh token" is the most recently created refresh token that has
80
+ * not been used yet. The parent of the active refresh token can always be used to
81
+ * obtain the active refresh token.
82
+ * - A refresh token can be used within a 10 second window ("reuse window") to
83
+ * obtain a new refresh token.
84
+ * - On any invalid use of a refresh token, the token itself and all its descendants
85
+ * are invalidated.
86
+ */
87
+ token: defineTable({
88
+ sessionId: v.id("session"),
89
+ expirationTime: v.number(),
90
+ firstUsedTime: v.optional(v.number()),
91
+ // This is the ID of the refresh token that was exchanged to create this one.
92
+ parentRefreshTokenId: v.optional(v.id("token")),
93
+ })
94
+ // Sort by creationTime
95
+ .index("sessionId", ["sessionId"])
96
+ .index("sessionIdAndParentRefreshTokenId", [
97
+ "sessionId",
98
+ "parentRefreshTokenId",
99
+ ]),
100
+ /**
101
+ * Verification codes:
102
+ * - OTP tokens
103
+ * - magic link tokens
104
+ * - OAuth codes
105
+ */
106
+ verification: defineTable({
107
+ accountId: v.id("account"),
108
+ provider: v.string(),
109
+ code: v.string(),
110
+ expirationTime: v.number(),
111
+ verifier: v.optional(v.string()),
112
+ emailVerified: v.optional(v.string()),
113
+ phoneVerified: v.optional(v.string()),
114
+ })
115
+ .index("accountId", ["accountId"])
116
+ .index("code", ["code"]),
117
+ /**
118
+ * PKCE verifiers for OAuth.
119
+ */
120
+ verifier: defineTable({
121
+ sessionId: v.optional(v.id("session")),
122
+ signature: v.optional(v.string()),
123
+ }).index("signature", ["signature"]),
124
+ /**
125
+ * Rate limits for OTP and password sign-in.
126
+ */
127
+ limit: defineTable({
128
+ identifier: v.string(),
129
+ lastAttemptTime: v.number(),
130
+ attemptsLeft: v.number(),
131
+ }).index("identifier", ["identifier"]),
132
+
133
+ organization: defineTable({
134
+ name: v.string(),
135
+ slug: v.optional(v.string()),
136
+ ownerUserId: v.optional(v.id("user")),
137
+ parentOrganizationId: v.optional(v.id("organization")),
138
+ metadata: v.optional(v.any()),
139
+ })
140
+ .index("slug", ["slug"])
141
+ .index("ownerUserId", ["ownerUserId"])
142
+ .index("parentOrganizationId", ["parentOrganizationId"]),
143
+ team: defineTable({
144
+ organizationId: v.id("organization"),
145
+ name: v.string(),
146
+ slug: v.optional(v.string()),
147
+ parentTeamId: v.optional(v.id("team")),
148
+ metadata: v.optional(v.any()),
149
+ })
150
+ .index("organizationId", ["organizationId"])
151
+ .index("organizationIdAndSlug", ["organizationId", "slug"])
152
+ .index("parentTeamId", ["parentTeamId"]),
153
+ teamRelation: defineTable({
154
+ organizationId: v.id("organization"),
155
+ parentTeamId: v.id("team"),
156
+ childTeamId: v.id("team"),
157
+ relation: v.optional(v.string()),
158
+ })
159
+ .index("organizationId", ["organizationId"])
160
+ .index("organizationIdAndParentTeamId", ["organizationId", "parentTeamId"])
161
+ .index("organizationIdAndChildTeamId", ["organizationId", "childTeamId"]),
162
+ member: defineTable({
163
+ organizationId: v.id("organization"),
164
+ userId: v.id("user"),
165
+ teamId: v.optional(v.id("team")),
166
+ role: v.optional(v.string()),
167
+ status: v.optional(v.string()),
168
+ metadata: v.optional(v.any()),
169
+ })
170
+ .index("organizationId", ["organizationId"])
171
+ .index("organizationIdAndUserId", ["organizationId", "userId"])
172
+ .index("teamId", ["teamId"])
173
+ .index("userId", ["userId"]),
174
+ invite: defineTable({
175
+ organizationId: v.optional(v.id("organization")),
176
+ teamId: v.optional(v.id("team")),
177
+ invitedByUserId: v.id("user"),
178
+ email: v.string(),
179
+ tokenHash: v.string(),
180
+ role: v.optional(v.string()),
181
+ status: v.union(
182
+ v.literal("pending"),
183
+ v.literal("accepted"),
184
+ v.literal("revoked"),
185
+ v.literal("expired"),
186
+ ),
187
+ expiresTime: v.number(),
188
+ acceptedByUserId: v.optional(v.id("user")),
189
+ acceptedTime: v.optional(v.number()),
190
+ metadata: v.optional(v.any()),
191
+ })
192
+ .index("tokenHash", ["tokenHash"])
193
+ .index("emailAndStatus", ["email", "status"])
194
+ .index("invitedByUserIdAndStatus", ["invitedByUserId", "status"])
195
+ .index("organizationId", ["organizationId"])
196
+ .index("organizationIdAndStatus", ["organizationId", "status"]),
197
+ };
198
+
199
+ type DefaultSchema = ReturnType<typeof defineSchema<typeof authTables>>;
200
+
201
+ export type AuthDataModel = DataModelFromSchemaDefinition<DefaultSchema>;
202
+ export type ActionCtx = GenericActionCtx<AuthDataModel>;
203
+ export type MutationCtx = GenericMutationCtx<AuthDataModel>;
204
+ export type QueryCtx = GenericQueryCtx<AuthDataModel>;
205
+ export type Doc<T extends TableNamesInDataModel<AuthDataModel>> = GenericDoc<
206
+ AuthDataModel,
207
+ T
208
+ >;
209
+
210
+ export type Tokens = { token: string; refreshToken: string };
211
+ export type SessionInfo = {
212
+ userId: GenericId<"user">;
213
+ sessionId: GenericId<"session">;
214
+ tokens: Tokens | null;
215
+ };
216
+ export type SessionInfoWithTokens = {
217
+ userId: GenericId<"user">;
218
+ sessionId: GenericId<"session">;
219
+ tokens: Tokens;
220
+ };