@better-auth/core 1.6.23 → 1.6.25

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 (125) hide show
  1. package/dist/api/index.d.mts +28 -28
  2. package/dist/async_hooks/index.d.mts +0 -1
  3. package/dist/async_hooks/pure.index.d.mts +0 -1
  4. package/dist/context/endpoint-context.d.mts +1 -1
  5. package/dist/context/global.d.mts +0 -1
  6. package/dist/context/global.mjs +1 -1
  7. package/dist/context/request-state.d.mts +0 -1
  8. package/dist/context/request-state.mjs +8 -5
  9. package/dist/context/transaction.d.mts +1 -1
  10. package/dist/db/adapter/factory.d.mts +3 -5
  11. package/dist/db/adapter/factory.mjs +1 -1
  12. package/dist/db/adapter/get-default-field-name.d.mts +2 -9
  13. package/dist/db/adapter/get-default-model-name.d.mts +1 -5
  14. package/dist/db/adapter/get-default-model-name.mjs +6 -5
  15. package/dist/db/adapter/get-field-attributes.d.mts +4 -11
  16. package/dist/db/adapter/get-field-name.d.mts +2 -9
  17. package/dist/db/adapter/get-id-field.d.mts +4 -12
  18. package/dist/db/adapter/get-model-name.d.mts +1 -5
  19. package/dist/db/adapter/index.d.mts +7 -32
  20. package/dist/db/adapter/types.d.mts +5 -17
  21. package/dist/db/adapter/utils.d.mts +0 -1
  22. package/dist/db/get-tables.d.mts +1 -0
  23. package/dist/db/get-tables.mjs +2 -2
  24. package/dist/db/plugin.d.mts +1 -2
  25. package/dist/db/schema/account.d.mts +1 -1
  26. package/dist/db/schema/account.mjs +12 -0
  27. package/dist/db/schema/rate-limit.d.mts +1 -1
  28. package/dist/db/schema/rate-limit.mjs +9 -0
  29. package/dist/db/schema/session.d.mts +1 -1
  30. package/dist/db/schema/shared.d.mts +0 -1
  31. package/dist/db/schema/user.d.mts +1 -1
  32. package/dist/db/schema/verification.d.mts +1 -1
  33. package/dist/db/type.d.mts +5 -8
  34. package/dist/env/logger.d.mts +25 -25
  35. package/dist/error/codes.d.mts +0 -1
  36. package/dist/error/index.d.mts +0 -1
  37. package/dist/index.d.mts +1 -1
  38. package/dist/instrumentation/attributes.d.mts +0 -1
  39. package/dist/instrumentation/pure.index.d.mts +0 -1
  40. package/dist/instrumentation/tracer.mjs +1 -1
  41. package/dist/oauth2/client-credentials-token.d.mts +4 -20
  42. package/dist/oauth2/client-credentials-token.mjs +1 -4
  43. package/dist/oauth2/create-authorization-url.d.mts +3 -20
  44. package/dist/oauth2/oauth-provider.d.mts +9 -3
  45. package/dist/oauth2/refresh-access-token.d.mts +4 -22
  46. package/dist/oauth2/refresh-access-token.mjs +2 -8
  47. package/dist/oauth2/reject-redirects.mjs +1 -1
  48. package/dist/oauth2/utils.d.mts +0 -1
  49. package/dist/oauth2/utils.mjs +1 -2
  50. package/dist/oauth2/validate-authorization-code.d.mts +6 -37
  51. package/dist/oauth2/verify.d.mts +12 -10
  52. package/dist/oauth2/verify.mjs +2 -2
  53. package/dist/social-providers/apple.d.mts +7 -28
  54. package/dist/social-providers/apple.mjs +4 -3
  55. package/dist/social-providers/atlassian.d.mts +3 -11
  56. package/dist/social-providers/cognito.d.mts +6 -12
  57. package/dist/social-providers/cognito.mjs +2 -2
  58. package/dist/social-providers/discord.d.mts +3 -9
  59. package/dist/social-providers/dropbox.d.mts +3 -11
  60. package/dist/social-providers/facebook.d.mts +6 -11
  61. package/dist/social-providers/facebook.mjs +2 -2
  62. package/dist/social-providers/figma.d.mts +3 -11
  63. package/dist/social-providers/github.d.mts +3 -12
  64. package/dist/social-providers/gitlab.d.mts +3 -12
  65. package/dist/social-providers/google.d.mts +7 -20
  66. package/dist/social-providers/google.mjs +2 -2
  67. package/dist/social-providers/huggingface.d.mts +3 -11
  68. package/dist/social-providers/index.d.mts +78 -362
  69. package/dist/social-providers/kakao.d.mts +3 -9
  70. package/dist/social-providers/kick.d.mts +3 -11
  71. package/dist/social-providers/line.d.mts +6 -13
  72. package/dist/social-providers/line.mjs +2 -2
  73. package/dist/social-providers/linear.d.mts +3 -10
  74. package/dist/social-providers/linkedin.d.mts +3 -10
  75. package/dist/social-providers/microsoft-entra-id.d.mts +5 -6
  76. package/dist/social-providers/microsoft-entra-id.mjs +4 -3
  77. package/dist/social-providers/naver.d.mts +22 -18
  78. package/dist/social-providers/notion.d.mts +3 -10
  79. package/dist/social-providers/paybin.d.mts +3 -12
  80. package/dist/social-providers/paypal.d.mts +6 -10
  81. package/dist/social-providers/paypal.mjs +2 -2
  82. package/dist/social-providers/polar.d.mts +3 -11
  83. package/dist/social-providers/railway.d.mts +3 -11
  84. package/dist/social-providers/reddit.d.mts +3 -9
  85. package/dist/social-providers/roblox.d.mts +3 -9
  86. package/dist/social-providers/salesforce.d.mts +3 -11
  87. package/dist/social-providers/slack.d.mts +3 -9
  88. package/dist/social-providers/spotify.d.mts +3 -11
  89. package/dist/social-providers/tiktok.d.mts +5 -10
  90. package/dist/social-providers/twitch.d.mts +3 -9
  91. package/dist/social-providers/twitter.d.mts +26 -17
  92. package/dist/social-providers/vercel.d.mts +3 -11
  93. package/dist/social-providers/vk.d.mts +3 -12
  94. package/dist/social-providers/vk.mjs +4 -5
  95. package/dist/social-providers/wechat.d.mts +3 -8
  96. package/dist/social-providers/zoom.d.mts +5 -14
  97. package/dist/types/context.d.mts +2 -1
  98. package/dist/types/cookie.d.mts +0 -1
  99. package/dist/types/helper.d.mts +1 -1
  100. package/dist/types/index.d.mts +1 -1
  101. package/dist/types/init-options.d.mts +10 -7
  102. package/dist/types/plugin-client.d.mts +1 -3
  103. package/dist/types/plugin.d.mts +2 -2
  104. package/dist/utils/async.d.mts +0 -1
  105. package/dist/utils/db.d.mts +1 -0
  106. package/dist/utils/deprecate.d.mts +0 -1
  107. package/dist/utils/error-codes.d.mts +2 -2
  108. package/dist/utils/host.d.mts +27 -1
  109. package/dist/utils/host.mjs +1 -1
  110. package/dist/utils/ip.d.mts +1 -0
  111. package/dist/utils/ip.mjs +1 -1
  112. package/dist/utils/is-api-error.d.mts +0 -1
  113. package/dist/utils/redirect-uri.d.mts +0 -1
  114. package/package.json +2 -2
  115. package/src/context/request-state.ts +26 -6
  116. package/src/db/adapter/get-default-model-name.ts +23 -14
  117. package/src/db/get-tables.ts +17 -2
  118. package/src/oauth2/oauth-provider.ts +20 -4
  119. package/src/social-providers/apple.ts +9 -3
  120. package/src/social-providers/cognito.ts +7 -2
  121. package/src/social-providers/facebook.ts +7 -2
  122. package/src/social-providers/google.ts +7 -2
  123. package/src/social-providers/line.ts +7 -2
  124. package/src/social-providers/microsoft-entra-id.ts +7 -2
  125. package/src/social-providers/paypal.ts +7 -2
@@ -20,26 +20,35 @@ export const initGetDefaultModelName = ({
20
20
  * 3. Using this function helps us get the actual model name based on the user's defined custom modelName.
21
21
  */
22
22
  const getDefaultModelName = (model: string) => {
23
+ // Resolve a model string (either a schema key or a user-defined
24
+ // `modelName`) back to its canonical schema key.
25
+ //
26
+ // An exact schema-key match must win over a `modelName` match.
27
+ // Better-auth internals (and `references.model`, see get-tables.ts)
28
+ // always pass canonical schema keys, so when a user remaps a
29
+ // built-in table onto another table's schema key (e.g.
30
+ // `user.modelName = "account"`), preferring the modelName alias
31
+ // would silently reroute every internal "account" query to the
32
+ // user table. The modelName lookup is only a fallback for
33
+ // externally supplied physical table names.
34
+ // @see https://github.com/better-auth/better-auth/issues/8111
35
+ // @see https://github.com/better-auth/better-auth/issues/10136
36
+ const resolve = (candidate: string): string | undefined => {
37
+ if (schema[candidate]) return candidate;
38
+ return Object.entries(schema).find(
39
+ ([_, f]) => f.modelName === candidate,
40
+ )?.[0];
41
+ };
42
+
23
43
  // It's possible this `model` could had applied `usePlural`.
24
44
  // Thus we'll try the search but without the trailing `s`.
25
45
  if (usePlural && model.charAt(model.length - 1) === "s") {
26
46
  const pluralessModel = model.slice(0, -1);
27
- let m = schema[pluralessModel] ? pluralessModel : undefined;
28
- if (!m) {
29
- m = Object.entries(schema).find(
30
- ([_, f]) => f.modelName === pluralessModel,
31
- )?.[0];
32
- }
33
-
34
- if (m) {
35
- return m;
36
- }
47
+ const m = resolve(pluralessModel);
48
+ if (m) return m;
37
49
  }
38
50
 
39
- let m = schema[model] ? model : undefined;
40
- if (!m) {
41
- m = Object.entries(schema).find(([_, f]) => f.modelName === model)?.[0];
42
- }
51
+ const m = resolve(model);
43
52
 
44
53
  if (!m) {
45
54
  throw new BetterAuthError(`Model "${model}" not found in schema`);
@@ -142,7 +142,18 @@ export const getAuthTables = (
142
142
  type: "string",
143
143
  fieldName: options.session?.fields?.userId || "userId",
144
144
  references: {
145
- model: options.user?.modelName || "user",
145
+ // Use the canonical user schema key here rather than
146
+ // `options.user.modelName`. Downstream consumers (e.g.
147
+ // `getSchema`, `getMigrations`, and the runtime adapter
148
+ // resolvers) treat `references.model` as a schema key
149
+ // and look it up via `tables[references.model]` /
150
+ // `getDefaultModelName`. Writing the modelName alias
151
+ // here would collide when a user picks a modelName that
152
+ // matches another schema key (for example
153
+ // `user.modelName = "account"`), causing the FK to
154
+ // resolve to the wrong table.
155
+ // @see https://github.com/better-auth/better-auth/issues/8111
156
+ model: "user",
146
157
  field: "id",
147
158
  onDelete: "cascade",
148
159
  },
@@ -225,7 +236,11 @@ export const getAuthTables = (
225
236
  userId: {
226
237
  type: "string",
227
238
  references: {
228
- model: options.user?.modelName || "user",
239
+ // See note on `session.userId.references.model` above:
240
+ // always use the canonical user schema key so the FK
241
+ // target survives `user.modelName` aliasing.
242
+ // @see https://github.com/better-auth/better-auth/issues/8111
243
+ model: "user",
229
244
  field: "id",
230
245
  onDelete: "cascade",
231
246
  },
@@ -1,4 +1,8 @@
1
- import type { Awaitable, LiteralString } from "../types";
1
+ import type {
2
+ Awaitable,
3
+ GenericEndpointContext,
4
+ LiteralString,
5
+ } from "../types";
2
6
 
3
7
  export interface OAuth2Tokens {
4
8
  tokenType?: string | undefined;
@@ -74,10 +78,15 @@ export interface OAuthProvider<
74
78
  * Verify the id token
75
79
  * @param token - The id token
76
80
  * @param nonce - The nonce
81
+ * @param ctx - Request endpoint context (headers via `ctx.headers`)
77
82
  * @returns True if the id token is valid, false otherwise
78
83
  */
79
84
  verifyIdToken?:
80
- | ((token: string, nonce?: string) => Promise<boolean>)
85
+ | ((
86
+ token: string,
87
+ nonce?: string,
88
+ ctx?: GenericEndpointContext,
89
+ ) => Promise<boolean>)
81
90
  | undefined;
82
91
  /**
83
92
  * Disable implicit sign up for new users. When set to true for the provider,
@@ -137,10 +146,17 @@ export type ProviderOptions<Profile extends Record<string, any> = any> = {
137
146
  */
138
147
  disableIdTokenSignIn?: boolean | undefined;
139
148
  /**
140
- * verifyIdToken function to verify the id token
149
+ * Custom function to verify the id token.
150
+ * @param token - The id token
151
+ * @param nonce - The nonce
152
+ * @param ctx - Request endpoint context (headers via `ctx.headers`)
141
153
  */
142
154
  verifyIdToken?:
143
- | ((token: string, nonce?: string) => Promise<boolean>)
155
+ | ((
156
+ token: string,
157
+ nonce?: string,
158
+ ctx?: GenericEndpointContext,
159
+ ) => Promise<boolean>)
144
160
  | undefined;
145
161
  /**
146
162
  * Custom function to get user info from the provider
@@ -10,6 +10,7 @@ import {
10
10
  refreshAccessToken,
11
11
  validateAuthorizationCode,
12
12
  } from "../oauth2";
13
+ import type { GenericEndpointContext } from "../types";
13
14
  export interface AppleProfile {
14
15
  /**
15
16
  * The subject registered claim identifies the principal that’s the subject
@@ -100,7 +101,7 @@ export const apple = (options: AppleOptions) => {
100
101
  return {
101
102
  id: "apple",
102
103
  name: "Apple",
103
- async createAuthorizationURL({ state, scopes, redirectURI }) {
104
+ async createAuthorizationURL({ state, scopes, redirectURI, codeVerifier }) {
104
105
  if (!getPrimaryClientId(options.clientId) || !options.clientSecret) {
105
106
  logger.error(
106
107
  "Client ID and client secret are required for Apple. Make sure to provide them in the options.",
@@ -117,6 +118,7 @@ export const apple = (options: AppleOptions) => {
117
118
  scopes: _scope,
118
119
  state,
119
120
  redirectURI,
121
+ codeVerifier,
120
122
  responseMode: "form_post",
121
123
  responseType: "code id_token",
122
124
  });
@@ -131,12 +133,16 @@ export const apple = (options: AppleOptions) => {
131
133
  tokenEndpoint,
132
134
  });
133
135
  },
134
- async verifyIdToken(token, nonce) {
136
+ async verifyIdToken(
137
+ token: string,
138
+ nonce?: string,
139
+ ctx?: GenericEndpointContext,
140
+ ) {
135
141
  if (options.disableIdTokenSignIn) {
136
142
  return false;
137
143
  }
138
144
  if (options.verifyIdToken) {
139
- return options.verifyIdToken(token, nonce);
145
+ return options.verifyIdToken(token, nonce, ctx);
140
146
  }
141
147
  try {
142
148
  const decodedHeader = decodeProtectedHeader(token);
@@ -9,6 +9,7 @@ import {
9
9
  refreshAccessToken,
10
10
  validateAuthorizationCode,
11
11
  } from "../oauth2";
12
+ import type { GenericEndpointContext } from "../types";
12
13
 
13
14
  export interface CognitoProfile {
14
15
  sub: string;
@@ -130,12 +131,16 @@ export const cognito = (options: CognitoOptions) => {
130
131
  });
131
132
  },
132
133
 
133
- async verifyIdToken(token, nonce) {
134
+ async verifyIdToken(
135
+ token: string,
136
+ nonce?: string,
137
+ ctx?: GenericEndpointContext,
138
+ ) {
134
139
  if (options.disableIdTokenSignIn) {
135
140
  return false;
136
141
  }
137
142
  if (options.verifyIdToken) {
138
- return options.verifyIdToken(token, nonce);
143
+ return options.verifyIdToken(token, nonce, ctx);
139
144
  }
140
145
 
141
146
  try {
@@ -9,6 +9,7 @@ import {
9
9
  refreshAccessToken,
10
10
  validateAuthorizationCode,
11
11
  } from "../oauth2";
12
+ import type { GenericEndpointContext } from "../types";
12
13
  export interface FacebookProfile {
13
14
  id: string;
14
15
  name: string;
@@ -130,13 +131,17 @@ export const facebook = (options: FacebookOptions) => {
130
131
  tokenEndpoint: "https://graph.facebook.com/v24.0/oauth/access_token",
131
132
  });
132
133
  },
133
- async verifyIdToken(token, nonce) {
134
+ async verifyIdToken(
135
+ token: string,
136
+ nonce?: string,
137
+ ctx?: GenericEndpointContext,
138
+ ) {
134
139
  if (options.disableIdTokenSignIn) {
135
140
  return false;
136
141
  }
137
142
 
138
143
  if (options.verifyIdToken) {
139
- return options.verifyIdToken(token, nonce);
144
+ return options.verifyIdToken(token, nonce, ctx);
140
145
  }
141
146
 
142
147
  /* limited login */
@@ -10,6 +10,7 @@ import {
10
10
  refreshAccessToken,
11
11
  validateAuthorizationCode,
12
12
  } from "../oauth2";
13
+ import type { GenericEndpointContext } from "../types";
13
14
 
14
15
  export interface GoogleProfile {
15
16
  aud: string;
@@ -181,12 +182,16 @@ export const google = (options: GoogleOptions) => {
181
182
  tokenEndpoint: "https://oauth2.googleapis.com/token",
182
183
  });
183
184
  },
184
- async verifyIdToken(token, nonce) {
185
+ async verifyIdToken(
186
+ token: string,
187
+ nonce?: string,
188
+ ctx?: GenericEndpointContext,
189
+ ) {
185
190
  if (options.disableIdTokenSignIn) {
186
191
  return false;
187
192
  }
188
193
  if (options.verifyIdToken) {
189
- return options.verifyIdToken(token, nonce);
194
+ return options.verifyIdToken(token, nonce, ctx);
190
195
  }
191
196
 
192
197
  const jwtClaims = await verifyGoogleIdToken({
@@ -6,6 +6,7 @@ import {
6
6
  refreshAccessToken,
7
7
  validateAuthorizationCode,
8
8
  } from "../oauth2";
9
+ import type { GenericEndpointContext } from "../types";
9
10
 
10
11
  export interface LineIdTokenPayload {
11
12
  iss: string;
@@ -94,12 +95,16 @@ export const line = (options: LineOptions) => {
94
95
  tokenEndpoint,
95
96
  });
96
97
  },
97
- async verifyIdToken(token, nonce) {
98
+ async verifyIdToken(
99
+ token: string,
100
+ nonce?: string,
101
+ ctx?: GenericEndpointContext,
102
+ ) {
98
103
  if (options.disableIdTokenSignIn) {
99
104
  return false;
100
105
  }
101
106
  if (options.verifyIdToken) {
102
- return options.verifyIdToken(token, nonce);
107
+ return options.verifyIdToken(token, nonce, ctx);
103
108
  }
104
109
  const body = new URLSearchParams();
105
110
  body.set("id_token", token);
@@ -10,6 +10,7 @@ import {
10
10
  refreshAccessToken,
11
11
  validateAuthorizationCode,
12
12
  } from "../oauth2";
13
+ import type { GenericEndpointContext } from "../types";
13
14
 
14
15
  /**
15
16
  * Microsoft's fixed tenant id for personal (consumer) Microsoft accounts. Every
@@ -200,12 +201,16 @@ export const microsoft = (options: MicrosoftOptions) => {
200
201
  tokenEndpoint,
201
202
  });
202
203
  },
203
- async verifyIdToken(token, nonce) {
204
+ async verifyIdToken(
205
+ token: string,
206
+ nonce?: string,
207
+ ctx?: GenericEndpointContext,
208
+ ) {
204
209
  if (options.disableIdTokenSignIn) {
205
210
  return false;
206
211
  }
207
212
  if (options.verifyIdToken) {
208
- return options.verifyIdToken(token, nonce);
213
+ return options.verifyIdToken(token, nonce, ctx);
209
214
  }
210
215
 
211
216
  try {
@@ -5,6 +5,7 @@ import { logger } from "../env";
5
5
  import { APIError, BetterAuthError } from "../error";
6
6
  import type { OAuthProvider, ProviderOptions } from "../oauth2";
7
7
  import { createAuthorizationURL } from "../oauth2";
8
+ import type { GenericEndpointContext } from "../types";
8
9
 
9
10
  /**
10
11
  * ID token signing algorithms advertised by PayPal's OpenID configuration.
@@ -217,12 +218,16 @@ export const paypal = (options: PayPalOptions) => {
217
218
  }
218
219
  },
219
220
 
220
- async verifyIdToken(token, nonce) {
221
+ async verifyIdToken(
222
+ token: string,
223
+ nonce?: string,
224
+ ctx?: GenericEndpointContext,
225
+ ) {
221
226
  if (options.disableIdTokenSignIn) {
222
227
  return false;
223
228
  }
224
229
  if (options.verifyIdToken) {
225
- return options.verifyIdToken(token, nonce);
230
+ return options.verifyIdToken(token, nonce, ctx);
226
231
  }
227
232
 
228
233
  // Cryptographically verify the ID token. Decoding alone is not enough: