@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,815 @@
1
+ import { OAuth2Config, OAuthConfig } from "@auth/core/providers";
2
+ import {
3
+ Auth,
4
+ DocumentByName,
5
+ GenericActionCtx,
6
+ GenericDataModel,
7
+ HttpRouter,
8
+ WithoutSystemFields,
9
+ actionGeneric,
10
+ httpActionGeneric,
11
+ internalMutationGeneric,
12
+ } from "convex/server";
13
+ import { ConvexError, GenericId, Value, v } from "convex/values";
14
+ import { parse as parseCookies, serialize as serializeCookie } from "cookie";
15
+ import { redirectToParamCookie, useRedirectToParam } from "../cookies.js";
16
+ import { FunctionReferenceFromExport, GenericDoc } from "../convex_types.js";
17
+ import {
18
+ configDefaults,
19
+ listAvailableProviders,
20
+ materializeProvider,
21
+ } from "../provider_utils.js";
22
+ import {
23
+ AuthProviderConfig,
24
+ ConvexAuthConfig,
25
+ GenericActionCtxWithAuthConfig,
26
+ } from "../types.js";
27
+ import { requireEnv } from "../utils.js";
28
+ import { ActionCtx, MutationCtx, Tokens } from "./types.js";
29
+ export { authTables, Doc, Tokens } from "./types.js";
30
+ import {
31
+ LOG_LEVELS,
32
+ TOKEN_SUB_CLAIM_DIVIDER,
33
+ logError,
34
+ logWithLevel,
35
+ } from "./utils.js";
36
+ import { GetProviderOrThrowFunc } from "./provider.js";
37
+ import {
38
+ callCreateAccountFromCredentials,
39
+ callInvalidateSessions,
40
+ callModifyAccount,
41
+ callRetreiveAccountWithCredentials,
42
+ callSignOut,
43
+ callUserOAuth,
44
+ callVerifierSignature,
45
+ storeArgs,
46
+ storeImpl,
47
+ } from "./mutations/index.js";
48
+ import { signInImpl } from "./signIn.js";
49
+ import { redirectAbsoluteUrl, setURLSearchParam } from "./redirects.js";
50
+ import { getAuthorizationUrl } from "../oauth/authorizationUrl.js";
51
+ import {
52
+ defaultCookiesOptions,
53
+ oAuthConfigToInternalProvider,
54
+ } from "../oauth/convexAuth.js";
55
+ import { handleOAuth } from "../oauth/callback.js";
56
+ export { getAuthSessionId } from "./sessions.js";
57
+
58
+ /**
59
+ * The type of the signIn Convex Action returned from the auth() helper.
60
+ *
61
+ * This type is exported for implementors of other client integrations.
62
+ * However it is not stable, and may change until this library reaches 1.0.
63
+ */
64
+ export type SignInAction = FunctionReferenceFromExport<
65
+ ReturnType<typeof Auth>["signIn"]
66
+ >;
67
+ /**
68
+ * The type of the signOut Convex Action returned from the auth() helper.
69
+ *
70
+ * This type is exported for implementors of other client integrations.
71
+ * However it is not stable, and may change until this library reaches 1.0.
72
+ */
73
+ export type SignOutAction = FunctionReferenceFromExport<
74
+ ReturnType<typeof Auth>["signOut"]
75
+ >;
76
+ /**
77
+ * Configure the Convex Auth library. Returns an object with
78
+ * functions and `auth` helper. You must export the functions
79
+ * from `convex/auth.ts` to make them callable:
80
+ *
81
+ * ```ts filename="convex/auth.ts"
82
+ * import { Auth } from "@robelest/convex-auth/component";
83
+ *
84
+ * export const { auth, signIn, signOut, store } = Auth({
85
+ * providers: [],
86
+ * });
87
+ * ```
88
+ *
89
+ * @returns An object with fields you should reexport from your
90
+ * `convex/auth.ts` file.
91
+ */
92
+ export function Auth(config_: ConvexAuthConfig) {
93
+ const config = configDefaults(config_ as any);
94
+ const hasOAuth = config.providers.some(
95
+ (provider) => provider.type === "oauth" || provider.type === "oidc",
96
+ );
97
+ const getProvider = (id: string, allowExtraProviders: boolean = false) => {
98
+ return (
99
+ config.providers.find((provider) => provider.id === id) ??
100
+ (allowExtraProviders
101
+ ? config.extraProviders.find((provider) => provider.id === id)
102
+ : undefined)
103
+ );
104
+ };
105
+ const getProviderOrThrow: GetProviderOrThrowFunc = (
106
+ id: string,
107
+ allowExtraProviders: boolean = false,
108
+ ) => {
109
+ const provider = getProvider(id, allowExtraProviders);
110
+ if (provider === undefined) {
111
+ const message =
112
+ `Provider \`${id}\` is not configured, ` +
113
+ `available providers are ${listAvailableProviders(config, allowExtraProviders)}.`;
114
+ logWithLevel(LOG_LEVELS.ERROR, message);
115
+ throw new Error(message);
116
+ }
117
+ return provider;
118
+ };
119
+ const enrichCtx = <DataModel extends GenericDataModel>(
120
+ ctx: GenericActionCtx<DataModel>,
121
+ ) => ({ ...ctx, auth: { ...ctx.auth, config } });
122
+ const requireComponent = () => {
123
+ if (config.component === undefined) {
124
+ throw new Error(
125
+ "Auth component is not configured. Pass `component: components.auth` in Auth config.",
126
+ );
127
+ }
128
+ return config.component;
129
+ };
130
+ type ComponentCtx = Pick<
131
+ GenericActionCtx<GenericDataModel>,
132
+ "runQuery" | "runMutation"
133
+ >;
134
+ type ComponentReadCtx = Pick<GenericActionCtx<GenericDataModel>, "runQuery">;
135
+ type ComponentAuthReadCtx = ComponentReadCtx & { auth: Auth };
136
+
137
+ const auth = {
138
+ user: {
139
+ current: async (ctx: { auth: Auth }) => {
140
+ const identity = await ctx.auth.getUserIdentity();
141
+ if (identity === null) {
142
+ return null;
143
+ }
144
+ const [userId] = identity.subject.split(TOKEN_SUB_CLAIM_DIVIDER);
145
+ return userId as GenericId<"user">;
146
+ },
147
+ require: async (ctx: { auth: Auth }) => {
148
+ const identity = await ctx.auth.getUserIdentity();
149
+ if (identity === null) {
150
+ throw new Error("Not signed in");
151
+ }
152
+ const [userId] = identity.subject.split(TOKEN_SUB_CLAIM_DIVIDER);
153
+ return userId as GenericId<"user">;
154
+ },
155
+ get: async (ctx: ComponentReadCtx, userId: string) => {
156
+ const component = requireComponent();
157
+ return await ctx.runQuery(component.public.userGetById, { userId });
158
+ },
159
+ viewer: async (ctx: ComponentAuthReadCtx) => {
160
+ const userId = await auth.user.current(ctx);
161
+ if (userId === null) {
162
+ return null;
163
+ }
164
+ const component = requireComponent();
165
+ return await ctx.runQuery(component.public.userGetById, { userId });
166
+ },
167
+ },
168
+ organization: {
169
+ create: async (
170
+ ctx: ComponentCtx,
171
+ data: Record<string, unknown>,
172
+ ): Promise<string> => {
173
+ const component = requireComponent();
174
+ return (await ctx.runMutation(component.public.organizationCreate!, {
175
+ data,
176
+ })) as string;
177
+ },
178
+ get: async (ctx: ComponentCtx, organizationId: string) => {
179
+ const component = requireComponent();
180
+ return await ctx.runQuery(component.public.organizationGet!, {
181
+ organizationId,
182
+ });
183
+ },
184
+ list: async (
185
+ ctx: ComponentCtx,
186
+ ownerUserId?: string,
187
+ ) => {
188
+ const component = requireComponent();
189
+ return await ctx.runQuery(component.public.organizationList!, {
190
+ ownerUserId,
191
+ });
192
+ },
193
+ update: async (
194
+ ctx: ComponentCtx,
195
+ organizationId: string,
196
+ data: Record<string, unknown>,
197
+ ) => {
198
+ const component = requireComponent();
199
+ await ctx.runMutation(component.public.organizationUpdate!, {
200
+ organizationId,
201
+ data,
202
+ });
203
+ },
204
+ delete: async (ctx: ComponentCtx, organizationId: string) => {
205
+ const component = requireComponent();
206
+ await ctx.runMutation(component.public.organizationDelete!, {
207
+ organizationId,
208
+ });
209
+ },
210
+ member: {
211
+ add: async (
212
+ ctx: ComponentCtx,
213
+ data: Record<string, unknown>,
214
+ ): Promise<string> => {
215
+ const component = requireComponent();
216
+ return (await ctx.runMutation(component.public.memberAdd!, {
217
+ data,
218
+ })) as string;
219
+ },
220
+ remove: async (ctx: ComponentCtx, memberId: string) => {
221
+ const component = requireComponent();
222
+ await ctx.runMutation(component.public.memberRemove!, { memberId });
223
+ },
224
+ list: async (
225
+ ctx: ComponentCtx,
226
+ args: { organizationId: string; teamId?: string },
227
+ ) => {
228
+ const component = requireComponent();
229
+ return await ctx.runQuery(component.public.memberList!, args);
230
+ },
231
+ role: {
232
+ set: async (ctx: ComponentCtx, memberId: string, role: string) => {
233
+ const component = requireComponent();
234
+ await ctx.runMutation(component.public.memberRoleSet!, {
235
+ memberId,
236
+ role,
237
+ });
238
+ },
239
+ get: async (ctx: ComponentCtx, memberId: string) => {
240
+ const component = requireComponent();
241
+ return await ctx.runQuery(component.public.memberRoleGet!, {
242
+ memberId,
243
+ });
244
+ },
245
+ },
246
+ },
247
+ },
248
+ invite: {
249
+ create: async (
250
+ ctx: ComponentCtx,
251
+ data: Record<string, unknown>,
252
+ ): Promise<string> => {
253
+ const component = requireComponent();
254
+ return (await ctx.runMutation(component.public.inviteCreate!, {
255
+ data,
256
+ })) as string;
257
+ },
258
+ get: async (ctx: ComponentCtx, inviteId: string) => {
259
+ const component = requireComponent();
260
+ return await ctx.runQuery(component.public.inviteGet!, { inviteId });
261
+ },
262
+ list: async (
263
+ ctx: ComponentCtx,
264
+ args: { organizationId?: string; status?: string },
265
+ ) => {
266
+ const component = requireComponent();
267
+ return await ctx.runQuery(component.public.inviteList!, args);
268
+ },
269
+ accept: async (ctx: ComponentCtx, inviteId: string) => {
270
+ const component = requireComponent();
271
+ await ctx.runMutation(component.public.inviteAccept!, { inviteId });
272
+ },
273
+ revoke: async (ctx: ComponentCtx, inviteId: string) => {
274
+ const component = requireComponent();
275
+ await ctx.runMutation(component.public.inviteRevoke!, { inviteId });
276
+ },
277
+ },
278
+ /**
279
+ * Add HTTP actions for JWT verification and OAuth sign-in.
280
+ *
281
+ * ```ts
282
+ * import { httpRouter } from "convex/server";
283
+ * import { auth } from "./auth.js";
284
+ *
285
+ * const http = httpRouter();
286
+ *
287
+ * auth.addHttpRoutes(http);
288
+ *
289
+ * export default http;
290
+ * ```
291
+ *
292
+ * The following routes are handled always:
293
+ *
294
+ * - `/.well-known/openid-configuration`
295
+ * - `/.well-known/jwks.json`
296
+ *
297
+ * The following routes are handled if OAuth is configured:
298
+ *
299
+ * - `/api/auth/signin/*`
300
+ * - `/api/auth/callback/*`
301
+ *
302
+ * @param http your HTTP router
303
+ */
304
+ addHttpRoutes: (http: HttpRouter) => {
305
+ http.route({
306
+ path: "/.well-known/openid-configuration",
307
+ method: "GET",
308
+ handler: httpActionGeneric(async () => {
309
+ return new Response(
310
+ JSON.stringify({
311
+ issuer: requireEnv("CONVEX_SITE_URL"),
312
+ jwks_uri:
313
+ requireEnv("CONVEX_SITE_URL") + "/.well-known/jwks.json",
314
+ authorization_endpoint:
315
+ requireEnv("CONVEX_SITE_URL") + "/oauth/authorize",
316
+ }),
317
+ {
318
+ status: 200,
319
+ headers: {
320
+ "Content-Type": "application/json",
321
+ "Cache-Control":
322
+ "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400",
323
+ },
324
+ },
325
+ );
326
+ }),
327
+ });
328
+
329
+ http.route({
330
+ path: "/.well-known/jwks.json",
331
+ method: "GET",
332
+ handler: httpActionGeneric(async () => {
333
+ return new Response(requireEnv("JWKS"), {
334
+ status: 200,
335
+ headers: {
336
+ "Content-Type": "application/json",
337
+ "Cache-Control":
338
+ "public, max-age=15, stale-while-revalidate=15, stale-if-error=86400",
339
+ },
340
+ });
341
+ }),
342
+ });
343
+
344
+ if (hasOAuth) {
345
+ http.route({
346
+ pathPrefix: "/api/auth/signin/",
347
+ method: "GET",
348
+ handler: httpActionGeneric(
349
+ convertErrorsToResponse(400, async (ctx, request) => {
350
+ const url = new URL(request.url);
351
+ const pathParts = url.pathname.split("/");
352
+ const providerId = pathParts.at(-1)!;
353
+ if (providerId === null) {
354
+ throw new Error("Missing provider id");
355
+ }
356
+ const verifier = url.searchParams.get("code");
357
+ if (verifier === null) {
358
+ throw new Error("Missing sign-in verifier");
359
+ }
360
+ const provider = getProviderOrThrow(
361
+ providerId,
362
+ ) as OAuthConfig<any>;
363
+ const { redirect, cookies, signature } =
364
+ await getAuthorizationUrl({
365
+ provider: await oAuthConfigToInternalProvider(provider),
366
+ cookies: defaultCookiesOptions(providerId),
367
+ });
368
+
369
+ await callVerifierSignature(ctx, {
370
+ verifier,
371
+ signature,
372
+ });
373
+
374
+ const redirectTo = url.searchParams.get("redirectTo");
375
+
376
+ if (redirectTo !== null) {
377
+ cookies.push(redirectToParamCookie(providerId, redirectTo));
378
+ }
379
+
380
+ const headers = new Headers({ Location: redirect });
381
+ for (const { name, value, options } of cookies) {
382
+ headers.append(
383
+ "Set-Cookie",
384
+ serializeCookie(name, value, options),
385
+ );
386
+ }
387
+
388
+ return new Response(null, { status: 302, headers });
389
+ }),
390
+ ),
391
+ });
392
+
393
+ const callbackAction = httpActionGeneric(
394
+ async (genericCtx, request) => {
395
+ const ctx = genericCtx as unknown as ActionCtx;
396
+ const url = new URL(request.url);
397
+ const pathParts = url.pathname.split("/");
398
+ const providerId = pathParts.at(-1)!;
399
+ logWithLevel(
400
+ LOG_LEVELS.DEBUG,
401
+ "Handling OAuth callback for provider:",
402
+ providerId,
403
+ );
404
+ const provider = getProviderOrThrow(
405
+ providerId,
406
+ ) as OAuth2Config<any>;
407
+
408
+ const cookies = getCookies(request);
409
+
410
+ const maybeRedirectTo = useRedirectToParam(provider.id, cookies);
411
+
412
+ const destinationUrl = await redirectAbsoluteUrl(config, {
413
+ redirectTo: maybeRedirectTo?.redirectTo,
414
+ });
415
+
416
+ const params = url.searchParams;
417
+
418
+ // Handle OAuth providers that use formData (such as Apple)
419
+ if (
420
+ request.headers.get("Content-Type") ===
421
+ "application/x-www-form-urlencoded"
422
+ ) {
423
+ const formData = await request.formData();
424
+ for (const [key, value] of formData.entries()) {
425
+ if (typeof value === "string") {
426
+ params.append(key, value);
427
+ }
428
+ }
429
+ }
430
+
431
+ try {
432
+ const { profile, tokens, signature } = await handleOAuth(
433
+ Object.fromEntries(params.entries()),
434
+ cookies,
435
+ {
436
+ provider: await oAuthConfigToInternalProvider(provider),
437
+ cookies: defaultCookiesOptions(provider.id),
438
+ },
439
+ );
440
+
441
+ const { id, ...profileFromCallback } = await provider.profile!(
442
+ profile,
443
+ tokens,
444
+ );
445
+
446
+ if (typeof id !== "string") {
447
+ throw new Error(
448
+ `The profile method of the ${providerId} config must return a string ID`,
449
+ );
450
+ }
451
+
452
+ const verificationCode = await callUserOAuth(ctx, {
453
+ provider: providerId,
454
+ providerAccountId: id,
455
+ profile: profileFromCallback,
456
+ signature,
457
+ });
458
+
459
+ return new Response(null, {
460
+ status: 302,
461
+ headers: {
462
+ Location: setURLSearchParam(
463
+ destinationUrl,
464
+ "code",
465
+ verificationCode,
466
+ ),
467
+ "Cache-Control": "must-revalidate",
468
+ },
469
+ });
470
+ } catch (error) {
471
+ logError(error);
472
+ return Response.redirect(destinationUrl);
473
+ }
474
+ },
475
+ );
476
+
477
+ http.route({
478
+ pathPrefix: "/api/auth/callback/",
479
+ method: "GET",
480
+ handler: callbackAction,
481
+ });
482
+
483
+ http.route({
484
+ pathPrefix: "/api/auth/callback/",
485
+ method: "POST",
486
+ handler: callbackAction,
487
+ });
488
+ }
489
+ },
490
+ };
491
+ return {
492
+ /**
493
+ * Helper for configuring HTTP actions.
494
+ */
495
+ auth,
496
+ /**
497
+ * Action called by the client to sign the user in.
498
+ *
499
+ * Also used for refreshing the session.
500
+ */
501
+ signIn: actionGeneric({
502
+ args: {
503
+ provider: v.optional(v.string()),
504
+ params: v.optional(v.any()),
505
+ verifier: v.optional(v.string()),
506
+ refreshToken: v.optional(v.string()),
507
+ calledBy: v.optional(v.string()),
508
+ },
509
+ handler: async (
510
+ ctx,
511
+ args,
512
+ ): Promise<{
513
+ redirect?: string;
514
+ verifier?: string;
515
+ tokens?: Tokens | null;
516
+ started?: boolean;
517
+ }> => {
518
+ if (args.calledBy !== undefined) {
519
+ logWithLevel("INFO", `\`auth:signIn\` called by ${args.calledBy}`);
520
+ }
521
+ const provider =
522
+ args.provider !== undefined
523
+ ? getProviderOrThrow(args.provider)
524
+ : null;
525
+ const result = await signInImpl(enrichCtx(ctx), provider, args, {
526
+ generateTokens: true,
527
+ allowExtraProviders: false,
528
+ });
529
+ switch (result.kind) {
530
+ case "redirect":
531
+ return { redirect: result.redirect, verifier: result.verifier };
532
+ case "signedIn":
533
+ case "refreshTokens":
534
+ return { tokens: result.signedIn?.tokens ?? null };
535
+ case "started":
536
+ return { started: true };
537
+ default: {
538
+ const _typecheck: never = result;
539
+ throw new Error(`Unexpected result from signIn, ${result as any}`);
540
+ }
541
+ }
542
+ },
543
+ }),
544
+ /**
545
+ * Action called by the client to invalidate the current session.
546
+ */
547
+ signOut: actionGeneric({
548
+ args: {},
549
+ handler: async (ctx) => {
550
+ await callSignOut(ctx);
551
+ },
552
+ }),
553
+
554
+ /**
555
+ * Internal mutation used by the library to read and write
556
+ * to the database during signin and signout.
557
+ */
558
+ store: internalMutationGeneric({
559
+ args: storeArgs,
560
+ handler: async (ctx: MutationCtx, args) => {
561
+ return storeImpl(ctx, args, getProviderOrThrow, config);
562
+ },
563
+ }),
564
+
565
+ };
566
+ }
567
+
568
+ /**
569
+ * Return the currently signed-in user's ID.
570
+ *
571
+ * ```ts filename="convex/myFunctions.tsx"
572
+ * import { mutation } from "./_generated/server";
573
+ * import { getAuthUserId } from "@robelest/convex-auth/component";
574
+ *
575
+ * export const doSomething = mutation({
576
+ * args: {/* ... *\/},
577
+ * handler: async (ctx, args) => {
578
+ * const userId = await getAuthUserId(ctx);
579
+ * if (userId === null) {
580
+ * throw new Error("Client is not authenticated!")
581
+ * }
582
+ * const user = await ctx.db.get(userId);
583
+ * // ...
584
+ * },
585
+ * });
586
+ * ```
587
+ *
588
+ * @param ctx query, mutation or action `ctx`
589
+ * @returns the user ID or `null` if the client isn't authenticated
590
+ */
591
+ export async function getAuthUserId(ctx: { auth: Auth }) {
592
+ const identity = await ctx.auth.getUserIdentity();
593
+ if (identity === null) {
594
+ return null;
595
+ }
596
+ const [userId] = identity.subject.split(TOKEN_SUB_CLAIM_DIVIDER);
597
+ return userId as GenericId<"user">;
598
+ }
599
+
600
+ /**
601
+ * Use this function from a
602
+ * [`ConvexCredentials`](https://labs.convex.dev/auth/api_reference/providers/ConvexCredentials)
603
+ * provider to create an account and a user with a unique account "id" (OAuth
604
+ * provider ID, email address, phone number, username etc.).
605
+ *
606
+ * @returns user ID if it successfully creates the account
607
+ * or throws an error.
608
+ */
609
+ export async function createAccount<
610
+ DataModel extends GenericDataModel = GenericDataModel,
611
+ >(
612
+ ctx: GenericActionCtx<DataModel>,
613
+ args: {
614
+ /**
615
+ * The provider ID (like "password"), used to disambiguate accounts.
616
+ *
617
+ * It is also used to configure account secret hashing via the provider's
618
+ * `crypto` option.
619
+ */
620
+ provider: string;
621
+ account: {
622
+ /**
623
+ * The unique external ID for the account, for example email address.
624
+ */
625
+ id: string;
626
+ /**
627
+ * The secret credential to store for this account, if given.
628
+ */
629
+ secret?: string;
630
+ };
631
+ /**
632
+ * The profile data to store for the user.
633
+ * These must fit the `users` table schema.
634
+ */
635
+ profile: WithoutSystemFields<DocumentByName<DataModel, "user">>;
636
+ /**
637
+ * If `true`, the account will be linked to an existing user
638
+ * with the same verified email address.
639
+ * This is only safe if the returned account's email is verified
640
+ * before the user is allowed to sign in with it.
641
+ */
642
+ shouldLinkViaEmail?: boolean;
643
+ /**
644
+ * If `true`, the account will be linked to an existing user
645
+ * with the same verified phone number.
646
+ * This is only safe if the returned account's phone is verified
647
+ * before the user is allowed to sign in with it.
648
+ */
649
+ shouldLinkViaPhone?: boolean;
650
+ },
651
+ ): Promise<{
652
+ account: GenericDoc<DataModel, "account">;
653
+ user: GenericDoc<DataModel, "user">;
654
+ }> {
655
+ const actionCtx = ctx as unknown as ActionCtx;
656
+ return (await callCreateAccountFromCredentials(
657
+ actionCtx,
658
+ args as any,
659
+ )) as any;
660
+ }
661
+
662
+ /**
663
+ * Use this function from a
664
+ * [`ConvexCredentials`](https://labs.convex.dev/auth/api_reference/providers/ConvexCredentials)
665
+ * provider to retrieve a user given the account provider ID and
666
+ * the provider-specific account ID.
667
+ *
668
+ * @returns the retrieved user document, or `null` if there is no account
669
+ * for given account ID or throws if the provided
670
+ * secret does not match.
671
+ */
672
+ export async function retrieveAccount<
673
+ DataModel extends GenericDataModel = GenericDataModel,
674
+ >(
675
+ ctx: GenericActionCtx<DataModel>,
676
+ args: {
677
+ /**
678
+ * The provider ID (like "password"), used to disambiguate accounts.
679
+ *
680
+ * It is also used to configure account secret hashing via the provider's
681
+ * `crypto` option.
682
+ */
683
+ provider: string;
684
+ account: {
685
+ /**
686
+ * The unique external ID for the account, for example email address.
687
+ */
688
+ id: string;
689
+ /**
690
+ * The secret that should match the stored credential, if given.
691
+ */
692
+ secret?: string;
693
+ };
694
+ },
695
+ ): Promise<{
696
+ account: GenericDoc<DataModel, "account">;
697
+ user: GenericDoc<DataModel, "user">;
698
+ }> {
699
+ const actionCtx = ctx as unknown as ActionCtx;
700
+ const result = await callRetreiveAccountWithCredentials(actionCtx, args);
701
+ if (typeof result === "string") {
702
+ throw new Error(result);
703
+ }
704
+ return result as any;
705
+ }
706
+
707
+ /**
708
+ * Use this function to modify the account credentials
709
+ * from a [`ConvexCredentials`](https://labs.convex.dev/auth/api_reference/providers/ConvexCredentials)
710
+ * provider.
711
+ */
712
+ export async function modifyAccountCredentials<
713
+ DataModel extends GenericDataModel = GenericDataModel,
714
+ >(
715
+ ctx: GenericActionCtx<DataModel>,
716
+ args: {
717
+ /**
718
+ * The provider ID (like "password"), used to disambiguate accounts.
719
+ *
720
+ * It is also used to configure account secret hashing via the `crypto` option.
721
+ */
722
+ provider: string;
723
+ account: {
724
+ /**
725
+ * The unique external ID for the account, for example email address.
726
+ */
727
+ id: string;
728
+ /**
729
+ * The new secret credential to store for this account.
730
+ */
731
+ secret: string;
732
+ };
733
+ },
734
+ ): Promise<void> {
735
+ const actionCtx = ctx as unknown as ActionCtx;
736
+ return await callModifyAccount(actionCtx, args);
737
+ }
738
+
739
+ /**
740
+ * Use this function to invalidate existing sessions.
741
+ */
742
+ export async function invalidateSessions<
743
+ DataModel extends GenericDataModel = GenericDataModel,
744
+ >(
745
+ ctx: GenericActionCtx<DataModel>,
746
+ args: {
747
+ userId: GenericId<"user">;
748
+ except?: GenericId<"session">[];
749
+ },
750
+ ): Promise<void> {
751
+ const actionCtx = ctx as unknown as ActionCtx;
752
+ return await callInvalidateSessions(actionCtx, args);
753
+ }
754
+
755
+ /**
756
+ * Use this function from a
757
+ * [`ConvexCredentials`](https://labs.convex.dev/auth/api_reference/providers/ConvexCredentials)
758
+ * provider to sign in the user via another provider (usually
759
+ * for email verification on sign up or password reset).
760
+ *
761
+ * Returns the user ID if the sign can proceed,
762
+ * or `null`.
763
+ */
764
+ export async function signInViaProvider<
765
+ DataModel extends GenericDataModel = GenericDataModel,
766
+ >(
767
+ ctx: GenericActionCtxWithAuthConfig<DataModel>,
768
+ provider: AuthProviderConfig,
769
+ args: {
770
+ accountId?: GenericId<"account">;
771
+ params?: Record<string, Value | undefined>;
772
+ },
773
+ ) {
774
+ const result = await signInImpl(
775
+ ctx,
776
+ materializeProvider(provider),
777
+ args as any,
778
+ {
779
+ generateTokens: false,
780
+ allowExtraProviders: true,
781
+ },
782
+ );
783
+ return result.kind === "signedIn"
784
+ ? result.signedIn !== null
785
+ ? { userId: result.signedIn.userId, sessionId: result.signedIn.sessionId }
786
+ : null
787
+ : null;
788
+ }
789
+
790
+ function convertErrorsToResponse(
791
+ errorStatusCode: number,
792
+ action: (ctx: GenericActionCtx<any>, request: Request) => Promise<Response>,
793
+ ) {
794
+ return async (ctx: GenericActionCtx<any>, request: Request) => {
795
+ try {
796
+ return await action(ctx, request);
797
+ } catch (error) {
798
+ if (error instanceof ConvexError) {
799
+ return new Response(null, {
800
+ status: errorStatusCode,
801
+ statusText: error.data,
802
+ });
803
+ } else {
804
+ logError(error);
805
+ return new Response(null, {
806
+ status: 500,
807
+ statusText: "Internal Server Error",
808
+ });
809
+ }
810
+ }
811
+ };
812
+ }
813
+ function getCookies(request: Request): Record<string, string | undefined> {
814
+ return parseCookies(request.headers.get("Cookie") ?? "");
815
+ }