@better-auth/core 1.7.0-beta.1 → 1.7.0-beta.10

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 (218) hide show
  1. package/dist/api/index.d.mts +44 -1
  2. package/dist/api/index.mjs +69 -4
  3. package/dist/context/global.mjs +1 -1
  4. package/dist/context/transaction.d.mts +7 -4
  5. package/dist/context/transaction.mjs +6 -3
  6. package/dist/db/adapter/factory.mjs +91 -3
  7. package/dist/db/adapter/get-id-field.mjs +1 -1
  8. package/dist/db/adapter/index.d.mts +87 -3
  9. package/dist/db/adapter/types.d.mts +1 -1
  10. package/dist/db/get-tables.mjs +2 -1
  11. package/dist/db/type.d.mts +17 -0
  12. package/dist/env/env-impl.mjs +1 -1
  13. package/dist/error/codes.d.mts +1 -0
  14. package/dist/error/codes.mjs +1 -0
  15. package/dist/error/index.d.mts +7 -0
  16. package/dist/index.d.mts +2 -2
  17. package/dist/instrumentation/api.mjs +12 -0
  18. package/dist/instrumentation/noop.mjs +42 -0
  19. package/dist/instrumentation/pure.index.d.mts +7 -0
  20. package/dist/instrumentation/pure.index.mjs +7 -0
  21. package/dist/instrumentation/tracer.mjs +6 -3
  22. package/dist/oauth2/authorization-params.d.mts +12 -0
  23. package/dist/oauth2/authorization-params.mjs +12 -0
  24. package/dist/oauth2/basic-credentials.d.mts +30 -0
  25. package/dist/oauth2/basic-credentials.mjs +64 -0
  26. package/dist/oauth2/client-assertion.d.mts +38 -22
  27. package/dist/oauth2/client-assertion.mjs +63 -28
  28. package/dist/oauth2/client-credentials-token.d.mts +19 -40
  29. package/dist/oauth2/client-credentials-token.mjs +20 -31
  30. package/dist/oauth2/create-authorization-url.d.mts +11 -1
  31. package/dist/oauth2/create-authorization-url.mjs +27 -7
  32. package/dist/oauth2/dpop.d.mts +142 -0
  33. package/dist/oauth2/dpop.mjs +246 -0
  34. package/dist/oauth2/index.d.mts +14 -9
  35. package/dist/oauth2/index.mjs +12 -8
  36. package/dist/oauth2/oauth-provider.d.mts +144 -10
  37. package/dist/oauth2/refresh-access-token.d.mts +20 -40
  38. package/dist/oauth2/refresh-access-token.mjs +38 -36
  39. package/dist/oauth2/reject-redirects.mjs +65 -0
  40. package/dist/oauth2/token-endpoint-auth.d.mts +17 -0
  41. package/dist/oauth2/token-endpoint-auth.mjs +89 -0
  42. package/dist/oauth2/utils.d.mts +23 -1
  43. package/dist/oauth2/utils.mjs +48 -2
  44. package/dist/oauth2/validate-authorization-code.d.mts +17 -52
  45. package/dist/oauth2/validate-authorization-code.mjs +28 -34
  46. package/dist/oauth2/verify-id-token.d.mts +27 -0
  47. package/dist/oauth2/verify-id-token.mjs +62 -0
  48. package/dist/oauth2/verify.d.mts +88 -15
  49. package/dist/oauth2/verify.mjs +203 -25
  50. package/dist/social-providers/apple.d.mts +16 -4
  51. package/dist/social-providers/apple.mjs +17 -24
  52. package/dist/social-providers/atlassian.d.mts +5 -1
  53. package/dist/social-providers/atlassian.mjs +6 -3
  54. package/dist/social-providers/cognito.d.mts +27 -3
  55. package/dist/social-providers/cognito.mjs +15 -25
  56. package/dist/social-providers/discord.d.mts +7 -3
  57. package/dist/social-providers/discord.mjs +16 -3
  58. package/dist/social-providers/dropbox.d.mts +5 -1
  59. package/dist/social-providers/dropbox.mjs +5 -4
  60. package/dist/social-providers/facebook.d.mts +24 -5
  61. package/dist/social-providers/facebook.mjs +54 -20
  62. package/dist/social-providers/figma.d.mts +5 -1
  63. package/dist/social-providers/figma.mjs +4 -3
  64. package/dist/social-providers/github.d.mts +6 -2
  65. package/dist/social-providers/github.mjs +5 -4
  66. package/dist/social-providers/gitlab.d.mts +5 -1
  67. package/dist/social-providers/gitlab.mjs +3 -2
  68. package/dist/social-providers/google.d.mts +53 -5
  69. package/dist/social-providers/google.mjs +69 -25
  70. package/dist/social-providers/huggingface.d.mts +5 -1
  71. package/dist/social-providers/huggingface.mjs +3 -2
  72. package/dist/social-providers/index.d.mts +221 -45
  73. package/dist/social-providers/index.mjs +2 -2
  74. package/dist/social-providers/kakao.d.mts +5 -1
  75. package/dist/social-providers/kakao.mjs +3 -2
  76. package/dist/social-providers/kick.d.mts +5 -1
  77. package/dist/social-providers/kick.mjs +3 -2
  78. package/dist/social-providers/line.d.mts +8 -2
  79. package/dist/social-providers/line.mjs +5 -6
  80. package/dist/social-providers/linear.d.mts +5 -1
  81. package/dist/social-providers/linear.mjs +3 -2
  82. package/dist/social-providers/linkedin.d.mts +7 -3
  83. package/dist/social-providers/linkedin.mjs +4 -3
  84. package/dist/social-providers/microsoft-entra-id.d.mts +41 -7
  85. package/dist/social-providers/microsoft-entra-id.mjs +37 -28
  86. package/dist/social-providers/naver.d.mts +5 -1
  87. package/dist/social-providers/naver.mjs +3 -2
  88. package/dist/social-providers/notion.d.mts +5 -1
  89. package/dist/social-providers/notion.mjs +5 -2
  90. package/dist/social-providers/paybin.d.mts +5 -1
  91. package/dist/social-providers/paybin.mjs +4 -3
  92. package/dist/social-providers/paypal.d.mts +6 -2
  93. package/dist/social-providers/paypal.mjs +19 -14
  94. package/dist/social-providers/polar.d.mts +5 -1
  95. package/dist/social-providers/polar.mjs +3 -2
  96. package/dist/social-providers/railway.d.mts +5 -1
  97. package/dist/social-providers/railway.mjs +3 -2
  98. package/dist/social-providers/reddit.d.mts +5 -1
  99. package/dist/social-providers/reddit.mjs +7 -5
  100. package/dist/social-providers/roblox.d.mts +6 -2
  101. package/dist/social-providers/roblox.mjs +12 -2
  102. package/dist/social-providers/salesforce.d.mts +5 -1
  103. package/dist/social-providers/salesforce.mjs +4 -3
  104. package/dist/social-providers/slack.d.mts +6 -2
  105. package/dist/social-providers/slack.mjs +11 -8
  106. package/dist/social-providers/spotify.d.mts +5 -1
  107. package/dist/social-providers/spotify.mjs +3 -2
  108. package/dist/social-providers/tiktok.d.mts +5 -1
  109. package/dist/social-providers/tiktok.mjs +14 -2
  110. package/dist/social-providers/twitch.d.mts +5 -1
  111. package/dist/social-providers/twitch.mjs +3 -2
  112. package/dist/social-providers/twitter.d.mts +4 -1
  113. package/dist/social-providers/twitter.mjs +2 -1
  114. package/dist/social-providers/vercel.d.mts +5 -1
  115. package/dist/social-providers/vercel.mjs +3 -2
  116. package/dist/social-providers/vk.d.mts +5 -1
  117. package/dist/social-providers/vk.mjs +3 -2
  118. package/dist/social-providers/wechat.d.mts +5 -1
  119. package/dist/social-providers/wechat.mjs +8 -2
  120. package/dist/social-providers/zoom.d.mts +6 -2
  121. package/dist/social-providers/zoom.mjs +10 -17
  122. package/dist/types/context.d.mts +55 -6
  123. package/dist/types/index.d.mts +1 -1
  124. package/dist/types/init-options.d.mts +182 -12
  125. package/dist/types/plugin-client.d.mts +12 -2
  126. package/dist/utils/async.d.mts +22 -0
  127. package/dist/utils/async.mjs +32 -0
  128. package/dist/utils/host.d.mts +147 -0
  129. package/dist/utils/host.mjs +298 -0
  130. package/dist/utils/ip.d.mts +28 -5
  131. package/dist/utils/ip.mjs +118 -4
  132. package/dist/utils/is-api-error.d.mts +6 -0
  133. package/dist/utils/is-api-error.mjs +8 -0
  134. package/dist/utils/redirect-uri.d.mts +20 -0
  135. package/dist/utils/redirect-uri.mjs +48 -0
  136. package/dist/utils/string.d.mts +5 -1
  137. package/dist/utils/string.mjs +20 -1
  138. package/dist/utils/url.d.mts +18 -1
  139. package/dist/utils/url.mjs +34 -4
  140. package/package.json +26 -16
  141. package/src/api/index.ts +121 -5
  142. package/src/context/transaction.ts +45 -12
  143. package/src/db/adapter/factory.ts +193 -7
  144. package/src/db/adapter/get-id-field.ts +2 -2
  145. package/src/db/adapter/index.ts +85 -2
  146. package/src/db/adapter/types.ts +2 -0
  147. package/src/db/get-tables.ts +9 -1
  148. package/src/db/schema/account.ts +4 -1
  149. package/src/db/schema/user.ts +3 -0
  150. package/src/db/type.ts +17 -0
  151. package/src/env/env-impl.ts +1 -2
  152. package/src/error/codes.ts +1 -0
  153. package/src/error/index.ts +9 -0
  154. package/src/instrumentation/api.ts +17 -0
  155. package/src/instrumentation/noop.ts +74 -0
  156. package/src/instrumentation/pure.index.ts +31 -0
  157. package/src/instrumentation/tracer.ts +8 -3
  158. package/src/oauth2/authorization-params.ts +28 -0
  159. package/src/oauth2/basic-credentials.ts +87 -0
  160. package/src/oauth2/client-assertion.ts +131 -58
  161. package/src/oauth2/client-credentials-token.ts +50 -74
  162. package/src/oauth2/create-authorization-url.ts +33 -7
  163. package/src/oauth2/dpop.ts +568 -0
  164. package/src/oauth2/index.ts +81 -11
  165. package/src/oauth2/oauth-provider.ts +153 -11
  166. package/src/oauth2/refresh-access-token.ts +82 -83
  167. package/src/oauth2/reject-redirects.ts +75 -0
  168. package/src/oauth2/token-endpoint-auth.ts +221 -0
  169. package/src/oauth2/utils.ts +72 -5
  170. package/src/oauth2/validate-authorization-code.ts +69 -89
  171. package/src/oauth2/verify-id-token.ts +115 -0
  172. package/src/oauth2/verify.ts +409 -78
  173. package/src/social-providers/apple.ts +30 -40
  174. package/src/social-providers/atlassian.ts +8 -1
  175. package/src/social-providers/cognito.ts +36 -39
  176. package/src/social-providers/discord.ts +22 -18
  177. package/src/social-providers/dropbox.ts +7 -5
  178. package/src/social-providers/facebook.ts +108 -52
  179. package/src/social-providers/figma.ts +8 -1
  180. package/src/social-providers/github.ts +5 -3
  181. package/src/social-providers/gitlab.ts +2 -0
  182. package/src/social-providers/google.ts +141 -39
  183. package/src/social-providers/huggingface.ts +8 -1
  184. package/src/social-providers/kakao.ts +2 -1
  185. package/src/social-providers/kick.ts +8 -1
  186. package/src/social-providers/line.ts +25 -27
  187. package/src/social-providers/linear.ts +8 -1
  188. package/src/social-providers/linkedin.ts +5 -3
  189. package/src/social-providers/microsoft-entra-id.ts +100 -55
  190. package/src/social-providers/naver.ts +2 -1
  191. package/src/social-providers/notion.ts +8 -1
  192. package/src/social-providers/paybin.ts +2 -0
  193. package/src/social-providers/paypal.ts +28 -17
  194. package/src/social-providers/polar.ts +8 -1
  195. package/src/social-providers/railway.ts +8 -1
  196. package/src/social-providers/reddit.ts +9 -4
  197. package/src/social-providers/roblox.ts +16 -11
  198. package/src/social-providers/salesforce.ts +8 -1
  199. package/src/social-providers/slack.ts +15 -9
  200. package/src/social-providers/spotify.ts +8 -1
  201. package/src/social-providers/tiktok.ts +22 -9
  202. package/src/social-providers/twitch.ts +2 -1
  203. package/src/social-providers/twitter.ts +1 -0
  204. package/src/social-providers/vercel.ts +8 -1
  205. package/src/social-providers/vk.ts +8 -1
  206. package/src/social-providers/wechat.ts +17 -2
  207. package/src/social-providers/zoom.ts +15 -19
  208. package/src/types/context.ts +57 -5
  209. package/src/types/index.ts +7 -0
  210. package/src/types/init-options.ts +204 -14
  211. package/src/types/plugin-client.ts +16 -2
  212. package/src/utils/async.ts +53 -0
  213. package/src/utils/host.ts +425 -0
  214. package/src/utils/ip.ts +197 -13
  215. package/src/utils/is-api-error.ts +10 -0
  216. package/src/utils/redirect-uri.ts +54 -0
  217. package/src/utils/string.ts +37 -0
  218. package/src/utils/url.ts +38 -4
@@ -7,7 +7,7 @@ import { facebook } from "./facebook.mjs";
7
7
  import { figma } from "./figma.mjs";
8
8
  import { github } from "./github.mjs";
9
9
  import { gitlab } from "./gitlab.mjs";
10
- import { getGooglePublicKey, google } from "./google.mjs";
10
+ import { getGooglePublicKey, google, isGoogleHostedDomainAllowed, verifyGoogleIdToken } from "./google.mjs";
11
11
  import { huggingface } from "./huggingface.mjs";
12
12
  import { kakao } from "./kakao.mjs";
13
13
  import { kick } from "./kick.mjs";
@@ -75,4 +75,4 @@ const socialProviders = {
75
75
  const socialProviderList = Object.keys(socialProviders);
76
76
  const SocialProviderListEnum = z.enum(socialProviderList).or(z.string());
77
77
  //#endregion
78
- export { SocialProviderListEnum, apple, atlassian, cognito, discord, dropbox, facebook, figma, getApplePublicKey, getCognitoPublicKey, getGooglePublicKey, getMicrosoftPublicKey, github, gitlab, google, huggingface, kakao, kick, line, linear, linkedin, microsoft, naver, notion, paybin, paypal, polar, railway, reddit, roblox, salesforce, slack, socialProviderList, socialProviders, spotify, tiktok, twitch, twitter, vercel, vk, wechat, zoom };
78
+ export { SocialProviderListEnum, apple, atlassian, cognito, discord, dropbox, facebook, figma, getApplePublicKey, getCognitoPublicKey, getGooglePublicKey, getMicrosoftPublicKey, github, gitlab, google, huggingface, isGoogleHostedDomainAllowed, kakao, kick, line, linear, linkedin, microsoft, naver, notion, paybin, paypal, polar, railway, reddit, roblox, salesforce, slack, socialProviderList, socialProviders, spotify, tiktok, twitch, twitter, vercel, verifyGoogleIdToken, vk, wechat, zoom };
@@ -96,7 +96,8 @@ declare const kakao: (options: KakaoOptions) => {
96
96
  createAuthorizationURL({
97
97
  state,
98
98
  scopes,
99
- redirectURI
99
+ redirectURI,
100
+ additionalParams
100
101
  }: {
101
102
  state: string;
102
103
  codeVerifier: string;
@@ -104,6 +105,8 @@ declare const kakao: (options: KakaoOptions) => {
104
105
  redirectURI: string;
105
106
  display?: string | undefined;
106
107
  loginHint?: string | undefined;
108
+ idTokenNonce?: string | undefined;
109
+ additionalParams?: Record<string, string> | undefined;
107
110
  }): Promise<URL>;
108
111
  validateAuthorizationCode: ({
109
112
  code,
@@ -116,6 +119,7 @@ declare const kakao: (options: KakaoOptions) => {
116
119
  }) => Promise<OAuth2Tokens>;
117
120
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
118
121
  getUserInfo(token: OAuth2Tokens & {
122
+ expectedIdTokenNonce?: string | undefined;
119
123
  user?: {
120
124
  name?: {
121
125
  firstName?: string;
@@ -8,7 +8,7 @@ const kakao = (options) => {
8
8
  return {
9
9
  id: "kakao",
10
10
  name: "Kakao",
11
- createAuthorizationURL({ state, scopes, redirectURI }) {
11
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
12
12
  const _scopes = options.disableDefaultScope ? [] : [
13
13
  "account_email",
14
14
  "profile_image",
@@ -22,7 +22,8 @@ const kakao = (options) => {
22
22
  authorizationEndpoint: "https://kauth.kakao.com/oauth/authorize",
23
23
  scopes: _scopes,
24
24
  state,
25
- redirectURI
25
+ redirectURI,
26
+ additionalParams
26
27
  });
27
28
  },
28
29
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -28,7 +28,8 @@ declare const kick: (options: KickOptions) => {
28
28
  state,
29
29
  scopes,
30
30
  redirectURI,
31
- codeVerifier
31
+ codeVerifier,
32
+ additionalParams
32
33
  }: {
33
34
  state: string;
34
35
  codeVerifier: string;
@@ -36,6 +37,8 @@ declare const kick: (options: KickOptions) => {
36
37
  redirectURI: string;
37
38
  display?: string | undefined;
38
39
  loginHint?: string | undefined;
40
+ idTokenNonce?: string | undefined;
41
+ additionalParams?: Record<string, string> | undefined;
39
42
  }): Promise<URL>;
40
43
  validateAuthorizationCode({
41
44
  code,
@@ -49,6 +52,7 @@ declare const kick: (options: KickOptions) => {
49
52
  }): Promise<OAuth2Tokens>;
50
53
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
51
54
  getUserInfo(token: OAuth2Tokens & {
55
+ expectedIdTokenNonce?: string | undefined;
52
56
  user?: {
53
57
  name?: {
54
58
  firstName?: string;
@@ -7,7 +7,7 @@ const kick = (options) => {
7
7
  return {
8
8
  id: "kick",
9
9
  name: "Kick",
10
- createAuthorizationURL({ state, scopes, redirectURI, codeVerifier }) {
10
+ createAuthorizationURL({ state, scopes, redirectURI, codeVerifier, additionalParams }) {
11
11
  const _scopes = options.disableDefaultScope ? [] : ["user:read"];
12
12
  if (options.scope) _scopes.push(...options.scope);
13
13
  if (scopes) _scopes.push(...scopes);
@@ -18,7 +18,8 @@ const kick = (options) => {
18
18
  authorizationEndpoint: "https://id.kick.com/oauth/authorize",
19
19
  scopes: _scopes,
20
20
  codeVerifier,
21
- state
21
+ state,
22
+ additionalParams
22
23
  });
23
24
  },
24
25
  async validateAuthorizationCode({ code, redirectURI, codeVerifier }) {
@@ -38,7 +38,8 @@ declare const line: (options: LineOptions) => {
38
38
  scopes,
39
39
  codeVerifier,
40
40
  redirectURI,
41
- loginHint
41
+ loginHint,
42
+ additionalParams
42
43
  }: {
43
44
  state: string;
44
45
  codeVerifier: string;
@@ -46,6 +47,8 @@ declare const line: (options: LineOptions) => {
46
47
  redirectURI: string;
47
48
  display?: string | undefined;
48
49
  loginHint?: string | undefined;
50
+ idTokenNonce?: string | undefined;
51
+ additionalParams?: Record<string, string> | undefined;
49
52
  }): Promise<URL>;
50
53
  validateAuthorizationCode: ({
51
54
  code,
@@ -58,8 +61,11 @@ declare const line: (options: LineOptions) => {
58
61
  deviceId?: string | undefined;
59
62
  }) => Promise<OAuth2Tokens>;
60
63
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
61
- verifyIdToken(token: string, nonce: string | undefined): Promise<boolean>;
64
+ idToken: {
65
+ verify: (token: string, nonce: string | undefined) => Promise<boolean>;
66
+ };
62
67
  getUserInfo(token: OAuth2Tokens & {
68
+ expectedIdTokenNonce?: string | undefined;
63
69
  user?: {
64
70
  name?: {
65
71
  firstName?: string;
@@ -21,7 +21,7 @@ const line = (options) => {
21
21
  return {
22
22
  id: "line",
23
23
  name: "LINE",
24
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint }) {
24
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint, additionalParams }) {
25
25
  const _scopes = options.disableDefaultScope ? [] : [
26
26
  "openid",
27
27
  "profile",
@@ -37,7 +37,8 @@ const line = (options) => {
37
37
  state,
38
38
  codeVerifier,
39
39
  redirectURI,
40
- loginHint
40
+ loginHint,
41
+ additionalParams
41
42
  });
42
43
  },
43
44
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {
@@ -59,9 +60,7 @@ const line = (options) => {
59
60
  tokenEndpoint
60
61
  });
61
62
  },
62
- async verifyIdToken(token, nonce) {
63
- if (options.disableIdTokenSignIn) return false;
64
- if (options.verifyIdToken) return options.verifyIdToken(token, nonce);
63
+ idToken: { verify: async (token, nonce) => {
65
64
  const body = new URLSearchParams();
66
65
  body.set("id_token", token);
67
66
  body.set("client_id", options.clientId);
@@ -75,7 +74,7 @@ const line = (options) => {
75
74
  if (data.aud !== options.clientId) return false;
76
75
  if (data.nonce && data.nonce !== nonce) return false;
77
76
  return true;
78
- },
77
+ } },
79
78
  async getUserInfo(token) {
80
79
  if (options.getUserInfo) return options.getUserInfo(token);
81
80
  let profile = null;
@@ -24,7 +24,8 @@ declare const linear: (options: LinearOptions) => {
24
24
  state,
25
25
  scopes,
26
26
  loginHint,
27
- redirectURI
27
+ redirectURI,
28
+ additionalParams
28
29
  }: {
29
30
  state: string;
30
31
  codeVerifier: string;
@@ -32,6 +33,8 @@ declare const linear: (options: LinearOptions) => {
32
33
  redirectURI: string;
33
34
  display?: string | undefined;
34
35
  loginHint?: string | undefined;
36
+ idTokenNonce?: string | undefined;
37
+ additionalParams?: Record<string, string> | undefined;
35
38
  }): Promise<URL>;
36
39
  validateAuthorizationCode: ({
37
40
  code,
@@ -44,6 +47,7 @@ declare const linear: (options: LinearOptions) => {
44
47
  }) => Promise<OAuth2Tokens>;
45
48
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
46
49
  getUserInfo(token: OAuth2Tokens & {
50
+ expectedIdTokenNonce?: string | undefined;
47
51
  user?: {
48
52
  name?: {
49
53
  firstName?: string;
@@ -8,7 +8,7 @@ const linear = (options) => {
8
8
  return {
9
9
  id: "linear",
10
10
  name: "Linear",
11
- createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
11
+ createAuthorizationURL({ state, scopes, loginHint, redirectURI, additionalParams }) {
12
12
  const _scopes = options.disableDefaultScope ? [] : ["read"];
13
13
  if (options.scope) _scopes.push(...options.scope);
14
14
  if (scopes) _scopes.push(...scopes);
@@ -19,7 +19,8 @@ const linear = (options) => {
19
19
  scopes: _scopes,
20
20
  state,
21
21
  redirectURI,
22
- loginHint
22
+ loginHint,
23
+ additionalParams
23
24
  });
24
25
  },
25
26
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -10,8 +10,8 @@ interface LinkedInProfile {
10
10
  country: string;
11
11
  language: string;
12
12
  };
13
- email: string;
14
- email_verified: boolean;
13
+ email?: string;
14
+ email_verified?: boolean;
15
15
  }
16
16
  interface LinkedInOptions extends ProviderOptions<LinkedInProfile> {
17
17
  clientId: string;
@@ -23,7 +23,8 @@ declare const linkedin: (options: LinkedInOptions) => {
23
23
  state,
24
24
  scopes,
25
25
  redirectURI,
26
- loginHint
26
+ loginHint,
27
+ additionalParams
27
28
  }: {
28
29
  state: string;
29
30
  codeVerifier: string;
@@ -31,6 +32,8 @@ declare const linkedin: (options: LinkedInOptions) => {
31
32
  redirectURI: string;
32
33
  display?: string | undefined;
33
34
  loginHint?: string | undefined;
35
+ idTokenNonce?: string | undefined;
36
+ additionalParams?: Record<string, string> | undefined;
34
37
  }) => Promise<URL>;
35
38
  validateAuthorizationCode: ({
36
39
  code,
@@ -43,6 +46,7 @@ declare const linkedin: (options: LinkedInOptions) => {
43
46
  }) => Promise<OAuth2Tokens>;
44
47
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
45
48
  getUserInfo(token: OAuth2Tokens & {
49
+ expectedIdTokenNonce?: string | undefined;
46
50
  user?: {
47
51
  name?: {
48
52
  firstName?: string;
@@ -9,7 +9,7 @@ const linkedin = (options) => {
9
9
  return {
10
10
  id: "linkedin",
11
11
  name: "Linkedin",
12
- createAuthorizationURL: async ({ state, scopes, redirectURI, loginHint }) => {
12
+ createAuthorizationURL: async ({ state, scopes, redirectURI, loginHint, additionalParams }) => {
13
13
  const _scopes = options.disableDefaultScope ? [] : [
14
14
  "profile",
15
15
  "email",
@@ -24,7 +24,8 @@ const linkedin = (options) => {
24
24
  scopes: _scopes,
25
25
  state,
26
26
  loginHint,
27
- redirectURI
27
+ redirectURI,
28
+ additionalParams
28
29
  });
29
30
  },
30
31
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -59,7 +60,7 @@ const linkedin = (options) => {
59
60
  id: profile.sub,
60
61
  name: profile.name,
61
62
  email: profile.email,
62
- emailVerified: profile.email_verified || false,
63
+ emailVerified: profile.email_verified ?? false,
63
64
  image: profile.picture,
64
65
  ...userMap
65
66
  },
@@ -1,4 +1,7 @@
1
+ import { ClientAssertionGetter } from "../oauth2/client-assertion.mjs";
1
2
  import { OAuth2Tokens, ProviderOptions } from "../oauth2/oauth-provider.mjs";
3
+ import * as jose from "jose";
4
+
2
5
  //#region src/social-providers/microsoft-entra-id.d.ts
3
6
  /**
4
7
  * @see [Microsoft Identity Platform - Optional claims reference](https://learn.microsoft.com/en-us/entra/identity-platform/optional-claims-reference)
@@ -25,7 +28,7 @@ interface MicrosoftEntraIDProfile extends Record<string, any> {
25
28
  /** The primary username that represents the user */
26
29
  preferred_username: string;
27
30
  /** User's email address */
28
- email: string;
31
+ email?: string;
29
32
  /** Human-readable value that identifies the subject of the token */
30
33
  name: string;
31
34
  /** Matches the parameter included in the original authorize request */
@@ -104,26 +107,34 @@ interface MicrosoftEntraIDProfile extends Record<string, any> {
104
107
  given_name: string;
105
108
  }
106
109
  interface MicrosoftOptions extends ProviderOptions<MicrosoftEntraIDProfile> {
107
- clientId: string;
110
+ clientId: string | string[];
108
111
  /**
109
112
  * The tenant ID of the Microsoft account
110
113
  * @default "common"
111
114
  */
112
- tenantId?: string | undefined;
115
+ tenantId?: string;
113
116
  /**
114
117
  * The authentication authority URL. Use the default "https://login.microsoftonline.com" for standard Entra ID or "https://<tenant-id>.ciamlogin.com" for CIAM scenarios.
115
118
  * @default "https://login.microsoftonline.com"
116
119
  */
117
- authority?: string | undefined;
120
+ authority?: string;
121
+ /**
122
+ * Function that returns a JWT client assertion for token endpoint authentication.
123
+ *
124
+ * Use this instead of `clientSecret` when your Microsoft Entra ID app is
125
+ * configured for client authentication with assertions (private_key_jwt or
126
+ * workload identity federation).
127
+ */
128
+ clientAssertion?: ClientAssertionGetter;
118
129
  /**
119
130
  * The size of the profile photo
120
131
  * @default 48
121
132
  */
122
- profilePhotoSize?: (48 | 64 | 96 | 120 | 240 | 360 | 432 | 504 | 648) | undefined;
133
+ profilePhotoSize?: 48 | 64 | 96 | 120 | 240 | 360 | 432 | 504 | 648;
123
134
  /**
124
135
  * Disable profile photo
125
136
  */
126
- disableProfilePhoto?: boolean | undefined;
137
+ disableProfilePhoto?: boolean;
127
138
  }
128
139
  declare const microsoft: (options: MicrosoftOptions) => {
129
140
  id: "microsoft";
@@ -135,6 +146,8 @@ declare const microsoft: (options: MicrosoftOptions) => {
135
146
  redirectURI: string;
136
147
  display?: string | undefined;
137
148
  loginHint?: string | undefined;
149
+ idTokenNonce?: string | undefined;
150
+ additionalParams?: Record<string, string> | undefined;
138
151
  }): Promise<URL>;
139
152
  validateAuthorizationCode({
140
153
  code,
@@ -146,8 +159,29 @@ declare const microsoft: (options: MicrosoftOptions) => {
146
159
  codeVerifier?: string | undefined;
147
160
  deviceId?: string | undefined;
148
161
  }): Promise<OAuth2Tokens>;
149
- verifyIdToken(token: string, nonce: string | undefined): Promise<boolean>;
162
+ idToken: {
163
+ jwks: (header: jose.JWTHeaderParameters) => Promise<Uint8Array<ArrayBufferLike> | CryptoKey>;
164
+ audience: string | string[];
165
+ maxTokenAge: string;
166
+ /**
167
+ * Issuer varies per tenant for multi-tenant endpoints, so only validate it for
168
+ * specific tenants.
169
+ * @see https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols#endpoints
170
+ */
171
+ issuer: string | undefined;
172
+ /**
173
+ * The multi-tenant endpoints (common/organizations/consumers) skip the
174
+ * issuer check above because the issuer varies per tenant, and the
175
+ * organizations and consumers JWKS sets overlap. Enforce the tenant
176
+ * binding explicitly so a token from a disallowed account class cannot
177
+ * pass: the issuer must name the token's own tenant, and the account
178
+ * class must match the configured restriction.
179
+ * @see https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference
180
+ */
181
+ verifyClaims: (claims: Record<string, unknown>) => boolean;
182
+ };
150
183
  getUserInfo(token: OAuth2Tokens & {
184
+ expectedIdTokenNonce?: string | undefined;
151
185
  user?: {
152
186
  name?: {
153
187
  firstName?: string;
@@ -1,21 +1,39 @@
1
+ import { APIError, BetterAuthError } from "../error/index.mjs";
1
2
  import { logger } from "../env/logger.mjs";
2
- import { APIError } from "../error/index.mjs";
3
+ import { getPrimaryClientId } from "../oauth2/utils.mjs";
3
4
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
4
5
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
5
6
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
6
- import { decodeJwt, decodeProtectedHeader, importJWK, jwtVerify } from "jose";
7
7
  import { base64 } from "@better-auth/utils/base64";
8
+ import { decodeJwt, importJWK } from "jose";
8
9
  import { betterFetch } from "@better-fetch/fetch";
9
10
  //#region src/social-providers/microsoft-entra-id.ts
11
+ /**
12
+ * Microsoft's fixed tenant id for personal (consumer) Microsoft accounts. Every
13
+ * personal-account token carries it as the `tid` claim, so it distinguishes the
14
+ * consumer account class from work/school tenants.
15
+ * @see https://learn.microsoft.com/en-us/entra/identity-platform/id-token-claims-reference
16
+ */
17
+ const MICROSOFT_CONSUMER_TENANT_ID = "9188040d-6c67-4c5b-b112-36a304b66dad";
10
18
  const microsoft = (options) => {
11
19
  const tenant = options.tenantId || "common";
12
- const authority = options.authority || "https://login.microsoftonline.com";
20
+ let authority = options.authority || "https://login.microsoftonline.com";
21
+ while (authority.endsWith("/")) authority = authority.slice(0, -1);
13
22
  const authorizationEndpoint = `${authority}/${tenant}/oauth2/v2.0/authorize`;
14
23
  const tokenEndpoint = `${authority}/${tenant}/oauth2/v2.0/token`;
24
+ if (options.clientSecret && options.clientAssertion) throw new BetterAuthError("Microsoft Entra ID clientAssertion cannot be combined with clientSecret");
25
+ const tokenEndpointAuth = options.clientAssertion ? {
26
+ method: "private_key_jwt",
27
+ getClientAssertion: options.clientAssertion
28
+ } : void 0;
15
29
  return {
16
30
  id: "microsoft",
17
31
  name: "Microsoft EntraID",
18
32
  createAuthorizationURL(data) {
33
+ if (!getPrimaryClientId(options.clientId)) {
34
+ logger.error("Client Id is required for Microsoft Entra ID. Make sure to provide it in the options.");
35
+ throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
36
+ }
19
37
  const scopes = options.disableDefaultScope ? [] : [
20
38
  "openid",
21
39
  "profile",
@@ -34,7 +52,8 @@ const microsoft = (options) => {
34
52
  scopes,
35
53
  redirectURI: data.redirectURI,
36
54
  prompt: options.prompt,
37
- loginHint: data.loginHint
55
+ loginHint: data.loginHint,
56
+ additionalParams: data.additionalParams
38
57
  });
39
58
  },
40
59
  validateAuthorizationCode({ code, codeVerifier, redirectURI }) {
@@ -43,32 +62,21 @@ const microsoft = (options) => {
43
62
  codeVerifier,
44
63
  redirectURI,
45
64
  options,
46
- tokenEndpoint
65
+ tokenEndpoint,
66
+ tokenEndpointAuth
47
67
  });
48
68
  },
49
- async verifyIdToken(token, nonce) {
50
- if (options.disableIdTokenSignIn) return false;
51
- if (options.verifyIdToken) return options.verifyIdToken(token, nonce);
52
- try {
53
- const { kid, alg: jwtAlg } = decodeProtectedHeader(token);
54
- if (!kid || !jwtAlg) return false;
55
- const publicKey = await getMicrosoftPublicKey(kid, tenant, authority);
56
- const verifyOptions = {
57
- algorithms: [jwtAlg],
58
- audience: options.clientId,
59
- maxTokenAge: "1h"
60
- };
61
- /**
62
- * Issuer varies per user's tenant for multi-tenant endpoints, so only validate for specific tenants.
63
- * @see https://learn.microsoft.com/en-us/entra/identity-platform/v2-protocols#endpoints
64
- */
65
- if (tenant !== "common" && tenant !== "organizations" && tenant !== "consumers") verifyOptions.issuer = `${authority}/${tenant}/v2.0`;
66
- const { payload: jwtClaims } = await jwtVerify(token, publicKey, verifyOptions);
67
- if (nonce && jwtClaims.nonce !== nonce) return false;
69
+ idToken: {
70
+ jwks: (header) => getMicrosoftPublicKey(header.kid, tenant, authority),
71
+ audience: options.clientId,
72
+ maxTokenAge: "1h",
73
+ issuer: tenant !== "common" && tenant !== "organizations" && tenant !== "consumers" ? `${authority}/${tenant}/v2.0` : void 0,
74
+ verifyClaims: (claims) => {
75
+ const tid = claims.tid;
76
+ if (typeof tid !== "string" || claims.iss !== `${authority}/${tid}/v2.0`) return false;
77
+ if (tenant === "organizations" && tid === MICROSOFT_CONSUMER_TENANT_ID) return false;
78
+ if (tenant === "consumers" && tid !== MICROSOFT_CONSUMER_TENANT_ID) return false;
68
79
  return true;
69
- } catch (error) {
70
- logger.error("Failed to verify ID token:", error);
71
- return false;
72
80
  }
73
81
  },
74
82
  async getUserInfo(token) {
@@ -118,7 +126,8 @@ const microsoft = (options) => {
118
126
  clientSecret: options.clientSecret
119
127
  },
120
128
  extraParams: { scope: scopes.join(" ") },
121
- tokenEndpoint
129
+ tokenEndpoint,
130
+ tokenEndpointAuth
122
131
  });
123
132
  },
124
133
  options
@@ -27,7 +27,8 @@ declare const naver: (options: NaverOptions) => {
27
27
  createAuthorizationURL({
28
28
  state,
29
29
  scopes,
30
- redirectURI
30
+ redirectURI,
31
+ additionalParams
31
32
  }: {
32
33
  state: string;
33
34
  codeVerifier: string;
@@ -35,6 +36,8 @@ declare const naver: (options: NaverOptions) => {
35
36
  redirectURI: string;
36
37
  display?: string | undefined;
37
38
  loginHint?: string | undefined;
39
+ idTokenNonce?: string | undefined;
40
+ additionalParams?: Record<string, string> | undefined;
38
41
  }): Promise<URL>;
39
42
  validateAuthorizationCode: ({
40
43
  code,
@@ -47,6 +50,7 @@ declare const naver: (options: NaverOptions) => {
47
50
  }) => Promise<OAuth2Tokens>;
48
51
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
49
52
  getUserInfo(token: OAuth2Tokens & {
53
+ expectedIdTokenNonce?: string | undefined;
50
54
  user?: {
51
55
  name?: {
52
56
  firstName?: string;
@@ -8,7 +8,7 @@ const naver = (options) => {
8
8
  return {
9
9
  id: "naver",
10
10
  name: "Naver",
11
- createAuthorizationURL({ state, scopes, redirectURI }) {
11
+ createAuthorizationURL({ state, scopes, redirectURI, additionalParams }) {
12
12
  const _scopes = options.disableDefaultScope ? [] : ["profile", "email"];
13
13
  if (options.scope) _scopes.push(...options.scope);
14
14
  if (scopes) _scopes.push(...scopes);
@@ -18,7 +18,8 @@ const naver = (options) => {
18
18
  authorizationEndpoint: "https://nid.naver.com/oauth2.0/authorize",
19
19
  scopes: _scopes,
20
20
  state,
21
- redirectURI
21
+ redirectURI,
22
+ additionalParams
22
23
  });
23
24
  },
24
25
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -20,7 +20,8 @@ declare const notion: (options: NotionOptions) => {
20
20
  state,
21
21
  scopes,
22
22
  loginHint,
23
- redirectURI
23
+ redirectURI,
24
+ additionalParams
24
25
  }: {
25
26
  state: string;
26
27
  codeVerifier: string;
@@ -28,6 +29,8 @@ declare const notion: (options: NotionOptions) => {
28
29
  redirectURI: string;
29
30
  display?: string | undefined;
30
31
  loginHint?: string | undefined;
32
+ idTokenNonce?: string | undefined;
33
+ additionalParams?: Record<string, string> | undefined;
31
34
  }): Promise<URL>;
32
35
  validateAuthorizationCode: ({
33
36
  code,
@@ -40,6 +43,7 @@ declare const notion: (options: NotionOptions) => {
40
43
  }) => Promise<OAuth2Tokens>;
41
44
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
42
45
  getUserInfo(token: OAuth2Tokens & {
46
+ expectedIdTokenNonce?: string | undefined;
43
47
  user?: {
44
48
  name?: {
45
49
  firstName?: string;
@@ -8,7 +8,7 @@ const notion = (options) => {
8
8
  return {
9
9
  id: "notion",
10
10
  name: "Notion",
11
- createAuthorizationURL({ state, scopes, loginHint, redirectURI }) {
11
+ createAuthorizationURL({ state, scopes, loginHint, redirectURI, additionalParams }) {
12
12
  const _scopes = options.disableDefaultScope ? [] : [];
13
13
  if (options.scope) _scopes.push(...options.scope);
14
14
  if (scopes) _scopes.push(...scopes);
@@ -20,7 +20,10 @@ const notion = (options) => {
20
20
  state,
21
21
  redirectURI,
22
22
  loginHint,
23
- additionalParams: { owner: "user" }
23
+ additionalParams: {
24
+ ...additionalParams ?? {},
25
+ owner: "user"
26
+ }
24
27
  });
25
28
  },
26
29
  validateAuthorizationCode: async ({ code, redirectURI }) => {
@@ -26,7 +26,8 @@ declare const paybin: (options: PaybinOptions) => {
26
26
  scopes,
27
27
  codeVerifier,
28
28
  redirectURI,
29
- loginHint
29
+ loginHint,
30
+ additionalParams
30
31
  }: {
31
32
  state: string;
32
33
  codeVerifier: string;
@@ -34,6 +35,8 @@ declare const paybin: (options: PaybinOptions) => {
34
35
  redirectURI: string;
35
36
  display?: string | undefined;
36
37
  loginHint?: string | undefined;
38
+ idTokenNonce?: string | undefined;
39
+ additionalParams?: Record<string, string> | undefined;
37
40
  }): Promise<URL>;
38
41
  validateAuthorizationCode: ({
39
42
  code,
@@ -47,6 +50,7 @@ declare const paybin: (options: PaybinOptions) => {
47
50
  }) => Promise<OAuth2Tokens>;
48
51
  refreshAccessToken: (refreshToken: string) => Promise<OAuth2Tokens>;
49
52
  getUserInfo(token: OAuth2Tokens & {
53
+ expectedIdTokenNonce?: string | undefined;
50
54
  user?: {
51
55
  name?: {
52
56
  firstName?: string;
@@ -1,5 +1,5 @@
1
- import { logger } from "../env/logger.mjs";
2
1
  import { BetterAuthError } from "../error/index.mjs";
2
+ import { logger } from "../env/logger.mjs";
3
3
  import { createAuthorizationURL } from "../oauth2/create-authorization-url.mjs";
4
4
  import { refreshAccessToken } from "../oauth2/refresh-access-token.mjs";
5
5
  import { validateAuthorizationCode } from "../oauth2/validate-authorization-code.mjs";
@@ -12,7 +12,7 @@ const paybin = (options) => {
12
12
  return {
13
13
  id: "paybin",
14
14
  name: "Paybin",
15
- async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint }) {
15
+ async createAuthorizationURL({ state, scopes, codeVerifier, redirectURI, loginHint, additionalParams }) {
16
16
  if (!options.clientId || !options.clientSecret) {
17
17
  logger.error("Client Id and Client Secret is required for Paybin. Make sure to provide them in the options.");
18
18
  throw new BetterAuthError("CLIENT_ID_AND_SECRET_REQUIRED");
@@ -34,7 +34,8 @@ const paybin = (options) => {
34
34
  codeVerifier,
35
35
  redirectURI,
36
36
  prompt: options.prompt,
37
- loginHint
37
+ loginHint,
38
+ additionalParams
38
39
  });
39
40
  },
40
41
  validateAuthorizationCode: async ({ code, codeVerifier, redirectURI }) => {