@better-auth/core 1.4.6-beta.2 → 1.4.6-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 (59) hide show
  1. package/.turbo/turbo-build.log +31 -30
  2. package/dist/api/index.d.mts +1 -2
  3. package/dist/api/index.mjs +3 -2
  4. package/dist/async_hooks/index.d.mts +1 -1
  5. package/dist/async_hooks/index.mjs +2 -1
  6. package/dist/async_hooks-CrTStdt6.mjs +45 -0
  7. package/dist/context/index.d.mts +2 -3
  8. package/dist/context/index.mjs +3 -2
  9. package/dist/{context-DgQ9XGBl.mjs → context-su4uu82y.mjs} +1 -1
  10. package/dist/db/adapter/index.d.mts +2 -3
  11. package/dist/db/adapter/index.mjs +955 -1
  12. package/dist/db/index.d.mts +2 -3
  13. package/dist/db/index.mjs +2 -1
  14. package/dist/env/index.d.mts +1 -1
  15. package/dist/env/index.mjs +1 -1
  16. package/dist/error/index.mjs +3 -2
  17. package/dist/{error-BhAKg8LX.mjs → error-CMXuwPsa.mjs} +1 -1
  18. package/dist/get-tables-BGfrxIVZ.mjs +252 -0
  19. package/dist/{index-D_XSRX55.d.mts → index-Bp1Bfmzt.d.mts} +307 -32
  20. package/dist/{index-DgwIISs7.d.mts → index-Da4Ujjef.d.mts} +0 -1
  21. package/dist/index.d.mts +2 -3
  22. package/dist/oauth2/index.d.mts +1 -2
  23. package/dist/oauth2/index.mjs +1 -1
  24. package/dist/social-providers/index.d.mts +2 -3
  25. package/dist/social-providers/index.mjs +22 -8
  26. package/dist/utils/index.d.mts +10 -1
  27. package/dist/utils/index.mjs +3 -2
  28. package/dist/utils-BqQC77zO.mjs +43 -0
  29. package/package.json +8 -6
  30. package/src/async_hooks/convex.spec.ts +12 -0
  31. package/src/async_hooks/index.ts +60 -25
  32. package/src/db/adapter/factory.ts +1368 -0
  33. package/src/db/adapter/get-default-field-name.ts +59 -0
  34. package/src/db/adapter/get-default-model-name.ts +51 -0
  35. package/src/db/adapter/get-field-attributes.ts +62 -0
  36. package/src/db/adapter/get-field-name.ts +43 -0
  37. package/src/db/adapter/get-id-field.ts +141 -0
  38. package/src/db/adapter/get-model-name.ts +36 -0
  39. package/src/db/adapter/index.ts +12 -0
  40. package/src/db/adapter/types.ts +161 -0
  41. package/src/db/adapter/utils.ts +61 -0
  42. package/src/db/get-tables.ts +276 -0
  43. package/src/db/index.ts +2 -0
  44. package/src/db/test/get-tables.test.ts +62 -0
  45. package/src/env/logger.ts +4 -6
  46. package/src/oauth2/oauth-provider.ts +1 -1
  47. package/src/social-providers/google.ts +46 -17
  48. package/src/types/context.ts +10 -0
  49. package/src/types/helper.ts +4 -0
  50. package/src/types/init-options.ts +24 -24
  51. package/src/utils/id.ts +5 -0
  52. package/src/utils/index.ts +3 -0
  53. package/src/utils/json.ts +25 -0
  54. package/src/utils/string.ts +3 -0
  55. package/dist/async_hooks-BfRfbd1J.mjs +0 -18
  56. package/dist/utils-C5EN75oV.mjs +0 -7
  57. /package/dist/{env-8yWFh7b8.mjs → env-D6s-lvJz.mjs} +0 -0
  58. /package/dist/{index-X1Fs3IbM.d.mts → index-D4vfN5ui.d.mts} +0 -0
  59. /package/dist/{oauth2-B2XPHgx5.mjs → oauth2-7k48hhcV.mjs} +0 -0
@@ -0,0 +1,276 @@
1
+ import type { BetterAuthOptions } from "../types";
2
+ import type { BetterAuthDBSchema, DBFieldAttribute } from "./type";
3
+
4
+ export const getAuthTables = (
5
+ options: BetterAuthOptions,
6
+ ): BetterAuthDBSchema => {
7
+ const pluginSchema = (options.plugins ?? []).reduce(
8
+ (acc, plugin) => {
9
+ const schema = plugin.schema;
10
+ if (!schema) return acc;
11
+ for (const [key, value] of Object.entries(schema)) {
12
+ acc[key] = {
13
+ fields: {
14
+ ...acc[key]?.fields,
15
+ ...value.fields,
16
+ },
17
+ modelName: value.modelName || key,
18
+ };
19
+ }
20
+ return acc;
21
+ },
22
+ {} as Record<
23
+ string,
24
+ { fields: Record<string, DBFieldAttribute>; modelName: string }
25
+ >,
26
+ );
27
+
28
+ const shouldAddRateLimitTable = options.rateLimit?.storage === "database";
29
+ const rateLimitTable = {
30
+ rateLimit: {
31
+ modelName: options.rateLimit?.modelName || "rateLimit",
32
+ fields: {
33
+ key: {
34
+ type: "string",
35
+ fieldName: options.rateLimit?.fields?.key || "key",
36
+ },
37
+ count: {
38
+ type: "number",
39
+ fieldName: options.rateLimit?.fields?.count || "count",
40
+ },
41
+ lastRequest: {
42
+ type: "number",
43
+ bigint: true,
44
+ fieldName: options.rateLimit?.fields?.lastRequest || "lastRequest",
45
+ },
46
+ },
47
+ },
48
+ } satisfies BetterAuthDBSchema;
49
+
50
+ const { user, session, account, ...pluginTables } = pluginSchema;
51
+
52
+ const sessionTable = {
53
+ session: {
54
+ modelName: options.session?.modelName || "session",
55
+ fields: {
56
+ expiresAt: {
57
+ type: "date",
58
+ required: true,
59
+ fieldName: options.session?.fields?.expiresAt || "expiresAt",
60
+ },
61
+ token: {
62
+ type: "string",
63
+ required: true,
64
+ fieldName: options.session?.fields?.token || "token",
65
+ unique: true,
66
+ },
67
+ createdAt: {
68
+ type: "date",
69
+ required: true,
70
+ fieldName: options.session?.fields?.createdAt || "createdAt",
71
+ defaultValue: () => new Date(),
72
+ },
73
+ updatedAt: {
74
+ type: "date",
75
+ required: true,
76
+ fieldName: options.session?.fields?.updatedAt || "updatedAt",
77
+ onUpdate: () => new Date(),
78
+ },
79
+ ipAddress: {
80
+ type: "string",
81
+ required: false,
82
+ fieldName: options.session?.fields?.ipAddress || "ipAddress",
83
+ },
84
+ userAgent: {
85
+ type: "string",
86
+ required: false,
87
+ fieldName: options.session?.fields?.userAgent || "userAgent",
88
+ },
89
+ userId: {
90
+ type: "string",
91
+ fieldName: options.session?.fields?.userId || "userId",
92
+ references: {
93
+ model: options.user?.modelName || "user",
94
+ field: "id",
95
+ onDelete: "cascade",
96
+ },
97
+ required: true,
98
+ index: true,
99
+ },
100
+ ...session?.fields,
101
+ ...options.session?.additionalFields,
102
+ },
103
+ order: 2,
104
+ },
105
+ } satisfies BetterAuthDBSchema;
106
+
107
+ return {
108
+ user: {
109
+ modelName: options.user?.modelName || "user",
110
+ fields: {
111
+ name: {
112
+ type: "string",
113
+ required: true,
114
+ fieldName: options.user?.fields?.name || "name",
115
+ sortable: true,
116
+ },
117
+ email: {
118
+ type: "string",
119
+ unique: true,
120
+ required: true,
121
+ fieldName: options.user?.fields?.email || "email",
122
+ sortable: true,
123
+ },
124
+ emailVerified: {
125
+ type: "boolean",
126
+ defaultValue: false,
127
+ required: true,
128
+ fieldName: options.user?.fields?.emailVerified || "emailVerified",
129
+ input: false,
130
+ },
131
+ image: {
132
+ type: "string",
133
+ required: false,
134
+ fieldName: options.user?.fields?.image || "image",
135
+ },
136
+ createdAt: {
137
+ type: "date",
138
+ defaultValue: () => new Date(),
139
+ required: true,
140
+ fieldName: options.user?.fields?.createdAt || "createdAt",
141
+ },
142
+ updatedAt: {
143
+ type: "date",
144
+ defaultValue: () => new Date(),
145
+ onUpdate: () => new Date(),
146
+ required: true,
147
+ fieldName: options.user?.fields?.updatedAt || "updatedAt",
148
+ },
149
+ ...user?.fields,
150
+ ...options.user?.additionalFields,
151
+ },
152
+ order: 1,
153
+ },
154
+ //only add session table if it's not stored in secondary storage
155
+ ...(!options.secondaryStorage || options.session?.storeSessionInDatabase
156
+ ? sessionTable
157
+ : {}),
158
+ account: {
159
+ modelName: options.account?.modelName || "account",
160
+ fields: {
161
+ accountId: {
162
+ type: "string",
163
+ required: true,
164
+ fieldName: options.account?.fields?.accountId || "accountId",
165
+ },
166
+ providerId: {
167
+ type: "string",
168
+ required: true,
169
+ fieldName: options.account?.fields?.providerId || "providerId",
170
+ },
171
+ userId: {
172
+ type: "string",
173
+ references: {
174
+ model: options.user?.modelName || "user",
175
+ field: "id",
176
+ onDelete: "cascade",
177
+ },
178
+ required: true,
179
+ fieldName: options.account?.fields?.userId || "userId",
180
+ index: true,
181
+ },
182
+ accessToken: {
183
+ type: "string",
184
+ required: false,
185
+ fieldName: options.account?.fields?.accessToken || "accessToken",
186
+ },
187
+ refreshToken: {
188
+ type: "string",
189
+ required: false,
190
+ fieldName: options.account?.fields?.refreshToken || "refreshToken",
191
+ },
192
+ idToken: {
193
+ type: "string",
194
+ required: false,
195
+ fieldName: options.account?.fields?.idToken || "idToken",
196
+ },
197
+ accessTokenExpiresAt: {
198
+ type: "date",
199
+ required: false,
200
+ fieldName:
201
+ options.account?.fields?.accessTokenExpiresAt ||
202
+ "accessTokenExpiresAt",
203
+ },
204
+ refreshTokenExpiresAt: {
205
+ type: "date",
206
+ required: false,
207
+ fieldName:
208
+ options.account?.fields?.refreshTokenExpiresAt ||
209
+ "refreshTokenExpiresAt",
210
+ },
211
+ scope: {
212
+ type: "string",
213
+ required: false,
214
+ fieldName: options.account?.fields?.scope || "scope",
215
+ },
216
+ password: {
217
+ type: "string",
218
+ required: false,
219
+ fieldName: options.account?.fields?.password || "password",
220
+ },
221
+ createdAt: {
222
+ type: "date",
223
+ required: true,
224
+ fieldName: options.account?.fields?.createdAt || "createdAt",
225
+ defaultValue: () => new Date(),
226
+ },
227
+ updatedAt: {
228
+ type: "date",
229
+ required: true,
230
+ fieldName: options.account?.fields?.updatedAt || "updatedAt",
231
+ onUpdate: () => new Date(),
232
+ },
233
+ ...account?.fields,
234
+ ...options.account?.additionalFields,
235
+ },
236
+ order: 3,
237
+ },
238
+ verification: {
239
+ modelName: options.verification?.modelName || "verification",
240
+ fields: {
241
+ identifier: {
242
+ type: "string",
243
+ required: true,
244
+ fieldName: options.verification?.fields?.identifier || "identifier",
245
+ index: true,
246
+ },
247
+ value: {
248
+ type: "string",
249
+ required: true,
250
+ fieldName: options.verification?.fields?.value || "value",
251
+ },
252
+ expiresAt: {
253
+ type: "date",
254
+ required: true,
255
+ fieldName: options.verification?.fields?.expiresAt || "expiresAt",
256
+ },
257
+ createdAt: {
258
+ type: "date",
259
+ required: true,
260
+ defaultValue: () => new Date(),
261
+ fieldName: options.verification?.fields?.createdAt || "createdAt",
262
+ },
263
+ updatedAt: {
264
+ type: "date",
265
+ required: true,
266
+ defaultValue: () => new Date(),
267
+ onUpdate: () => new Date(),
268
+ fieldName: options.verification?.fields?.updatedAt || "updatedAt",
269
+ },
270
+ },
271
+ order: 4,
272
+ },
273
+ ...pluginTables,
274
+ ...(shouldAddRateLimitTable ? rateLimitTable : {}),
275
+ } satisfies BetterAuthDBSchema;
276
+ };
package/src/db/index.ts CHANGED
@@ -49,3 +49,5 @@ export type Primitive = DBPrimitive;
49
49
  * @deprecated Backport for 1.3.x, we will remove this in 1.4.x
50
50
  */
51
51
  export type BetterAuthDbSchema = BetterAuthDBSchema;
52
+
53
+ export { getAuthTables } from "./get-tables";
@@ -0,0 +1,62 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { getAuthTables } from "../get-tables";
3
+
4
+ describe("getAuthTables", () => {
5
+ it("should use correct field name for refreshTokenExpiresAt", () => {
6
+ const tables = getAuthTables({
7
+ account: {
8
+ fields: {
9
+ refreshTokenExpiresAt: "custom_refresh_token_expires_at",
10
+ },
11
+ },
12
+ });
13
+
14
+ const accountTable = tables.account;
15
+ const refreshTokenExpiresAtField =
16
+ accountTable!.fields.refreshTokenExpiresAt!;
17
+
18
+ expect(refreshTokenExpiresAtField.fieldName).toBe(
19
+ "custom_refresh_token_expires_at",
20
+ );
21
+ });
22
+
23
+ it("should not use accessTokenExpiresAt field name for refreshTokenExpiresAt", () => {
24
+ const tables = getAuthTables({
25
+ account: {
26
+ fields: {
27
+ accessTokenExpiresAt: "custom_access_token_expires_at",
28
+ refreshTokenExpiresAt: "custom_refresh_token_expires_at",
29
+ },
30
+ },
31
+ });
32
+
33
+ const accountTable = tables.account;
34
+ const refreshTokenExpiresAtField =
35
+ accountTable!.fields.refreshTokenExpiresAt!;
36
+ const accessTokenExpiresAtField =
37
+ accountTable!.fields.accessTokenExpiresAt!;
38
+
39
+ expect(refreshTokenExpiresAtField.fieldName).toBe(
40
+ "custom_refresh_token_expires_at",
41
+ );
42
+ expect(accessTokenExpiresAtField.fieldName).toBe(
43
+ "custom_access_token_expires_at",
44
+ );
45
+ expect(refreshTokenExpiresAtField.fieldName).not.toBe(
46
+ accessTokenExpiresAtField.fieldName,
47
+ );
48
+ });
49
+
50
+ it("should use default field names when no custom names provided", () => {
51
+ const tables = getAuthTables({});
52
+
53
+ const accountTable = tables.account;
54
+ const refreshTokenExpiresAtField =
55
+ accountTable!.fields.refreshTokenExpiresAt!;
56
+ const accessTokenExpiresAtField =
57
+ accountTable!.fields.accessTokenExpiresAt!;
58
+
59
+ expect(refreshTokenExpiresAtField.fieldName).toBe("refreshTokenExpiresAt");
60
+ expect(accessTokenExpiresAtField.fieldName).toBe("accessTokenExpiresAt");
61
+ });
62
+ });
package/src/env/logger.ts CHANGED
@@ -55,12 +55,10 @@ export interface Logger {
55
55
  | undefined;
56
56
  }
57
57
 
58
- export type LogHandlerParams = Parameters<NonNullable<Logger["log"]>> extends [
59
- LogLevel,
60
- ...infer Rest,
61
- ]
62
- ? Rest
63
- : never;
58
+ export type LogHandlerParams =
59
+ Parameters<NonNullable<Logger["log"]>> extends [LogLevel, ...infer Rest]
60
+ ? Rest
61
+ : never;
64
62
 
65
63
  const levelColors: Record<LogLevel, string> = {
66
64
  info: TTY_COLORS.fg.blue,
@@ -150,7 +150,7 @@ export type ProviderOptions<Profile extends Record<string, any> = any> = {
150
150
  [key: string]: any;
151
151
  };
152
152
  data: any;
153
- }>)
153
+ } | null>)
154
154
  | undefined;
155
155
  /**
156
156
  * Custom function to refresh a token
@@ -1,5 +1,6 @@
1
1
  import { betterFetch } from "@better-fetch/fetch";
2
- import { decodeJwt } from "jose";
2
+ import { APIError } from "better-call";
3
+ import { decodeJwt, decodeProtectedHeader, importJWK, jwtVerify } from "jose";
3
4
  import { logger } from "../env";
4
5
  import { BetterAuthError } from "../error";
5
6
  import type { OAuthProvider, ProviderOptions } from "../oauth2";
@@ -126,24 +127,26 @@ export const google = (options: GoogleOptions) => {
126
127
  if (options.verifyIdToken) {
127
128
  return options.verifyIdToken(token, nonce);
128
129
  }
129
- const googlePublicKeyUrl = `https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=${token}`;
130
- const { data: tokenInfo } = await betterFetch<{
131
- aud: string;
132
- iss: string;
133
- email: string;
134
- email_verified: boolean;
135
- name: string;
136
- picture: string;
137
- sub: string;
138
- }>(googlePublicKeyUrl);
139
- if (!tokenInfo) {
130
+
131
+ // Verify JWT integrity
132
+ // See https://developers.google.com/identity/sign-in/web/backend-auth#verify-the-integrity-of-the-id-token
133
+
134
+ const { kid, alg: jwtAlg } = decodeProtectedHeader(token);
135
+ if (!kid || !jwtAlg) return false;
136
+
137
+ const publicKey = await getGooglePublicKey(kid);
138
+ const { payload: jwtClaims } = await jwtVerify(token, publicKey, {
139
+ algorithms: [jwtAlg],
140
+ issuer: ["https://accounts.google.com", "accounts.google.com"],
141
+ audience: options.clientId,
142
+ maxTokenAge: "1h",
143
+ });
144
+
145
+ if (nonce && jwtClaims.nonce !== nonce) {
140
146
  return false;
141
147
  }
142
- const isValid =
143
- tokenInfo.aud === options.clientId &&
144
- (tokenInfo.iss === "https://accounts.google.com" ||
145
- tokenInfo.iss === "accounts.google.com");
146
- return isValid;
148
+
149
+ return true;
147
150
  },
148
151
  async getUserInfo(token) {
149
152
  if (options.getUserInfo) {
@@ -169,3 +172,29 @@ export const google = (options: GoogleOptions) => {
169
172
  options,
170
173
  } satisfies OAuthProvider<GoogleProfile>;
171
174
  };
175
+
176
+ export const getGooglePublicKey = async (kid: string) => {
177
+ const { data } = await betterFetch<{
178
+ keys: Array<{
179
+ kid: string;
180
+ alg: string;
181
+ kty: string;
182
+ use: string;
183
+ n: string;
184
+ e: string;
185
+ }>;
186
+ }>("https://www.googleapis.com/oauth2/v3/certs");
187
+
188
+ if (!data?.keys) {
189
+ throw new APIError("BAD_REQUEST", {
190
+ message: "Keys not found",
191
+ });
192
+ }
193
+
194
+ const jwk = data.keys.find((key) => key.kid === kid);
195
+ if (!jwk) {
196
+ throw new Error(`JWK with kid ${kid} not found`);
197
+ }
198
+
199
+ return await importJWK(jwk, jwk.alg);
200
+ };
@@ -165,6 +165,16 @@ export type AuthContext<Options extends BetterAuthOptions = BetterAuthOptions> =
165
165
  appName: string;
166
166
  baseURL: string;
167
167
  trustedOrigins: string[];
168
+ /**
169
+ * Verifies whether url is a trusted origin according to the "trustedOrigins" configuration
170
+ * @param url The url to verify against the "trustedOrigins" configuration
171
+ * @param settings Specify supported pattern matching settings
172
+ * @returns {boolean} true if the URL matches the origin pattern, false otherwise.
173
+ */
174
+ isTrustedOrigin: (
175
+ url: string,
176
+ settings?: { allowRelativePaths: boolean },
177
+ ) => boolean;
168
178
  oauthConfig: {
169
179
  /**
170
180
  * This is dangerous and should only be used in dev or staging environments.
@@ -4,3 +4,7 @@ export type LiteralString = "" | (string & Record<never, never>);
4
4
  export type LiteralUnion<LiteralType, BaseType extends Primitive> =
5
5
  | LiteralType
6
6
  | (BaseType & Record<never, never>);
7
+
8
+ export type Prettify<T> = {
9
+ [K in keyof T]: T[K];
10
+ } & {};
@@ -995,7 +995,7 @@ export type BetterAuthOptions = {
995
995
  */
996
996
  before?: (
997
997
  user: User & Record<string, unknown>,
998
- context?: GenericEndpointContext,
998
+ context: GenericEndpointContext | null,
999
999
  ) => Promise<
1000
1000
  | boolean
1001
1001
  | void
@@ -1008,7 +1008,7 @@ export type BetterAuthOptions = {
1008
1008
  */
1009
1009
  after?: (
1010
1010
  user: User & Record<string, unknown>,
1011
- context?: GenericEndpointContext,
1011
+ context: GenericEndpointContext | null,
1012
1012
  ) => Promise<void>;
1013
1013
  };
1014
1014
  update?: {
@@ -1019,7 +1019,7 @@ export type BetterAuthOptions = {
1019
1019
  */
1020
1020
  before?: (
1021
1021
  user: Partial<User> & Record<string, unknown>,
1022
- context?: GenericEndpointContext,
1022
+ context: GenericEndpointContext | null,
1023
1023
  ) => Promise<
1024
1024
  | boolean
1025
1025
  | void
@@ -1032,7 +1032,7 @@ export type BetterAuthOptions = {
1032
1032
  */
1033
1033
  after?: (
1034
1034
  user: User & Record<string, unknown>,
1035
- context?: GenericEndpointContext,
1035
+ context: GenericEndpointContext | null,
1036
1036
  ) => Promise<void>;
1037
1037
  };
1038
1038
  delete?: {
@@ -1042,14 +1042,14 @@ export type BetterAuthOptions = {
1042
1042
  */
1043
1043
  before?: (
1044
1044
  user: User & Record<string, unknown>,
1045
- context?: GenericEndpointContext,
1045
+ context: GenericEndpointContext | null,
1046
1046
  ) => Promise<boolean | void>;
1047
1047
  /**
1048
1048
  * Hook that is called after a user is deleted.
1049
1049
  */
1050
1050
  after?: (
1051
1051
  user: User & Record<string, unknown>,
1052
- context?: GenericEndpointContext,
1052
+ context: GenericEndpointContext | null,
1053
1053
  ) => Promise<void>;
1054
1054
  };
1055
1055
  };
@@ -1065,7 +1065,7 @@ export type BetterAuthOptions = {
1065
1065
  */
1066
1066
  before?: (
1067
1067
  session: Session & Record<string, unknown>,
1068
- context?: GenericEndpointContext,
1068
+ context: GenericEndpointContext | null,
1069
1069
  ) => Promise<
1070
1070
  | boolean
1071
1071
  | void
@@ -1078,7 +1078,7 @@ export type BetterAuthOptions = {
1078
1078
  */
1079
1079
  after?: (
1080
1080
  session: Session & Record<string, unknown>,
1081
- context?: GenericEndpointContext,
1081
+ context: GenericEndpointContext | null,
1082
1082
  ) => Promise<void>;
1083
1083
  };
1084
1084
  /**
@@ -1092,7 +1092,7 @@ export type BetterAuthOptions = {
1092
1092
  */
1093
1093
  before?: (
1094
1094
  session: Partial<Session> & Record<string, unknown>,
1095
- context?: GenericEndpointContext,
1095
+ context: GenericEndpointContext | null,
1096
1096
  ) => Promise<
1097
1097
  | boolean
1098
1098
  | void
@@ -1105,7 +1105,7 @@ export type BetterAuthOptions = {
1105
1105
  */
1106
1106
  after?: (
1107
1107
  session: Session & Record<string, unknown>,
1108
- context?: GenericEndpointContext,
1108
+ context: GenericEndpointContext | null,
1109
1109
  ) => Promise<void>;
1110
1110
  };
1111
1111
  delete?: {
@@ -1115,14 +1115,14 @@ export type BetterAuthOptions = {
1115
1115
  */
1116
1116
  before?: (
1117
1117
  session: Session & Record<string, unknown>,
1118
- context?: GenericEndpointContext,
1118
+ context: GenericEndpointContext | null,
1119
1119
  ) => Promise<boolean | void>;
1120
1120
  /**
1121
1121
  * Hook that is called after a session is deleted.
1122
1122
  */
1123
1123
  after?: (
1124
1124
  session: Session & Record<string, unknown>,
1125
- context?: GenericEndpointContext,
1125
+ context: GenericEndpointContext | null,
1126
1126
  ) => Promise<void>;
1127
1127
  };
1128
1128
  };
@@ -1138,7 +1138,7 @@ export type BetterAuthOptions = {
1138
1138
  */
1139
1139
  before?: (
1140
1140
  account: Account,
1141
- context?: GenericEndpointContext,
1141
+ context: GenericEndpointContext | null,
1142
1142
  ) => Promise<
1143
1143
  | boolean
1144
1144
  | void
@@ -1151,7 +1151,7 @@ export type BetterAuthOptions = {
1151
1151
  */
1152
1152
  after?: (
1153
1153
  account: Account,
1154
- context?: GenericEndpointContext,
1154
+ context: GenericEndpointContext | null,
1155
1155
  ) => Promise<void>;
1156
1156
  };
1157
1157
  /**
@@ -1165,7 +1165,7 @@ export type BetterAuthOptions = {
1165
1165
  */
1166
1166
  before?: (
1167
1167
  account: Partial<Account> & Record<string, unknown>,
1168
- context?: GenericEndpointContext,
1168
+ context: GenericEndpointContext | null,
1169
1169
  ) => Promise<
1170
1170
  | boolean
1171
1171
  | void
@@ -1178,7 +1178,7 @@ export type BetterAuthOptions = {
1178
1178
  */
1179
1179
  after?: (
1180
1180
  account: Account & Record<string, unknown>,
1181
- context?: GenericEndpointContext,
1181
+ context: GenericEndpointContext | null,
1182
1182
  ) => Promise<void>;
1183
1183
  };
1184
1184
  delete?: {
@@ -1188,14 +1188,14 @@ export type BetterAuthOptions = {
1188
1188
  */
1189
1189
  before?: (
1190
1190
  account: Account & Record<string, unknown>,
1191
- context?: GenericEndpointContext,
1191
+ context: GenericEndpointContext | null,
1192
1192
  ) => Promise<boolean | void>;
1193
1193
  /**
1194
1194
  * Hook that is called after an account is deleted.
1195
1195
  */
1196
1196
  after?: (
1197
1197
  account: Account & Record<string, unknown>,
1198
- context?: GenericEndpointContext,
1198
+ context: GenericEndpointContext | null,
1199
1199
  ) => Promise<void>;
1200
1200
  };
1201
1201
  };
@@ -1211,7 +1211,7 @@ export type BetterAuthOptions = {
1211
1211
  */
1212
1212
  before?: (
1213
1213
  verification: Verification & Record<string, unknown>,
1214
- context?: GenericEndpointContext,
1214
+ context: GenericEndpointContext | null,
1215
1215
  ) => Promise<
1216
1216
  | boolean
1217
1217
  | void
@@ -1224,7 +1224,7 @@ export type BetterAuthOptions = {
1224
1224
  */
1225
1225
  after?: (
1226
1226
  verification: Verification & Record<string, unknown>,
1227
- context?: GenericEndpointContext,
1227
+ context: GenericEndpointContext | null,
1228
1228
  ) => Promise<void>;
1229
1229
  };
1230
1230
  update?: {
@@ -1235,7 +1235,7 @@ export type BetterAuthOptions = {
1235
1235
  */
1236
1236
  before?: (
1237
1237
  verification: Partial<Verification> & Record<string, unknown>,
1238
- context?: GenericEndpointContext,
1238
+ context: GenericEndpointContext | null,
1239
1239
  ) => Promise<
1240
1240
  | boolean
1241
1241
  | void
@@ -1248,7 +1248,7 @@ export type BetterAuthOptions = {
1248
1248
  */
1249
1249
  after?: (
1250
1250
  verification: Verification & Record<string, unknown>,
1251
- context?: GenericEndpointContext,
1251
+ context: GenericEndpointContext | null,
1252
1252
  ) => Promise<void>;
1253
1253
  };
1254
1254
  delete?: {
@@ -1258,14 +1258,14 @@ export type BetterAuthOptions = {
1258
1258
  */
1259
1259
  before?: (
1260
1260
  verification: Verification & Record<string, unknown>,
1261
- context?: GenericEndpointContext,
1261
+ context: GenericEndpointContext | null,
1262
1262
  ) => Promise<boolean | void>;
1263
1263
  /**
1264
1264
  * Hook that is called after a verification is deleted.
1265
1265
  */
1266
1266
  after?: (
1267
1267
  verification: Verification & Record<string, unknown>,
1268
- context?: GenericEndpointContext,
1268
+ context: GenericEndpointContext | null,
1269
1269
  ) => Promise<void>;
1270
1270
  };
1271
1271
  };
@@ -0,0 +1,5 @@
1
+ import { createRandomStringGenerator } from "@better-auth/utils/random";
2
+
3
+ export const generateId = (size?: number) => {
4
+ return createRandomStringGenerator("a-z", "A-Z", "0-9")(size || 32);
5
+ };
@@ -1 +1,4 @@
1
1
  export { defineErrorCodes } from "./error-codes";
2
+ export { generateId } from "./id";
3
+ export { safeJSONParse } from "./json";
4
+ export { capitalizeFirstLetter } from "./string";