@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,243 @@
1
+ // This maps to packages/core/src/lib/actions/callback/oauth/callback.ts in the @auth/core package (commit 5af1f30a32e64591abc50ae4d2dba4682e525431)
2
+
3
+ import * as checks from "./checks.js";
4
+ import * as o from "oauth4webapi";
5
+ import { InternalOptions } from "./types.js";
6
+ import { fetchOpt } from "./lib/utils/customFetch.js";
7
+ import { Cookie } from "@auth/core/lib/utils/cookie.js";
8
+ import { logWithLevel } from "../implementation/utils.js";
9
+ import { Account, Profile, TokenSet } from "@auth/core/types.js";
10
+ import { isOIDCProvider } from "./lib/utils/providers.js";
11
+ import {
12
+ callbackUrl,
13
+ getAuthorizationSignature,
14
+ } from "./convexAuth.js";
15
+
16
+ function formUrlEncode(token: string) {
17
+ return encodeURIComponent(token).replace(/%20/g, "+");
18
+ }
19
+
20
+ /**
21
+ * Formats client_id and client_secret as an HTTP Basic Authentication header as per the OAuth 2.0
22
+ * specified in RFC6749.
23
+ */
24
+ function clientSecretBasic(clientId: string, clientSecret: string) {
25
+ const username = formUrlEncode(clientId);
26
+ const password = formUrlEncode(clientSecret);
27
+ const credentials = btoa(`${username}:${password}`);
28
+ return `Basic ${credentials}`;
29
+ }
30
+
31
+ /**
32
+ * Handles the following OAuth steps.
33
+ * https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1
34
+ * https://www.rfc-editor.org/rfc/rfc6749#section-4.1.3
35
+ * https://openid.net/specs/openid-connect-core-1_0.html#UserInfoRequest
36
+ *
37
+ * @note Although requesting userinfo is not required by the OAuth2.0 spec,
38
+ * we fetch it anyway. This is because we always want a user profile.
39
+ */
40
+ export async function handleOAuth(
41
+ // ConvexAuth: `params` is a Record<string, string> instead of RequestInternal["query"]
42
+ params: Record<string, string>,
43
+ // ConvexAuth: `cookies` is a Record<string, string | undefined> instead of RequestInternal["cookies"]
44
+ cookies: Record<string, string | undefined>,
45
+ options: InternalOptions<"oauth" | "oidc">,
46
+ ): Promise<{
47
+ profile: Profile,
48
+ tokens: TokenSet & Pick<Account, "expires_at">,
49
+ cookies: Cookie[],
50
+ signature: string,
51
+ }> {
52
+ const { provider } = options;
53
+
54
+ // ConvexAuth: The `token` property is not used here
55
+ const { userinfo, as } = provider;
56
+
57
+ const client: o.Client = {
58
+ client_id: provider.clientId,
59
+ ...provider.client,
60
+ };
61
+
62
+ let clientAuth: o.ClientAuth;
63
+
64
+ switch (client.token_endpoint_auth_method) {
65
+ // TODO: in the next breaking major version have undefined be `client_secret_post`
66
+ case undefined:
67
+ case "client_secret_basic":
68
+ // TODO: in the next breaking major version use o.ClientSecretBasic() here
69
+ clientAuth = (_as, _client, _body, headers) => {
70
+ headers.set(
71
+ "authorization",
72
+ clientSecretBasic(provider.clientId, provider.clientSecret!),
73
+ );
74
+ };
75
+ break;
76
+ case "client_secret_post":
77
+ clientAuth = o.ClientSecretPost(provider.clientSecret!);
78
+ break;
79
+ case "client_secret_jwt":
80
+ clientAuth = o.ClientSecretJwt(provider.clientSecret!);
81
+ break;
82
+ case "private_key_jwt":
83
+ clientAuth = o.PrivateKeyJwt(provider.token!.clientPrivateKey!, {
84
+ // TODO: review in the next breaking change
85
+ [o.modifyAssertion](_header, payload) {
86
+ payload.aud = [as.issuer, as.token_endpoint!];
87
+ },
88
+ });
89
+ break;
90
+ default:
91
+ throw new Error("unsupported client authentication method");
92
+ }
93
+
94
+ const resCookies: Cookie[] = [];
95
+
96
+ const state = await checks.state.use(cookies, resCookies, options);
97
+
98
+ let codeGrantParams: URLSearchParams;
99
+ try {
100
+ codeGrantParams = o.validateAuthResponse(
101
+ as,
102
+ client,
103
+ new URLSearchParams(params),
104
+ provider.checks.includes("state") ? state : o.skipStateCheck,
105
+ );
106
+ } catch (err) {
107
+ if (err instanceof o.AuthorizationResponseError) {
108
+ const cause = {
109
+ providerId: provider.id,
110
+ ...Object.fromEntries(err.cause.entries()),
111
+ };
112
+ logWithLevel("DEBUG", "OAuthCallbackError", cause);
113
+ throw new Error("OAuth Provider returned an error", { cause });
114
+ }
115
+ throw err;
116
+ }
117
+
118
+ const codeVerifier = await checks.pkce.use(cookies, resCookies, options);
119
+
120
+ // ConvexAuth: The logic for the callback URL is different from Auth.js
121
+ const redirect_uri = callbackUrl(provider.id);
122
+ // TODO(ConvexAuth): Support redirect proxy URLs
123
+ // if (!options.isOnRedirectProxy && provider.redirectProxyUrl) {
124
+ // redirect_uri = provider.redirectProxyUrl;
125
+ // }
126
+
127
+ let codeGrantResponse = await o.authorizationCodeGrantRequest(
128
+ as,
129
+ client,
130
+ clientAuth,
131
+ codeGrantParams,
132
+ redirect_uri,
133
+ codeVerifier ?? "decoy",
134
+ {
135
+ // TODO: move away from allowing insecure HTTP requests
136
+ [o.allowInsecureRequests]: true,
137
+ [o.customFetch]: (...args) => {
138
+ if (!provider.checks.includes("pkce")) {
139
+ args[1].body.delete("code_verifier");
140
+ }
141
+ return fetchOpt(provider)[o.customFetch](...args);
142
+ },
143
+ },
144
+ );
145
+
146
+ if (provider.token?.conform) {
147
+ codeGrantResponse =
148
+ (await provider.token.conform(codeGrantResponse.clone())) ??
149
+ codeGrantResponse;
150
+ }
151
+
152
+ let profile: Profile = {};
153
+
154
+ // ConvexAuth: We use the value of the nonce later, aside from feeding it into the
155
+ // `processAuthorizationCodeResponse` function.
156
+ const nonce = await checks.nonce.use(cookies, resCookies, options);
157
+
158
+ const isOidc = isOIDCProvider(provider);
159
+ const processedCodeResponse = await o.processAuthorizationCodeResponse(
160
+ as,
161
+ client,
162
+ codeGrantResponse,
163
+ {
164
+ expectedNonce: nonce,
165
+ requireIdToken: isOidc,
166
+ },
167
+ );
168
+
169
+ const tokens: TokenSet & Pick<Account, "expires_at"> = processedCodeResponse;
170
+
171
+ if (isOidc) {
172
+ // ConvexAuth: the next few lines are changed slightly to make TypeScript happy
173
+ const idTokenClaimsOrUndefined = o.getValidatedIdTokenClaims(
174
+ processedCodeResponse,
175
+ );
176
+ if (idTokenClaimsOrUndefined === undefined) {
177
+ throw new Error("ID Token claims are missing");
178
+ }
179
+ const idTokenClaims = idTokenClaimsOrUndefined;
180
+ profile = idTokenClaims;
181
+
182
+ // Apple sends some of the user information in a `user` parameter as a stringified JSON.
183
+ // It also only does so the first time the user consents to share their information.
184
+ if (provider.id === "apple") {
185
+ try {
186
+ profile.user = JSON.parse(params?.user)
187
+ // ConvexAuth: disabled lint for empty block
188
+ // eslint-disable-next-line no-empty
189
+ } catch {}
190
+ }
191
+
192
+ if (provider.idToken === false) {
193
+ const userinfoResponse = await o.userInfoRequest(
194
+ as,
195
+ client,
196
+ processedCodeResponse.access_token,
197
+ {
198
+ ...fetchOpt(provider),
199
+ // TODO: move away from allowing insecure HTTP requests
200
+ [o.allowInsecureRequests]: true,
201
+ },
202
+ );
203
+
204
+ profile = await o.processUserInfoResponse(
205
+ as,
206
+ client,
207
+ idTokenClaims.sub,
208
+ userinfoResponse,
209
+ );
210
+ }
211
+ } else {
212
+ if (userinfo?.request) {
213
+ const _profile = await userinfo.request({ tokens, provider });
214
+ if (_profile instanceof Object) profile = _profile;
215
+ } else if (userinfo?.url) {
216
+ const userinfoResponse = await o.userInfoRequest(
217
+ as,
218
+ client,
219
+ processedCodeResponse.access_token,
220
+ fetchOpt(provider),
221
+ );
222
+ profile = await userinfoResponse.json();
223
+ } else {
224
+ throw new TypeError("No userinfo endpoint configured");
225
+ }
226
+ }
227
+
228
+ if (tokens.expires_in) {
229
+ tokens.expires_at =
230
+ Math.floor(Date.now() / 1000) + Number(tokens.expires_in);
231
+ }
232
+
233
+ // ConvexAuth: The Auth.js code would handle user + account creation here, but for
234
+ // ConvexAuth we want to handle that in a Convex function. Instead, we return the
235
+ // information needed for the mutation.
236
+
237
+ return {
238
+ profile,
239
+ tokens,
240
+ cookies: resCookies,
241
+ signature: getAuthorizationSignature({ codeVerifier, state, nonce }),
242
+ };
243
+ }
@@ -0,0 +1,136 @@
1
+ // This maps to packages/core/src/lib/actions/callback/oauth/checks.ts in the @auth/core package (commit 5af1f30a32e64591abc50ae4d2dba4682e525431)
2
+
3
+ import * as o from "oauth4webapi";
4
+
5
+ import type { InternalOptions } from "./types.js";
6
+ import { Cookie } from "@auth/core/lib/utils/cookie.js";
7
+ import { CookiesOptions } from "@auth/core/types.js";
8
+ import { logWithLevel } from "../implementation/utils.js";
9
+
10
+ const COOKIE_TTL = 60 * 15; // 15 minutes
11
+
12
+ /** Returns a cookie with the given payload and options. */
13
+ // ConvexAuth: Auth.js calls this `sealCookie` and encrypts the payload wrapped in a JWT.
14
+ async function createCookie(
15
+ name: keyof CookiesOptions,
16
+ payload: string,
17
+ options: InternalOptions<"oauth" | "oidc">,
18
+ ): Promise<Cookie> {
19
+ const { cookies } = options;
20
+ const cookie = cookies[name];
21
+ const expires = new Date();
22
+ expires.setTime(expires.getTime() + COOKIE_TTL * 1000);
23
+
24
+ logWithLevel("DEBUG", `CREATE_${name.toUpperCase()}`, {
25
+ name: cookie.name,
26
+ payload,
27
+ COOKIE_TTL,
28
+ expires,
29
+ });
30
+
31
+ const cookieOptions = { ...cookie.options, expires };
32
+
33
+ return { name: cookie.name, value: payload, options: cookieOptions };
34
+ }
35
+
36
+ function clearCookie(
37
+ name: keyof CookiesOptions,
38
+ options: InternalOptions<"oauth" | "oidc">,
39
+ resCookies: Cookie[],
40
+ ) {
41
+ const { cookies } = options;
42
+ const cookie = cookies[name];
43
+ logWithLevel("DEBUG", `CLEAR_${name.toUpperCase()}`, { cookie });
44
+ resCookies.push({
45
+ name: cookie.name,
46
+ value: "",
47
+ options: { ...cookies[name].options, maxAge: 0 },
48
+ });
49
+ }
50
+
51
+ function useCookie(
52
+ check: "state" | "pkce" | "nonce",
53
+ name: keyof CookiesOptions,
54
+ ) {
55
+ return async function (
56
+ // ConvexAuth: `cookies` is a Record<string, string | undefined> instead of RequestInternal["cookies"]
57
+ cookies: Record<string, string | undefined>,
58
+ resCookies: Cookie[],
59
+ options: InternalOptions<"oidc">,
60
+ ) {
61
+ const { provider } = options;
62
+ if (!provider?.checks?.includes(check)) return;
63
+ const cookieValue = cookies?.[options.cookies[name].name];
64
+ logWithLevel("DEBUG", `USE_${name.toUpperCase()}`, { value: cookieValue });
65
+ clearCookie(name, options, resCookies);
66
+ return cookieValue;
67
+ };
68
+ }
69
+
70
+ /**
71
+ * @see https://www.rfc-editor.org/rfc/rfc7636
72
+ * @see https://danielfett.de/2020/05/16/pkce-vs-nonce-equivalent-or-not/#pkce
73
+ */
74
+ export const pkce = {
75
+ /** Creates a PKCE code challenge and verifier pair. The verifier is stored in the cookie. */
76
+ async create(options: InternalOptions<"oauth">) {
77
+ const codeVerifier = o.generateRandomCodeVerifier();
78
+ const codeChallenge = await o.calculatePKCECodeChallenge(codeVerifier);
79
+ const cookie = await createCookie("pkceCodeVerifier", codeVerifier, options);
80
+ return { cookie, codeChallenge: codeChallenge, codeVerifier };
81
+ },
82
+ /**
83
+ * Returns code_verifier if the provider is configured to use PKCE,
84
+ * and clears the container cookie afterwards.
85
+ * An error is thrown if the code_verifier is missing or invalid.
86
+ */
87
+ use: useCookie("pkce", "pkceCodeVerifier"),
88
+ };
89
+
90
+ /**
91
+ * @see https://www.rfc-editor.org/rfc/rfc6749#section-10.12
92
+ * @see https://www.rfc-editor.org/rfc/rfc6749#section-4.1.1
93
+ */
94
+ export const state = {
95
+ /** Creates a state cookie with an optionally encoded body. */
96
+ async create(options: InternalOptions<"oauth">, origin?: string) {
97
+ const { provider } = options;
98
+ if (!provider.checks.includes("state")) {
99
+ if (origin) {
100
+ throw new Error(
101
+ "State data was provided but the provider is not configured to use state",
102
+ );
103
+ }
104
+ return;
105
+ }
106
+
107
+ const payload = o.generateRandomState();
108
+ const cookie = await createCookie("state", payload, options);
109
+ return { cookie, value: payload };
110
+ },
111
+ /**
112
+ * Returns state if the provider is configured to use state,
113
+ * and clears the container cookie afterwards.
114
+ * An error is thrown if the state is missing or invalid.
115
+ */
116
+ use: useCookie("state", "state"),
117
+ };
118
+
119
+ export const nonce = {
120
+ async create(options: InternalOptions<"oidc">) {
121
+ if (!options.provider.checks.includes("nonce")) return;
122
+ const value = o.generateRandomNonce();
123
+ const cookie = await createCookie("nonce", value, options);
124
+ return { cookie, value };
125
+ },
126
+ /**
127
+ * Returns nonce if the provider is configured to use nonce,
128
+ * and clears the container cookie afterwards.
129
+ * An error is thrown if the nonce is missing or invalid.
130
+ * @see https://openid.net/specs/openid-connect-core-1_0.html#NonceNotes
131
+ * @see https://danielfett.de/2020/05/16/pkce-vs-nonce-equivalent-or-not/#nonce
132
+ */
133
+ use: useCookie("nonce", "nonce"),
134
+ };
135
+
136
+ // ConvexAuth: All WebAuthn checks are omitted.
@@ -0,0 +1,168 @@
1
+ import { CookieOption, CookiesOptions } from "@auth/core/types.js";
2
+ import { requireEnv } from "../utils.js";
3
+ import { InternalProvider } from "./types.js";
4
+ import { SHARED_COOKIE_OPTIONS } from "../cookies.js";
5
+ import { fetchOpt } from "./lib/utils/customFetch.js";
6
+ import * as o from "oauth4webapi";
7
+ import { normalizeEndpoint } from "../provider_utils.js";
8
+ import { isLocalHost } from "../utils.js";
9
+ import { OAuthConfig } from "@auth/core/providers/oauth.js";
10
+
11
+ // ConvexAuth: The logic for the callback URL is different from Auth.js
12
+ export function callbackUrl(providerId: string) {
13
+ return (process.env.CUSTOM_AUTH_SITE_URL ?? requireEnv("CONVEX_SITE_URL")) + "/api/auth/callback/" + providerId;
14
+ }
15
+
16
+ // ConvexAuth: This is a ConvexAuth specific function that produces a string that the
17
+ // Convex functions will validate
18
+ export function getAuthorizationSignature({
19
+ codeVerifier,
20
+ state,
21
+ nonce,
22
+ }: {
23
+ codeVerifier?: string;
24
+ state?: string;
25
+ nonce?: string;
26
+ }) {
27
+ return [codeVerifier, state, nonce]
28
+ .filter((param) => param !== undefined)
29
+ .join(" ");
30
+ }
31
+
32
+ function oauthStateCookieName(
33
+ type: "state" | "pkce" | "nonce",
34
+ providerId: string,
35
+ ) {
36
+ return (!isLocalHost(process.env.CONVEX_SITE_URL) ? "__Host-" : "") + providerId + "OAuth" + type;
37
+ }
38
+
39
+ export const defaultCookiesOptions: (
40
+ providerId: string,
41
+ ) => Record<keyof CookiesOptions, CookieOption> = (providerId) => {
42
+ return {
43
+ pkceCodeVerifier: {
44
+ name: oauthStateCookieName("pkce", providerId),
45
+ options: {
46
+ ...SHARED_COOKIE_OPTIONS,
47
+ },
48
+ },
49
+ state: {
50
+ name: oauthStateCookieName("state", providerId),
51
+ options: {
52
+ ...SHARED_COOKIE_OPTIONS,
53
+ },
54
+ },
55
+ nonce: {
56
+ name: oauthStateCookieName("nonce", providerId),
57
+ options: {
58
+ ...SHARED_COOKIE_OPTIONS,
59
+ },
60
+ },
61
+ // ConvexAuth: We don't support webauthn, so this value doesn't actually matter
62
+ webauthnChallenge: {
63
+ name: "ConvexAuth_shouldNotBeUsed_webauthnChallenge",
64
+ options: {
65
+ ...SHARED_COOKIE_OPTIONS,
66
+ },
67
+ },
68
+ // ConvexAuth: We don't use these cookies, so their values should never be used
69
+ sessionToken: {
70
+ name: "ConvexAuth_shouldNotBeUsed_sessionToken",
71
+ options: {
72
+ ...SHARED_COOKIE_OPTIONS,
73
+ },
74
+ },
75
+ callbackUrl: {
76
+ name: "ConvexAuth_shouldNotBeUsed_callbackUrl",
77
+ options: {
78
+ ...SHARED_COOKIE_OPTIONS,
79
+ },
80
+ },
81
+ csrfToken: {
82
+ name: "ConvexAuth_shouldNotBeUsed_csrfToken",
83
+ options: {
84
+ ...SHARED_COOKIE_OPTIONS,
85
+ },
86
+ },
87
+ };
88
+ };
89
+
90
+ export async function oAuthConfigToInternalProvider(config: OAuthConfig<any>): Promise<InternalProvider<"oauth" | "oidc">> {
91
+ // Only do service discovery if the provider does not have the required configuration
92
+ if (!config.authorization || !config.token || !config.userinfo) {
93
+ // Taken from https://github.com/nextauthjs/next-auth/blob/a7491dcb9355ff2d01fb8e9236636605e2090145/packages/core/src/lib/actions/callback/oauth/callback.ts#L63
94
+ if (!config.issuer) {
95
+ throw new Error(
96
+ `Provider \`${config.id}\` is missing an \`issuer\` URL configuration. Consult the provider docs.`,
97
+ );
98
+ }
99
+
100
+ const issuer = new URL(config.issuer);
101
+ // TODO: move away from allowing insecure HTTP requests
102
+ const discoveryResponse = await o.discoveryRequest(issuer, {
103
+ ...fetchOpt(config),
104
+ [o.allowInsecureRequests]: true,
105
+ });
106
+ const discoveredAs = await o.processDiscoveryResponse(
107
+ issuer,
108
+ discoveryResponse,
109
+ );
110
+
111
+ if (!discoveredAs.token_endpoint)
112
+ throw new TypeError(
113
+ "TODO: Authorization server did not provide a token endpoint.",
114
+ );
115
+
116
+ const as: o.AuthorizationServer = discoveredAs;
117
+ return {
118
+ ...config,
119
+ checks: config.checks!,
120
+ profile: config.profile!,
121
+ account: config.account!,
122
+ clientId: config.clientId!,
123
+ idToken: config.type === "oidc" ? config.idToken : undefined,
124
+ // ConvexAuth: Apparently it's important for us to normalize the endpoint after
125
+ // service discovery (https://github.com/get-convex/convex-auth/commit/35bf716bfb0d29dbce1cbca318973b0732f75015)
126
+ authorization: normalizeEndpoint({
127
+ ...config.authorization,
128
+ url: as.authorization_endpoint,
129
+ }),
130
+ token: normalizeEndpoint({
131
+ ...config.token,
132
+ url: as.token_endpoint,
133
+ }),
134
+ userinfo: as.userinfo_endpoint
135
+ ? normalizeEndpoint({
136
+ ...config.userinfo,
137
+ url: as.userinfo_endpoint,
138
+ })
139
+ : config.userinfo,
140
+ as,
141
+ configSource: "discovered"
142
+ };
143
+ }
144
+
145
+ const authorization = normalizeEndpoint(config.authorization);
146
+ const token = normalizeEndpoint(config.token);
147
+ const userinfo = config.userinfo
148
+ ? normalizeEndpoint(config.userinfo)
149
+ : undefined;
150
+ return {
151
+ ...config,
152
+ checks: config.checks!,
153
+ profile: config.profile!,
154
+ account: config.account!,
155
+ clientId: config.clientId!,
156
+ idToken: config.type === "oidc" ? config.idToken : undefined,
157
+ authorization,
158
+ token,
159
+ userinfo,
160
+ as: {
161
+ issuer: config.issuer ?? "theremustbeastringhere.dev",
162
+ authorization_endpoint: authorization?.url.toString(),
163
+ token_endpoint: token?.url.toString(),
164
+ userinfo_endpoint: userinfo?.url.toString(),
165
+ },
166
+ configSource: "provided",
167
+ };
168
+ }
@@ -0,0 +1,18 @@
1
+ // This file is adapted from packages/core/src/lib/utils/custom-fetch.ts in the @auth/core package (commit 5af1f30a32e64591abc50ae4d2dba4682e525431).
2
+
3
+ import * as o from "oauth4webapi";
4
+ import type { InternalProvider } from "../../types";
5
+ import { customFetch } from "@auth/core";
6
+ import { OAuthConfig } from "@auth/core/providers/index.js";
7
+ // ConvexAuth:re-export the symbol from @auth/core
8
+ export { customFetch } from "@auth/core";
9
+
10
+ type FetchOptResult = {
11
+ [o.customFetch]: typeof fetch;
12
+ };
13
+
14
+ // ConvexAuth: Expose this internal function so we can use it.
15
+ // ConvexAuth: Make a version that works on InternalProvider and OAuthConfig
16
+ export function fetchOpt(providerOrConfig: InternalProvider<"oauth" | "oidc"> | OAuthConfig<any>): FetchOptResult {
17
+ return { [o.customFetch]: providerOrConfig[customFetch] ?? fetch };
18
+ }
@@ -0,0 +1,12 @@
1
+ // This file maps to packages/core/src/lib/utils/providers.ts in the @auth/core package (commit 5af1f30a32e64591abc50ae4d2dba4682e525431).
2
+
3
+ import { InternalProvider } from "../../types.js";
4
+
5
+ export function isOIDCProvider(
6
+ provider: InternalProvider<"oidc" | "oauth">,
7
+ ): provider is InternalProvider<"oidc"> {
8
+ return provider.type === "oidc";
9
+ }
10
+
11
+ // ConvexAuth: There are several more functions in the original file which we don't need,
12
+ // and are omitted here.
@@ -0,0 +1,56 @@
1
+ // This maps to packages/core/src/providers/oauth.ts in the @auth/core package (commit 5af1f30a32e64591abc50ae4d2dba4682e525431)
2
+
3
+ // ConvexAuth: only a few types were brought in.
4
+
5
+ import {
6
+ OAuth2Config,
7
+ OAuthConfig,
8
+ OAuthEndpointType,
9
+ OIDCConfig,
10
+ TokenEndpointHandler,
11
+ UserinfoEndpointHandler,
12
+ } from "@auth/core/providers/index.js";
13
+ import { PrivateKey } from "oauth4webapi";
14
+
15
+ /**
16
+ * We parsed `authorization`, `token` and `userinfo`
17
+ * to always contain a valid `URL`, with the params
18
+ * @internal
19
+ */
20
+ export type OAuthConfigInternal<Profile> = Omit<
21
+ OAuthConfig<Profile>,
22
+ OAuthEndpointType | "redirectProxyUrl"
23
+ > & {
24
+ authorization?: { url: URL };
25
+ token?: {
26
+ url: URL;
27
+ request?: TokenEndpointHandler["request"];
28
+ clientPrivateKey?: CryptoKey | PrivateKey;
29
+ /**
30
+ * @internal
31
+ * @deprecated
32
+ */
33
+ conform?: TokenEndpointHandler["conform"];
34
+ };
35
+ userinfo?: { url: URL; request?: UserinfoEndpointHandler["request"] };
36
+ /**
37
+ * Reconstructed from {@link OAuth2Config.redirectProxyUrl},
38
+ * adding the callback action and provider id onto the URL.
39
+ *
40
+ * If defined, it is favoured over {@link OAuthConfigInternal.callbackUrl} in the authorization request.
41
+ *
42
+ * When {@link InternalOptions.isOnRedirectProxy} is set, the actual value is saved in the decoded `state.origin` parameter.
43
+ *
44
+ * @example `"https://auth.example.com/api/auth/callback/:provider"`
45
+ *
46
+ */
47
+ redirectProxyUrl?: OAuth2Config<Profile>["redirectProxyUrl"];
48
+ } & Pick<
49
+ Required<OAuthConfig<Profile>>,
50
+ "clientId" | "checks" | "profile" | "account"
51
+ >;
52
+
53
+ export type OIDCConfigInternal<Profile> = OAuthConfigInternal<Profile> & {
54
+ checks: OIDCConfig<Profile>["checks"];
55
+ idToken: OIDCConfig<Profile>["idToken"];
56
+ };
@@ -0,0 +1,60 @@
1
+ // This maps to packages/core/src/types.ts in the @auth/core package (commit 5af1f30a32e64591abc50ae4d2dba4682e525431).
2
+
3
+ import * as AuthCoreJwt from "@auth/core/jwt";
4
+ import { CookieOption, CookiesOptions } from "@auth/core/types.js";
5
+ import { OAuthConfigInternal, OIDCConfigInternal } from "./providers/oauth.js";
6
+ import { ProviderType } from "@auth/core/providers/index.js";
7
+ import * as o from "oauth4webapi";
8
+
9
+ export type ConvexAuthProviderType = "oauth" | "oidc";
10
+
11
+ export type ConfigSource = "discovered" | "provided";
12
+
13
+ // ConvexAuth: Auth.js has a more complex type for this, ours is stripped down.
14
+ export type InternalProvider<T = ProviderType> = (T extends "oauth"
15
+ ? OAuthConfigInternal<any>
16
+ : T extends "oidc"
17
+ ? OIDCConfigInternal<any>
18
+ : never) & {as: o.AuthorizationServer, configSource: ConfigSource}
19
+
20
+
21
+ // ConvexAuth: `secret` is internal to @auth/core, so we copy its type here
22
+ export type JWTOptions = AuthCoreJwt.JWTOptions & { secret: string | string[] };
23
+
24
+ /** @internal */
25
+ export interface InternalOptions<TProviderType extends ConvexAuthProviderType> {
26
+ // providers: InternalProvider<TProviderType>[];
27
+ // url: URL;
28
+ // ConvexAuth: omit this option for now
29
+ // action: AuthAction;
30
+ provider: InternalProvider<TProviderType>;
31
+ // csrfToken?: string;
32
+ /**
33
+ * `true` if the [Double-submit CSRF check](https://owasp.org/www-chapter-london/assets/slides/David_Johansson-Double_Defeat_of_Double-Submit_Cookie.pdf) was succesful
34
+ * or [`skipCSRFCheck`](https://authjs.dev/reference/core#skipcsrfcheck) was enabled.
35
+ */
36
+ // csrfTokenVerified?: boolean;
37
+ // secret: string | string[];
38
+ // ConvexAuth: omit the following options for now
39
+ // theme: Theme;
40
+ // debug: boolean;
41
+ // logger: LoggerInstance;
42
+ // session: NonNullable<Required<AuthConfig["session"]>>;
43
+ // pages: Partial<PagesOptions>;
44
+ // jwt: JWTOptions;
45
+ // events: NonNullable<AuthConfig["events"]>;
46
+ // adapter: Required<Adapter> | undefined;
47
+ // callbacks: NonNullable<Required<AuthConfig["callbacks"]>>;
48
+ cookies: Record<keyof CookiesOptions, CookieOption>;
49
+ // callbackUrl: string;
50
+ /**
51
+ * If true, the OAuth callback is being proxied by the server to the original URL.
52
+ * See also {@link OAuthConfigInternal.redirectProxyUrl}.
53
+ */
54
+ // isOnRedirectProxy: boolean;
55
+ // experimental: NonNullable<AuthConfig["experimental"]>;
56
+ // basePath: string;
57
+ }
58
+
59
+ // ConvexAuth: There are several more types in the original file which we don't need,
60
+ // and are omitted here.