better-auth 0.0.2-beta.7 → 0.0.2

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 (96) hide show
  1. package/dist/access.d.ts +4 -0
  2. package/dist/access.js +126 -0
  3. package/dist/access.js.map +1 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +553 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/client/plugins.d.ts +2436 -0
  8. package/dist/client/plugins.js +411 -0
  9. package/dist/client/plugins.js.map +1 -0
  10. package/dist/client-A2Mt04KQ.d.ts +3503 -0
  11. package/dist/client.d.ts +1433 -0
  12. package/dist/client.js +693 -0
  13. package/dist/client.js.map +1 -0
  14. package/dist/helper-B5_2Vzba.d.ts +14 -0
  15. package/dist/index-Dg4eEXZW.d.ts +24 -0
  16. package/dist/index-W5nXvJ-p.d.ts +1498 -0
  17. package/dist/index.d.ts +6 -4
  18. package/dist/index.js +2195 -1191
  19. package/dist/index.js.map +1 -1
  20. package/dist/next-js.d.ts +14 -0
  21. package/dist/next-js.js +14 -0
  22. package/dist/next-js.js.map +1 -0
  23. package/dist/plugins.d.ts +892 -49
  24. package/dist/plugins.js +3951 -253
  25. package/dist/plugins.js.map +1 -1
  26. package/dist/preact.d.ts +8 -0
  27. package/dist/preact.js +294 -0
  28. package/dist/preact.js.map +1 -0
  29. package/dist/react.d.ts +14 -0
  30. package/dist/react.js +314 -0
  31. package/dist/react.js.map +1 -0
  32. package/dist/schema-BOszzrbQ.d.ts +792 -0
  33. package/dist/social.d.ts +4 -0
  34. package/dist/social.js +509 -0
  35. package/dist/social.js.map +1 -0
  36. package/dist/solid-start.d.ts +18 -0
  37. package/dist/solid-start.js +14 -0
  38. package/dist/solid-start.js.map +1 -0
  39. package/dist/solid.d.ts +2790 -0
  40. package/dist/solid.js +306 -0
  41. package/dist/solid.js.map +1 -0
  42. package/dist/statement-COylZd3J.d.ts +81 -0
  43. package/dist/svelte-kit.d.ts +10 -7
  44. package/dist/svelte-kit.js +12 -17
  45. package/dist/svelte-kit.js.map +1 -1
  46. package/dist/svelte.d.ts +2791 -0
  47. package/dist/svelte.js +304 -0
  48. package/dist/svelte.js.map +1 -0
  49. package/dist/type-DbMyI3b5.d.ts +5724 -0
  50. package/dist/types.d.ts +7 -0
  51. package/dist/types.js +1 -0
  52. package/dist/types.js.map +1 -0
  53. package/dist/vue.d.ts +14 -0
  54. package/dist/vue.js +311 -0
  55. package/dist/vue.js.map +1 -0
  56. package/package.json +80 -54
  57. package/LICENSE +0 -21
  58. package/dist/actions.d.ts +0 -33
  59. package/dist/actions.js +0 -1373
  60. package/dist/actions.js.map +0 -1
  61. package/dist/adapters/drizzle-adapter.d.ts +0 -10
  62. package/dist/adapters/drizzle-adapter.js +0 -1095
  63. package/dist/adapters/drizzle-adapter.js.map +0 -1
  64. package/dist/adapters/memory.d.ts +0 -8
  65. package/dist/adapters/memory.js +0 -136
  66. package/dist/adapters/memory.js.map +0 -1
  67. package/dist/adapters/mongodb-adapter.d.ts +0 -9
  68. package/dist/adapters/mongodb-adapter.js +0 -97
  69. package/dist/adapters/mongodb-adapter.js.map +0 -1
  70. package/dist/adapters/prisma-adapter.d.ts +0 -7
  71. package/dist/adapters/prisma-adapter.js +0 -144
  72. package/dist/adapters/prisma-adapter.js.map +0 -1
  73. package/dist/adapters/redis-adapter.d.ts +0 -7
  74. package/dist/adapters/redis-adapter.js +0 -65
  75. package/dist/adapters/redis-adapter.js.map +0 -1
  76. package/dist/adapters.d.ts +0 -3
  77. package/dist/adapters.js +0 -206
  78. package/dist/adapters.js.map +0 -1
  79. package/dist/h3.d.ts +0 -10
  80. package/dist/h3.js +0 -326
  81. package/dist/h3.js.map +0 -1
  82. package/dist/hono.d.ts +0 -10
  83. package/dist/hono.js +0 -25
  84. package/dist/hono.js.map +0 -1
  85. package/dist/index-UcTu1vUg.d.ts +0 -107
  86. package/dist/next.d.ts +0 -17
  87. package/dist/next.js +0 -26
  88. package/dist/next.js.map +0 -1
  89. package/dist/options-CH15FEBw.d.ts +0 -1562
  90. package/dist/providers.d.ts +0 -3
  91. package/dist/providers.js +0 -653
  92. package/dist/providers.js.map +0 -1
  93. package/dist/routes/session.d.ts +0 -39
  94. package/dist/routes/session.js +0 -128
  95. package/dist/routes/session.js.map +0 -1
  96. package/dist/types-DAxaMWCy.d.ts +0 -136
package/dist/index.js CHANGED
@@ -1,81 +1,90 @@
1
- // src/cookies/index.ts
2
- function serialize(name, value, attributes) {
3
- const keyValueEntries = [];
4
- keyValueEntries.push([encodeURIComponent(name), encodeURIComponent(value)]);
5
- if (attributes?.domain !== void 0) {
6
- keyValueEntries.push(["Domain", attributes.domain]);
7
- }
8
- if (attributes?.expires !== void 0) {
9
- keyValueEntries.push(["Expires", attributes.expires.toUTCString()]);
10
- }
11
- if (attributes?.httpOnly) {
12
- keyValueEntries.push(["HttpOnly"]);
13
- }
14
- if (attributes?.maxAge !== void 0) {
15
- keyValueEntries.push(["Max-Age", attributes.maxAge.toString()]);
16
- }
17
- if (attributes?.path !== void 0) {
18
- keyValueEntries.push(["Path", attributes.path]);
19
- }
20
- if (attributes?.sameSite === "lax") {
21
- keyValueEntries.push(["SameSite", "Lax"]);
22
- }
23
- if (attributes?.sameSite === "none") {
24
- keyValueEntries.push(["SameSite", "None"]);
25
- }
26
- if (attributes?.sameSite === "strict") {
27
- keyValueEntries.push(["SameSite", "Strict"]);
28
- }
29
- if (attributes?.secure) {
30
- keyValueEntries.push(["Secure"]);
31
- }
32
- return keyValueEntries.map((pair) => pair.join("=")).join("; ");
33
- }
34
- function parse(header) {
35
- const cookies = /* @__PURE__ */ new Map();
36
- const items = header.split("; ");
37
- for (const item of items) {
38
- const pair = item.split("=");
39
- const rawKey = pair[0];
40
- const rawValue = pair[1] ?? "";
41
- if (!rawKey)
1
+ // src/api/index.ts
2
+ import { createRouter } from "better-call";
3
+
4
+ // src/adapters/schema.ts
5
+ import { z } from "zod";
6
+ var accountSchema = z.object({
7
+ id: z.string(),
8
+ providerId: z.string(),
9
+ accountId: z.string(),
10
+ userId: z.string(),
11
+ accessToken: z.string().nullable().optional(),
12
+ refreshToken: z.string().nullable().optional(),
13
+ idToken: z.string().nullable().optional(),
14
+ accessTokenExpiresAt: z.date().nullable().optional(),
15
+ refreshTokenExpiresAt: z.date().nullable().optional(),
16
+ /**
17
+ * Password is only stored in the credential provider
18
+ */
19
+ password: z.string().optional().nullable()
20
+ });
21
+ var userSchema = z.object({
22
+ id: z.string(),
23
+ email: z.string().transform((val) => val.toLowerCase()),
24
+ emailVerified: z.boolean().default(false),
25
+ name: z.string(),
26
+ image: z.string().optional(),
27
+ createdAt: z.date().default(/* @__PURE__ */ new Date()),
28
+ updatedAt: z.date().default(/* @__PURE__ */ new Date())
29
+ });
30
+ var sessionSchema = z.object({
31
+ id: z.string(),
32
+ userId: z.string(),
33
+ expiresAt: z.date(),
34
+ ipAddress: z.string().optional(),
35
+ userAgent: z.string().optional()
36
+ });
37
+ function parseData(data, schema) {
38
+ const fields = schema.fields;
39
+ const parsedData = {};
40
+ for (const key in data) {
41
+ const field = fields[key];
42
+ if (!field) {
43
+ parsedData[key] = data[key];
44
+ continue;
45
+ }
46
+ if (field.returned === false) {
42
47
  continue;
43
- cookies.set(decodeURIComponent(rawKey), decodeURIComponent(rawValue));
48
+ }
49
+ parsedData[key] = data[key];
44
50
  }
45
- return cookies;
51
+ return parsedData;
46
52
  }
47
- var cookieManager = (header) => {
48
- return {
49
- set(name, value, options = {}) {
50
- const cookieStr = serialize(name, value, options);
51
- header.append("set-cookie", cookieStr);
52
- },
53
- get(name) {
54
- const cookie = header.get("cookie");
55
- if (!cookie)
56
- return null;
57
- const cookies = parse(cookie);
58
- const value = cookies.get(name);
59
- return value;
53
+ function getAllFields(options, table) {
54
+ let schema = {};
55
+ for (const plugin of options.plugins || []) {
56
+ if (plugin.schema && plugin.schema[table]) {
57
+ schema = {
58
+ ...schema,
59
+ ...plugin.schema[table].fields
60
+ };
60
61
  }
61
- };
62
- };
63
- function setSessionCookie(context, sessionId) {
64
- context.request.cookies.set(
65
- context.cookies.sessionToken.name,
66
- sessionId,
67
- context.cookies.sessionToken.options
68
- );
62
+ }
63
+ return schema;
69
64
  }
70
- function deleteSessionCooke(context) {
71
- context.request.cookies.set(context.cookies.sessionToken.name, "", {
72
- ...context.cookies.sessionToken.options,
73
- maxAge: 0
74
- });
65
+ function parseUser(options, user) {
66
+ const schema = getAllFields(options, "user");
67
+ return parseData(user, { fields: schema });
75
68
  }
69
+ function parseAccount(options, account) {
70
+ const schema = getAllFields(options, "account");
71
+ return parseData(account, { fields: schema });
72
+ }
73
+ function parseSession(options, session) {
74
+ const schema = getAllFields(options, "session");
75
+ return parseData(session, { fields: schema });
76
+ }
77
+
78
+ // src/api/middlewares/csrf.ts
79
+ import { APIError } from "better-call";
80
+ import { z as z2 } from "zod";
76
81
 
77
- // src/crypto/hmac.ts
78
- async function hmac(secretKey, message) {
82
+ // src/crypto/index.ts
83
+ import { xchacha20poly1305 } from "@noble/ciphers/chacha";
84
+ import { bytesToHex, hexToBytes, utf8ToBytes } from "@noble/ciphers/utils";
85
+ import { managedNonce } from "@noble/ciphers/webcrypto";
86
+ import { sha256 } from "@noble/hashes/sha256";
87
+ async function hs256(secretKey, message) {
79
88
  const enc = new TextEncoder();
80
89
  const algorithm = { name: "HMAC", hash: "SHA-256" };
81
90
  const key = await crypto.subtle.importKey(
@@ -93,638 +102,2089 @@ async function hmac(secretKey, message) {
93
102
  return btoa(String.fromCharCode(...new Uint8Array(signature)));
94
103
  }
95
104
 
96
- // src/crypto/random.ts
97
- function generateRandomString(size) {
98
- const i2hex = (i) => `0${i.toString(16)}`.slice(-2);
99
- const r = (a, i) => a + i2hex(i);
100
- const bytes = crypto.getRandomValues(new Uint8Array(size));
101
- return Array.from(bytes).reduce(r, "");
102
- }
105
+ // src/api/call.ts
106
+ import {
107
+ createEndpointCreator,
108
+ createMiddleware,
109
+ createMiddlewareCreator
110
+ } from "better-call";
111
+ var optionsMiddleware = createMiddleware(async () => {
112
+ return {};
113
+ });
114
+ var createAuthMiddleware = createMiddlewareCreator({
115
+ use: [optionsMiddleware]
116
+ });
117
+ var createAuthEndpoint = createEndpointCreator({
118
+ use: [optionsMiddleware]
119
+ });
120
+
121
+ // src/api/middlewares/csrf.ts
122
+ var csrfMiddleware = createAuthMiddleware(
123
+ {
124
+ body: z2.object({
125
+ csrfToken: z2.string().optional()
126
+ }).optional()
127
+ },
128
+ async (ctx) => {
129
+ if (ctx.request?.method !== "POST" || ctx.context.options.advanced?.disableCSRFCheck) {
130
+ return;
131
+ }
132
+ const url = new URL(ctx.request.url);
133
+ console.log(url.origin, ctx.context.options.baseURL);
134
+ if (url.origin === ctx.context.options.baseURL || ctx.context.options.trustedOrigins?.includes(url.origin)) {
135
+ return;
136
+ }
137
+ const csrfToken = ctx.body?.csrfToken;
138
+ const csrfCookie = await ctx.getSignedCookie(
139
+ ctx.context.authCookies.csrfToken.name,
140
+ ctx.context.secret
141
+ );
142
+ const [token, hash] = csrfCookie?.split("!") || [null, null];
143
+ if (!csrfToken || !csrfCookie || !token || !hash || csrfCookie !== csrfToken) {
144
+ ctx.setCookie(ctx.context.authCookies.csrfToken.name, "", {
145
+ maxAge: 0
146
+ });
147
+ throw new APIError("UNAUTHORIZED", {
148
+ message: "Invalid CSRF Token"
149
+ });
150
+ }
151
+ const expectedHash = await hs256(ctx.context.secret, token);
152
+ if (hash !== expectedHash) {
153
+ ctx.setCookie(ctx.context.authCookies.csrfToken.name, "", {
154
+ maxAge: 0
155
+ });
156
+ throw new APIError("UNAUTHORIZED", {
157
+ message: "Invalid CSRF Token"
158
+ });
159
+ }
160
+ }
161
+ );
162
+
163
+ // src/api/routes/sign-in.ts
164
+ import { APIError as APIError2 } from "better-call";
165
+ import { generateCodeVerifier } from "oslo/oauth2";
166
+ import { Argon2id } from "oslo/password";
167
+ import { z as z3 } from "zod";
168
+
169
+ // src/social-providers/apple.ts
170
+ import "arctic";
171
+ import { parseJWT } from "oslo/jwt";
172
+ import { betterFetch } from "@better-fetch/fetch";
173
+
174
+ // src/error/better-auth-error.ts
175
+ var BetterAuthError = class extends Error {
176
+ constructor(message) {
177
+ super(message);
178
+ }
179
+ };
103
180
 
104
- // src/plugins/csrf-check.ts
105
- var csrfHandler = async (context) => {
106
- const csrfToken = context.request.cookies.get(context.cookies.csrfToken.name);
107
- if (csrfToken) {
181
+ // src/utils/base-url.ts
182
+ function checkHasPath(url) {
183
+ try {
184
+ const parsedUrl = new URL(url);
185
+ return parsedUrl.pathname !== "/";
186
+ } catch (error2) {
187
+ console.error("Invalid URL:", error2);
188
+ return false;
189
+ }
190
+ }
191
+ function withPath(url, path = "/api/auth") {
192
+ const hasPath = checkHasPath(url);
193
+ if (hasPath) {
108
194
  return {
109
- status: 200,
110
- body: {
111
- csrfToken
112
- }
195
+ baseURL: new URL(url).origin,
196
+ withPath: url
197
+ };
198
+ }
199
+ path = path.startsWith("/") ? path : `/${path}`;
200
+ return {
201
+ baseURL: url,
202
+ withPath: `${url}${path}`
203
+ };
204
+ }
205
+ function getBaseURL(url, path) {
206
+ if (url) {
207
+ return withPath(url, path);
208
+ }
209
+ const env = typeof process !== "undefined" ? process.env : typeof import.meta !== "undefined" ? (
210
+ //@ts-ignore
211
+ import.meta.env
212
+ ) : {};
213
+ const fromEnv = env.BETTER_AUTH_URL || env.AUTH_URL || env.NEXT_PUBLIC_AUTH_URL || env.NEXT_PUBLIC_BETTER_AUTH_URL || env.PUBLIC_AUTH_URL || env.PUBLIC_BETTER_AUTH_URL || env.NUXT_PUBLIC_BETTER_AUTH_URL || env.NUXT_PUBLIC_AUTH_URL;
214
+ if (fromEnv) {
215
+ return withPath(fromEnv, path);
216
+ }
217
+ const isDev = !fromEnv && (env.NODE_ENV === "development" || env.NODE_ENV === "test");
218
+ if (isDev) {
219
+ return {
220
+ baseURL: "http://localhost:3000",
221
+ withPath: "http://localhost:3000/api/auth"
113
222
  };
114
223
  }
115
- const token = generateRandomString(32);
116
- const hash = await hmac(context.secret, token);
117
- const cookie = `${token}!${hash}`;
118
- context.request.cookies.set(
119
- context.cookies.csrfToken.name,
120
- cookie,
121
- context.cookies.csrfToken.options
224
+ throw new BetterAuthError(
225
+ "Could not infer baseURL from environment variables"
122
226
  );
227
+ }
228
+
229
+ // src/social-providers/utils.ts
230
+ function getRedirectURI(providerId, redirectURI) {
231
+ return redirectURI || `${getBaseURL()}/api/auth/callback/${providerId}`;
232
+ }
233
+
234
+ // src/social-providers/apple.ts
235
+ var apple = ({
236
+ clientId,
237
+ clientSecret,
238
+ redirectURI
239
+ }) => {
240
+ const tokenEndpoint = "https://appleid.apple.com/auth/token";
241
+ redirectURI = getRedirectURI("apple", redirectURI);
123
242
  return {
124
- status: 200,
125
- body: {
126
- csrfToken: cookie
243
+ id: "apple",
244
+ name: "Apple",
245
+ createAuthorizationURL({ state, scopes }) {
246
+ const _scope = scopes || ["email", "name", "openid"];
247
+ return new URL(
248
+ `https://appleid.apple.com/auth/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectURI}&scope=${_scope.join(
249
+ " "
250
+ )}&state=${state}`
251
+ );
252
+ },
253
+ validateAuthorizationCode: async (code) => {
254
+ const data = await betterFetch(tokenEndpoint, {
255
+ method: "POST",
256
+ body: new URLSearchParams({
257
+ client_id: clientId,
258
+ client_secret: clientSecret,
259
+ grant_type: "authorization_code",
260
+ code
261
+ }),
262
+ headers: {
263
+ "Content-Type": "application/x-www-form-urlencoded"
264
+ }
265
+ });
266
+ if (data.error) {
267
+ throw new BetterAuthError(data.error?.message || "");
268
+ }
269
+ return data.data;
270
+ },
271
+ async getUserInfo(token) {
272
+ const data = parseJWT(token.idToken())?.payload;
273
+ if (!data) {
274
+ return null;
275
+ }
276
+ return {
277
+ user: {
278
+ id: data.sub,
279
+ name: data.name,
280
+ email: data.email,
281
+ emailVerified: data.email_verified === "true"
282
+ },
283
+ data
284
+ };
127
285
  }
128
286
  };
129
287
  };
130
- var CSRFCheckPlugin = () => {
288
+
289
+ // src/social-providers/discord.ts
290
+ import { betterFetch as betterFetch2 } from "@better-fetch/fetch";
291
+ import { Discord } from "arctic";
292
+ var discord = ({
293
+ clientId,
294
+ clientSecret,
295
+ redirectURI
296
+ }) => {
297
+ const discordArctic = new Discord(
298
+ clientId,
299
+ clientSecret,
300
+ getRedirectURI("discord", redirectURI)
301
+ );
131
302
  return {
132
- id: "csrf",
133
- name: "CSRF Check",
134
- version: "1.0.0",
135
- hooks: {
136
- matcher: (context) => !context.disableCSRF,
137
- before: async (context) => {
138
- const csrfToken = context.request.body.csrfToken;
139
- const csrfCookie = context.request.cookies.get(
140
- context.cookies.csrfToken.name
141
- );
142
- const [token, hash] = csrfCookie?.split("!") || [null, null];
143
- if (!csrfToken || !csrfCookie || !token || !hash || csrfCookie !== csrfToken) {
144
- context.request.cookies.set(context.cookies.csrfToken.name, "", {
145
- ...context.cookies.csrfToken.options,
146
- maxAge: 0
147
- });
148
- return {
149
- response: {
150
- status: 403,
151
- statusText: "Invalid CSRF Token"
152
- }
153
- };
154
- }
155
- const expectedHash = await hmac(context.secret, token);
156
- if (hash !== expectedHash) {
157
- context.request.cookies.set(context.cookies.csrfToken.name, "", {
158
- ...context.cookies.csrfToken.options,
159
- maxAge: 0
160
- });
161
- return {
162
- response: {
163
- status: 403,
164
- statusText: "Invalid CSRF Token"
165
- }
166
- };
303
+ id: "discord",
304
+ name: "Discord",
305
+ createAuthorizationURL({ state, scopes }) {
306
+ const _scope = scopes || ["email"];
307
+ return discordArctic.createAuthorizationURL(state, _scope);
308
+ },
309
+ validateAuthorizationCode: discordArctic.validateAuthorizationCode,
310
+ async getUserInfo(token) {
311
+ const { data: profile, error: error2 } = await betterFetch2(
312
+ "https://discord.com/api/users/@me",
313
+ {
314
+ auth: {
315
+ type: "Bearer",
316
+ token: token.accessToken
317
+ }
167
318
  }
319
+ );
320
+ if (error2) {
168
321
  return null;
169
322
  }
170
- },
171
- handler: csrfHandler
323
+ return {
324
+ user: {
325
+ id: profile.id,
326
+ name: profile.display_name || profile.username || "",
327
+ email: profile.email,
328
+ emailVerified: profile.verified
329
+ },
330
+ data: profile
331
+ };
332
+ }
172
333
  };
173
334
  };
174
335
 
175
- // src/plugins/utils.ts
176
- var getPlugins = (options) => {
177
- const plugins = {
178
- post: [],
179
- pre: [],
180
- unordered: []
181
- };
182
- for (const plugin of options.plugins || []) {
183
- plugins[plugin.order || "unordered"].push(plugin);
184
- }
185
- const internalPlugins = [CSRFCheckPlugin()];
186
- return [
187
- ...plugins.pre,
188
- ...plugins.unordered,
189
- ...internalPlugins,
190
- ...plugins.post
191
- ];
192
- };
193
- var usePlugins = (context, ignorePlugins) => {
194
- const plugins = context.plugins.filter(
195
- (pl) => pl.hooks && !ignorePlugins?.includes(pl.id)
336
+ // src/social-providers/facebook.ts
337
+ import { betterFetch as betterFetch3 } from "@better-fetch/fetch";
338
+ import { Facebook } from "arctic";
339
+ var facebook = ({
340
+ clientId,
341
+ clientSecret,
342
+ redirectURI
343
+ }) => {
344
+ const facebookArctic = new Facebook(
345
+ clientId,
346
+ clientSecret,
347
+ getRedirectURI("facebook", redirectURI)
196
348
  );
197
- const hooks = plugins.map((plugin) => {
198
- return plugin.hooks;
199
- });
200
- const before = [];
201
- const after = [];
202
- for (const hook of hooks) {
203
- if (hook.matcher(context)) {
204
- hook.before && before.push(hook.before);
205
- hook.after && after.push(hook.after);
206
- }
207
- }
208
349
  return {
209
- before: async (context2) => {
210
- let ctx = context2;
211
- let response;
212
- for (const hook of before) {
213
- const res = await hook(ctx);
214
- if (res?.context) {
215
- ctx = res.context;
216
- }
217
- if (res?.response) {
218
- response = res.response;
219
- break;
350
+ id: "facebook",
351
+ name: "Facebook",
352
+ createAuthorizationURL({ state, scopes }) {
353
+ const _scopes = scopes || ["email", "public_profile"];
354
+ return facebookArctic.createAuthorizationURL(state, _scopes);
355
+ },
356
+ validateAuthorizationCode: facebookArctic.validateAuthorizationCode,
357
+ async getUserInfo(token) {
358
+ const { data: profile, error: error2 } = await betterFetch3(
359
+ "https://graph.facebook.com/me",
360
+ {
361
+ auth: {
362
+ type: "Bearer",
363
+ token: token.accessToken
364
+ }
220
365
  }
366
+ );
367
+ if (error2) {
368
+ return null;
221
369
  }
222
370
  return {
223
- context: ctx,
224
- response
371
+ user: {
372
+ id: profile.id,
373
+ name: profile.name,
374
+ email: profile.email,
375
+ emailVerified: profile.email_verified
376
+ },
377
+ data: profile
225
378
  };
379
+ }
380
+ };
381
+ };
382
+
383
+ // src/social-providers/github.ts
384
+ import { betterFetch as betterFetch4 } from "@better-fetch/fetch";
385
+ import { GitHub } from "arctic";
386
+ var github = ({
387
+ clientId,
388
+ clientSecret,
389
+ redirectURI
390
+ }) => {
391
+ const githubArctic = new GitHub(
392
+ clientId,
393
+ clientSecret,
394
+ getRedirectURI("github", redirectURI)
395
+ );
396
+ return {
397
+ id: "github",
398
+ name: "Github",
399
+ createAuthorizationURL({ state, scopes }) {
400
+ const _scopes = scopes || ["user:email"];
401
+ return githubArctic.createAuthorizationURL(state, _scopes);
226
402
  },
227
- after: async (context2, fnResponse) => {
228
- let ctx = context2;
229
- let response;
230
- for (const hook of after) {
231
- const res = await hook(ctx, fnResponse);
232
- if (res?.context) {
233
- ctx = res.context;
403
+ validateAuthorizationCode: githubArctic.validateAuthorizationCode,
404
+ async getUserInfo(token) {
405
+ const { data: profile, error: error2 } = await betterFetch4(
406
+ "https://api.github.com/user",
407
+ {
408
+ method: "GET",
409
+ headers: {
410
+ Authorization: `Bearer ${token.accessToken}`
411
+ }
234
412
  }
235
- if (res?.response) {
236
- response = res.response;
237
- break;
413
+ );
414
+ if (error2) {
415
+ return null;
416
+ }
417
+ let emailVerified = false;
418
+ if (!profile.email) {
419
+ const { data, error: error3 } = await betterFetch4("https://api.github.com/user/emails", {
420
+ headers: {
421
+ Authorization: `Bearer ${token.accessToken}`,
422
+ "User-Agent": "better-auth"
423
+ }
424
+ });
425
+ if (!error3) {
426
+ profile.email = (data.find((e) => e.primary) ?? data[0])?.email;
427
+ emailVerified = data.find((e) => e.email === profile.email)?.verified ?? false;
238
428
  }
239
429
  }
240
430
  return {
241
- context: ctx,
242
- response
431
+ user: {
432
+ id: profile.id,
433
+ name: profile.name,
434
+ email: profile.email,
435
+ image: profile.avatar_url,
436
+ emailVerified,
437
+ createdAt: /* @__PURE__ */ new Date(),
438
+ updatedAt: /* @__PURE__ */ new Date()
439
+ },
440
+ data: profile
243
441
  };
244
442
  }
245
443
  };
246
444
  };
247
- var withPlugins = (fn, ignorePlugins) => {
248
- return async (ctx) => {
249
- const { before, after } = usePlugins(ctx, ignorePlugins);
250
- const { context, response } = await before(ctx);
251
- if (response) {
252
- return response;
253
- }
254
- const res = await fn(context);
255
- const { response: afterResponse } = await after(context, res);
256
- if (afterResponse) {
257
- return afterResponse;
445
+
446
+ // src/social-providers/google.ts
447
+ import { Google } from "arctic";
448
+ import { parseJWT as parseJWT2 } from "oslo/jwt";
449
+ var google = ({
450
+ clientId,
451
+ clientSecret,
452
+ redirectURI
453
+ }) => {
454
+ const googleArctic = new Google(
455
+ clientId,
456
+ clientSecret,
457
+ getRedirectURI("google", redirectURI)
458
+ );
459
+ return {
460
+ id: "google",
461
+ name: "Google",
462
+ createAuthorizationURL({ state, scopes, codeVerifier }) {
463
+ if (!codeVerifier) {
464
+ throw new BetterAuthError("codeVerifier is required for Google");
465
+ }
466
+ const _scopes = scopes || ["email", "profile"];
467
+ return googleArctic.createAuthorizationURL(state, codeVerifier, _scopes);
468
+ },
469
+ validateAuthorizationCode: async (code, codeVerifier) => {
470
+ if (!codeVerifier) {
471
+ throw new BetterAuthError("codeVerifier is required for Google");
472
+ }
473
+ return googleArctic.validateAuthorizationCode(code, codeVerifier);
474
+ },
475
+ async getUserInfo(token) {
476
+ if (!token.idToken) {
477
+ return null;
478
+ }
479
+ const user = parseJWT2(token.idToken())?.payload;
480
+ return {
481
+ user: {
482
+ id: user.sub,
483
+ name: user.name,
484
+ email: user.email,
485
+ image: user.picture,
486
+ emailVerified: user.email_verified
487
+ },
488
+ data: user
489
+ };
258
490
  }
259
- return res;
260
491
  };
261
492
  };
262
493
 
263
- // src/routes/session.ts
264
- var session = async (context) => {
265
- const session2 = await getServerSession(context);
266
- if (!session2) {
267
- return {
268
- status: 401,
269
- statusText: "Unauthorize"
270
- };
271
- }
494
+ // src/social-providers/spotify.ts
495
+ import { betterFetch as betterFetch5 } from "@better-fetch/fetch";
496
+ import { Spotify } from "arctic";
497
+ var spotify = ({
498
+ clientId,
499
+ clientSecret,
500
+ redirectURI
501
+ }) => {
502
+ const spotifyArctic = new Spotify(
503
+ clientId,
504
+ clientSecret,
505
+ getRedirectURI("spotify", redirectURI)
506
+ );
272
507
  return {
273
- status: 200,
274
- body: session2
508
+ id: "spotify",
509
+ name: "Spotify",
510
+ createAuthorizationURL({ state, scopes }) {
511
+ const _scopes = scopes || ["user-read-email"];
512
+ return spotifyArctic.createAuthorizationURL(state, _scopes);
513
+ },
514
+ validateAuthorizationCode: spotifyArctic.validateAuthorizationCode,
515
+ async getUserInfo(token) {
516
+ const { data: profile, error: error2 } = await betterFetch5(
517
+ "https://api.spotify.com/v1/me",
518
+ {
519
+ method: "GET",
520
+ headers: {
521
+ Authorization: `Bearer ${token.accessToken}`
522
+ }
523
+ }
524
+ );
525
+ if (error2) {
526
+ return null;
527
+ }
528
+ return {
529
+ user: {
530
+ id: profile.id,
531
+ name: profile.display_name,
532
+ email: profile.email,
533
+ image: profile.images[0]?.url,
534
+ emailVerified: false
535
+ },
536
+ data: profile
537
+ };
538
+ }
275
539
  };
276
540
  };
277
- var getServerSession = async (context) => {
278
- const sessionFromCookie = context.request.cookies.get(
279
- context.cookies.sessionToken.name
541
+
542
+ // src/social-providers/twitch.ts
543
+ import { betterFetch as betterFetch6 } from "@better-fetch/fetch";
544
+ import { Twitch } from "arctic";
545
+ var twitch = ({
546
+ clientId,
547
+ clientSecret,
548
+ redirectURI
549
+ }) => {
550
+ const twitchArctic = new Twitch(
551
+ clientId,
552
+ clientSecret,
553
+ getRedirectURI("twitch", redirectURI)
280
554
  );
281
- if (!sessionFromCookie) {
282
- return null;
283
- }
284
- const session2 = await context.adapter.findSession(sessionFromCookie, context);
285
- if (!session2 || session2.expiresAt < /* @__PURE__ */ new Date()) {
286
- session2 && await context.adapter.deleteSession(session2.id, context);
287
- deleteSessionCooke(context);
288
- return null;
289
- }
290
- const user = await context.adapter.findUserById(session2.userId, context);
291
- if (!user) {
292
- return null;
293
- }
294
- const updatedSession = await context.adapter.updateSession(session2, context);
295
- context.request.cookies.set(context.cookies.sessionToken.name, session2.id, {
296
- ...context.cookies.sessionToken.options,
297
- maxAge: updatedSession.expiresAt.valueOf() - Date.now()
298
- });
299
- sessionFromCookie;
300
555
  return {
301
- user,
302
- expiresAt: session2.expiresAt
556
+ id: "twitch",
557
+ name: "Twitch",
558
+ createAuthorizationURL({ state, scopes }) {
559
+ const _scopes = scopes || ["activity:write", "read"];
560
+ return twitchArctic.createAuthorizationURL(state, _scopes);
561
+ },
562
+ validateAuthorizationCode: twitchArctic.validateAuthorizationCode,
563
+ async getUserInfo(token) {
564
+ const { data: profile, error: error2 } = await betterFetch6(
565
+ "https://api.twitch.tv/helix/users",
566
+ {
567
+ method: "GET",
568
+ headers: {
569
+ Authorization: `Bearer ${token.accessToken}`
570
+ }
571
+ }
572
+ );
573
+ if (error2) {
574
+ return null;
575
+ }
576
+ return {
577
+ user: {
578
+ id: profile.sub,
579
+ name: profile.preferred_username,
580
+ email: profile.email,
581
+ image: profile.picture,
582
+ emailVerified: false
583
+ },
584
+ data: profile
585
+ };
586
+ }
303
587
  };
304
588
  };
305
- var sessionHandler = withPlugins(session);
306
589
 
307
- // ../shared/src/error.ts
308
- var BetterAuthError = class extends Error {
309
- constructor(message) {
310
- super(`${message}`);
311
- this.name = this.constructor.name;
312
- Object.setPrototypeOf(this, new.target.prototype);
313
- Error.captureStackTrace(this, this.constructor);
314
- }
315
- };
316
- var MissingSecret = class extends BetterAuthError {
317
- constructor() {
318
- super("Missing Secret: A secret is required in a production environment.");
319
- }
320
- };
321
- var InvalidURL = class extends BetterAuthError {
322
- constructor(message) {
323
- super(
324
- message || "Please make sure your config includes valid base URL and base PATH."
325
- );
326
- }
327
- };
328
- var InvalidRequest = class extends BetterAuthError {
329
- constructor() {
330
- super("Post requests must include a valid JSON parsable body.");
331
- }
332
- };
333
- var ProviderMissing = class extends BetterAuthError {
334
- constructor(id) {
335
- super(`Provider ${id} is missing on your configuration.`);
336
- }
337
- };
338
- var ProviderError = class extends BetterAuthError {
590
+ // src/social-providers/twitter.ts
591
+ import { betterFetch as betterFetch7 } from "@better-fetch/fetch";
592
+ import { Twitter } from "arctic";
593
+ var twitter = ({
594
+ clientId,
595
+ clientSecret,
596
+ redirectURI
597
+ }) => {
598
+ const twitterArctic = new Twitter(
599
+ clientId,
600
+ clientSecret,
601
+ getRedirectURI("twitter", redirectURI)
602
+ );
603
+ return {
604
+ id: "twitter",
605
+ name: "Twitter",
606
+ createAuthorizationURL(data) {
607
+ const _scopes = data.scopes || ["account_info.read"];
608
+ return twitterArctic.createAuthorizationURL(
609
+ data.state,
610
+ data.codeVerifier,
611
+ _scopes
612
+ );
613
+ },
614
+ validateAuthorizationCode: async (code, codeVerifier) => {
615
+ if (!codeVerifier) {
616
+ throw new BetterAuthError("codeVerifier is required for Twitter");
617
+ }
618
+ return twitterArctic.validateAuthorizationCode(code, codeVerifier);
619
+ },
620
+ async getUserInfo(token) {
621
+ const { data: profile, error: error2 } = await betterFetch7(
622
+ "https://api.x.com/2/users/me?user.fields=profile_image_url",
623
+ {
624
+ method: "GET",
625
+ headers: {
626
+ Authorization: `Bearer ${token.accessToken}`
627
+ }
628
+ }
629
+ );
630
+ if (error2) {
631
+ return null;
632
+ }
633
+ if (!profile.data.email) {
634
+ return null;
635
+ }
636
+ return {
637
+ user: {
638
+ id: profile.data.id,
639
+ name: profile.data.name,
640
+ email: profile.data.email,
641
+ image: profile.data.profile_image_url,
642
+ emailVerified: profile.data.verified || false
643
+ },
644
+ data: profile
645
+ };
646
+ }
647
+ };
339
648
  };
340
649
 
341
- // src/routes/signin.ts
342
- import { z } from "zod";
650
+ // src/types/provider.ts
651
+ import "arctic";
343
652
 
344
- // src/oauth2/signin.ts
345
- import { base64url } from "jose";
653
+ // src/social-providers/index.ts
654
+ var oAuthProviders = {
655
+ apple,
656
+ discord,
657
+ facebook,
658
+ github,
659
+ google,
660
+ spotify,
661
+ twitch,
662
+ twitter
663
+ };
664
+ var oAuthProviderList = Object.keys(oAuthProviders);
346
665
 
347
- // src/crypto/sha.ts
348
- async function sha256(data) {
349
- return await crypto.subtle.digest("SHA-256", data);
666
+ // src/utils/state.ts
667
+ import { generateState as generateStateOAuth } from "oslo/oauth2";
668
+ function generateState(callbackURL, currentURL) {
669
+ const code = generateStateOAuth();
670
+ const state = `${code}!${callbackURL}!${currentURL}`;
671
+ return { state, code };
672
+ }
673
+ function parseState(state) {
674
+ const [code, callbackURL, currentURL] = state.split("!");
675
+ return { code, callbackURL, currentURL };
350
676
  }
351
677
 
352
- // src/oauth2/utils.ts
353
- async function discoveryRequest(context, provider) {
354
- const issuerIdentifier = new URL(provider.issuer);
355
- if (!(issuerIdentifier instanceof URL)) {
356
- throw new TypeError('"issuerIdentifier" must be an instance of URL');
357
- }
358
- if (issuerIdentifier.protocol !== "https:" && issuerIdentifier.protocol !== "http:") {
359
- throw new TypeError('"issuer.protocol" must be "https:" or "http:"');
360
- }
361
- const url = new URL(issuerIdentifier.href);
362
- switch (provider.type) {
363
- case void 0:
364
- case "oidc":
365
- url.pathname = `${url.pathname}/.well-known/openid-configuration`.replace(
366
- "//",
367
- "/"
368
- );
369
- break;
370
- case "oauth":
371
- if (url.pathname === "/") {
372
- url.pathname = ".well-known/oauth-authorization-server";
373
- } else {
374
- url.pathname = `.well-known/oauth-authorization-server/${url.pathname}`.replace(
375
- "//",
376
- "/"
377
- );
378
- }
379
- break;
380
- default:
381
- throw new TypeError(`"provider.type" must be "oidc" or "oauth"`);
382
- }
383
- const headers = new Headers(context.request.headers);
384
- headers.set("accept", "application/json");
385
- return fetch(url.href, {
386
- headers: Object.fromEntries(headers.entries()),
678
+ // src/api/routes/session.ts
679
+ var getSession = createAuthEndpoint(
680
+ "/session",
681
+ {
387
682
  method: "GET",
388
- redirect: "manual"
389
- }).then((res) => {
390
- if (!res.ok) {
391
- throw new Error(`HTTP error! status: ${res.status}`);
683
+ requireHeaders: true
684
+ },
685
+ async (ctx) => {
686
+ const sessionCookieToken = await ctx.getSignedCookie(
687
+ ctx.context.authCookies.sessionToken.name,
688
+ ctx.context.secret
689
+ );
690
+ if (!sessionCookieToken) {
691
+ return ctx.json(null, {
692
+ status: 401
693
+ });
392
694
  }
393
- return res.json();
394
- });
395
- }
396
-
397
- // src/oauth2/signin.ts
398
- async function signInOAuth(context, provider, {
399
- onlySignUp,
400
- autoCreateSession
401
- } = {
402
- autoCreateSession: true,
403
- onlySignUp: false
404
- }) {
405
- if (!provider.params.clientId) {
406
- throw new ProviderError("clientId is required");
407
- }
408
- const scopes = Array.from(new Set(provider?.scopes ?? []));
409
- const { currentURL, callbackURL, data } = context.request.body;
410
- const state = generateState(
411
- currentURL,
412
- callbackURL,
413
- data,
414
- autoCreateSession,
415
- onlySignUp
416
- );
417
- let url = provider.params.authorizationEndpoint;
418
- if (!url) {
419
- const discovery = await discoveryRequest(context, provider);
420
- if (!discovery.authorization_endpoint)
421
- throw new ProviderError("Missing authorization endpoint");
422
- url = discovery.authorization_endpoint;
423
- }
424
- const authorizationUrl = new URL(url);
425
- authorizationUrl.searchParams.set("response_type", "code");
426
- authorizationUrl.searchParams.set("client_id", provider.params.clientId);
427
- authorizationUrl.searchParams.set("state", state);
428
- authorizationUrl.searchParams.set("scope", scopes.join(" "));
429
- authorizationUrl.searchParams.set(
430
- "redirect_uri",
431
- provider.params.redirectURL || `${context.request.url.toString()}/callback/${context.request.body.provider}`
432
- );
433
- if (provider.type === "oidc") {
434
- if (provider.nonce) {
435
- const nonce = generateRandomString(24);
436
- authorizationUrl.searchParams.set("nonce", nonce);
437
- context.request.cookies.set(
438
- context.cookies.nonce.name,
439
- nonce,
440
- context.cookies.nonce.options
695
+ const session = await ctx.context.internalAdapter.findSession(sessionCookieToken);
696
+ if (!session || session.session.expiresAt < /* @__PURE__ */ new Date()) {
697
+ ctx.setSignedCookie(
698
+ ctx.context.authCookies.sessionToken.name,
699
+ "",
700
+ ctx.context.secret,
701
+ {
702
+ maxAge: 0
703
+ }
441
704
  );
705
+ return ctx.json(null, {
706
+ status: 401
707
+ });
442
708
  }
709
+ const updatedSession = await ctx.context.internalAdapter.updateSession(
710
+ session.session
711
+ );
712
+ await ctx.setSignedCookie(
713
+ ctx.context.authCookies.sessionToken.name,
714
+ updatedSession.id,
715
+ ctx.context.secret,
716
+ {
717
+ ...ctx.context.authCookies.sessionToken.options,
718
+ maxAge: updatedSession.expiresAt.valueOf() - Date.now()
719
+ }
720
+ );
721
+ return ctx.json({
722
+ session: updatedSession,
723
+ user: session.user
724
+ });
443
725
  }
444
- provider.params.responseMode && authorizationUrl.searchParams.set(
445
- "response_mode",
446
- provider.params.responseMode
447
- );
448
- if (provider.params.extra) {
449
- const extra = Object.entries(provider.params.extra);
450
- for (const [key, value] of extra) {
451
- authorizationUrl.searchParams.set(key, value);
452
- }
453
- }
454
- const codeVerifier = provider.pkCodeVerifier ? generateCodeVerifier() : void 0;
455
- if (provider.pkCodeVerifier && codeVerifier) {
456
- const codeChallengeMethod = provider?.codeChallengeMethod ?? "S256";
457
- if (codeChallengeMethod === "S256") {
458
- const codeChallengeBuffer = await sha256(
459
- new TextEncoder().encode(codeVerifier)
460
- );
461
- const codeChallenge = base64url.encode(
462
- new Uint8Array(codeChallengeBuffer)
726
+ );
727
+ var getSessionFromCtx = async (ctx) => {
728
+ const session = await getSession({
729
+ ...ctx,
730
+ //@ts-expect-error: By default since this request context comes from a router it'll have a `router` flag which force it to be a request object
731
+ _flag: void 0
732
+ });
733
+ return session;
734
+ };
735
+
736
+ // src/api/routes/sign-in.ts
737
+ var signInOAuth = createAuthEndpoint(
738
+ "/sign-in/social",
739
+ {
740
+ method: "POST",
741
+ requireHeaders: true,
742
+ query: z3.object({
743
+ /**
744
+ * Redirect to the current URL after the
745
+ * user has signed in.
746
+ */
747
+ currentURL: z3.string().optional()
748
+ }).optional(),
749
+ body: z3.object({
750
+ /**
751
+ * Callback URL to redirect to after the user has signed in.
752
+ */
753
+ callbackURL: z3.string().optional(),
754
+ /**
755
+ * OAuth2 provider to use`
756
+ */
757
+ provider: z3.enum(oAuthProviderList)
758
+ })
759
+ },
760
+ async (c) => {
761
+ const provider = c.context.options.socialProvider?.find(
762
+ (p) => p.id === c.body.provider
763
+ );
764
+ if (!provider) {
765
+ c.context.logger.error(
766
+ "Provider not found. Make sure to add the provider to your auth config",
767
+ {
768
+ provider: c.body.provider
769
+ }
463
770
  );
464
- authorizationUrl.searchParams.set("code_challenge", codeChallenge);
465
- authorizationUrl.searchParams.set("code_challenge_method", "S256");
466
- } else {
467
- authorizationUrl.searchParams.set("code_challenge", codeVerifier);
468
- authorizationUrl.searchParams.set("code_challenge_method", "plain");
771
+ throw new APIError2("NOT_FOUND");
469
772
  }
470
- }
471
- context.request.cookies.set(
472
- context.cookies.state.name,
473
- state,
474
- context.cookies.state.options
475
- );
476
- if (codeVerifier) {
477
- context.request.cookies.set(
478
- context.cookies.pkCodeVerifier.name,
479
- codeVerifier,
480
- context.cookies.pkCodeVerifier.options
773
+ const cookie = c.context.authCookies;
774
+ const currentURL = c.query?.currentURL ? new URL(c.query?.currentURL) : null;
775
+ const state = generateState(
776
+ c.body.callbackURL || currentURL?.origin || c.context.baseURL,
777
+ c.query?.currentURL
481
778
  );
779
+ try {
780
+ await c.setSignedCookie(
781
+ cookie.state.name,
782
+ state.code,
783
+ c.context.secret,
784
+ cookie.state.options
785
+ );
786
+ const codeVerifier = generateCodeVerifier();
787
+ await c.setSignedCookie(
788
+ cookie.pkCodeVerifier.name,
789
+ codeVerifier,
790
+ c.context.secret,
791
+ cookie.pkCodeVerifier.options
792
+ );
793
+ const url = provider.createAuthorizationURL({
794
+ state: state.state,
795
+ codeVerifier
796
+ });
797
+ return {
798
+ url: url.toString(),
799
+ state: state.state,
800
+ codeVerifier,
801
+ redirect: true
802
+ };
803
+ } catch (e) {
804
+ throw new APIError2("INTERNAL_SERVER_ERROR");
805
+ }
482
806
  }
483
- return authorizationUrl.toString();
484
- }
485
- function generateCodeVerifier() {
486
- const randomValues = new Uint8Array(32);
487
- crypto.getRandomValues(randomValues);
488
- return base64url.encode(randomValues);
489
- }
490
- function generateState(currentURL, callbackURL, signUp2, autoCreateSession, onlySignUp) {
491
- let state = generateRandomString(24);
492
- state += `!${currentURL}`;
493
- state += `!${callbackURL || currentURL}`;
494
- state += `!${JSON.stringify({
495
- data: signUp2,
496
- autoCreateSession,
497
- onlySignUp
498
- })}`;
499
- return state;
500
- }
501
- function getState(state) {
502
- const [hash, currentURL, callbackURL, signUpString] = state.split("!");
503
- if (!hash || !currentURL || !callbackURL) {
504
- throw new ProviderError("Invalid state");
505
- }
506
- const signUp2 = signUpString ? JSON.parse(signUpString) : void 0;
507
- return {
508
- hash,
509
- currentURL,
510
- callbackURL,
511
- signUp: {
512
- data: signUp2?.data,
513
- autoCreateSession: signUp2?.autoCreateSession,
514
- onlySignUp: signUp2?.onlySignUp
807
+ );
808
+ var signInEmail = createAuthEndpoint(
809
+ "/sign-in/email",
810
+ {
811
+ method: "POST",
812
+ body: z3.object({
813
+ email: z3.string().email(),
814
+ password: z3.string(),
815
+ callbackURL: z3.string().optional(),
816
+ /**
817
+ * If this is true the session will only be valid for the current browser session
818
+ * @default false
819
+ */
820
+ dontRememberMe: z3.boolean().default(false).optional()
821
+ })
822
+ },
823
+ async (ctx) => {
824
+ if (!ctx.context.options?.emailAndPassword?.enabled) {
825
+ ctx.context.logger.error("Email and password is not enabled");
826
+ throw new APIError2("BAD_REQUEST", {
827
+ message: "Email and password is not enabled"
828
+ });
515
829
  }
516
- };
517
- }
830
+ const currentSession = await getSessionFromCtx(ctx);
831
+ if (currentSession) {
832
+ return ctx.json({
833
+ user: currentSession.user,
834
+ session: currentSession.session,
835
+ redirect: !!ctx.body.callbackURL,
836
+ url: ctx.body.callbackURL
837
+ });
838
+ }
839
+ const { email, password } = ctx.body;
840
+ const argon2id = new Argon2id();
841
+ const user = await ctx.context.internalAdapter.findUserByEmail(email);
842
+ if (!user) {
843
+ await argon2id.hash(password);
844
+ ctx.context.logger.error("User not found", { email });
845
+ throw new APIError2("UNAUTHORIZED", {
846
+ message: "Invalid email or password"
847
+ });
848
+ }
849
+ const credentialAccount = user.accounts.find(
850
+ (a) => a.providerId === "credential"
851
+ );
852
+ if (!credentialAccount) {
853
+ ctx.context.logger.error("Credential account not found", { email });
854
+ throw new APIError2("UNAUTHORIZED", {
855
+ message: "Invalid email or password"
856
+ });
857
+ }
858
+ const currentPassword = credentialAccount?.password;
859
+ if (!currentPassword) {
860
+ ctx.context.logger.error("Password not found", { email });
861
+ throw new APIError2("UNAUTHORIZED", {
862
+ message: "Unexpected error"
863
+ });
864
+ }
865
+ const validPassword = await argon2id.verify(currentPassword, password);
866
+ if (!validPassword) {
867
+ ctx.context.logger.error("Invalid password");
868
+ throw new APIError2("UNAUTHORIZED", {
869
+ message: "Invalid email or password"
870
+ });
871
+ }
872
+ const session = await ctx.context.internalAdapter.createSession(
873
+ user.user.id,
874
+ ctx.request
875
+ );
876
+ await ctx.setSignedCookie(
877
+ ctx.context.authCookies.sessionToken.name,
878
+ session.id,
879
+ ctx.context.secret,
880
+ ctx.body.dontRememberMe ? {
881
+ ...ctx.context.authCookies.sessionToken.options,
882
+ maxAge: void 0
883
+ } : ctx.context.authCookies.sessionToken.options
884
+ );
885
+ return ctx.json({
886
+ user: user.user,
887
+ session,
888
+ redirect: !!ctx.body.callbackURL,
889
+ url: ctx.body.callbackURL
890
+ });
891
+ }
892
+ );
893
+
894
+ // src/api/routes/callback.ts
895
+ import { APIError as APIError3 } from "better-call";
896
+ import { z as z4 } from "zod";
518
897
 
519
- // src/providers/utils.ts
520
- var getProvider = (context, providerId) => {
521
- const providers = context.providers;
522
- const provider = providers.find((provider2) => provider2.id === providerId);
523
- return provider;
898
+ // src/client/client-utils.ts
899
+ var HIDE_ON_CLIENT_METADATA = {
900
+ onClient: "hide"
524
901
  };
525
902
 
526
- // src/routes/signin.ts
527
- var bodySchema = z.object({
528
- provider: z.string(),
529
- data: z.record(z.string(), z.any()).optional(),
530
- callbackURL: z.string().optional(),
531
- currentURL: z.string()
532
- });
533
- var signIn = async (context) => {
534
- const data = bodySchema.parse(context.request.body);
535
- const provider = getProvider(context, data.provider);
536
- if (!provider) {
537
- throw new ProviderMissing(data.provider);
903
+ // src/utils/id.ts
904
+ import { alphabet, generateRandomString } from "oslo/crypto";
905
+ var generateId = () => {
906
+ return generateRandomString(36, alphabet("a-z", "0-9"));
907
+ };
908
+
909
+ // src/api/routes/callback.ts
910
+ var callbackOAuth = createAuthEndpoint(
911
+ "/callback/:id",
912
+ {
913
+ method: "GET",
914
+ query: z4.object({
915
+ state: z4.string(),
916
+ code: z4.string(),
917
+ code_verifier: z4.string().optional()
918
+ }),
919
+ metadata: HIDE_ON_CLIENT_METADATA
920
+ },
921
+ async (c) => {
922
+ const provider = c.context.options.socialProvider?.find(
923
+ (p) => p.id === c.params.id
924
+ );
925
+ if (!provider) {
926
+ c.context.logger.error(
927
+ "Oauth provider with id",
928
+ c.params.id,
929
+ "not found"
930
+ );
931
+ throw new APIError3("NOT_FOUND");
932
+ }
933
+ const tokens = await provider.validateAuthorizationCode(
934
+ c.query.code,
935
+ c.query.code_verifier || ""
936
+ );
937
+ if (!tokens) {
938
+ c.context.logger.error("Code verification failed");
939
+ throw new APIError3("UNAUTHORIZED");
940
+ }
941
+ const user = await provider.getUserInfo(tokens).then((res) => res?.user);
942
+ const id = generateId();
943
+ const data = userSchema.safeParse({
944
+ ...user,
945
+ id
946
+ });
947
+ const { callbackURL, currentURL } = parseState(c.query.state);
948
+ if (!user || data.success === false) {
949
+ if (currentURL) {
950
+ throw c.redirect(`${currentURL}?error=oauth_validation_failed`);
951
+ } else {
952
+ throw new APIError3("BAD_REQUEST");
953
+ }
954
+ }
955
+ if (!callbackURL) {
956
+ c.context.logger.error("Callback URL not found");
957
+ throw new APIError3("FORBIDDEN");
958
+ }
959
+ const dbUser = await c.context.internalAdapter.findUserByEmail(user.email);
960
+ const userId = dbUser?.user.id;
961
+ if (dbUser) {
962
+ const hasBeenLinked = dbUser.accounts.find(
963
+ (a) => a.providerId === provider.id
964
+ );
965
+ if (!hasBeenLinked && !user.emailVerified) {
966
+ c.context.logger.error("User already exists");
967
+ const url = new URL(currentURL || callbackURL);
968
+ url.searchParams.set("error", "user_already_exists");
969
+ throw c.redirect(url.toString());
970
+ }
971
+ if (!hasBeenLinked && user.emailVerified) {
972
+ await c.context.internalAdapter.linkAccount({
973
+ providerId: provider.id,
974
+ accountId: user.id,
975
+ id: `${provider.id}:${user.id}`,
976
+ userId: dbUser.user.id,
977
+ ...tokens
978
+ });
979
+ }
980
+ } else {
981
+ try {
982
+ await c.context.internalAdapter.createOAuthUser(data.data, {
983
+ ...tokens,
984
+ id: `${provider.id}:${user.id}`,
985
+ providerId: provider.id,
986
+ accountId: user.id,
987
+ userId: id
988
+ });
989
+ } catch (e) {
990
+ const url = new URL(currentURL || callbackURL);
991
+ url.searchParams.set("error", "unable_to_create_user");
992
+ c.setHeader("Location", url.toString());
993
+ throw c.redirect(url.toString());
994
+ }
995
+ }
996
+ if (!userId && !id)
997
+ throw new APIError3("INTERNAL_SERVER_ERROR", {
998
+ message: "Unable to create user"
999
+ });
1000
+ const session = await c.context.internalAdapter.createSession(
1001
+ userId || id,
1002
+ c.request
1003
+ );
1004
+ try {
1005
+ await c.setSignedCookie(
1006
+ c.context.authCookies.sessionToken.name,
1007
+ session.id,
1008
+ c.context.secret,
1009
+ c.context.authCookies.sessionToken.options
1010
+ );
1011
+ } catch (e) {
1012
+ c.context.logger.error("Unable to set session cookie", e);
1013
+ const url = new URL(currentURL || callbackURL);
1014
+ url.searchParams.set("error", "unable_to_create_session");
1015
+ throw c.redirect(url.toString());
1016
+ }
1017
+ throw c.redirect(callbackURL);
538
1018
  }
539
- if (provider.type === "oauth" || provider.type === "oidc") {
540
- const url = await signInOAuth(context, provider);
541
- return {
542
- status: 200,
1019
+ );
1020
+
1021
+ // src/api/routes/sign-out.ts
1022
+ import { z as z5 } from "zod";
1023
+ var signOut = createAuthEndpoint(
1024
+ "/sign-out",
1025
+ {
1026
+ method: "POST",
1027
+ body: z5.object({
1028
+ callbackURL: z5.string().optional()
1029
+ }).optional()
1030
+ },
1031
+ async (ctx) => {
1032
+ const sessionCookieToken = await ctx.getSignedCookie(
1033
+ ctx.context.authCookies.sessionToken.name,
1034
+ ctx.context.secret
1035
+ );
1036
+ if (!sessionCookieToken) {
1037
+ return ctx.json(null);
1038
+ }
1039
+ await ctx.context.internalAdapter.deleteSession(sessionCookieToken);
1040
+ ctx.setCookie(ctx.context.authCookies.sessionToken.name, "", {
1041
+ maxAge: 0
1042
+ });
1043
+ return ctx.json(null, {
543
1044
  body: {
544
- url,
545
- redirect: true
1045
+ redirect: !!ctx.body?.callbackURL,
1046
+ url: ctx.body?.callbackURL
546
1047
  }
1048
+ });
1049
+ }
1050
+ );
1051
+
1052
+ // src/api/routes/forget-password.ts
1053
+ import { TimeSpan } from "oslo";
1054
+ import { createJWT } from "oslo/jwt";
1055
+ import { validateJWT } from "oslo/jwt";
1056
+ import { Argon2id as Argon2id2 } from "oslo/password";
1057
+ import { z as z6 } from "zod";
1058
+ var forgetPassword = createAuthEndpoint(
1059
+ "/forget-password",
1060
+ {
1061
+ method: "POST",
1062
+ body: z6.object({
1063
+ /**
1064
+ * The email address of the user to send a password reset email to.
1065
+ */
1066
+ email: z6.string().email()
1067
+ })
1068
+ },
1069
+ async (ctx) => {
1070
+ if (!ctx.context.options.emailAndPassword?.sendResetPasswordToken) {
1071
+ ctx.context.logger.error(
1072
+ "Reset password isn't enabled.Please pass an emailAndPassword.sendResetPasswordToken function to your auth config!"
1073
+ );
1074
+ return ctx.json(null, {
1075
+ status: 400,
1076
+ statusText: "RESET_PASSWORD_EMAIL_NOT_SENT",
1077
+ body: {
1078
+ message: "Reset password isn't enabled"
1079
+ }
1080
+ });
1081
+ }
1082
+ const { email } = ctx.body;
1083
+ const user = await ctx.context.internalAdapter.findUserByEmail(email);
1084
+ if (!user) {
1085
+ return ctx.json(
1086
+ {
1087
+ error: "User not found"
1088
+ },
1089
+ {
1090
+ status: 400,
1091
+ statusText: "USER_NOT_FOUND",
1092
+ body: {
1093
+ message: "User not found"
1094
+ }
1095
+ }
1096
+ );
1097
+ }
1098
+ const token = await createJWT(
1099
+ "HS256",
1100
+ Buffer.from(ctx.context.secret),
1101
+ {
1102
+ email: user.user.email
1103
+ },
1104
+ {
1105
+ expiresIn: new TimeSpan(1, "h"),
1106
+ issuer: "better-auth",
1107
+ subject: "forget-password",
1108
+ audiences: [user.user.email],
1109
+ includeIssuedTimestamp: true
1110
+ }
1111
+ );
1112
+ await ctx.context.options.emailAndPassword.sendResetPasswordToken(
1113
+ token,
1114
+ user.user
1115
+ );
1116
+ return ctx.json({
1117
+ status: true
1118
+ });
1119
+ }
1120
+ );
1121
+ var resetPassword = createAuthEndpoint(
1122
+ "/reset-password",
1123
+ {
1124
+ method: "POST",
1125
+ body: z6.object({
1126
+ token: z6.string(),
1127
+ newPassword: z6.string(),
1128
+ callbackURL: z6.string().optional()
1129
+ })
1130
+ },
1131
+ async (ctx) => {
1132
+ const { token, newPassword } = ctx.body;
1133
+ try {
1134
+ const jwt = await validateJWT(
1135
+ "HS256",
1136
+ Buffer.from(ctx.context.secret),
1137
+ token
1138
+ );
1139
+ const email = z6.string().email().parse(jwt.payload.email);
1140
+ const user = await ctx.context.internalAdapter.findUserByEmail(email);
1141
+ if (!user) {
1142
+ return ctx.json(null, {
1143
+ status: 400,
1144
+ statusText: "USER_NOT_FOUND",
1145
+ body: {
1146
+ message: "User not found"
1147
+ }
1148
+ });
1149
+ }
1150
+ if (newPassword.length < (ctx.context.options.emailAndPassword?.minPasswordLength || 8) || newPassword.length > (ctx.context.options.emailAndPassword?.maxPasswordLength || 32)) {
1151
+ return ctx.json(null, {
1152
+ status: 400,
1153
+ statusText: "INVALID_PASSWORD_LENGTH",
1154
+ body: {
1155
+ message: "Password length must be between 8 and 32"
1156
+ }
1157
+ });
1158
+ }
1159
+ const argon2id = new Argon2id2();
1160
+ const hashedPassword = await argon2id.hash(newPassword);
1161
+ const updatedUser = await ctx.context.internalAdapter.updatePassword(
1162
+ user.user.id,
1163
+ hashedPassword
1164
+ );
1165
+ if (!updatedUser) {
1166
+ return ctx.json(null, {
1167
+ status: 500,
1168
+ statusText: "INTERNAL_SERVER_ERROR",
1169
+ body: {
1170
+ message: "Internal server error"
1171
+ }
1172
+ });
1173
+ }
1174
+ return ctx.json({
1175
+ status: true,
1176
+ url: ctx.body.callbackURL,
1177
+ redirect: !!ctx.body.callbackURL
1178
+ });
1179
+ } catch (e) {
1180
+ console.log(e);
1181
+ return ctx.json(null, {
1182
+ status: 400,
1183
+ statusText: "INVALID_TOKEN",
1184
+ body: {
1185
+ message: "Invalid token"
1186
+ }
1187
+ });
1188
+ }
1189
+ }
1190
+ );
1191
+
1192
+ // src/api/routes/verify-email.ts
1193
+ import { TimeSpan as TimeSpan2 } from "oslo";
1194
+ import { createJWT as createJWT2, validateJWT as validateJWT2 } from "oslo/jwt";
1195
+ import { z as z7 } from "zod";
1196
+ var sendVerificationEmail = createAuthEndpoint(
1197
+ "/send-verification-email",
1198
+ {
1199
+ method: "POST",
1200
+ body: z7.object({
1201
+ email: z7.string().email(),
1202
+ callbackURL: z7.string().optional()
1203
+ })
1204
+ },
1205
+ async (ctx) => {
1206
+ if (!ctx.context.options.emailAndPassword?.sendVerificationEmail) {
1207
+ return ctx.json(null, {
1208
+ status: 400,
1209
+ statusText: "VERIFICATION_EMAIL_NOT_SENT",
1210
+ body: {
1211
+ message: "Verification email isn't enabled"
1212
+ }
1213
+ });
1214
+ }
1215
+ const { email } = ctx.body;
1216
+ const token = await createJWT2(
1217
+ "HS256",
1218
+ Buffer.from(ctx.context.secret),
1219
+ {
1220
+ email: email.toLowerCase()
1221
+ },
1222
+ {
1223
+ expiresIn: new TimeSpan2(1, "h"),
1224
+ issuer: "better-auth",
1225
+ subject: "verify-email",
1226
+ audiences: [email],
1227
+ includeIssuedTimestamp: true
1228
+ }
1229
+ );
1230
+ const url = `${ctx.context.baseURL}/verify-email?token=${token}?callbackURL=${ctx.body.callbackURL}`;
1231
+ await ctx.context.options.emailAndPassword.sendVerificationEmail(
1232
+ email,
1233
+ url
1234
+ );
1235
+ return ctx.json({
1236
+ status: true
1237
+ });
1238
+ }
1239
+ );
1240
+ var verifyEmail = createAuthEndpoint(
1241
+ "/verify-email",
1242
+ {
1243
+ method: "GET",
1244
+ query: z7.object({
1245
+ token: z7.string(),
1246
+ callbackURL: z7.string()
1247
+ })
1248
+ },
1249
+ async (ctx) => {
1250
+ const { token } = ctx.query;
1251
+ try {
1252
+ const jwt = await validateJWT2(
1253
+ "HS256",
1254
+ Buffer.from(ctx.context.secret),
1255
+ token
1256
+ );
1257
+ const schema = z7.object({
1258
+ email: z7.string().email()
1259
+ });
1260
+ const parsed = schema.parse(jwt.payload);
1261
+ const user = await ctx.context.internalAdapter.findUserByEmail(
1262
+ parsed.email
1263
+ );
1264
+ if (!user) {
1265
+ return ctx.json(null, {
1266
+ status: 400,
1267
+ statusText: "USER_NOT_FOUND",
1268
+ body: {
1269
+ message: "User not found"
1270
+ }
1271
+ });
1272
+ }
1273
+ const account = user.accounts.find((a) => a.providerId === "credential");
1274
+ if (!account) {
1275
+ return ctx.json(null, {
1276
+ status: 400,
1277
+ statusText: "ACCOUNT_NOT_FOUND",
1278
+ body: {
1279
+ message: "Account not found"
1280
+ }
1281
+ });
1282
+ }
1283
+ await ctx.context.internalAdapter.updateUserByEmail(parsed.email, {
1284
+ emailVerified: true
1285
+ });
1286
+ if (ctx.query.callbackURL) {
1287
+ throw ctx.redirect(ctx.query.callbackURL);
1288
+ }
1289
+ return ctx.json({
1290
+ status: true
1291
+ });
1292
+ } catch (e) {
1293
+ return ctx.json(null, {
1294
+ status: 400,
1295
+ statusText: "INVALID_TOKEN",
1296
+ body: {
1297
+ message: "Invalid token"
1298
+ }
1299
+ });
1300
+ }
1301
+ }
1302
+ );
1303
+
1304
+ // src/api/routes/csrf.ts
1305
+ import { alphabet as alphabet2, generateRandomString as generateRandomString2 } from "oslo/crypto";
1306
+ var getCSRFToken = createAuthEndpoint(
1307
+ "/csrf",
1308
+ {
1309
+ method: "GET",
1310
+ metadata: HIDE_ON_CLIENT_METADATA
1311
+ },
1312
+ async (ctx) => {
1313
+ const csrfToken = await ctx.getSignedCookie(
1314
+ ctx.context.authCookies.csrfToken.name,
1315
+ ctx.context.secret
1316
+ );
1317
+ if (csrfToken) {
1318
+ return {
1319
+ csrfToken
1320
+ };
1321
+ }
1322
+ const token = generateRandomString2(32, alphabet2("a-z", "0-9", "A-Z"));
1323
+ const hash = await hs256(ctx.context.secret, token);
1324
+ const cookie = `${token}!${hash}`;
1325
+ await ctx.setSignedCookie(
1326
+ ctx.context.authCookies.csrfToken.name,
1327
+ cookie,
1328
+ ctx.context.secret,
1329
+ ctx.context.authCookies.csrfToken.options
1330
+ );
1331
+ return {
1332
+ csrfToken: token
547
1333
  };
548
1334
  }
549
- if (provider.type === "custom") {
550
- if (!provider.signIn) {
551
- throw new ProviderError("Sign in method not implemented");
1335
+ );
1336
+
1337
+ // src/api/routes/ok.ts
1338
+ var ok = createAuthEndpoint(
1339
+ "/ok",
1340
+ {
1341
+ method: "GET"
1342
+ },
1343
+ async (ctx) => {
1344
+ return ctx.json({
1345
+ ok: true
1346
+ });
1347
+ }
1348
+ );
1349
+ var welcome = createAuthEndpoint(
1350
+ "/welcome/ok",
1351
+ {
1352
+ method: "GET"
1353
+ },
1354
+ async () => {
1355
+ return new Response("Welcome to Better Auth");
1356
+ }
1357
+ );
1358
+
1359
+ // src/api/routes/sign-up.ts
1360
+ import { alphabet as alphabet3, generateRandomString as generateRandomString3 } from "oslo/crypto";
1361
+ import { Argon2id as Argon2id3 } from "oslo/password";
1362
+ import { z as z8 } from "zod";
1363
+ var signUpEmail = createAuthEndpoint(
1364
+ "/sign-up/email",
1365
+ {
1366
+ method: "POST",
1367
+ body: z8.object({
1368
+ name: z8.string(),
1369
+ email: z8.string().email(),
1370
+ password: z8.string(),
1371
+ image: z8.string().optional(),
1372
+ callbackURL: z8.string().optional()
1373
+ })
1374
+ },
1375
+ async (ctx) => {
1376
+ if (!ctx.context.options.emailAndPassword?.enabled) {
1377
+ return ctx.json(null, {
1378
+ status: 400,
1379
+ body: {
1380
+ message: "Email and password is not enabled"
1381
+ }
1382
+ });
1383
+ }
1384
+ const { name, email, password, image } = ctx.body;
1385
+ const minPasswordLength = ctx.context.options?.emailAndPassword?.minPasswordLength || 8;
1386
+ if (password.length < minPasswordLength) {
1387
+ ctx.context.logger.error("Password is too short");
1388
+ return ctx.json(null, {
1389
+ status: 400,
1390
+ body: { message: "Password is too short" }
1391
+ });
552
1392
  }
553
- const response = await provider.signIn(context);
554
- return response;
1393
+ const argon2id = new Argon2id3();
1394
+ const dbUser = await ctx.context.internalAdapter.findUserByEmail(email);
1395
+ const hash = await argon2id.hash(password);
1396
+ if (dbUser?.user) {
1397
+ return ctx.json(null, {
1398
+ status: 400,
1399
+ body: {
1400
+ message: "User already exists"
1401
+ }
1402
+ });
1403
+ }
1404
+ const createdUser = await ctx.context.internalAdapter.createUser({
1405
+ id: generateRandomString3(32, alphabet3("a-z", "0-9", "A-Z")),
1406
+ email: email.toLowerCase(),
1407
+ name,
1408
+ image,
1409
+ emailVerified: false,
1410
+ createdAt: /* @__PURE__ */ new Date(),
1411
+ updatedAt: /* @__PURE__ */ new Date()
1412
+ });
1413
+ await ctx.context.internalAdapter.linkAccount({
1414
+ id: generateRandomString3(32, alphabet3("a-z", "0-9", "A-Z")),
1415
+ userId: createdUser.id,
1416
+ providerId: "credential",
1417
+ accountId: createdUser.id,
1418
+ password: hash
1419
+ });
1420
+ const session = await ctx.context.internalAdapter.createSession(
1421
+ createdUser.id,
1422
+ ctx.request
1423
+ );
1424
+ await ctx.setSignedCookie(
1425
+ ctx.context.authCookies.sessionToken.name,
1426
+ session.id,
1427
+ ctx.context.secret,
1428
+ ctx.context.authCookies.sessionToken.options
1429
+ );
1430
+ return ctx.json(
1431
+ {
1432
+ user: createdUser,
1433
+ session
1434
+ },
1435
+ {
1436
+ body: ctx.body.callbackURL ? {
1437
+ url: ctx.body.callbackURL,
1438
+ redirect: true
1439
+ } : {
1440
+ user: createdUser,
1441
+ session
1442
+ }
1443
+ }
1444
+ );
555
1445
  }
556
- throw new ProviderError("Invalid provider type");
1446
+ );
1447
+
1448
+ // src/api/routes/error.ts
1449
+ var html = (errorCode = "Unknown") => `<!DOCTYPE html>
1450
+ <html lang="en">
1451
+ <head>
1452
+ <meta charset="UTF-8">
1453
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
1454
+ <title>Authentication Error</title>
1455
+ <style>
1456
+ :root {
1457
+ --bg-color: #f8f9fa;
1458
+ --text-color: #212529;
1459
+ --accent-color: #000000;
1460
+ --error-color: #dc3545;
1461
+ --border-color: #e9ecef;
1462
+ }
1463
+ body {
1464
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
1465
+ background-color: var(--bg-color);
1466
+ color: var(--text-color);
1467
+ display: flex;
1468
+ justify-content: center;
1469
+ align-items: center;
1470
+ height: 100vh;
1471
+ margin: 0;
1472
+ line-height: 1.5;
1473
+ }
1474
+ .error-container {
1475
+ background-color: #ffffff;
1476
+ border-radius: 12px;
1477
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
1478
+ padding: 2.5rem;
1479
+ text-align: center;
1480
+ max-width: 90%;
1481
+ width: 400px;
1482
+ }
1483
+ h1 {
1484
+ color: var(--error-color);
1485
+ font-size: 1.75rem;
1486
+ margin-bottom: 1rem;
1487
+ font-weight: 600;
1488
+ }
1489
+ p {
1490
+ margin-bottom: 1.5rem;
1491
+ color: #495057;
1492
+ }
1493
+ .btn {
1494
+ background-color: var(--accent-color);
1495
+ color: #ffffff;
1496
+ text-decoration: none;
1497
+ padding: 0.75rem 1.5rem;
1498
+ border-radius: 6px;
1499
+ transition: all 0.3s ease;
1500
+ display: inline-block;
1501
+ font-weight: 500;
1502
+ border: 2px solid var(--accent-color);
1503
+ }
1504
+ .btn:hover {
1505
+ background-color: #131721;
1506
+ }
1507
+ .error-code {
1508
+ font-size: 0.875rem;
1509
+ color: #6c757d;
1510
+ margin-top: 1.5rem;
1511
+ padding-top: 1.5rem;
1512
+ border-top: 1px solid var(--border-color);
1513
+ }
1514
+ .icon {
1515
+ font-size: 3rem;
1516
+ margin-bottom: 1rem;
1517
+ }
1518
+ </style>
1519
+ </head>
1520
+ <body>
1521
+ <div class="error-container">
1522
+ <div class="icon">\u26A0\uFE0F</div>
1523
+ <h1>Better Auth Error</h1>
1524
+ <p>We encountered an issue while processing your request. Please try again or contact the application owner if the problem persists.</p>
1525
+ <a href="#" id="returnLink" class="btn">Return to Application</a>
1526
+ <div class="error-code">Error Code: <span id="errorCode">${errorCode}</span></div>
1527
+ </div>
1528
+ </body>
1529
+ </html>`;
1530
+ var error = createAuthEndpoint(
1531
+ "/error",
1532
+ {
1533
+ method: "GET"
1534
+ },
1535
+ async (c) => {
1536
+ const query = new URL(c.request?.url || "").searchParams.get("error") || "Unknown";
1537
+ return new Response(html(query), {
1538
+ headers: {
1539
+ "Content-Type": "text/html"
1540
+ }
1541
+ });
1542
+ }
1543
+ );
1544
+
1545
+ // src/api/index.ts
1546
+ var router = (ctx) => {
1547
+ const pluginEndpoints = ctx.options.plugins?.reduce(
1548
+ (acc, plugin) => {
1549
+ return {
1550
+ ...acc,
1551
+ ...plugin.endpoints
1552
+ };
1553
+ },
1554
+ {}
1555
+ );
1556
+ const middlewares = ctx.options.plugins?.map(
1557
+ (plugin) => plugin.middlewares?.map((m) => {
1558
+ const middleware = async (context) => {
1559
+ return m.middleware({
1560
+ ...context,
1561
+ context: {
1562
+ ...ctx,
1563
+ ...context.context
1564
+ }
1565
+ });
1566
+ };
1567
+ middleware.path = m.path;
1568
+ middleware.options = m.middleware.options;
1569
+ middleware.headers = m.middleware.headers;
1570
+ return {
1571
+ path: m.path,
1572
+ middleware
1573
+ };
1574
+ })
1575
+ ).filter((plugin) => plugin !== void 0).flat() || [];
1576
+ async function typedSession(ctx2) {
1577
+ const handler = await getSession(ctx2);
1578
+ return handler;
1579
+ }
1580
+ typedSession.path = getSession.path;
1581
+ typedSession.method = getSession.method;
1582
+ typedSession.options = getSession.options;
1583
+ typedSession.headers = getSession.headers;
1584
+ const baseEndpoints = {
1585
+ signInOAuth,
1586
+ callbackOAuth,
1587
+ getCSRFToken,
1588
+ getSession: typedSession,
1589
+ signOut,
1590
+ signUpEmail,
1591
+ signInEmail,
1592
+ forgetPassword,
1593
+ resetPassword,
1594
+ verifyEmail,
1595
+ sendVerificationEmail
1596
+ };
1597
+ const endpoints = {
1598
+ ...baseEndpoints,
1599
+ ...pluginEndpoints,
1600
+ ok,
1601
+ welcome,
1602
+ error
1603
+ };
1604
+ let api = {};
1605
+ for (const [key, value] of Object.entries(endpoints)) {
1606
+ api[key] = async (context) => {
1607
+ for (const plugin of ctx.options.plugins || []) {
1608
+ if (plugin.hooks?.before) {
1609
+ for (const hook of plugin.hooks.before) {
1610
+ const match = hook.matcher({
1611
+ ...context,
1612
+ ...value
1613
+ });
1614
+ if (match) {
1615
+ const hookRes = await hook.handler(context);
1616
+ if (hookRes && "context" in hookRes) {
1617
+ context = {
1618
+ ...context,
1619
+ ...hookRes.context,
1620
+ ...value
1621
+ };
1622
+ }
1623
+ }
1624
+ }
1625
+ }
1626
+ }
1627
+ const endpointRes = value({
1628
+ ...context,
1629
+ context: {
1630
+ ...ctx,
1631
+ ...context.context
1632
+ }
1633
+ });
1634
+ let response = endpointRes;
1635
+ for (const plugin of ctx.options.plugins || []) {
1636
+ if (plugin.hooks?.after) {
1637
+ for (const hook of plugin.hooks.after) {
1638
+ const match = hook.matcher(context);
1639
+ if (match) {
1640
+ const obj = Object.assign(context, {
1641
+ returned: endpointRes
1642
+ });
1643
+ const hookRes = await hook.handler(obj);
1644
+ if (hookRes && "response" in hookRes) {
1645
+ response = hookRes.response;
1646
+ }
1647
+ }
1648
+ }
1649
+ }
1650
+ }
1651
+ return response;
1652
+ };
1653
+ api[key].path = value.path;
1654
+ api[key].method = value.method;
1655
+ api[key].options = value.options;
1656
+ api[key].headers = value.headers;
1657
+ }
1658
+ return createRouter(api, {
1659
+ extraContext: ctx,
1660
+ basePath: ctx.options.basePath,
1661
+ routerMiddleware: [
1662
+ {
1663
+ path: "/**",
1664
+ middleware: csrfMiddleware
1665
+ },
1666
+ ...middlewares
1667
+ ],
1668
+ /**
1669
+ * this is to remove any sensitive data from the response
1670
+ */
1671
+ async transformResponse(res) {
1672
+ let body = {};
1673
+ try {
1674
+ body = await res.json();
1675
+ } catch (e) {
1676
+ return res;
1677
+ }
1678
+ if (body?.user) {
1679
+ body.user = parseUser(ctx.options, body.user);
1680
+ }
1681
+ if (body?.session) {
1682
+ body.session = parseSession(ctx.options, body.session);
1683
+ }
1684
+ if (body?.account) {
1685
+ body.account = parseAccount(ctx.options, body.account);
1686
+ }
1687
+ return new Response(body ? JSON.stringify(body) : null, {
1688
+ headers: res.headers,
1689
+ status: res.status,
1690
+ statusText: res.statusText
1691
+ });
1692
+ },
1693
+ onError(e) {
1694
+ console.log(e);
1695
+ }
1696
+ });
557
1697
  };
558
- var signInHandler = withPlugins(signIn);
559
1698
 
560
- // src/routes/signout.ts
561
- var signOut = async (context) => {
562
- const session2 = context.request.cookies.get(
563
- context.cookies.sessionToken.name
1699
+ // src/adapters/kysely.ts
1700
+ import Database from "better-sqlite3";
1701
+ import { Kysely } from "kysely";
1702
+ import {
1703
+ MysqlDialect,
1704
+ PostgresDialect,
1705
+ SqliteDialect
1706
+ } from "kysely";
1707
+ import { createPool } from "mysql2";
1708
+ import pg from "pg";
1709
+ var { Pool } = pg;
1710
+ function convertWhere(w) {
1711
+ if (!w)
1712
+ return {
1713
+ and: null,
1714
+ or: null
1715
+ };
1716
+ const and = w?.filter((w2) => w2.connector === "AND" || !w2.connector).reduce(
1717
+ (acc, w2) => ({
1718
+ ...acc,
1719
+ [w2.field]: w2.value
1720
+ }),
1721
+ {}
564
1722
  );
565
- deleteSessionCooke(context);
566
- if (session2) {
567
- try {
568
- await context.adapter.deleteSession(session2, context);
569
- } catch (e) {
1723
+ const or = w?.filter((w2) => w2.connector === "OR").reduce(
1724
+ (acc, w2) => ({
1725
+ ...acc,
1726
+ [w2.field]: w2.value
1727
+ }),
1728
+ {}
1729
+ );
1730
+ return {
1731
+ and: Object.keys(and).length ? and : null,
1732
+ or: Object.keys(or).length ? or : null
1733
+ };
1734
+ }
1735
+ function transformTo(val, fields, transform) {
1736
+ for (const key in val) {
1737
+ if (val[key] === 0 && fields[key]?.type === "boolean" && transform?.boolean) {
1738
+ val[key] = false;
1739
+ }
1740
+ if (val[key] === 1 && fields[key]?.type === "boolean" && transform?.boolean) {
1741
+ val[key] = true;
1742
+ }
1743
+ if (fields[key]?.type === "date") {
1744
+ if (!(val[key] instanceof Date)) {
1745
+ val[key] = new Date(val[key]);
1746
+ }
1747
+ }
1748
+ }
1749
+ return val;
1750
+ }
1751
+ function transformFrom(val, transform) {
1752
+ for (const key in val) {
1753
+ if (typeof val[key] === "boolean" && transform?.boolean) {
1754
+ val[key] = val[key] ? 1 : 0;
1755
+ }
1756
+ if (val[key] instanceof Date) {
1757
+ val[key] = val[key].toISOString();
1758
+ }
1759
+ }
1760
+ return val;
1761
+ }
1762
+ var kyselyAdapter = (db, config) => {
1763
+ return {
1764
+ async create(data) {
1765
+ let { model, data: val, select } = data;
1766
+ if (config?.transform) {
1767
+ val = transformFrom(val, config.transform);
1768
+ }
1769
+ let res = await db.insertInto(model).values(val).returningAll().executeTakeFirst();
1770
+ if (config?.transform) {
1771
+ const schema = config.transform.schema[model];
1772
+ res = schema ? transformTo(val, schema, config.transform) : res;
1773
+ }
1774
+ if (select?.length) {
1775
+ const data2 = res ? select.reduce((acc, cur) => {
1776
+ if (res?.[cur]) {
1777
+ return {
1778
+ ...acc,
1779
+ [cur]: res[cur]
1780
+ };
1781
+ }
1782
+ return acc;
1783
+ }, {}) : null;
1784
+ res = data2;
1785
+ }
1786
+ return res;
1787
+ },
1788
+ async findOne(data) {
1789
+ const { model, where, select } = data;
1790
+ const { and, or } = convertWhere(where);
1791
+ let query = db.selectFrom(model).selectAll();
1792
+ if (or) {
1793
+ query = query.where((eb) => eb.or(or));
1794
+ }
1795
+ if (and) {
1796
+ query = query.where((eb) => eb.and(and));
1797
+ }
1798
+ let res = await query.executeTakeFirst();
1799
+ if (select?.length) {
1800
+ const data2 = res ? select.reduce((acc, cur) => {
1801
+ if (res?.[cur]) {
1802
+ return {
1803
+ ...acc,
1804
+ [cur]: res[cur]
1805
+ };
1806
+ }
1807
+ return acc;
1808
+ }, {}) : null;
1809
+ res = data2;
1810
+ }
1811
+ if (config?.transform) {
1812
+ const schema = config.transform.schema[model];
1813
+ res = res && schema ? transformTo(res, schema, config.transform) : res;
1814
+ return res || null;
1815
+ }
1816
+ return res || null;
1817
+ },
1818
+ async findMany(data) {
1819
+ const { model, where } = data;
1820
+ let query = db.selectFrom(model);
1821
+ const { and, or } = convertWhere(where);
1822
+ if (and) {
1823
+ query = query.where((eb) => eb.and(and));
1824
+ }
1825
+ if (or) {
1826
+ query = query.where((eb) => eb.or(or));
1827
+ }
1828
+ const res = await query.selectAll().execute();
1829
+ if (config?.transform) {
1830
+ const schema = config.transform.schema[model];
1831
+ return schema ? res.map((v) => transformTo(v, schema, config.transform)) : res;
1832
+ }
1833
+ return res;
1834
+ },
1835
+ async update(data) {
1836
+ let { model, where, update: val } = data;
1837
+ const { and, or } = convertWhere(where);
1838
+ if (config?.transform) {
1839
+ val = transformFrom(val, config.transform);
1840
+ }
1841
+ let query = db.updateTable(model).set(val);
1842
+ if (and) {
1843
+ query = query.where((eb) => eb.and(and));
1844
+ }
1845
+ if (or) {
1846
+ query = query.where((eb) => eb.or(or));
1847
+ }
1848
+ const res = await query.returningAll().executeTakeFirst();
1849
+ if (config?.transform) {
1850
+ const schema = config.transform.schema[model];
1851
+ return schema ? transformTo(res, schema, config.transform) : res;
1852
+ }
1853
+ return res;
1854
+ },
1855
+ async delete(data) {
1856
+ const { model, where } = data;
1857
+ const { and, or } = convertWhere(where);
1858
+ let query = db.deleteFrom(model);
1859
+ if (and) {
1860
+ query = query.where((eb) => eb.and(and));
1861
+ }
1862
+ if (or) {
1863
+ query = query.where((eb) => eb.or(or));
1864
+ }
1865
+ await query.execute();
1866
+ }
1867
+ };
1868
+ };
1869
+ var getDialect = (config) => {
1870
+ if (!config.database) {
1871
+ return null;
1872
+ }
1873
+ let dialect = null;
1874
+ if ("provider" in config.database) {
1875
+ const provider = config.database.provider;
1876
+ const connectionString = config.database.url.trim();
1877
+ if (provider === "postgres") {
1878
+ const pool = new Pool({
1879
+ connectionString
1880
+ });
1881
+ dialect = new PostgresDialect({
1882
+ pool
1883
+ });
1884
+ }
1885
+ if (provider === "mysql") {
1886
+ const params = new URL(connectionString);
1887
+ const pool = createPool({
1888
+ host: params.hostname,
1889
+ user: params.username,
1890
+ password: params.password,
1891
+ database: params.pathname.split("/")[1],
1892
+ port: Number(params.port)
1893
+ });
1894
+ dialect = new MysqlDialect({ pool });
1895
+ }
1896
+ if (provider === "sqlite") {
1897
+ const db = new Database(connectionString);
1898
+ dialect = new SqliteDialect({
1899
+ database: db
1900
+ });
1901
+ }
1902
+ }
1903
+ return dialect;
1904
+ };
1905
+ var createKyselyAdapter = (config) => {
1906
+ const dialect = getDialect(config);
1907
+ if (!dialect) {
1908
+ return null;
1909
+ }
1910
+ const db = new Kysely({
1911
+ dialect
1912
+ });
1913
+ return db;
1914
+ };
1915
+ var getDatabaseType = (config) => {
1916
+ if ("provider" in config.database) {
1917
+ return config.database.provider;
1918
+ }
1919
+ if ("dialect" in config.database) {
1920
+ if (config.database.dialect instanceof PostgresDialect) {
1921
+ return "postgres";
1922
+ }
1923
+ if (config.database.dialect instanceof MysqlDialect) {
1924
+ return "mysql";
1925
+ }
1926
+ if (config.database.dialect instanceof SqliteDialect) {
1927
+ return "sqlite";
1928
+ }
1929
+ }
1930
+ return "sqlite";
1931
+ };
1932
+
1933
+ // src/adapters/get-tables.ts
1934
+ var getAuthTables = (options) => {
1935
+ const pluginSchema = options.plugins?.reduce((acc, plugin) => {
1936
+ const schema = plugin.schema;
1937
+ return {
1938
+ ...acc,
1939
+ ...schema
1940
+ };
1941
+ }, {});
1942
+ return {
1943
+ ...pluginSchema,
1944
+ user: {
1945
+ tableName: options.user?.modelName || "user",
1946
+ fields: {
1947
+ name: {
1948
+ type: "string"
1949
+ },
1950
+ email: {
1951
+ type: "string"
1952
+ },
1953
+ emailVerified: {
1954
+ type: "boolean",
1955
+ defaultValue: () => false
1956
+ },
1957
+ image: {
1958
+ type: "string",
1959
+ required: false
1960
+ },
1961
+ createdAt: {
1962
+ type: "date",
1963
+ defaultValue: () => /* @__PURE__ */ new Date()
1964
+ },
1965
+ updatedAt: {
1966
+ type: "date",
1967
+ defaultValue: () => /* @__PURE__ */ new Date()
1968
+ }
1969
+ }
1970
+ },
1971
+ session: {
1972
+ tableName: options.session?.modelName || "session",
1973
+ fields: {
1974
+ expiresAt: {
1975
+ type: "date"
1976
+ },
1977
+ ipAddress: {
1978
+ type: "string",
1979
+ required: false
1980
+ },
1981
+ userAgent: {
1982
+ type: "string",
1983
+ required: false
1984
+ },
1985
+ userId: {
1986
+ type: "string",
1987
+ references: {
1988
+ model: "user",
1989
+ field: "id",
1990
+ onDelete: "cascade"
1991
+ }
1992
+ }
1993
+ }
1994
+ },
1995
+ account: {
1996
+ tableName: options.account?.modelName || "account",
1997
+ fields: {
1998
+ accountId: {
1999
+ type: "string"
2000
+ },
2001
+ providerId: {
2002
+ type: "string"
2003
+ },
2004
+ userId: {
2005
+ type: "string",
2006
+ references: {
2007
+ model: "user",
2008
+ field: "id",
2009
+ onDelete: "cascade"
2010
+ }
2011
+ },
2012
+ accessToken: {
2013
+ type: "string",
2014
+ required: false
2015
+ },
2016
+ refreshToken: {
2017
+ type: "string",
2018
+ required: false
2019
+ },
2020
+ idToken: {
2021
+ type: "string",
2022
+ required: false
2023
+ },
2024
+ accessTokenExpiresAt: {
2025
+ type: "date",
2026
+ required: false
2027
+ },
2028
+ refreshTokenExpiresAt: {
2029
+ type: "date",
2030
+ required: false
2031
+ },
2032
+ password: {
2033
+ type: "string",
2034
+ required: false
2035
+ }
2036
+ }
570
2037
  }
571
- }
572
- return {
573
- status: 200
574
2038
  };
575
2039
  };
576
- var signOutHandler = withPlugins(signOut);
577
2040
 
578
- // src/actions/index.ts
579
- function getActions(options, handlerOptions) {
580
- const actions = {
581
- /**
582
- * Sign in with a provider. This action will return response object. If
583
- * it's oauth, it will redirect to the provider. If it's custom, it
584
- * will return the response object and it'll set the cookies.
585
- *
586
- * In most cases you should just be using
587
- * client sdk for sign in instead unless you
588
- * have a good reason to use this.
589
- */
590
- signIn: async (request, input) => {
591
- const url = request instanceof Headers ? request.get("referer") : request.url;
592
- const req = new Request(url, {
593
- body: JSON.stringify(input),
594
- method: "POST"
595
- });
596
- const context = await toContext(options, req);
597
- context.disableCSRF = true;
598
- context.request.body = {
599
- currentURL: url,
600
- provider: input.provider
601
- };
602
- const response = await signInHandler(context);
603
- if (response.body.redirect) {
604
- return toResponse(
605
- {
606
- status: 302,
607
- headers: {
608
- Location: response.body.url
609
- }
610
- },
611
- context
612
- );
613
- }
614
- return toResponse(response, context, handlerOptions);
615
- },
616
- /**
617
- * Get the current logged in user session.
618
- */
619
- getSession: async (request) => {
620
- const url = request instanceof Headers ? request.get("referer") || request.get("x-forwarded-host") || "http://localhost" : request.url;
621
- const req = request instanceof Request ? request : new Request(url, {
622
- method: "POST",
623
- body: JSON.stringify({}),
624
- headers: request
625
- });
626
- const context = await toContext(options, req);
627
- context.disableCSRF = true;
628
- const response = await getServerSession(context);
629
- return response;
630
- },
631
- /**
632
- * Signout the current user.
633
- * Delete the session and clear the cookies.
634
- */
635
- signOut: async (request) => {
636
- const url = request instanceof Headers ? request.get("referer") : request.url;
637
- const req = request instanceof Request ? request : new Request(url, {
638
- method: "POST",
639
- body: JSON.stringify({}),
640
- headers: request
641
- });
642
- const context = await toContext(options, req);
643
- context.disableCSRF = true;
644
- const response = await signOutHandler(context);
645
- return toResponse(response, context, handlerOptions);
2041
+ // src/adapters/utils.ts
2042
+ function getAdapter(options) {
2043
+ if (!options.database) {
2044
+ throw new BetterAuthError("Database configuration is required");
2045
+ }
2046
+ if ("provider" in options.database) {
2047
+ const db = createKyselyAdapter(options);
2048
+ if (!db) {
2049
+ throw new BetterAuthError("Failed to initialize database adapter");
646
2050
  }
647
- };
648
- return actions;
2051
+ const tables = getAuthTables(options);
2052
+ return kyselyAdapter(db, {
2053
+ transform: {
2054
+ schema: {
2055
+ [tables.user.tableName]: tables.user.fields,
2056
+ [tables.session.tableName]: tables.session.fields,
2057
+ [tables.account.tableName]: tables.account.fields
2058
+ },
2059
+ date: true,
2060
+ boolean: getDatabaseType(options) === "sqlite"
2061
+ }
2062
+ });
2063
+ }
2064
+ return options.database;
649
2065
  }
650
2066
 
651
- // src/utils/time.ts
652
- var timeSpan = (span) => {
653
- const [time, unit] = span;
654
- const timeInMs = Number.parseInt(time) * 1e3;
655
- switch (unit) {
656
- case "s":
657
- return timeInMs;
658
- case "m":
659
- return timeInMs * 60;
660
- case "hr":
661
- return timeInMs * 60 * 60;
662
- case "d":
663
- return timeInMs * 60 * 60 * 24;
664
- case "w":
665
- return timeInMs * 60 * 60 * 24 * 7;
666
- default:
667
- return 0;
668
- }
669
- };
2067
+ // src/adapters/internal-adapter.ts
2068
+ import { alphabet as alphabet4, generateRandomString as generateRandomString4 } from "oslo/crypto";
2069
+
2070
+ // src/utils/date.ts
670
2071
  var getDate = (span) => {
671
- const sec = typeof span === "number" ? span : timeSpan(span);
672
2072
  const date = /* @__PURE__ */ new Date();
673
- return new Date(date.getTime() + sec);
2073
+ return new Date(date.getTime() + span);
674
2074
  };
675
2075
 
676
2076
  // src/adapters/internal-adapter.ts
677
- var createInternalAdapter = (db) => {
2077
+ var createInternalAdapter = (adapter, options) => {
2078
+ const sessionExpiration = options.session?.expiresIn || 60 * 60 * 24 * 7;
2079
+ const tables = getAuthTables(options);
678
2080
  return {
679
- createSession: async (userId, context) => {
680
- if (context.sessionAdapter) {
681
- return context.sessionAdapter.create({
682
- userId,
683
- expiresAt: new Date(Date.now() + context.session.expiresIn)
2081
+ createOAuthUser: async (user, account) => {
2082
+ try {
2083
+ const createdUser = await adapter.create({
2084
+ model: tables.user.tableName,
2085
+ data: user
2086
+ });
2087
+ const createdAccount = await adapter.create({
2088
+ model: tables.account.tableName,
2089
+ data: account
684
2090
  });
2091
+ return {
2092
+ user: createdUser,
2093
+ account: createdAccount
2094
+ };
2095
+ } catch (e) {
2096
+ console.log(e);
2097
+ return null;
685
2098
  }
686
- const session2 = await db.create({
687
- model: context.session.modelName,
688
- data: {
689
- id: generateRandomString(32),
690
- userId,
691
- expiresAt: db.config?.dateFormat === "number" ? Date.now() + context.session.expiresIn : new Date(Date.now() + context.session.expiresIn)
692
- },
693
- select: context.session.selectFields
2099
+ },
2100
+ createUser: async (user) => {
2101
+ const createdUser = await adapter.create({
2102
+ model: tables.user.tableName,
2103
+ data: user
2104
+ });
2105
+ return createdUser;
2106
+ },
2107
+ createSession: async (userId, request) => {
2108
+ const data = {
2109
+ id: generateRandomString4(32, alphabet4("a-z", "0-9", "A-Z")),
2110
+ userId,
2111
+ expiresAt: getDate(sessionExpiration),
2112
+ ipAddress: request?.headers.get("x-forwarded-for") || "",
2113
+ userAgent: request?.headers.get("user-agent") || ""
2114
+ };
2115
+ const session = adapter.create({
2116
+ model: tables.session.tableName,
2117
+ data
2118
+ });
2119
+ return session;
2120
+ },
2121
+ findSession: async (sessionId) => {
2122
+ const session = await adapter.findOne({
2123
+ model: tables.session.tableName,
2124
+ where: [
2125
+ {
2126
+ value: sessionId,
2127
+ field: "id"
2128
+ }
2129
+ ]
2130
+ });
2131
+ if (!session) {
2132
+ return null;
2133
+ }
2134
+ const user = await adapter.findOne({
2135
+ model: tables.user.tableName,
2136
+ where: [
2137
+ {
2138
+ value: session.userId,
2139
+ field: "id"
2140
+ }
2141
+ ]
694
2142
  });
695
- return session2;
2143
+ if (!user) {
2144
+ return null;
2145
+ }
2146
+ return {
2147
+ session,
2148
+ user
2149
+ };
696
2150
  },
697
- updateSession: async (session2, context) => {
698
- const updateDate = context.session.updateAge === 0 ? 0 : getDate(context.session.updateAge).valueOf();
699
- const maxAge = getDate(context.session.expiresIn);
700
- const shouldBeUpdated = session2.expiresAt.valueOf() - maxAge.valueOf() + updateDate <= Date.now();
2151
+ updateSession: async (session) => {
2152
+ const updateAge = options.session?.updateAge === void 0 ? 1e3 : options.session?.updateAge;
2153
+ const updateDate = updateAge === 0 ? 0 : getDate(updateAge).valueOf();
2154
+ const maxAge = getDate(sessionExpiration);
2155
+ const shouldBeUpdated = session.expiresAt.valueOf() - maxAge.valueOf() + updateDate <= Date.now();
701
2156
  if (shouldBeUpdated) {
702
- if (context.sessionAdapter) {
703
- return context.sessionAdapter.update({
704
- id: session2.id,
705
- userId: session2.userId,
706
- expiresAt: new Date(Date.now() + context.session.expiresIn)
707
- });
708
- }
709
- const updatedSession = await db.update({
710
- model: context.session.modelName,
2157
+ const updatedSession = await adapter.create({
2158
+ model: tables.session.tableName,
2159
+ data: {
2160
+ ...session,
2161
+ id: generateRandomString4(32, alphabet4("a-z", "0-9", "A-Z")),
2162
+ expiresAt: new Date(Date.now() + sessionExpiration)
2163
+ }
2164
+ });
2165
+ await adapter.update({
2166
+ model: tables.session.tableName,
711
2167
  where: [
712
2168
  {
713
2169
  field: "id",
714
- value: session2.id
2170
+ value: session.id
715
2171
  }
716
2172
  ],
717
2173
  update: {
718
- expiresAt: db.config?.dateFormat === "number" ? Date.now() + context.session.expiresIn : new Date(Date.now() + context.session.expiresIn)
2174
+ /**
2175
+ * update the session to expire in 2 minute. This is to prevent
2176
+ * the session from expiring too quickly and logging the user out.
2177
+ */
2178
+ expiresAt: new Date(Date.now() + 1e3 * 60 * 2)
719
2179
  }
720
2180
  });
721
2181
  return updatedSession;
722
2182
  }
723
- return session2;
2183
+ return session;
724
2184
  },
725
- deleteSession: async (id, context) => {
726
- const session2 = await db.delete({
727
- model: context.session.modelName,
2185
+ deleteSession: async (id) => {
2186
+ const session = await adapter.delete({
2187
+ model: tables.session.tableName,
728
2188
  where: [
729
2189
  {
730
2190
  field: "id",
@@ -732,110 +2192,80 @@ var createInternalAdapter = (db) => {
732
2192
  }
733
2193
  ]
734
2194
  });
735
- return session2;
2195
+ return session;
736
2196
  },
737
- createUser: async (data, context) => {
738
- const user = await db.create({
739
- model: context.user.modelName,
740
- data: {
741
- id: generateRandomString(32),
742
- ...data.user
743
- },
744
- select: context.user.selectFields
745
- });
746
- const account = await db.create({
747
- model: context.account.modelName,
748
- data: {
749
- ...data.account,
750
- userId: user.id,
751
- providerId: data.account.providerId.toString(),
752
- accountId: data.account.accountId.toString()
753
- }
754
- });
755
- return { user, account };
756
- },
757
- updateUserByEmail: async (email, data, context) => {
758
- const user = await db.update({
759
- model: context.user.modelName,
2197
+ findUserByEmail: async (email) => {
2198
+ const user = await adapter.findOne({
2199
+ model: tables.user.tableName,
760
2200
  where: [
761
2201
  {
762
- field: "email",
763
- value: email
2202
+ value: email.toLowerCase(),
2203
+ field: "email"
764
2204
  }
765
- ],
766
- update: data
2205
+ ]
767
2206
  });
768
- return user;
769
- },
770
- findUserByEmail: async (email, context) => {
771
- const user = await db.findOne({
772
- model: context.user.modelName,
2207
+ if (!user) return null;
2208
+ const accounts = await adapter.findMany({
2209
+ model: tables.account.tableName,
773
2210
  where: [
774
2211
  {
775
- field: "email",
776
- value: email
2212
+ value: user.id,
2213
+ field: "userId"
777
2214
  }
778
- ],
779
- select: context.user.selectFields
2215
+ ]
780
2216
  });
781
- return user;
2217
+ return {
2218
+ user,
2219
+ accounts
2220
+ };
782
2221
  },
783
- findSession: async (id, context) => {
784
- if (context.sessionAdapter) {
785
- return context.sessionAdapter.findOne({
786
- userId: id
787
- });
788
- }
789
- const session2 = await db.findOne({
790
- model: context.session.modelName,
2222
+ findUserById: async (userId) => {
2223
+ const user = await adapter.findOne({
2224
+ model: tables.user.tableName,
791
2225
  where: [
792
2226
  {
793
2227
  field: "id",
794
- value: id
2228
+ value: userId
795
2229
  }
796
- ],
797
- select: context.session.selectFields
2230
+ ]
2231
+ });
2232
+ return user;
2233
+ },
2234
+ linkAccount: async (account) => {
2235
+ const _account = await adapter.create({
2236
+ model: tables.account.tableName,
2237
+ data: account
798
2238
  });
799
- return session2;
2239
+ return _account;
800
2240
  },
801
- findUserById: async (id, context) => {
802
- const user = await db.findOne({
803
- model: context.user.modelName,
2241
+ updateUserByEmail: async (email, data) => {
2242
+ const user = await adapter.update({
2243
+ model: tables.user.tableName,
804
2244
  where: [
805
2245
  {
806
- field: "id",
807
- value: id
2246
+ value: email,
2247
+ field: "email"
808
2248
  }
809
2249
  ],
810
- select: context.user.selectFields
2250
+ update: data
811
2251
  });
812
2252
  return user;
813
2253
  },
814
- findAccount: async (input, context) => {
815
- const account = await db.findOne({
816
- model: context.account.modelName,
2254
+ updatePassword: async (userId, password) => {
2255
+ const account = await adapter.update({
2256
+ model: tables.account.tableName,
817
2257
  where: [
818
2258
  {
819
- field: "providerId",
820
- value: input.providerId.toString()
2259
+ value: userId,
2260
+ field: "userId"
821
2261
  },
822
2262
  {
823
- field: "accountId",
824
- value: input.accountId.toString()
2263
+ field: "providerId",
2264
+ value: "credential"
825
2265
  }
826
2266
  ],
827
- select: context.account.selectFields
828
- });
829
- return account;
830
- },
831
- linkAccount: async (input, context) => {
832
- const { userId, providerId, accountId } = input;
833
- const account = await db.create({
834
- model: context.account.modelName,
835
- data: {
836
- userId,
837
- providerId: providerId.toString(),
838
- accountId: accountId.toString()
2267
+ update: {
2268
+ password
839
2269
  }
840
2270
  });
841
2271
  return account;
@@ -843,51 +2273,21 @@ var createInternalAdapter = (db) => {
843
2273
  };
844
2274
  };
845
2275
 
846
- // src/adapters/utils.ts
847
- import { z as z2 } from "zod";
848
- function toInternalFields(fields) {
849
- const internalFields = {};
850
- for (const field in fields) {
851
- const { type, required, returned, hashValue, transform } = fields[field];
852
- internalFields[field] = {
853
- required: required ?? false,
854
- returned: returned ?? true,
855
- hashValue: hashValue ?? false,
856
- validator: z2[type]().transform(transform || ((x) => x))
857
- };
858
- }
859
- return internalFields;
860
- }
861
- function getSelectFields(fields, table) {
862
- const select = Object.keys(fields).filter((column) => {
863
- return fields[column]?.returned !== false;
864
- });
865
- const defaultSelect = {
866
- session: ["id", "userId", "expiresAt"],
867
- user: ["id", "email", "emailVerified"],
868
- account: ["providerId", "accountId", "userId"]
2276
+ // src/db/field.ts
2277
+ var createFieldAttribute = (type, config) => {
2278
+ return {
2279
+ type,
2280
+ ...config
869
2281
  };
870
- return [...defaultSelect[table], ...select];
871
- }
872
- var parseUser = (data, context) => {
873
- const user = Object.keys(data).map((key) => {
874
- const parsed = context.user.fields[key]?.validator.safeParse(data?.[key]);
875
- return { key, value: parsed?.success ? parsed.data : data?.[key] };
876
- }).reduce(
877
- (acc, { key, value }) => {
878
- acc[key] = value;
879
- return acc;
880
- },
881
- {}
882
- );
883
- return user;
884
2282
  };
885
2283
 
886
- // src/cookies/cookies.ts
2284
+ // src/utils/cookies.ts
2285
+ import { TimeSpan as TimeSpan3 } from "oslo";
887
2286
  function getCookies(options) {
888
- const secure = !!options.advanced?.useSecureCookies;
2287
+ const secure = !!options.advanced?.useSecureCookies || process.env.NODE_ENV === "production";
889
2288
  const secureCookiePrefix = secure ? "__Secure-" : "";
890
2289
  const cookiePrefix = "better-auth";
2290
+ const sessionMaxAge = new TimeSpan3(7, "d").seconds();
891
2291
  return {
892
2292
  sessionToken: {
893
2293
  name: `${secureCookiePrefix}${cookiePrefix}.session_token`,
@@ -896,17 +2296,17 @@ function getCookies(options) {
896
2296
  sameSite: "lax",
897
2297
  path: "/",
898
2298
  secure,
899
- maxAge: options.session?.expiresIn || timeSpan("7d"),
900
- ...options.advanced?.sessionCookie
2299
+ maxAge: sessionMaxAge
901
2300
  }
902
2301
  },
903
2302
  csrfToken: {
904
- name: `${secureCookiePrefix}${cookiePrefix}.csrf_token`,
2303
+ name: `${secureCookiePrefix ? "__Host-" : ""}${cookiePrefix}.csrf_token`,
905
2304
  options: {
906
2305
  httpOnly: true,
907
2306
  sameSite: "lax",
908
2307
  path: "/",
909
- secure
2308
+ secure,
2309
+ maxAge: 60 * 60 * 24 * 7
910
2310
  }
911
2311
  },
912
2312
  state: {
@@ -944,496 +2344,100 @@ function getCookies(options) {
944
2344
  }
945
2345
  };
946
2346
  }
947
-
948
- // src/routes/callback.ts
949
- import { z as z3 } from "zod";
950
-
951
- // src/oauth2/tokens.ts
952
- import { base64url as base64url2 } from "jose";
953
- async function getTokens(context, provider) {
954
- const redirectURL = provider.params.redirectURL || `${context.baseURL}${context.basePath}/callback/${provider.id}`;
955
- const headers = new Headers();
956
- headers.set("Content-Type", "application/x-www-form-urlencoded");
957
- headers.set("Accept", "application/json");
958
- headers.set("User-Agent", "better-auth");
959
- const encodedCredentials = base64url2.encode(
960
- `${provider.params.clientId}:${provider.params.clientSecret}`
961
- );
962
- headers.set("Authorization", `Basic ${encodedCredentials}`);
963
- const body = new URLSearchParams();
964
- body.set("grant_type", "authorization_code");
965
- body.set("code", context.request.query.code);
966
- body.set("redirect_uri", redirectURL);
967
- if (provider.pkCodeVerifier) {
968
- const codeVerifier = context.request.cookies.get(
969
- context.cookies.pkCodeVerifier.name
970
- );
971
- codeVerifier && body.set("code_verifier", codeVerifier);
972
- }
973
- body.set("client_id", provider.params.clientId);
974
- body.set("client_secret", provider.params.clientSecret);
975
- let url = provider.params.tokenEndpoint;
976
- if (!url) {
977
- const discovery = await discoveryRequest(context, provider);
978
- if (!discovery.token_endpoint) {
979
- throw new Error("Missing token endpoint");
980
- }
981
- url = discovery.token_endpoint;
982
- }
983
- const response = await fetch(url, {
984
- method: "POST",
985
- body,
986
- headers
987
- });
988
- const data = await response.json();
989
- return data;
990
- }
991
-
992
- // src/routes/callback.ts
993
- var callbackQuerySchema = z3.object({
994
- code: z3.string(),
995
- state: z3.string(),
996
- provider: z3.string()
997
- });
998
- var callback = async (context) => {
999
- const parsedQuery = callbackQuerySchema.safeParse(context.request.query);
1000
- if (!parsedQuery.success) {
1001
- const error = context.request.url.searchParams.get("error");
1002
- const errorDesc = context.request.url.searchParams.get("error_description");
1003
- const state = context.request.url.searchParams.get("state");
1004
- if (!state) {
1005
- throw new ProviderError(
1006
- `state is not returned from ${context.request.url.searchParams.get(
1007
- "provider"
1008
- )}`
1009
- );
1010
- }
1011
- const { currentURL } = getState(state);
2347
+ function createCookieGetter(options) {
2348
+ const secure = !!options.advanced?.useSecureCookies || process.env.NODE_ENV === "production";
2349
+ const secureCookiePrefix = secure ? "__Secure-" : "";
2350
+ const cookiePrefix = "better-auth";
2351
+ function getCookie(cookieName, options2) {
1012
2352
  return {
1013
- status: 302,
1014
- headers: {
1015
- Location: `${currentURL}?error=${error}&error_description=${errorDesc}`
1016
- }
1017
- };
1018
- }
1019
- const provider = getProvider(context, parsedQuery.data.provider);
1020
- if (provider?.type === "oauth" || provider?.type === "oidc") {
1021
- const storedState = context.request.cookies.get(context.cookies.state.name);
1022
- const state = parsedQuery.data.state;
1023
- const { currentURL } = getState(state);
1024
- if (storedState !== state) {
1025
- return {
1026
- status: 302,
1027
- headers: {
1028
- Location: `${currentURL}?error=invalid_state`
1029
- }
1030
- };
1031
- }
1032
- const tokens = await getTokens(context, provider);
1033
- if (tokens.error) {
1034
- return {
1035
- status: 302,
1036
- headers: {
1037
- Location: `${currentURL}?error=${tokens.error}`
1038
- }
1039
- };
1040
- }
1041
- if (provider.type === "oauth" || provider.type === "oidc") {
1042
- const profile = await provider.getUserInfo(tokens);
1043
- const {
1044
- callbackURL,
1045
- currentURL: currentURL2,
1046
- signUp: { data, autoCreateSession, onlySignUp }
1047
- } = getState(state);
1048
- let userAccount = await context.adapter.findAccount(
1049
- {
1050
- providerId: provider.id,
1051
- accountId: profile.id
1052
- },
1053
- context
1054
- );
1055
- if (provider.type === "oidc") {
1056
- if (profile.nonce) {
1057
- const nonce = context.request.cookies.get(context.cookies.nonce.name);
1058
- if (profile.nonce !== nonce) {
1059
- return {
1060
- status: 302,
1061
- headers: {
1062
- Location: `${currentURL2}?error=invalid_nonce`
1063
- }
1064
- };
1065
- }
1066
- }
1067
- }
1068
- if (onlySignUp && userAccount) {
1069
- return {
1070
- status: 302,
1071
- headers: {
1072
- Location: `${currentURL2}?error=user_already_exist`
1073
- }
1074
- };
1075
- }
1076
- let userData = null;
1077
- if (!userAccount) {
1078
- if (provider.params.linkAccounts) {
1079
- const shouldLink = provider.params.linkAccounts.enabler ? await provider.params.linkAccounts.enabler(profile) : true;
1080
- if (shouldLink) {
1081
- const { field, key } = provider.params.linkAccounts;
1082
- const user = await context._db.findOne({
1083
- model: context.user.modelName,
1084
- where: [
1085
- {
1086
- field,
1087
- value: profile[key]
1088
- }
1089
- ]
1090
- });
1091
- if (user) {
1092
- userAccount = await context.adapter.linkAccount(
1093
- {
1094
- userId: user.id,
1095
- providerId: provider.id,
1096
- accountId: profile.id
1097
- },
1098
- context
1099
- );
1100
- userData = user;
1101
- }
1102
- }
1103
- }
1104
- if (!userData && !data) {
1105
- return {
1106
- status: 302,
1107
- headers: {
1108
- Location: `${callbackURL}?error=user_not_found`
1109
- }
1110
- };
1111
- }
1112
- if (!userData) {
1113
- let signUpData = {};
1114
- for (const key in data) {
1115
- if (typeof data[key] === "string") {
1116
- const constructedKey = data[key].split(".");
1117
- let value = profile;
1118
- for (const k of constructedKey) {
1119
- value = value[k];
1120
- }
1121
- signUpData[key] = value;
1122
- } else if ("value" in data[key]) {
1123
- signUpData[key] = data[key].value;
1124
- }
1125
- }
1126
- signUpData = parseUser(signUpData, context);
1127
- const accountData = {
1128
- providerId: provider.id,
1129
- accountId: profile.id
1130
- };
1131
- for (const key in context.account.additionalFields) {
1132
- accountData[key] = tokens[context.account.additionalFields[key]];
1133
- }
1134
- try {
1135
- const { user, account } = await context.adapter.createUser(
1136
- {
1137
- user: signUpData,
1138
- account: accountData
1139
- },
1140
- context
1141
- );
1142
- userAccount = account;
1143
- userData = user;
1144
- } catch (e) {
1145
- return {
1146
- status: 302,
1147
- headers: {
1148
- Location: `${currentURL2}?error=user_already_exist`
1149
- }
1150
- };
1151
- }
1152
- }
1153
- }
1154
- if (!userData) {
1155
- userData = await context.adapter.findUserById(
1156
- userAccount?.userId,
1157
- context
1158
- );
1159
- }
1160
- if (autoCreateSession) {
1161
- const session2 = await context.adapter.createSession(
1162
- userData?.id,
1163
- context
1164
- );
1165
- setSessionCookie(context, session2.id);
2353
+ name: process.env.NODE_ENV === "production" ? `${secureCookiePrefix}${cookiePrefix}.${cookieName}` : `${cookiePrefix}.${cookieName}`,
2354
+ options: {
2355
+ secure,
2356
+ sameSite: "lax",
2357
+ path: "/",
2358
+ maxAge: 60 * 15,
2359
+ // 15 minutes in seconds
2360
+ ...options2
1166
2361
  }
1167
- return {
1168
- status: 302,
1169
- headers: {
1170
- Location: callbackURL
1171
- }
1172
- };
1173
- }
1174
- return {
1175
- status: 200
1176
2362
  };
1177
2363
  }
1178
- throw new ProviderError("Invalid provider type");
1179
- };
1180
- var callbackHandler = withPlugins(callback, ["csrf"]);
2364
+ return getCookie;
2365
+ }
1181
2366
 
1182
- // src/routes/signup.ts
1183
- import { z as z4 } from "zod";
1184
- var signUpSchema = z4.object({
1185
- data: z4.record(z4.string(), z4.any()).optional(),
1186
- provider: z4.string(),
1187
- currentURL: z4.string(),
1188
- callbackURL: z4.string().optional(),
1189
- autoCreateSession: z4.boolean().optional()
1190
- });
1191
- var signUp = async (context) => {
1192
- const data = signUpSchema.parse(context.request.body);
1193
- const provider = getProvider(context, data.provider);
1194
- if (!provider) {
1195
- throw new ProviderMissing(data.provider);
1196
- }
1197
- if (provider?.type === "oauth" || provider?.type === "oidc") {
1198
- context.request.body.signUp = context.request.body.data;
1199
- const url = await signInOAuth(context, provider, {
1200
- autoCreateSession: data.autoCreateSession ?? true,
1201
- onlySignUp: true
1202
- });
1203
- return {
1204
- status: 200,
1205
- body: {
1206
- url,
1207
- redirect: true
1208
- }
1209
- };
1210
- }
1211
- if (!provider.signUp) {
1212
- throw new ProviderError("Sign up method not implemented");
2367
+ // src/utils/logger.ts
2368
+ import { createConsola } from "consola";
2369
+ var consola = createConsola({
2370
+ formatOptions: {
2371
+ date: false
1213
2372
  }
1214
- return await provider.signUp(context);
1215
- };
1216
- var signUpHandler = withPlugins(signUp);
1217
-
1218
- // src/routes/index.ts
1219
- var router = async (context) => {
1220
- const action = context.request.action;
1221
- switch (action) {
1222
- case "signin":
1223
- return signInHandler(context);
1224
- case "callback":
1225
- return await callbackHandler(context);
1226
- case "signup":
1227
- return await signUpHandler(context);
1228
- case "signout":
1229
- return await signOutHandler(context);
1230
- case "session":
1231
- return await sessionHandler(context);
1232
- default: {
1233
- const plugin = context.plugins.find((plugin2) => {
1234
- return action.startsWith(plugin2.id);
1235
- });
1236
- const providerHandler = context.providers.find((provider) => {
1237
- return provider.type === "custom" && provider.handler?.matcher(context);
1238
- });
1239
- if (plugin?.handler) {
1240
- return await plugin.handler(context);
1241
- }
1242
- if (providerHandler?.handler) {
1243
- return await providerHandler.handler.handler(context);
1244
- }
1245
- return {
1246
- status: 404
1247
- };
2373
+ });
2374
+ var createLogger = (options) => {
2375
+ return {
2376
+ log: (...args) => {
2377
+ !options?.disabled && consola.log("", ...args);
2378
+ },
2379
+ error: (...args) => {
2380
+ !options?.disabled && consola.error("", ...args);
2381
+ },
2382
+ warn: (...args) => {
2383
+ !options?.disabled && consola.warn("", ...args);
2384
+ },
2385
+ info: (...args) => {
2386
+ !options?.disabled && consola.info("", ...args);
2387
+ },
2388
+ debug: (...args) => {
2389
+ !options?.disabled && consola.debug("", ...args);
2390
+ },
2391
+ box: (...args) => {
2392
+ !options?.disabled && consola.box("", ...args);
2393
+ },
2394
+ success: (...args) => {
2395
+ !options?.disabled && consola.success("", ...args);
2396
+ },
2397
+ break: (...args) => {
2398
+ !options?.disabled && console.log("\n");
1248
2399
  }
1249
- }
2400
+ };
1250
2401
  };
2402
+ var logger = createLogger();
1251
2403
 
1252
- // src/utils/request.ts
1253
- import { IncomingMessage } from "http";
1254
- import { z as z5 } from "zod";
1255
- async function getBody(request) {
1256
- try {
1257
- if (request instanceof Request)
1258
- return await request.json();
1259
- return new Promise((resolve) => {
1260
- const bodyParts = [];
1261
- let body;
1262
- request.on("data", (chunk) => {
1263
- bodyParts.push(chunk);
1264
- }).on("end", () => {
1265
- body = Buffer.concat(bodyParts).toString();
1266
- resolve(JSON.parse(body));
1267
- });
1268
- });
1269
- } catch {
1270
- throw new InvalidRequest();
1271
- }
1272
- }
1273
- var toRequestHeader = (incomingHeaders) => {
1274
- if (incomingHeaders instanceof Headers)
1275
- return incomingHeaders;
1276
- const headers = new Headers();
1277
- for (const [key, value] of Object.entries(incomingHeaders)) {
1278
- if (Array.isArray(value)) {
1279
- for (const val of value) {
1280
- headers.append(key, val);
1281
- }
1282
- } else if (value) {
1283
- headers.append(key, value);
1284
- }
1285
- }
1286
- return headers;
1287
- };
1288
- function isValidHttpMethod(method) {
1289
- if (method?.toUpperCase() !== "POST" && method?.toUpperCase() !== "GET") {
1290
- return false;
1291
- }
1292
- return true;
1293
- }
1294
- function parseUrl(request, options) {
1295
- let requestStringURL = request instanceof IncomingMessage ? `${request.headers.host}${request.url}` : request.url;
1296
- if (!requestStringURL.startsWith("http")) {
1297
- if (requestStringURL.startsWith("localhost")) {
1298
- requestStringURL = `http://${requestStringURL}`;
1299
- } else {
1300
- requestStringURL = `https://${requestStringURL}`;
1301
- }
1302
- }
1303
- const requestURL = new URL(requestStringURL);
1304
- const baseURL = requestURL.origin;
1305
- const basePath = options.basePath || "/api/auth";
1306
- let urlString = `${baseURL}${basePath}`;
1307
- if (!urlString.startsWith("http")) {
1308
- urlString = `https://${urlString}`;
1309
- }
1310
- const isValidURL = z5.string().url().safeParse(urlString);
1311
- let action = requestURL.pathname.split(basePath)[1] || "";
1312
- action = action.replace("/", "");
1313
- if (isValidURL.error) {
1314
- throw new InvalidURL();
1315
- }
1316
- if (action.startsWith("callback")) {
1317
- const provider = action.split("/")[1];
1318
- if (!provider)
1319
- throw new InvalidURL("Provider is missing in the URL.");
1320
- action = "callback";
1321
- requestURL.searchParams.set("provider", provider);
1322
- }
1323
- const url = new URL(urlString);
1324
- requestURL.searchParams.forEach((value, key) => {
1325
- url.searchParams.set(key, value);
1326
- });
2404
+ // src/init.ts
2405
+ var init = (options) => {
2406
+ const adapter = getAdapter(options);
2407
+ const db = createKyselyAdapter(options);
2408
+ const { baseURL, withPath: withPath2 } = getBaseURL(options.baseURL, options.basePath);
1327
2409
  return {
1328
- url,
1329
- action
2410
+ options: {
2411
+ ...options,
2412
+ baseURL,
2413
+ basePath: options.basePath || "/api/auth"
2414
+ },
2415
+ baseURL: withPath2,
2416
+ secret: options.secret || process.env.BETTER_AUTH_SECRET || process.env.AUTH_SECRET || "better-auth-secret-123456789",
2417
+ authCookies: getCookies(options),
2418
+ logger: createLogger({
2419
+ disabled: options.disableLog
2420
+ }),
2421
+ db,
2422
+ adapter,
2423
+ internalAdapter: createInternalAdapter(adapter, options),
2424
+ createAuthCookie: createCookieGetter(options)
1330
2425
  };
1331
- }
1332
-
1333
- // src/utils/secret.ts
1334
- var DEFAULT_SECRET = "better-auth-secret-key-123456789";
1335
- var getSecret = (secret) => {
1336
- secret = secret || process.env.BETTER_AUTH_SECRET || process.env.AUTH_SECRET;
1337
- if (process.env.NODE_ENV === "production" && !secret) {
1338
- throw new MissingSecret();
1339
- }
1340
- return secret || DEFAULT_SECRET;
1341
2426
  };
1342
2427
 
1343
2428
  // src/auth.ts
1344
2429
  var betterAuth = (options) => {
1345
- const defaultActions = getActions(options);
1346
- const auth = {
1347
- /**
1348
- * The handler for the better auth routes.
1349
- */
1350
- handler: async (request, opts) => {
1351
- const context = await toContext(options, request, opts);
1352
- if (!isValidHttpMethod(request.method)) {
1353
- return toResponse({ status: 200 }, context, opts);
1354
- }
1355
- const response = await router(context);
1356
- return toResponse(response, context, opts);
1357
- },
1358
- caller: {
1359
- ...defaultActions,
1360
- ...options.plugins?.map((plugin) => plugin.getActions?.(options))
1361
- },
1362
- options
1363
- };
1364
- return auth;
1365
- };
1366
- var toContext = async (options, request, handlerOptions) => {
1367
- const basePath = options.basePath || "/api/auth";
1368
- const headers = toRequestHeader(request.headers);
1369
- const { url, action } = parseUrl(request, options);
1370
- const body = request.method?.toUpperCase() === "POST" ? await getBody(request) : null;
2430
+ const authContext = init(options);
2431
+ const { handler, endpoints } = router(authContext);
1371
2432
  return {
1372
- baseURL: url.origin,
1373
- basePath,
1374
- request: {
1375
- method: request.method,
1376
- url,
1377
- query: Object.fromEntries(url.searchParams),
1378
- action,
1379
- body,
1380
- headers,
1381
- cookies: handlerOptions?.cookieManager || cookieManager(headers)
1382
- },
1383
- _db: options.adapter,
1384
- providers: options.providers,
1385
- secret: getSecret(options.secret),
1386
- adapter: createInternalAdapter(options.adapter),
1387
- plugins: getPlugins(options),
1388
- cookies: getCookies(options),
1389
- disableCSRF: options.advanced?.skipCSRFCheck || false,
1390
- session: {
1391
- modelName: options.session?.modelName || "session",
1392
- updateAge: options.session?.updateAge === void 0 ? timeSpan("1d") : options.session.updateAge,
1393
- expiresIn: options.session?.expiresIn || timeSpan("1w"),
1394
- additionalFields: options.session?.additionalFields ? toInternalFields(options.session.additionalFields) : {},
1395
- selectFields: getSelectFields(
1396
- options.session?.additionalFields || {},
1397
- "session"
1398
- )
1399
- },
1400
- user: {
1401
- modelName: options.user?.modelName || "user",
1402
- fields: options.user?.fields ? toInternalFields(options.user.fields) : {},
1403
- selectFields: getSelectFields(options.user?.fields || {}, "user")
1404
- },
1405
- account: {
1406
- modelName: options.account?.modelName || "account",
1407
- additionalFields: options.account?.additionalFields || {},
1408
- selectFields: [
1409
- ...Object.keys(options.account?.additionalFields || {}),
1410
- "userId",
1411
- "providerId",
1412
- "accountId"
1413
- ]
1414
- },
1415
- sessionAdapter: options.sessionAdapter
2433
+ handler,
2434
+ api: endpoints,
2435
+ options
1416
2436
  };
1417
2437
  };
1418
- var toResponse = (res, context, handlerOptions) => {
1419
- if (handlerOptions?.toResponse) {
1420
- return handlerOptions.toResponse(res, context);
1421
- }
1422
- context.request.headers.set("content-type", "application/json");
1423
- const response = new Response(res.body ? JSON.stringify(res.body) : null, {
1424
- headers: {
1425
- ...context.request.headers,
1426
- "Set-Cookie": context.request.headers.get("Set-Cookie") ?? "",
1427
- ...res.headers
1428
- },
1429
- status: res.status,
1430
- statusText: res.statusText
1431
- });
1432
- return response;
1433
- };
1434
2438
  export {
1435
2439
  betterAuth,
1436
- toContext,
1437
- toResponse
2440
+ createFieldAttribute,
2441
+ createInternalAdapter
1438
2442
  };
1439
2443
  //# sourceMappingURL=index.js.map