UNPKG

@zpg6-test-pkgs/better-auth

Version:

The most comprehensive authentication library for TypeScript.

492 lines (486 loc) 17.3 kB
'use strict'; const index = require('./better-auth.ANpbi45u.cjs'); const date = require('./better-auth.C1hdVENX.cjs'); const env = require('./better-auth.B6fIklBU.cjs'); const base64 = require('@better-auth/utils/base64'); const hmac = require('@better-auth/utils/hmac'); const json = require('./better-auth.vPQBmXQL.cjs'); const url = require('./better-auth.DRmln2Nr.cjs'); const binary = require('@better-auth/utils/binary'); const JOSE_REGEX = /^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i; const JOSE_MINUTE = 60; const JOSE_HOUR = JOSE_MINUTE * 60; const JOSE_DAY = JOSE_HOUR * 24; const JOSE_WEEK = JOSE_DAY * 7; const JOSE_YEAR = JOSE_DAY * 365.25; const createTime = (value, format) => { const toMilliseconds = () => { switch (format) { case "ms": return value; case "s": return value * 1e3; case "m": return value * 1e3 * 60; case "h": return value * 1e3 * 60 * 60; case "d": return value * 1e3 * 60 * 60 * 24; case "w": return value * 1e3 * 60 * 60 * 24 * 7; case "y": return value * 1e3 * 60 * 60 * 24 * 365; } }; const time = { t: `${value}${format}`, value, tFormat: format, toMilliseconds, toSeconds: () => time.toMilliseconds() / 1e3, toMinutes: () => time.toSeconds() / 60, toHours: () => time.toMinutes() / 60, toDays: () => time.toHours() / 24, toWeeks: () => time.toDays() / 7, toYears: () => time.toDays() / 365, getDate: () => new Date(Date.now() + time.toMilliseconds()), add: (other) => { const otherMs = typeof other === "string" ? parseTime(other).toMilliseconds() : other.toMilliseconds(); return createTime(time.toMilliseconds() + otherMs, "ms"); }, subtract: (other) => { const otherMs = typeof other === "string" ? parseTime(other).toMilliseconds() : other.toMilliseconds(); return createTime(time.toMilliseconds() - otherMs, "ms"); }, multiply: (factor) => createTime(time.toMilliseconds() * factor, "ms"), divide: (divisor) => createTime(time.toMilliseconds() / divisor, "ms"), equals: (other) => { const otherMs = typeof other === "string" ? parseTime(other).toMilliseconds() : other.toMilliseconds(); return time.toMilliseconds() === otherMs; }, lessThan: (other) => { const otherMs = typeof other === "string" ? parseTime(other).toMilliseconds() : other.toMilliseconds(); return time.toMilliseconds() < otherMs; }, greaterThan: (other) => { const otherMs = typeof other === "string" ? parseTime(other).toMilliseconds() : other.toMilliseconds(); return time.toMilliseconds() > otherMs; }, format: (pattern) => { const date = time.getDate(); return pattern.replace(/YYYY|MM|DD|HH|mm|ss/g, (match) => { switch (match) { case "YYYY": return date.getFullYear().toString(); case "MM": return (date.getMonth() + 1).toString().padStart(2, "0"); case "DD": return date.getDate().toString().padStart(2, "0"); case "HH": return date.getHours().toString().padStart(2, "0"); case "mm": return date.getMinutes().toString().padStart(2, "0"); case "ss": return date.getSeconds().toString().padStart(2, "0"); default: return match; } }); }, fromNow: () => { const ms = time.toMilliseconds(); if (ms < 0) return time.ago(); if (ms < 1e3) return "in a few seconds"; if (ms < 6e4) return `in ${Math.round(ms / 1e3)} seconds`; if (ms < 36e5) return `in ${Math.round(ms / 6e4)} minutes`; if (ms < 864e5) return `in ${Math.round(ms / 36e5)} hours`; if (ms < 6048e5) return `in ${Math.round(ms / 864e5)} days`; if (ms < 26298e5) return `in ${Math.round(ms / 6048e5)} weeks`; if (ms < 315576e5) return `in ${Math.round(ms / 26298e5)} months`; return `in ${Math.round(ms / 315576e5)} years`; }, ago: () => { const ms = -time.toMilliseconds(); if (ms < 0) return time.fromNow(); if (ms < 1e3) return "a few seconds ago"; if (ms < 6e4) return `${Math.round(ms / 1e3)} seconds ago`; if (ms < 36e5) return `${Math.round(ms / 6e4)} minutes ago`; if (ms < 864e5) return `${Math.round(ms / 36e5)} hours ago`; if (ms < 6048e5) return `${Math.round(ms / 864e5)} days ago`; if (ms < 26298e5) return `${Math.round(ms / 6048e5)} weeks ago`; if (ms < 315576e5) return `${Math.round(ms / 26298e5)} months ago`; return `${Math.round(ms / 315576e5)} years ago`; } }; return time; }; const parseTime = (time) => { const match = time.match(/^(\d+)(ms|s|m|h|d|w|y)$/); if (!match) throw new Error("Invalid time format"); return createTime(parseInt(match[1]), match[2]); }; const joseSecs = (str) => { const matched = JOSE_REGEX.exec(str); if (!matched || matched[4] && matched[1]) { throw new TypeError("Invalid time period format"); } const value = parseFloat(matched[2]); const unit = matched[3].toLowerCase(); let numericDate; switch (unit) { case "sec": case "secs": case "second": case "seconds": case "s": numericDate = Math.round(value); break; case "minute": case "minutes": case "min": case "mins": case "m": numericDate = Math.round(value * JOSE_MINUTE); break; case "hour": case "hours": case "hr": case "hrs": case "h": numericDate = Math.round(value * JOSE_HOUR); break; case "day": case "days": case "d": numericDate = Math.round(value * JOSE_DAY); break; case "week": case "weeks": case "w": numericDate = Math.round(value * JOSE_WEEK); break; // years matched default: numericDate = Math.round(value * JOSE_YEAR); break; } if (matched[1] === "-" || matched[4] === "ago") { return -numericDate; } return numericDate; }; function parseSetCookieHeader(setCookie) { const cookies = /* @__PURE__ */ new Map(); const cookieArray = setCookie.split(", "); cookieArray.forEach((cookieString) => { const parts = cookieString.split(";").map((part) => part.trim()); const [nameValue, ...attributes] = parts; const [name, ...valueParts] = nameValue.split("="); const value = valueParts.join("="); if (!name || value === void 0) { return; } const attrObj = { value }; attributes.forEach((attribute) => { const [attrName, ...attrValueParts] = attribute.split("="); const attrValue = attrValueParts.join("="); const normalizedAttrName = attrName.trim().toLowerCase(); switch (normalizedAttrName) { case "max-age": attrObj["max-age"] = attrValue ? parseInt(attrValue.trim(), 10) : void 0; break; case "expires": attrObj.expires = attrValue ? new Date(attrValue.trim()) : void 0; break; case "domain": attrObj.domain = attrValue ? attrValue.trim() : void 0; break; case "path": attrObj.path = attrValue ? attrValue.trim() : void 0; break; case "secure": attrObj.secure = true; break; case "httponly": attrObj.httponly = true; break; case "samesite": attrObj.samesite = attrValue ? attrValue.trim().toLowerCase() : void 0; break; default: attrObj[normalizedAttrName] = attrValue ? attrValue.trim() : true; break; } }); cookies.set(name, attrObj); }); return cookies; } function setCookieToHeader(headers) { return (context) => { const setCookieHeader = context.response.headers.get("set-cookie"); if (!setCookieHeader) { return; } const cookieMap = /* @__PURE__ */ new Map(); const existingCookiesHeader = headers.get("cookie") || ""; existingCookiesHeader.split(";").forEach((cookie) => { const [name, ...rest] = cookie.trim().split("="); if (name && rest.length > 0) { cookieMap.set(name, rest.join("=")); } }); const setCookieHeaders = setCookieHeader.split(","); setCookieHeaders.forEach((header) => { const cookies = parseSetCookieHeader(header); cookies.forEach((value, name) => { cookieMap.set(name, value.value); }); }); const updatedCookies = Array.from(cookieMap.entries()).map(([name, value]) => `${name}=${value}`).join("; "); headers.set("cookie", updatedCookies); }; } function createCookieGetter(options) { const secure = options.advanced?.useSecureCookies !== void 0 ? options.advanced?.useSecureCookies : options.baseURL !== void 0 ? options.baseURL.startsWith("https://") ? true : false : env.isProduction; const secureCookiePrefix = secure ? "__Secure-" : ""; const crossSubdomainEnabled = !!options.advanced?.crossSubDomainCookies?.enabled; const domain = crossSubdomainEnabled ? options.advanced?.crossSubDomainCookies?.domain || (options.baseURL ? new URL(options.baseURL).hostname : void 0) : void 0; if (crossSubdomainEnabled && !domain) { throw new index.BetterAuthError( "baseURL is required when crossSubdomainCookies are enabled" ); } function createCookie(cookieName, overrideAttributes = {}) { const prefix = options.advanced?.cookiePrefix || "better-auth"; const name = options.advanced?.cookies?.[cookieName]?.name || `${prefix}.${cookieName}`; const attributes = options.advanced?.cookies?.[cookieName]?.attributes; return { name: `${secureCookiePrefix}${name}`, attributes: { secure: !!secureCookiePrefix, sameSite: "lax", path: "/", httpOnly: true, ...crossSubdomainEnabled ? { domain } : {}, ...options.advanced?.defaultCookieAttributes, ...overrideAttributes, ...attributes } }; } return createCookie; } function getCookies(options) { const createCookie = createCookieGetter(options); const sessionMaxAge = options.session?.expiresIn || createTime(7, "d").toSeconds(); const sessionToken = createCookie("session_token", { maxAge: sessionMaxAge }); const sessionData = createCookie("session_data", { maxAge: options.session?.cookieCache?.maxAge || 60 * 5 }); const dontRememberToken = createCookie("dont_remember"); return { sessionToken: { name: sessionToken.name, options: sessionToken.attributes }, /** * This cookie is used to store the session data in the cookie * This is useful for when you want to cache the session in the cookie */ sessionData: { name: sessionData.name, options: sessionData.attributes }, dontRememberToken: { name: dontRememberToken.name, options: dontRememberToken.attributes } }; } async function setCookieCache(ctx, session) { const shouldStoreSessionDataInCookie = ctx.context.options.session?.cookieCache?.enabled; if (shouldStoreSessionDataInCookie) { const filteredSession = Object.entries(session.session).reduce( (acc, [key, value]) => { const fieldConfig = ctx.context.options.session?.additionalFields?.[key]; if (!fieldConfig || fieldConfig.returned !== false) { acc[key] = value; } return acc; }, {} ); const sessionData = { session: filteredSession, user: session.user }; const expiresAtDate = date.getDate( ctx.context.authCookies.sessionData.options.maxAge || 60, "sec" ).getTime(); const data = base64.base64Url.encode( JSON.stringify({ session: sessionData, expiresAt: expiresAtDate, signature: await hmac.createHMAC("SHA-256", "base64urlnopad").sign( ctx.context.secret, JSON.stringify({ ...sessionData, expiresAt: expiresAtDate }) ) }), { padding: false } ); if (data.length > 4093) { throw new index.BetterAuthError( "Session data is too large to store in the cookie. Please disable session cookie caching or reduce the size of the session data" ); } ctx.setCookie( ctx.context.authCookies.sessionData.name, data, ctx.context.authCookies.sessionData.options ); } } async function setSessionCookie(ctx, session, dontRememberMe, overrides) { const dontRememberMeCookie = await ctx.getSignedCookie( ctx.context.authCookies.dontRememberToken.name, ctx.context.secret ); dontRememberMe = dontRememberMe !== void 0 ? dontRememberMe : !!dontRememberMeCookie; const options = ctx.context.authCookies.sessionToken.options; const maxAge = dontRememberMe ? void 0 : ctx.context.sessionConfig.expiresIn; await ctx.setSignedCookie( ctx.context.authCookies.sessionToken.name, session.session.token, ctx.context.secret, { ...options, maxAge, ...overrides } ); if (dontRememberMe) { await ctx.setSignedCookie( ctx.context.authCookies.dontRememberToken.name, "true", ctx.context.secret, ctx.context.authCookies.dontRememberToken.options ); } await setCookieCache(ctx, session); ctx.context.setNewSession(session); if (ctx.context.options.secondaryStorage) { await ctx.context.secondaryStorage?.set( session.session.token, JSON.stringify({ user: session.user, session: session.session }), Math.floor( (new Date(session.session.expiresAt).getTime() - Date.now()) / 1e3 ) ); } } function deleteSessionCookie(ctx, skipDontRememberMe) { ctx.setCookie(ctx.context.authCookies.sessionToken.name, "", { ...ctx.context.authCookies.sessionToken.options, maxAge: 0 }); ctx.setCookie(ctx.context.authCookies.sessionData.name, "", { ...ctx.context.authCookies.sessionData.options, maxAge: 0 }); if (!skipDontRememberMe) { ctx.setCookie(ctx.context.authCookies.dontRememberToken.name, "", { ...ctx.context.authCookies.dontRememberToken.options, maxAge: 0 }); } } function parseCookies(cookieHeader) { const cookies = cookieHeader.split("; "); const cookieMap = /* @__PURE__ */ new Map(); cookies.forEach((cookie) => { const [name, value] = cookie.split("="); cookieMap.set(name, value); }); return cookieMap; } const getSessionCookie = (request, config) => { if (config?.cookiePrefix) { if (config.cookieName) { config.cookiePrefix = `${config.cookiePrefix}-`; } else { config.cookiePrefix = `${config.cookiePrefix}.`; } } const headers = "headers" in request ? request.headers : request; const req = request instanceof Request ? request : void 0; url.getBaseURL(req?.url, config?.path, req); const cookies = headers.get("cookie"); if (!cookies) { return null; } const { cookieName = "session_token", cookiePrefix = "better-auth." } = config || {}; const name = `${cookiePrefix}${cookieName}`; const secureCookieName = `__Secure-${name}`; const parsedCookie = parseCookies(cookies); const sessionToken = parsedCookie.get(name) || parsedCookie.get(secureCookieName); if (sessionToken) { return sessionToken; } return null; }; const getCookieCache = async (request, config) => { const headers = request instanceof Headers ? request : request.headers; const cookies = headers.get("cookie"); if (!cookies) { return null; } const { cookieName = "session_data", cookiePrefix = "better-auth" } = config || {}; const name = config?.isSecure !== void 0 ? config.isSecure ? `__Secure-${cookiePrefix}.${cookieName}` : `${cookiePrefix}.${cookieName}` : env.isProduction ? `__Secure-${cookiePrefix}.${cookieName}` : `${cookiePrefix}.${cookieName}`; const parsedCookie = parseCookies(cookies); const sessionData = parsedCookie.get(name); if (sessionData) { const sessionDataPayload = json.safeJSONParse(binary.binary.decode(base64.base64Url.decode(sessionData))); if (!sessionDataPayload) { return null; } const secret = config?.secret || env.env.BETTER_AUTH_SECRET; if (!secret) { throw new index.BetterAuthError( "getCookieCache requires a secret to be provided. Either pass it as an option or set the BETTER_AUTH_SECRET environment variable" ); } const isValid = await hmac.createHMAC("SHA-256", "base64urlnopad").verify( secret, JSON.stringify({ ...sessionDataPayload.session, expiresAt: sessionDataPayload.expiresAt }), sessionDataPayload.signature ); if (!isValid) { return null; } return sessionDataPayload.session; } return null; }; exports.createCookieGetter = createCookieGetter; exports.deleteSessionCookie = deleteSessionCookie; exports.getCookieCache = getCookieCache; exports.getCookies = getCookies; exports.getSessionCookie = getSessionCookie; exports.joseSecs = joseSecs; exports.parseCookies = parseCookies; exports.parseSetCookieHeader = parseSetCookieHeader; exports.setCookieCache = setCookieCache; exports.setCookieToHeader = setCookieToHeader; exports.setSessionCookie = setSessionCookie;