@better-auth/core 1.4.5 → 1.4.6-beta.3
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.
- package/.turbo/turbo-build.log +30 -29
- package/dist/api/index.d.mts +1 -2
- package/dist/api/index.mjs +3 -2
- package/dist/async_hooks/index.d.mts +1 -1
- package/dist/async_hooks/index.mjs +2 -1
- package/dist/async_hooks-CrTStdt6.mjs +45 -0
- package/dist/context/index.d.mts +2 -3
- package/dist/context/index.mjs +3 -2
- package/dist/{context-DgQ9XGBl.mjs → context-su4uu82y.mjs} +1 -1
- package/dist/db/adapter/index.d.mts +2 -3
- package/dist/db/adapter/index.mjs +951 -1
- package/dist/db/index.d.mts +2 -3
- package/dist/db/index.mjs +2 -1
- package/dist/env/index.d.mts +1 -1
- package/dist/env/index.mjs +1 -1
- package/dist/error/index.mjs +3 -2
- package/dist/{error-BhAKg8LX.mjs → error-CMXuwPsa.mjs} +1 -1
- package/dist/get-tables-BGfrxIVZ.mjs +252 -0
- package/dist/{index-D_XSRX55.d.mts → index-BfhTkcnW.d.mts} +273 -8
- package/dist/{index-DgwIISs7.d.mts → index-Da4Ujjef.d.mts} +0 -1
- package/dist/index.d.mts +2 -3
- package/dist/oauth2/index.d.mts +1 -2
- package/dist/oauth2/index.mjs +1 -1
- package/dist/social-providers/index.d.mts +1 -2
- package/dist/social-providers/index.mjs +4 -4
- package/dist/utils/index.d.mts +10 -1
- package/dist/utils/index.mjs +3 -2
- package/dist/utils-BqQC77zO.mjs +43 -0
- package/package.json +2 -2
- package/src/async_hooks/convex.spec.ts +12 -0
- package/src/async_hooks/index.ts +60 -25
- package/src/db/adapter/factory.ts +1362 -0
- package/src/db/adapter/get-default-field-name.ts +59 -0
- package/src/db/adapter/get-default-model-name.ts +51 -0
- package/src/db/adapter/get-field-attributes.ts +62 -0
- package/src/db/adapter/get-field-name.ts +43 -0
- package/src/db/adapter/get-id-field.ts +141 -0
- package/src/db/adapter/get-model-name.ts +36 -0
- package/src/db/adapter/index.ts +4 -0
- package/src/db/adapter/types.ts +161 -0
- package/src/db/adapter/utils.ts +61 -0
- package/src/db/get-tables.ts +276 -0
- package/src/db/index.ts +2 -0
- package/src/db/test/get-tables.test.ts +62 -0
- package/src/oauth2/oauth-provider.ts +1 -1
- package/src/types/context.ts +10 -0
- package/src/types/helper.ts +4 -0
- package/src/utils/id.ts +5 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/json.ts +25 -0
- package/src/utils/string.ts +3 -0
- package/dist/async_hooks-BfRfbd1J.mjs +0 -18
- package/dist/utils-C5EN75oV.mjs +0 -7
- /package/dist/{env-8yWFh7b8.mjs → env-D6s-lvJz.mjs} +0 -0
- /package/dist/{index-X1Fs3IbM.d.mts → index-D4vfN5ui.d.mts} +0 -0
- /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
|
@@ -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/types/context.ts
CHANGED
|
@@ -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.
|
package/src/types/helper.ts
CHANGED
package/src/utils/id.ts
ADDED
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { logger } from "../env";
|
|
2
|
+
|
|
3
|
+
export function safeJSONParse<T>(data: unknown): T | null {
|
|
4
|
+
function reviver(_: string, value: any): any {
|
|
5
|
+
if (typeof value === "string") {
|
|
6
|
+
const iso8601Regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
|
|
7
|
+
if (iso8601Regex.test(value)) {
|
|
8
|
+
const date = new Date(value);
|
|
9
|
+
if (!isNaN(date.getTime())) {
|
|
10
|
+
return date;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
if (typeof data !== "string") {
|
|
18
|
+
return data as T;
|
|
19
|
+
}
|
|
20
|
+
return JSON.parse(data, reviver);
|
|
21
|
+
} catch (e) {
|
|
22
|
+
logger.error("Error parsing JSON", { error: e });
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
//#region src/async_hooks/index.ts
|
|
2
|
-
const AsyncLocalStoragePromise = import(
|
|
3
|
-
/* @vite-ignore */
|
|
4
|
-
/* webpackIgnore: true */
|
|
5
|
-
"node:async_hooks"
|
|
6
|
-
).then((mod) => mod.AsyncLocalStorage).catch((err) => {
|
|
7
|
-
if ("AsyncLocalStorage" in globalThis) return globalThis.AsyncLocalStorage;
|
|
8
|
-
console.warn("[better-auth] Warning: AsyncLocalStorage is not available in this environment. Some features may not work as expected.");
|
|
9
|
-
console.warn("[better-auth] Please read more about this warning at https://better-auth.com/docs/installation#mount-handler");
|
|
10
|
-
console.warn("[better-auth] If you are using Cloudflare Workers, please see: https://developers.cloudflare.com/workers/configuration/compatibility-flags/#nodejs-compatibility-flag");
|
|
11
|
-
throw err;
|
|
12
|
-
});
|
|
13
|
-
async function getAsyncLocalStorage() {
|
|
14
|
-
return AsyncLocalStoragePromise;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
//#endregion
|
|
18
|
-
export { getAsyncLocalStorage as t };
|
package/dist/utils-C5EN75oV.mjs
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|