better-auth
Version:
The most comprehensive authentication framework for TypeScript.
201 lines (198 loc) • 8.73 kB
JavaScript
import { getIp } from "../utils/get-request-ip.mjs";
import { getOAuthState } from "./middlewares/oauth.mjs";
import { originCheck, originCheckMiddleware } from "./middlewares/origin-check.mjs";
import "./middlewares/index.mjs";
import { onRequestRateLimit } from "./rate-limiter/index.mjs";
import { freshSessionMiddleware, getSession, getSessionFromCtx, listSessions, requestOnlySessionMiddleware, revokeOtherSessions, revokeSession, revokeSessions, sensitiveSessionMiddleware, sessionMiddleware } from "./routes/session.mjs";
import { accountInfo, getAccessToken, linkSocialAccount, listUserAccounts, refreshToken, unlinkAccount } from "./routes/account.mjs";
import { callbackOAuth } from "./routes/callback.mjs";
import { createEmailVerificationToken, sendVerificationEmail, sendVerificationEmailFn, verifyEmail } from "./routes/email-verification.mjs";
import { error } from "./routes/error.mjs";
import { ok } from "./routes/ok.mjs";
import { requestPasswordReset, requestPasswordResetCallback, resetPassword } from "./routes/reset-password.mjs";
import { signInEmail, signInSocial } from "./routes/sign-in.mjs";
import { signOut } from "./routes/sign-out.mjs";
import { signUpEmail } from "./routes/sign-up.mjs";
import { changeEmail, changePassword, deleteUser, deleteUserCallback, setPassword, updateUser } from "./routes/update-user.mjs";
import "./routes/index.mjs";
import { toAuthEndpoints } from "./to-auth-endpoints.mjs";
import { logger } from "@better-auth/core/env";
import { APIError, APIError as APIError$1, createRouter } from "better-call";
import { createAuthEndpoint, createAuthMiddleware, optionsMiddleware } from "@better-auth/core/api";
//#region src/api/index.ts
function checkEndpointConflicts(options, logger$1) {
const endpointRegistry = /* @__PURE__ */ new Map();
options.plugins?.forEach((plugin) => {
if (plugin.endpoints) {
for (const [key, endpoint] of Object.entries(plugin.endpoints)) if (endpoint && "path" in endpoint && typeof endpoint.path === "string") {
const path = endpoint.path;
let methods = [];
if (endpoint.options && "method" in endpoint.options) {
if (Array.isArray(endpoint.options.method)) methods = endpoint.options.method;
else if (typeof endpoint.options.method === "string") methods = [endpoint.options.method];
}
if (methods.length === 0) methods = ["*"];
if (!endpointRegistry.has(path)) endpointRegistry.set(path, []);
endpointRegistry.get(path).push({
pluginId: plugin.id,
endpointKey: key,
methods
});
}
}
});
const conflicts = [];
for (const [path, entries] of endpointRegistry.entries()) if (entries.length > 1) {
const methodMap = /* @__PURE__ */ new Map();
let hasConflict = false;
for (const entry of entries) for (const method of entry.methods) {
if (!methodMap.has(method)) methodMap.set(method, []);
methodMap.get(method).push(entry.pluginId);
if (methodMap.get(method).length > 1) hasConflict = true;
if (method === "*" && entries.length > 1) hasConflict = true;
else if (method !== "*" && methodMap.has("*")) hasConflict = true;
}
if (hasConflict) {
const uniquePlugins = [...new Set(entries.map((e) => e.pluginId))];
const conflictingMethods = [];
for (const [method, plugins] of methodMap.entries()) if (plugins.length > 1 || method === "*" && entries.length > 1 || method !== "*" && methodMap.has("*")) conflictingMethods.push(method);
conflicts.push({
path,
plugins: uniquePlugins,
conflictingMethods
});
}
}
if (conflicts.length > 0) {
const conflictMessages = conflicts.map((conflict) => ` - "${conflict.path}" [${conflict.conflictingMethods.join(", ")}] used by plugins: ${conflict.plugins.join(", ")}`).join("\n");
logger$1.error(`Endpoint path conflicts detected! Multiple plugins are trying to use the same endpoint paths with conflicting HTTP methods:
${conflictMessages}
To resolve this, you can:
1. Use only one of the conflicting plugins
2. Configure the plugins to use different paths (if supported)
3. Ensure plugins use different HTTP methods for the same path
`);
}
}
function getEndpoints(ctx, options) {
const pluginEndpoints = options.plugins?.reduce((acc, plugin) => {
return {
...acc,
...plugin.endpoints
};
}, {}) ?? {};
const middlewares = options.plugins?.map((plugin) => plugin.middlewares?.map((m) => {
const middleware = (async (context) => {
const authContext = await ctx;
return m.middleware({
...context,
context: {
...authContext,
...context.context
}
});
});
middleware.options = m.middleware.options;
return {
path: m.path,
middleware
};
})).filter((plugin) => plugin !== void 0).flat() || [];
return {
api: toAuthEndpoints({
signInSocial: signInSocial(),
callbackOAuth,
getSession: getSession(),
signOut,
signUpEmail: signUpEmail(),
signInEmail: signInEmail(),
resetPassword,
verifyEmail,
sendVerificationEmail,
changeEmail,
changePassword,
setPassword,
updateUser: updateUser(),
deleteUser,
requestPasswordReset,
requestPasswordResetCallback,
listSessions: listSessions(),
revokeSession,
revokeSessions,
revokeOtherSessions,
linkSocialAccount,
listUserAccounts,
deleteUserCallback,
unlinkAccount,
refreshToken,
getAccessToken,
accountInfo,
...pluginEndpoints,
ok,
error
}, ctx),
middlewares
};
}
const router = (ctx, options) => {
const { api, middlewares } = getEndpoints(ctx, options);
const basePath = new URL(ctx.baseURL).pathname;
return createRouter(api, {
routerContext: ctx,
openapi: { disabled: true },
basePath,
routerMiddleware: [{
path: "/**",
middleware: originCheckMiddleware
}, ...middlewares],
allowedMediaTypes: ["application/json"],
async onRequest(req) {
const disabledPaths = ctx.options.disabledPaths || [];
const pathname = new URL(req.url).pathname.replace(/\/+$/, "") || "/";
const normalizedPath = basePath === "/" ? pathname : pathname.startsWith(basePath) ? pathname.slice(basePath.length).replace(/\/+$/, "") || "/" : pathname;
if (disabledPaths.includes(normalizedPath)) return new Response("Not Found", { status: 404 });
for (const plugin of ctx.options.plugins || []) if (plugin.onRequest) {
const response = await plugin.onRequest(req, ctx);
if (response && "response" in response) return response.response;
if (response && "request" in response) {
const rateLimitResponse = await onRequestRateLimit(response.request, ctx);
if (rateLimitResponse) return rateLimitResponse;
return response.request;
}
}
return onRequestRateLimit(req, ctx);
},
async onResponse(res) {
for (const plugin of ctx.options.plugins || []) if (plugin.onResponse) {
const response = await plugin.onResponse(res, ctx);
if (response) return response.response;
}
return res;
},
onError(e) {
if (e instanceof APIError$1 && e.status === "FOUND") return;
if (options.onAPIError?.throw) throw e;
if (options.onAPIError?.onError) {
options.onAPIError.onError(e, ctx);
return;
}
const optLogLevel = options.logger?.level;
const log = optLogLevel === "error" || optLogLevel === "warn" || optLogLevel === "debug" ? logger : void 0;
if (options.logger?.disabled !== true) {
if (e && typeof e === "object" && "message" in e && typeof e.message === "string") {
if (e.message.includes("no column") || e.message.includes("column") || e.message.includes("relation") || e.message.includes("table") || e.message.includes("does not exist")) {
ctx.logger?.error(e.message);
return;
}
}
if (e instanceof APIError$1) {
if (e.status === "INTERNAL_SERVER_ERROR") ctx.logger.error(e.status, e);
log?.error(e.message);
} else ctx.logger?.error(e && typeof e === "object" && "name" in e ? e.name : "", e);
}
}
});
};
//#endregion
export { APIError, accountInfo, callbackOAuth, changeEmail, changePassword, checkEndpointConflicts, createAuthEndpoint, createAuthMiddleware, createEmailVerificationToken, deleteUser, deleteUserCallback, error, freshSessionMiddleware, getAccessToken, getEndpoints, getIp, getOAuthState, getSession, getSessionFromCtx, linkSocialAccount, listSessions, listUserAccounts, ok, optionsMiddleware, originCheck, originCheckMiddleware, refreshToken, requestOnlySessionMiddleware, requestPasswordReset, requestPasswordResetCallback, resetPassword, revokeOtherSessions, revokeSession, revokeSessions, router, sendVerificationEmail, sendVerificationEmailFn, sensitiveSessionMiddleware, sessionMiddleware, setPassword, signInEmail, signInSocial, signOut, signUpEmail, unlinkAccount, updateUser, verifyEmail };
//# sourceMappingURL=index.mjs.map