@better-auth/core 1.7.0-beta.3 → 1.7.0-beta.4

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 (170) hide show
  1. package/dist/context/global.mjs +1 -1
  2. package/dist/db/adapter/factory.mjs +63 -1
  3. package/dist/db/adapter/index.d.mts +35 -1
  4. package/dist/db/adapter/types.d.mts +1 -1
  5. package/dist/db/type.d.mts +12 -0
  6. package/dist/error/codes.d.mts +1 -0
  7. package/dist/error/codes.mjs +1 -0
  8. package/dist/instrumentation/tracer.mjs +1 -1
  9. package/dist/oauth2/authorization-params.d.mts +12 -0
  10. package/dist/oauth2/authorization-params.mjs +12 -0
  11. package/dist/oauth2/basic-credentials.d.mts +30 -0
  12. package/dist/oauth2/basic-credentials.mjs +64 -0
  13. package/dist/oauth2/client-assertion.d.mts +38 -22
  14. package/dist/oauth2/client-assertion.mjs +63 -28
  15. package/dist/oauth2/client-credentials-token.d.mts +19 -40
  16. package/dist/oauth2/client-credentials-token.mjs +18 -29
  17. package/dist/oauth2/create-authorization-url.d.mts +9 -1
  18. package/dist/oauth2/create-authorization-url.mjs +23 -5
  19. package/dist/oauth2/index.d.mts +10 -7
  20. package/dist/oauth2/index.mjs +9 -7
  21. package/dist/oauth2/oauth-provider.d.mts +21 -2
  22. package/dist/oauth2/refresh-access-token.d.mts +20 -40
  23. package/dist/oauth2/refresh-access-token.mjs +19 -32
  24. package/dist/oauth2/token-endpoint-auth.d.mts +17 -0
  25. package/dist/oauth2/token-endpoint-auth.mjs +89 -0
  26. package/dist/oauth2/utils.d.mts +9 -1
  27. package/dist/oauth2/utils.mjs +12 -1
  28. package/dist/oauth2/validate-authorization-code.d.mts +17 -52
  29. package/dist/oauth2/validate-authorization-code.mjs +17 -30
  30. package/dist/oauth2/verify.mjs +15 -5
  31. package/dist/social-providers/apple.d.mts +4 -18
  32. package/dist/social-providers/apple.mjs +14 -3
  33. package/dist/social-providers/atlassian.d.mts +3 -1
  34. package/dist/social-providers/atlassian.mjs +5 -2
  35. package/dist/social-providers/cognito.d.mts +16 -1
  36. package/dist/social-providers/cognito.mjs +6 -2
  37. package/dist/social-providers/discord.d.mts +4 -2
  38. package/dist/social-providers/discord.mjs +16 -3
  39. package/dist/social-providers/dropbox.d.mts +3 -1
  40. package/dist/social-providers/dropbox.mjs +5 -4
  41. package/dist/social-providers/facebook.d.mts +3 -1
  42. package/dist/social-providers/facebook.mjs +5 -2
  43. package/dist/social-providers/figma.d.mts +3 -1
  44. package/dist/social-providers/figma.mjs +3 -2
  45. package/dist/social-providers/github.d.mts +3 -1
  46. package/dist/social-providers/github.mjs +5 -4
  47. package/dist/social-providers/gitlab.d.mts +3 -1
  48. package/dist/social-providers/gitlab.mjs +3 -2
  49. package/dist/social-providers/google.d.mts +3 -1
  50. package/dist/social-providers/google.mjs +5 -2
  51. package/dist/social-providers/huggingface.d.mts +3 -1
  52. package/dist/social-providers/huggingface.mjs +3 -2
  53. package/dist/social-providers/index.d.mts +104 -36
  54. package/dist/social-providers/kakao.d.mts +3 -1
  55. package/dist/social-providers/kakao.mjs +3 -2
  56. package/dist/social-providers/kick.d.mts +3 -1
  57. package/dist/social-providers/kick.mjs +3 -2
  58. package/dist/social-providers/line.d.mts +3 -1
  59. package/dist/social-providers/line.mjs +3 -2
  60. package/dist/social-providers/linear.d.mts +3 -1
  61. package/dist/social-providers/linear.mjs +3 -2
  62. package/dist/social-providers/linkedin.d.mts +3 -1
  63. package/dist/social-providers/linkedin.mjs +3 -2
  64. package/dist/social-providers/microsoft-entra-id.d.mts +2 -1
  65. package/dist/social-providers/microsoft-entra-id.mjs +3 -2
  66. package/dist/social-providers/naver.d.mts +3 -1
  67. package/dist/social-providers/naver.mjs +3 -2
  68. package/dist/social-providers/notion.d.mts +3 -1
  69. package/dist/social-providers/notion.mjs +5 -2
  70. package/dist/social-providers/paybin.d.mts +3 -1
  71. package/dist/social-providers/paybin.mjs +3 -2
  72. package/dist/social-providers/paypal.d.mts +3 -1
  73. package/dist/social-providers/paypal.mjs +4 -3
  74. package/dist/social-providers/polar.d.mts +3 -1
  75. package/dist/social-providers/polar.mjs +3 -2
  76. package/dist/social-providers/railway.d.mts +3 -1
  77. package/dist/social-providers/railway.mjs +3 -2
  78. package/dist/social-providers/reddit.d.mts +3 -1
  79. package/dist/social-providers/reddit.mjs +3 -2
  80. package/dist/social-providers/roblox.d.mts +4 -2
  81. package/dist/social-providers/roblox.mjs +12 -2
  82. package/dist/social-providers/salesforce.d.mts +3 -1
  83. package/dist/social-providers/salesforce.mjs +3 -2
  84. package/dist/social-providers/slack.d.mts +4 -2
  85. package/dist/social-providers/slack.mjs +11 -8
  86. package/dist/social-providers/spotify.d.mts +3 -1
  87. package/dist/social-providers/spotify.mjs +3 -2
  88. package/dist/social-providers/tiktok.d.mts +3 -1
  89. package/dist/social-providers/tiktok.mjs +14 -2
  90. package/dist/social-providers/twitch.d.mts +3 -1
  91. package/dist/social-providers/twitch.mjs +3 -2
  92. package/dist/social-providers/twitter.d.mts +5 -2
  93. package/dist/social-providers/twitter.mjs +2 -1
  94. package/dist/social-providers/vercel.d.mts +3 -1
  95. package/dist/social-providers/vercel.mjs +3 -2
  96. package/dist/social-providers/vk.d.mts +3 -1
  97. package/dist/social-providers/vk.mjs +3 -2
  98. package/dist/social-providers/wechat.d.mts +3 -1
  99. package/dist/social-providers/wechat.mjs +7 -1
  100. package/dist/social-providers/zoom.d.mts +4 -2
  101. package/dist/social-providers/zoom.mjs +10 -17
  102. package/dist/types/context.d.mts +23 -3
  103. package/dist/types/init-options.d.mts +29 -5
  104. package/dist/utils/ip.d.mts +5 -4
  105. package/dist/utils/ip.mjs +3 -3
  106. package/dist/utils/redirect-uri.d.mts +20 -0
  107. package/dist/utils/redirect-uri.mjs +48 -0
  108. package/dist/utils/string.d.mts +5 -1
  109. package/dist/utils/string.mjs +20 -1
  110. package/dist/utils/url.d.mts +18 -1
  111. package/dist/utils/url.mjs +30 -1
  112. package/package.json +9 -8
  113. package/src/db/adapter/factory.ts +118 -0
  114. package/src/db/adapter/index.ts +32 -0
  115. package/src/db/adapter/types.ts +1 -0
  116. package/src/db/type.ts +12 -0
  117. package/src/error/codes.ts +1 -0
  118. package/src/oauth2/authorization-params.ts +28 -0
  119. package/src/oauth2/basic-credentials.ts +87 -0
  120. package/src/oauth2/client-assertion.ts +131 -58
  121. package/src/oauth2/client-credentials-token.ts +48 -72
  122. package/src/oauth2/create-authorization-url.ts +28 -6
  123. package/src/oauth2/index.ts +25 -9
  124. package/src/oauth2/oauth-provider.ts +21 -2
  125. package/src/oauth2/refresh-access-token.ts +50 -76
  126. package/src/oauth2/token-endpoint-auth.ts +221 -0
  127. package/src/oauth2/utils.ts +19 -0
  128. package/src/oauth2/validate-authorization-code.ts +55 -85
  129. package/src/oauth2/verify.ts +20 -4
  130. package/src/social-providers/apple.ts +26 -2
  131. package/src/social-providers/atlassian.ts +8 -1
  132. package/src/social-providers/cognito.ts +26 -1
  133. package/src/social-providers/discord.ts +21 -17
  134. package/src/social-providers/dropbox.ts +7 -5
  135. package/src/social-providers/facebook.ts +11 -6
  136. package/src/social-providers/figma.ts +8 -1
  137. package/src/social-providers/github.ts +4 -2
  138. package/src/social-providers/gitlab.ts +2 -0
  139. package/src/social-providers/google.ts +2 -0
  140. package/src/social-providers/huggingface.ts +8 -1
  141. package/src/social-providers/kakao.ts +2 -1
  142. package/src/social-providers/kick.ts +8 -1
  143. package/src/social-providers/line.ts +2 -0
  144. package/src/social-providers/linear.ts +8 -1
  145. package/src/social-providers/linkedin.ts +2 -0
  146. package/src/social-providers/microsoft-entra-id.ts +1 -0
  147. package/src/social-providers/naver.ts +2 -1
  148. package/src/social-providers/notion.ts +8 -1
  149. package/src/social-providers/paybin.ts +2 -0
  150. package/src/social-providers/paypal.ts +7 -1
  151. package/src/social-providers/polar.ts +8 -1
  152. package/src/social-providers/railway.ts +8 -1
  153. package/src/social-providers/reddit.ts +2 -1
  154. package/src/social-providers/roblox.ts +16 -11
  155. package/src/social-providers/salesforce.ts +8 -1
  156. package/src/social-providers/slack.ts +15 -9
  157. package/src/social-providers/spotify.ts +8 -1
  158. package/src/social-providers/tiktok.ts +22 -9
  159. package/src/social-providers/twitch.ts +2 -1
  160. package/src/social-providers/twitter.ts +1 -0
  161. package/src/social-providers/vercel.ts +8 -1
  162. package/src/social-providers/vk.ts +8 -1
  163. package/src/social-providers/wechat.ts +9 -1
  164. package/src/social-providers/zoom.ts +15 -19
  165. package/src/types/context.ts +25 -4
  166. package/src/types/init-options.ts +29 -5
  167. package/src/utils/ip.ts +12 -13
  168. package/src/utils/redirect-uri.ts +54 -0
  169. package/src/utils/string.ts +37 -0
  170. package/src/utils/url.ts +28 -0
@@ -18,7 +18,8 @@ declare const spotify: (options: SpotifyOptions) => {
18
18
  state,
19
19
  scopes,
20
20
  codeVerifier,
21
- redirectURI
21
+ redirectURI,
22
+ additionalParams
22
23
  }: {
23
24
  state: string;
24
25
  codeVerifier: string;
@@ -26,6 +27,7 @@ declare const spotify: (options: SpotifyOptions) => {
26
27
  redirectURI: string;
27
28
  display?: string | undefined;
28
29
  loginHint?: string | undefined;
30
+ additionalParams?: Record<string, string> | undefined;
29
31
  }): Promise<URL>;
30
32
  validateAuthorizationCode: ({
31
33
  code,
@@ -8,7 +8,7 @@ const spotify = (options) => {
8
8
  return {
9
9
  id: "spotify",
10
10
  name: "Spotify",
11
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
11
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
12
12
  const _scopes = options.disableDefaultScope ? [] : ["user-read-email"];
13
13
  if (options.scope) _scopes.push(...options.scope);
14
14
  if (scopes) _scopes.push(...scopes);
@@ -19,7 +19,8 @@ const spotify = (options) => {
19
19
  scopes: _scopes,
20
20
  state,
21
21
  codeVerifier,
22
- redirectURI
22
+ redirectURI,
23
+ additionalParams
23
24
  });
24
25
  },
25
26
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -124,7 +124,8 @@ declare const tiktok: (options: TiktokOptions) => {
124
124
  createAuthorizationURL({
125
125
  state,
126
126
  scopes,
127
- redirectURI
127
+ redirectURI,
128
+ additionalParams
128
129
  }: {
129
130
  state: string;
130
131
  codeVerifier: string;
@@ -132,6 +133,7 @@ declare const tiktok: (options: TiktokOptions) => {
132
133
  redirectURI: string;
133
134
  display?: string | undefined;
134
135
  loginHint?: string | undefined;
136
+ additionalParams?: Record<string, string> | undefined;
135
137
  }): URL;
136
138
  validateAuthorizationCode: ({
137
139
  code,
@@ -1,3 +1,4 @@
1
+ import { RESERVED_AUTHORIZATION_PARAMS_SET } from "../oauth2/create-authorization-url.mjs";
1
2
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
2
3
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
3
4
  import { betterFetch } from "@better-fetch/fetch";
@@ -7,11 +8,22 @@ const tiktok = (options) => {
7
8
  return {
8
9
  id: "tiktok",
9
10
  name: "TikTok",
10
- createAuthorizationURL({ state, scopes, redirectURI }) {
11
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
11
12
  const _scopes = options.disableDefaultScope ? [] : ["user.info.profile"];
12
13
  if (options.scope) _scopes.push(...options.scope);
13
14
  if (scopes) _scopes.push(...scopes);
14
- return new URL(`https://www.tiktok.com/v2/auth/authorize?scope=${_scopes.join(",")}&response_type=code&client_key=${options.clientKey}&redirect_uri=${encodeURIComponent(options.redirectURI || redirectURI)}&state=${state}`);
15
+ const url = new URL("https://www.tiktok.com/v2/auth/authorize");
16
+ url.searchParams.set("scope", _scopes.join(","));
17
+ url.searchParams.set("response_type", "code");
18
+ url.searchParams.set("client_key", options.clientKey);
19
+ url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
20
+ url.searchParams.set("state", state);
21
+ if (additionalParams) for (const [key, value] of Object.entries(additionalParams)) {
22
+ if (RESERVED_AUTHORIZATION_PARAMS_SET.has(key)) continue;
23
+ if (key === "client_key") continue;
24
+ url.searchParams.set(key, value);
25
+ }
26
+ return url;
15
27
  },
16
28
  validateAuthorizationCode: async ({ code, redirectURI }) => {
17
29
  return validateAuthorizationCode({
@@ -35,7 +35,8 @@ declare const twitch: (options: TwitchOptions) => {
35
35
  createAuthorizationURL({
36
36
  state,
37
37
  scopes,
38
- redirectURI
38
+ redirectURI,
39
+ additionalParams
39
40
  }: {
40
41
  state: string;
41
42
  codeVerifier: string;
@@ -43,6 +44,7 @@ declare const twitch: (options: TwitchOptions) => {
43
44
  redirectURI: string;
44
45
  display?: string | undefined;
45
46
  loginHint?: string | undefined;
47
+ additionalParams?: Record<string, string> | undefined;
46
48
  }): Promise<URL>;
47
49
  validateAuthorizationCode: ({
48
50
  code,
@@ -9,7 +9,7 @@ const twitch = (options) => {
9
9
  return {
10
10
  id: "twitch",
11
11
  name: "Twitch",
12
- createAuthorizationURL({ state, scopes, redirectURI }) {
12
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
13
13
  const _scopes = options.disableDefaultScope ? [] : ["user:read:email", "openid"];
14
14
  if (options.scope) _scopes.push(...options.scope);
15
15
  if (scopes) _scopes.push(...scopes);
@@ -25,7 +25,8 @@ const twitch = (options) => {
25
25
  "email_verified",
26
26
  "preferred_username",
27
27
  "picture"
28
- ]
28
+ ],
29
+ additionalParams
29
30
  });
30
31
  },
31
32
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -89,6 +89,7 @@ declare const twitter: (options: TwitterOption) => {
89
89
  redirectURI: string;
90
90
  display?: string | undefined;
91
91
  loginHint?: string | undefined;
92
+ additionalParams?: Record<string, string> | undefined;
92
93
  }): Promise<URL>;
93
94
  validateAuthorizationCode: ({
94
95
  code,
@@ -103,9 +104,11 @@ declare const twitter: (options: TwitterOption) => {
103
104
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
104
105
  getUserInfo(token: OAuth2Tokens & {
105
106
  user?: {
106
- name? /** The start position (zero-based) of the recognized user's profile website. All start indices are inclusive. */: {
107
+ name?: {
107
108
  firstName?: string;
108
- lastName?: string;
109
+ lastName
110
+
111
+ /** The fully resolved URL. */? /** The fully resolved URL. */: string;
109
112
  };
110
113
  email?: string;
111
114
  } | undefined;
@@ -24,7 +24,8 @@ const twitter = (options) => {
24
24
  scopes: _scopes,
25
25
  state: data.state,
26
26
  codeVerifier: data.codeVerifier,
27
- redirectURI: data.redirectURI
27
+ redirectURI: data.redirectURI,
28
+ additionalParams: data.additionalParams
28
29
  });
29
30
  },
30
31
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -18,7 +18,8 @@ declare const vercel: (options: VercelOptions) => {
18
18
  state,
19
19
  scopes,
20
20
  codeVerifier,
21
- redirectURI
21
+ redirectURI,
22
+ additionalParams
22
23
  }: {
23
24
  state: string;
24
25
  codeVerifier: string;
@@ -26,6 +27,7 @@ declare const vercel: (options: VercelOptions) => {
26
27
  redirectURI: string;
27
28
  display?: string | undefined;
28
29
  loginHint?: string | undefined;
30
+ additionalParams?: Record<string, string> | undefined;
29
31
  }): Promise<URL>;
30
32
  validateAuthorizationCode: ({
31
33
  code,
@@ -7,7 +7,7 @@ const vercel = (options) => {
7
7
  return {
8
8
  id: "vercel",
9
9
  name: "Vercel",
10
- createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
10
+ createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
11
11
  if (!codeVerifier) throw new BetterAuthError("codeVerifier is required for Vercel");
12
12
  let _scopes = void 0;
13
13
  if (options.scope !== void 0 || scopes !== void 0) {
@@ -22,7 +22,8 @@ const vercel = (options) => {
22
22
  scopes: _scopes,
23
23
  state,
24
24
  codeVerifier,
25
- redirectURI
25
+ redirectURI,
26
+ additionalParams
26
27
  });
27
28
  },
28
29
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -24,7 +24,8 @@ declare const vk: (options: VkOption) => {
24
24
  state,
25
25
  scopes,
26
26
  codeVerifier,
27
- redirectURI
27
+ redirectURI,
28
+ additionalParams
28
29
  }: {
29
30
  state: string;
30
31
  codeVerifier: string;
@@ -32,6 +33,7 @@ declare const vk: (options: VkOption) => {
32
33
  redirectURI: string;
33
34
  display?: string | undefined;
34
35
  loginHint?: string | undefined;
36
+ additionalParams?: Record<string, string> | undefined;
35
37
  }): Promise<URL>;
36
38
  validateAuthorizationCode: ({
37
39
  code,
@@ -8,7 +8,7 @@ const vk = (options) => {
8
8
  return {
9
9
  id: "vk",
10
10
  name: "VK",
11
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI }) {
11
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, additionalParams }) {
12
12
  const _scopes = options.disableDefaultScope ? [] : ["email", "phone"];
13
13
  if (options.scope) _scopes.push(...options.scope);
14
14
  if (scopes) _scopes.push(...scopes);
@@ -19,7 +19,8 @@ const vk = (options) => {
19
19
  scopes: _scopes,
20
20
  state,
21
21
  redirectURI,
22
- codeVerifier
22
+ codeVerifier,
23
+ additionalParams
23
24
  });
24
25
  },
25
26
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI, deviceId }) => {
@@ -56,7 +56,8 @@ declare const wechat: (options: WeChatOptions) => {
56
56
  createAuthorizationURL({
57
57
  state,
58
58
  scopes,
59
- redirectURI
59
+ redirectURI,
60
+ additionalParams
60
61
  }: {
61
62
  state: string;
62
63
  codeVerifier: string;
@@ -64,6 +65,7 @@ declare const wechat: (options: WeChatOptions) => {
64
65
  redirectURI: string;
65
66
  display?: string | undefined;
66
67
  loginHint?: string | undefined;
68
+ additionalParams?: Record<string, string> | undefined;
67
69
  }): URL;
68
70
  validateAuthorizationCode: ({
69
71
  code
@@ -1,10 +1,11 @@
1
+ import { RESERVED_AUTHORIZATION_PARAMS_SET } from "../oauth2/create-authorization-url.mjs";
1
2
  import { betterFetch } from "@better-fetch/fetch";
2
3
  //#region src/social-providers/wechat.ts
3
4
  const wechat = (options) => {
4
5
  return {
5
6
  id: "wechat",
6
7
  name: "WeChat",
7
- createAuthorizationURL({ state, scopes, redirectURI }) {
8
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
8
9
  const _scopes = options.disableDefaultScope ? [] : ["snsapi_login"];
9
10
  options.scope && _scopes.push(...options.scope);
10
11
  scopes && _scopes.push(...scopes);
@@ -15,6 +16,11 @@ const wechat = (options) => {
15
16
  url.searchParams.set("redirect_uri", options.redirectURI || redirectURI);
16
17
  url.searchParams.set("state", state);
17
18
  url.searchParams.set("lang", options.lang || "cn");
19
+ if (additionalParams) for (const [key, value] of Object.entries(additionalParams)) {
20
+ if (RESERVED_AUTHORIZATION_PARAMS_SET.has(key)) continue;
21
+ if (key === "appid") continue;
22
+ url.searchParams.set(key, value);
23
+ }
18
24
  url.hash = "wechat_redirect";
19
25
  return url;
20
26
  },
@@ -119,7 +119,8 @@ declare const zoom: (userOptions: ZoomOptions) => {
119
119
  createAuthorizationURL: ({
120
120
  state,
121
121
  redirectURI,
122
- codeVerifier
122
+ codeVerifier,
123
+ additionalParams
123
124
  }: {
124
125
  state: string;
125
126
  codeVerifier: string;
@@ -127,6 +128,7 @@ declare const zoom: (userOptions: ZoomOptions) => {
127
128
  redirectURI: string;
128
129
  display?: string | undefined;
129
130
  loginHint?: string | undefined;
131
+ additionalParams?: Record<string, string> | undefined;
130
132
  }) => Promise<URL>;
131
133
  validateAuthorizationCode: ({
132
134
  code,
@@ -151,7 +153,7 @@ declare const zoom: (userOptions: ZoomOptions) => {
151
153
  user: {
152
154
  id: string;
153
155
  name?: string;
154
- email /** The employee's unique ID. This field only returns when SAML single sign-on (SSO) is enabled. The `login_type` value is `101` (SSO) (Example: "HqDyI037Qjili1kNsSIrIg") */?: string | null;
156
+ email?: string | null;
155
157
  image?: string;
156
158
  emailVerified: boolean;
157
159
  [key: string]: any;
@@ -1,4 +1,4 @@
1
- import { generateCodeChallenge } from "../oauth2/utils.mjs";
1
+ import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
2
2
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
3
3
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
4
4
  import { betterFetch } from "@better-fetch/fetch";
@@ -11,22 +11,15 @@ const zoom = (userOptions) => {
11
11
  return {
12
12
  id: "zoom",
13
13
  name: "Zoom",
14
- createAuthorizationURL: async ({ state, redirectURI, codeVerifier }) => {
15
- const params = new URLSearchParams({
16
- response_type: "code",
17
- redirect_uri: options.redirectURI ? options.redirectURI : redirectURI,
18
- client_id: options.clientId,
19
- state
20
- });
21
- if (options.pkce) {
22
- const codeChallenge = await generateCodeChallenge(codeVerifier);
23
- params.set("code_challenge_method", "S256");
24
- params.set("code_challenge", codeChallenge);
25
- }
26
- const url = new URL("https://zoom.us/oauth/authorize");
27
- url.search = params.toString();
28
- return url;
29
- },
14
+ createAuthorizationURL: async ({ state, redirectURI, codeVerifier, additionalParams }) => createAuthorizationURL({
15
+ id: "zoom",
16
+ options,
17
+ authorizationEndpoint: "https://zoom.us/oauth/authorize",
18
+ state,
19
+ redirectURI,
20
+ codeVerifier: options.pkce ? codeVerifier : void 0,
21
+ additionalParams
22
+ }),
30
23
  validateAuthorizationCode: async ({ code, redirectURI, codeVerifier }) => {
31
24
  return validateAuthorizationCode({
32
25
  code,
@@ -89,7 +89,14 @@ interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions
89
89
  * @param id - The account row's primary key (the `id` column, not the `accountId` column).
90
90
  */
91
91
  deleteAccount(id: string): Promise<void>;
92
- deleteSessions(userIdOrSessionTokens: string | string[]): Promise<void>;
92
+ /**
93
+ * Delete every session belonging to a user.
94
+ */
95
+ deleteUserSessions(userId: string): Promise<void>;
96
+ /**
97
+ * Delete sessions by their session tokens.
98
+ */
99
+ deleteSessions(sessionTokens: string[]): Promise<void>;
93
100
  findOAuthUser(email: string, accountId: string, providerId: string): Promise<{
94
101
  user: User;
95
102
  linkedAccount: Account | null;
@@ -107,13 +114,26 @@ interface InternalAdapter<_Options extends BetterAuthOptions = BetterAuthOptions
107
114
  updateUserByEmail<T extends Record<string, any>>(email: string, data: Partial<User & Record<string, any>>): Promise<User & T>;
108
115
  updatePassword(userId: string, password: string): Promise<void>;
109
116
  findAccounts(userId: string): Promise<Account[]>;
110
- findAccount(accountId: string): Promise<Account | null>;
111
117
  findAccountByProviderId(accountId: string, providerId: string): Promise<Account | null>;
112
118
  findAccountByUserId(userId: string): Promise<Account[]>;
113
119
  updateAccount(id: string, data: Partial<Account>): Promise<Account>;
114
120
  createVerificationValue(data: Omit<Verification, "createdAt" | "id" | "updatedAt"> & Partial<Verification>): Promise<Verification>;
115
121
  findVerificationValue(identifier: string): Promise<Verification | null>;
116
122
  deleteVerificationByIdentifier(identifier: string): Promise<void>;
123
+ /**
124
+ * Atomically consume a single-use verification row by `identifier` and
125
+ * return it. Only the first concurrent caller receives the latest row;
126
+ * subsequent callers receive `null`. Consuming one row invalidates the
127
+ * whole identifier so stale rows cannot be replayed. Rows past their
128
+ * `expiresAt` are treated as already invalid: the row is deleted but
129
+ * `null` is returned, so callers do not need to gate on `expiresAt`
130
+ * themselves. Callers MUST gate any state change (issue session, mint
131
+ * token, change password) on a non-null result.
132
+ *
133
+ * Replaces the racy `findVerificationValue` + `deleteVerificationByIdentifier`
134
+ * pair at single-use credential consumption sites.
135
+ */
136
+ consumeVerificationValue(identifier: string): Promise<Verification | null>;
117
137
  updateVerificationByIdentifier(identifier: string, data: Partial<Verification>): Promise<Verification>;
118
138
  refreshUserSessions(user: User): Promise<void>;
119
139
  }
@@ -171,7 +191,7 @@ type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> = Plugin
171
191
  * - "cookie": Store state in an encrypted cookie (stateless)
172
192
  * - "database": Store state in the database
173
193
  *
174
- * @default "cookie"
194
+ * @default "database" when `database` or `secondaryStorage` is configured, "cookie" otherwise
175
195
  */
176
196
  storeStateStrategy: "database" | "cookie";
177
197
  };
@@ -157,12 +157,13 @@ type BetterAuthAdvancedOptions = {
157
157
  */
158
158
  disableIpTracking?: boolean;
159
159
  /**
160
- * IPv6 subnet prefix length for rate limiting.
161
- * IPv6 addresses will be normalized to this subnet.
160
+ * IPv6 prefix length used to collapse addresses before rate-limit keying.
161
+ * Any integer from 0 to 128 is accepted; common values are 32, 48, 56, 64, 128.
162
+ * Out-of-range values fall back to safe behavior (negative -> mask all, > 128 -> no mask).
162
163
  *
163
164
  * @default 64
164
165
  */
165
- ipv6Subnet?: 128 | 64 | 48 | 32;
166
+ ipv6Subnet?: number;
166
167
  } | undefined;
167
168
  /**
168
169
  * Force cookies to always use the `Secure` attribute. By default,
@@ -906,6 +907,25 @@ type BetterAuthOptions = {
906
907
  * @default false
907
908
  */
908
909
  disableImplicitLinking?: boolean;
910
+ /**
911
+ * Require the existing local user row to have
912
+ * `emailVerified: true` before implicit account linking
913
+ * uses the IdP's `email_verified` claim as ownership
914
+ * proof. Defaults to `true` so an attacker who
915
+ * pre-registers an unverified account at a victim's
916
+ * email cannot have the victim's OAuth identity linked
917
+ * into the attacker-owned row on first sign-in. Set to
918
+ * `false` for backward compatibility on apps whose
919
+ * users sign up via OAuth without verifying their email
920
+ * locally; understand the takeover risk before doing
921
+ * so.
922
+ *
923
+ * @default true
924
+ *
925
+ * @deprecated The option will be removed on the next
926
+ * minor; the gate will become unconditional.
927
+ */
928
+ requireLocalEmailVerified?: boolean;
909
929
  /**
910
930
  * List of trusted providers. Can be a static array or a function
911
931
  * that returns providers dynamically. The function is called
@@ -943,7 +963,11 @@ type BetterAuthOptions = {
943
963
  */
944
964
  allowUnlinkingAll?: boolean;
945
965
  /**
946
- * If enabled (true), this will update the user information based on the newly linked account
966
+ * When enabled, linking an account copies the provider's profile onto
967
+ * the local user, matching the fields persisted on sign-up (`name`,
968
+ * `image`, and any `mapProfileToUser` fields). The local `email` and
969
+ * `emailVerified` are never changed, so a link cannot rebind the
970
+ * account's identity.
947
971
  *
948
972
  * @default false
949
973
  */
@@ -976,7 +1000,7 @@ type BetterAuthOptions = {
976
1000
  * - "cookie": Store state in an encrypted cookie (stateless)
977
1001
  * - "database": Store state in the database
978
1002
  *
979
- * @default "cookie"
1003
+ * @default "database" when `database` or `secondaryStorage` is configured, "cookie" otherwise
980
1004
  */
981
1005
  storeStateStrategy?: "database" | "cookie";
982
1006
  /**
@@ -10,12 +10,13 @@
10
10
  */
11
11
  interface NormalizeIPOptions {
12
12
  /**
13
- * For IPv6 addresses, extract the subnet prefix instead of full address.
14
- * Common values: 32, 48, 64, 128 (default: 128 = full address)
13
+ * Prefix length used to collapse IPv6 addresses before keying.
14
+ * Any integer from 0 to 128 is accepted. Common values: 32, 48, 56, 64, 128.
15
+ * Values outside 0-128 are clamped.
15
16
  *
16
- * @default 128
17
+ * @default 64
17
18
  */
18
- ipv6Subnet?: 128 | 64 | 48 | 32;
19
+ ipv6Subnet?: number;
19
20
  }
20
21
  /**
21
22
  * Checks if an IP is valid IPv4 or IPv6
package/dist/utils/ip.mjs CHANGED
@@ -60,8 +60,8 @@ function expandIPv6(ipv6) {
60
60
  */
61
61
  function normalizeIPv6(ipv6, subnetPrefix) {
62
62
  const groups = expandIPv6(ipv6);
63
- if (subnetPrefix && subnetPrefix < 128) {
64
- let bitsRemaining = subnetPrefix;
63
+ if (subnetPrefix !== void 0 && subnetPrefix < 128) {
64
+ let bitsRemaining = Math.max(0, Math.floor(subnetPrefix));
65
65
  return groups.map((group) => {
66
66
  if (bitsRemaining <= 0) return "0000";
67
67
  if (bitsRemaining >= 16) {
@@ -99,7 +99,7 @@ function normalizeIP(ip, options = {}) {
99
99
  if (!isIPv6(ip)) return ip.toLowerCase();
100
100
  const ipv4 = extractIPv4FromMapped(ip);
101
101
  if (ipv4) return ipv4.toLowerCase();
102
- return normalizeIPv6(ip, options.ipv6Subnet || 64);
102
+ return normalizeIPv6(ip, options.ipv6Subnet ?? 64);
103
103
  }
104
104
  /**
105
105
  * Creates a rate limit key from IP and path
@@ -0,0 +1,20 @@
1
+ import * as z from "zod";
2
+
3
+ //#region src/utils/redirect-uri.d.ts
4
+ /**
5
+ * Zod schema for OAuth redirect URIs and other developer-supplied URLs that the
6
+ * server stores and later hands back to a browser.
7
+ *
8
+ * - Rejects dangerous schemes (`javascript:`, `data:`, `vbscript:`).
9
+ * - Rejects URIs with a fragment component (`#...`) per RFC 6749 §3.1.2.
10
+ * - Requires HTTPS, except for loopback hosts (`127.0.0.0/8`, `[::1]`,
11
+ * `*.localhost` per RFC 6761), where HTTP is allowed for local development.
12
+ * - Allows custom schemes for mobile apps (e.g. `myapp://callback`).
13
+ *
14
+ * This is the single source of truth for redirect-URI validation across the
15
+ * OAuth provider plugins. Consume it from `@better-auth/core/utils/redirect-uri`
16
+ * rather than re-implementing the scheme policy per plugin.
17
+ */
18
+ declare const SafeUrlSchema: z.ZodURL;
19
+ //#endregion
20
+ export { SafeUrlSchema };
@@ -0,0 +1,48 @@
1
+ import { isLoopbackHost } from "./host.mjs";
2
+ import { DANGEROUS_URL_SCHEMES } from "./url.mjs";
3
+ import * as z from "zod";
4
+ //#region src/utils/redirect-uri.ts
5
+ /**
6
+ * Zod schema for OAuth redirect URIs and other developer-supplied URLs that the
7
+ * server stores and later hands back to a browser.
8
+ *
9
+ * - Rejects dangerous schemes (`javascript:`, `data:`, `vbscript:`).
10
+ * - Rejects URIs with a fragment component (`#...`) per RFC 6749 §3.1.2.
11
+ * - Requires HTTPS, except for loopback hosts (`127.0.0.0/8`, `[::1]`,
12
+ * `*.localhost` per RFC 6761), where HTTP is allowed for local development.
13
+ * - Allows custom schemes for mobile apps (e.g. `myapp://callback`).
14
+ *
15
+ * This is the single source of truth for redirect-URI validation across the
16
+ * OAuth provider plugins. Consume it from `@better-auth/core/utils/redirect-uri`
17
+ * rather than re-implementing the scheme policy per plugin.
18
+ */
19
+ const SafeUrlSchema = z.url().superRefine((val, ctx) => {
20
+ let u;
21
+ try {
22
+ u = new URL(val);
23
+ } catch {
24
+ ctx.addIssue({
25
+ code: "custom",
26
+ message: "URL must be parseable",
27
+ fatal: true
28
+ });
29
+ return z.NEVER;
30
+ }
31
+ if (DANGEROUS_URL_SCHEMES.includes(u.protocol)) {
32
+ ctx.addIssue({
33
+ code: "custom",
34
+ message: "URL cannot use javascript:, data:, or vbscript: scheme"
35
+ });
36
+ return;
37
+ }
38
+ if (val.includes("#")) ctx.addIssue({
39
+ code: "custom",
40
+ message: "Redirect URI must not contain a fragment component"
41
+ });
42
+ if (u.protocol === "http:" && !isLoopbackHost(u.host)) ctx.addIssue({
43
+ code: "custom",
44
+ message: "Redirect URI must use HTTPS (HTTP allowed only for loopback hosts)"
45
+ });
46
+ });
47
+ //#endregion
48
+ export { SafeUrlSchema };
@@ -1,4 +1,8 @@
1
1
  //#region src/utils/string.d.ts
2
2
  declare function capitalizeFirstLetter(str: string): string;
3
+ declare function toSnakeCase(input: string): string;
4
+ declare function toKebabCase(input: string): string;
5
+ declare function toCamelCase(input: string): string;
6
+ declare function toPascalCase(input: string): string;
3
7
  //#endregion
4
- export { capitalizeFirstLetter };
8
+ export { capitalizeFirstLetter, toCamelCase, toKebabCase, toPascalCase, toSnakeCase };
@@ -2,5 +2,24 @@
2
2
  function capitalizeFirstLetter(str) {
3
3
  return str.charAt(0).toUpperCase() + str.slice(1);
4
4
  }
5
+ const WORD_PATTERN = /[\p{Ll}\d]+|\p{Lu}+(?!\p{Ll})|\p{Lu}[\p{Ll}\d]+|\p{Lo}+/gu;
6
+ const APOSTROPHE_PATTERN = /['\u2019]/g;
7
+ function splitWords(input) {
8
+ return input.replace(APOSTROPHE_PATTERN, "").match(WORD_PATTERN) ?? [];
9
+ }
10
+ function toSnakeCase(input) {
11
+ return splitWords(input).map((word) => word.toLowerCase()).join("_");
12
+ }
13
+ function toKebabCase(input) {
14
+ return splitWords(input).map((word) => word.toLowerCase()).join("-");
15
+ }
16
+ function toCamelCase(input) {
17
+ return splitWords(input).reduce((acc, word, i) => {
18
+ return acc + (i === 0 ? word.toLowerCase() : `${word[0].toUpperCase()}${word.slice(1)}`);
19
+ }, "");
20
+ }
21
+ function toPascalCase(input) {
22
+ return splitWords(input).map((word) => `${word[0].toUpperCase()}${word.slice(1).toLowerCase()}`).join("");
23
+ }
5
24
  //#endregion
6
- export { capitalizeFirstLetter };
25
+ export { capitalizeFirstLetter, toCamelCase, toKebabCase, toPascalCase, toSnakeCase };
@@ -16,5 +16,22 @@
16
16
  * // Returns: "/sso/saml2/callback/provider1"
17
17
  */
18
18
  declare function normalizePathname(requestUrl: string, basePath: string): string;
19
+ /**
20
+ * Schemes that execute or embed code when navigated to or accepted as a
21
+ * redirect target. These are never safe as an OAuth `redirect_uri` or as a
22
+ * client-side navigation target (`window.location.href`, `location.assign`, ...).
23
+ */
24
+ declare const DANGEROUS_URL_SCHEMES: string[];
25
+ /**
26
+ * Returns `false` only when `value` is an absolute URL using a dangerous scheme
27
+ * (`javascript:`, `data:`, `vbscript:`). Relative URLs (e.g. `/dashboard`) and
28
+ * safe absolute schemes (`http`, `https`, custom app schemes such as
29
+ * `myapp://`) return `true`.
30
+ *
31
+ * Use this to guard browser navigation sinks and any redirect target that may
32
+ * originate from untrusted input. It is intentionally narrow: it blocks code
33
+ * execution schemes without rejecting relative paths or mobile deep links.
34
+ */
35
+ declare function isSafeUrlScheme(value: string): boolean;
19
36
  //#endregion
20
- export { normalizePathname };
37
+ export { DANGEROUS_URL_SCHEMES, isSafeUrlScheme, normalizePathname };