@zpg6-test-pkgs/better-auth
Version:
The most comprehensive authentication library for TypeScript.
527 lines (519 loc) • 15.9 kB
JavaScript
'use strict';
const betterCall = require('better-call');
const date = require('./better-auth.C1hdVENX.cjs');
const cookies_index = require('./better-auth.l2-e84v_.cjs');
const z = require('zod/v4');
const json = require('./better-auth.vPQBmXQL.cjs');
const hmac = require('@better-auth/utils/hmac');
const base64 = require('@better-auth/utils/base64');
const binary = require('@better-auth/utils/binary');
function _interopNamespaceCompat(e) {
if (e && typeof e === 'object' && 'default' in e) return e;
const n = Object.create(null);
if (e) {
for (const k in e) {
n[k] = e[k];
}
}
n.default = e;
return n;
}
const z__namespace = /*#__PURE__*/_interopNamespaceCompat(z);
const optionsMiddleware = betterCall.createMiddleware(async () => {
return {};
});
const createAuthMiddleware = betterCall.createMiddleware.create({
use: [
optionsMiddleware,
/**
* Only use for post hooks
*/
betterCall.createMiddleware(async () => {
return {};
})
]
});
const createAuthEndpoint = betterCall.createEndpoint.create({
use: [optionsMiddleware]
});
const BASE_ERROR_CODES = {
USER_NOT_FOUND: "User not found",
FAILED_TO_CREATE_USER: "Failed to create user",
FAILED_TO_CREATE_SESSION: "Failed to create session",
FAILED_TO_UPDATE_USER: "Failed to update user",
FAILED_TO_GET_SESSION: "Failed to get session",
INVALID_PASSWORD: "Invalid password",
INVALID_EMAIL: "Invalid email",
INVALID_EMAIL_OR_PASSWORD: "Invalid email or password",
SOCIAL_ACCOUNT_ALREADY_LINKED: "Social account already linked",
PROVIDER_NOT_FOUND: "Provider not found",
INVALID_TOKEN: "invalid token",
ID_TOKEN_NOT_SUPPORTED: "id_token not supported",
FAILED_TO_GET_USER_INFO: "Failed to get user info",
USER_EMAIL_NOT_FOUND: "User email not found",
EMAIL_NOT_VERIFIED: "Email not verified",
PASSWORD_TOO_SHORT: "Password too short",
PASSWORD_TOO_LONG: "Password too long",
USER_ALREADY_EXISTS: "User already exists. Use another email.",
EMAIL_CAN_NOT_BE_UPDATED: "Email can not be updated",
CREDENTIAL_ACCOUNT_NOT_FOUND: "Credential account not found",
SESSION_EXPIRED: "Session expired. Re-authenticate to perform this action.",
FAILED_TO_UNLINK_LAST_ACCOUNT: "You can't unlink your last account",
ACCOUNT_NOT_FOUND: "Account not found",
USER_ALREADY_HAS_PASSWORD: "User already has a password. Provide that to delete the account."
};
const getSessionQuerySchema = z__namespace.optional(
z__namespace.object({
/**
* If cookie cache is enabled, it will disable the cache
* and fetch the session from the database
*/
disableCookieCache: z__namespace.coerce.boolean().meta({
description: "Disable cookie cache and fetch session from database"
}).optional(),
disableRefresh: z__namespace.coerce.boolean().meta({
description: "Disable session refresh. Useful for checking session status, without updating the session"
}).optional()
})
);
const getSession = () => createAuthEndpoint(
"/get-session",
{
method: "GET",
query: getSessionQuerySchema,
requireHeaders: true,
metadata: {
openapi: {
description: "Get the current session",
responses: {
"200": {
description: "Success",
content: {
"application/json": {
schema: {
type: "object",
properties: {
session: {
$ref: "#/components/schemas/Session"
},
user: {
$ref: "#/components/schemas/User"
}
},
required: ["session", "user"]
}
}
}
}
}
}
}
},
async (ctx) => {
try {
const sessionCookieToken = await ctx.getSignedCookie(
ctx.context.authCookies.sessionToken.name,
ctx.context.secret
);
if (!sessionCookieToken) {
return null;
}
const sessionDataCookie = ctx.getCookie(
ctx.context.authCookies.sessionData.name
);
const sessionDataPayload = sessionDataCookie ? json.safeJSONParse(binary.binary.decode(base64.base64Url.decode(sessionDataCookie))) : null;
if (sessionDataPayload) {
const isValid = await hmac.createHMAC("SHA-256", "base64urlnopad").verify(
ctx.context.secret,
JSON.stringify({
...sessionDataPayload.session,
expiresAt: sessionDataPayload.expiresAt
}),
sessionDataPayload.signature
);
if (!isValid) {
const dataCookie = ctx.context.authCookies.sessionData.name;
ctx.setCookie(dataCookie, "", {
maxAge: 0
});
return ctx.json(null);
}
}
const dontRememberMe = await ctx.getSignedCookie(
ctx.context.authCookies.dontRememberToken.name,
ctx.context.secret
);
if (sessionDataPayload?.session && ctx.context.options.session?.cookieCache?.enabled && !ctx.query?.disableCookieCache) {
const session2 = sessionDataPayload.session;
const hasExpired = sessionDataPayload.expiresAt < Date.now() || session2.session.expiresAt < /* @__PURE__ */ new Date();
if (!hasExpired) {
ctx.context.session = session2;
return ctx.json(
session2
);
} else {
const dataCookie = ctx.context.authCookies.sessionData.name;
ctx.setCookie(dataCookie, "", {
maxAge: 0
});
}
}
const session = await ctx.context.internalAdapter.findSession(sessionCookieToken);
ctx.context.session = session;
if (!session || session.session.expiresAt < /* @__PURE__ */ new Date()) {
cookies_index.deleteSessionCookie(ctx);
if (session) {
await ctx.context.internalAdapter.deleteSession(
session.session.token
);
}
return ctx.json(null);
}
if (dontRememberMe || ctx.query?.disableRefresh) {
return ctx.json(
session
);
}
const expiresIn = ctx.context.sessionConfig.expiresIn;
const updateAge = ctx.context.sessionConfig.updateAge;
const sessionIsDueToBeUpdatedDate = session.session.expiresAt.valueOf() - expiresIn * 1e3 + updateAge * 1e3;
const shouldBeUpdated = sessionIsDueToBeUpdatedDate <= Date.now();
if (shouldBeUpdated && (!ctx.query?.disableRefresh || !ctx.context.options.session?.disableSessionRefresh)) {
const updatedSession = await ctx.context.internalAdapter.updateSession(
session.session.token,
{
expiresAt: date.getDate(ctx.context.sessionConfig.expiresIn, "sec"),
updatedAt: /* @__PURE__ */ new Date()
}
);
if (!updatedSession) {
cookies_index.deleteSessionCookie(ctx);
return ctx.json(null, { status: 401 });
}
const maxAge = (updatedSession.expiresAt.valueOf() - Date.now()) / 1e3;
await cookies_index.setSessionCookie(
ctx,
{
session: updatedSession,
user: session.user
},
false,
{
maxAge
}
);
return ctx.json({
session: updatedSession,
user: session.user
});
}
await cookies_index.setCookieCache(ctx, session);
return ctx.json(
session
);
} catch (error) {
ctx.context.logger.error("INTERNAL_SERVER_ERROR", error);
throw new betterCall.APIError("INTERNAL_SERVER_ERROR", {
message: BASE_ERROR_CODES.FAILED_TO_GET_SESSION
});
}
}
);
const getSessionFromCtx = async (ctx, config) => {
if (ctx.context.session) {
return ctx.context.session;
}
const session = await getSession()({
...ctx,
asResponse: false,
headers: ctx.headers,
returnHeaders: false,
query: {
...config,
...ctx.query
}
}).catch((e) => {
return null;
});
ctx.context.session = session;
return session;
};
const sessionMiddleware = createAuthMiddleware(async (ctx) => {
const session = await getSessionFromCtx(ctx);
if (!session?.session) {
throw new betterCall.APIError("UNAUTHORIZED");
}
return {
session
};
});
const requestOnlySessionMiddleware = createAuthMiddleware(
async (ctx) => {
const session = await getSessionFromCtx(ctx);
if (!session?.session && (ctx.request || ctx.headers)) {
throw new betterCall.APIError("UNAUTHORIZED");
}
return { session };
}
);
const freshSessionMiddleware = createAuthMiddleware(async (ctx) => {
const session = await getSessionFromCtx(ctx);
if (!session?.session) {
throw new betterCall.APIError("UNAUTHORIZED");
}
if (ctx.context.sessionConfig.freshAge === 0) {
return {
session
};
}
const freshAge = ctx.context.sessionConfig.freshAge;
const lastUpdated = session.session.updatedAt?.valueOf() || session.session.createdAt.valueOf();
const now = Date.now();
const isFresh = now - lastUpdated < freshAge * 1e3;
if (!isFresh) {
throw new betterCall.APIError("FORBIDDEN", {
message: "Session is not fresh"
});
}
return {
session
};
});
const listSessions = () => createAuthEndpoint(
"/list-sessions",
{
method: "GET",
use: [sessionMiddleware],
requireHeaders: true,
metadata: {
openapi: {
description: "List all active sessions for the user",
responses: {
"200": {
description: "Success",
content: {
"application/json": {
schema: {
type: "array",
items: {
$ref: "#/components/schemas/Session"
}
}
}
}
}
}
}
}
},
async (ctx) => {
try {
const sessions = await ctx.context.internalAdapter.listSessions(
ctx.context.session.user.id
);
const activeSessions = sessions.filter((session) => {
return session.expiresAt > /* @__PURE__ */ new Date();
});
return ctx.json(
activeSessions
);
} catch (e) {
ctx.context.logger.error(e);
throw ctx.error("INTERNAL_SERVER_ERROR");
}
}
);
const revokeSession = createAuthEndpoint(
"/revoke-session",
{
method: "POST",
body: z__namespace.object({
token: z__namespace.string().meta({
description: "The token to revoke"
})
}),
use: [sessionMiddleware],
requireHeaders: true,
metadata: {
openapi: {
description: "Revoke a single session",
requestBody: {
content: {
"application/json": {
schema: {
type: "object",
properties: {
token: {
type: "string",
description: "The token to revoke"
}
},
required: ["token"]
}
}
}
},
responses: {
"200": {
description: "Success",
content: {
"application/json": {
schema: {
type: "object",
properties: {
status: {
type: "boolean",
description: "Indicates if the session was revoked successfully"
}
},
required: ["status"]
}
}
}
}
}
}
}
},
async (ctx) => {
const token = ctx.body.token;
const findSession = await ctx.context.internalAdapter.findSession(token);
if (!findSession) {
throw new betterCall.APIError("BAD_REQUEST", {
message: "Session not found"
});
}
if (findSession.session.userId !== ctx.context.session.user.id) {
throw new betterCall.APIError("UNAUTHORIZED");
}
try {
await ctx.context.internalAdapter.deleteSession(token);
} catch (error) {
ctx.context.logger.error(
error && typeof error === "object" && "name" in error ? error.name : "",
error
);
throw new betterCall.APIError("INTERNAL_SERVER_ERROR");
}
return ctx.json({
status: true
});
}
);
const revokeSessions = createAuthEndpoint(
"/revoke-sessions",
{
method: "POST",
use: [sessionMiddleware],
requireHeaders: true,
metadata: {
openapi: {
description: "Revoke all sessions for the user",
responses: {
"200": {
description: "Success",
content: {
"application/json": {
schema: {
type: "object",
properties: {
status: {
type: "boolean",
description: "Indicates if all sessions were revoked successfully"
}
},
required: ["status"]
}
}
}
}
}
}
}
},
async (ctx) => {
try {
await ctx.context.internalAdapter.deleteSessions(
ctx.context.session.user.id
);
} catch (error) {
ctx.context.logger.error(
error && typeof error === "object" && "name" in error ? error.name : "",
error
);
throw new betterCall.APIError("INTERNAL_SERVER_ERROR");
}
return ctx.json({
status: true
});
}
);
const revokeOtherSessions = createAuthEndpoint(
"/revoke-other-sessions",
{
method: "POST",
requireHeaders: true,
use: [sessionMiddleware],
metadata: {
openapi: {
description: "Revoke all other sessions for the user except the current one",
responses: {
"200": {
description: "Success",
content: {
"application/json": {
schema: {
type: "object",
properties: {
status: {
type: "boolean",
description: "Indicates if all other sessions were revoked successfully"
}
},
required: ["status"]
}
}
}
}
}
}
}
},
async (ctx) => {
const session = ctx.context.session;
if (!session.user) {
throw new betterCall.APIError("UNAUTHORIZED");
}
const sessions = await ctx.context.internalAdapter.listSessions(
session.user.id
);
const activeSessions = sessions.filter((session2) => {
return session2.expiresAt > /* @__PURE__ */ new Date();
});
const otherSessions = activeSessions.filter(
(session2) => session2.token !== ctx.context.session.session.token
);
await Promise.all(
otherSessions.map(
(session2) => ctx.context.internalAdapter.deleteSession(session2.token)
)
);
return ctx.json({
status: true
});
}
);
exports.BASE_ERROR_CODES = BASE_ERROR_CODES;
exports.createAuthEndpoint = createAuthEndpoint;
exports.createAuthMiddleware = createAuthMiddleware;
exports.freshSessionMiddleware = freshSessionMiddleware;
exports.getSession = getSession;
exports.getSessionFromCtx = getSessionFromCtx;
exports.getSessionQuerySchema = getSessionQuerySchema;
exports.listSessions = listSessions;
exports.optionsMiddleware = optionsMiddleware;
exports.requestOnlySessionMiddleware = requestOnlySessionMiddleware;
exports.revokeOtherSessions = revokeOtherSessions;
exports.revokeSession = revokeSession;
exports.revokeSessions = revokeSessions;
exports.sessionMiddleware = sessionMiddleware;