better-auth
Version:
The most comprehensive authentication framework for TypeScript.
1 lines • 25.5 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","names":["data: string","cleanCookies","chunks: Array<{ index: number; value: string }>","cookieName"],"sources":["../../src/cookies/index.ts"],"sourcesContent":["import type {\n\tBetterAuthCookies,\n\tBetterAuthOptions,\n\tGenericEndpointContext,\n} from \"@better-auth/core\";\nimport { env, isProduction } from \"@better-auth/core/env\";\nimport { BetterAuthError } from \"@better-auth/core/error\";\nimport { safeJSONParse } from \"@better-auth/core/utils\";\nimport { base64Url } from \"@better-auth/utils/base64\";\nimport { binary } from \"@better-auth/utils/binary\";\nimport { createHMAC } from \"@better-auth/utils/hmac\";\nimport type { CookieOptions } from \"better-call\";\nimport {\n\tsignJWT,\n\tsymmetricDecodeJWT,\n\tsymmetricEncodeJWT,\n\tverifyJWT,\n} from \"../crypto/jwt\";\nimport { parseUserOutput } from \"../db/schema\";\nimport type { Session, User } from \"../types\";\nimport { getDate } from \"../utils/date\";\nimport { sec } from \"../utils/time\";\nimport { createAccountStore, createSessionStore } from \"./session-store\";\n\nexport function createCookieGetter(options: BetterAuthOptions) {\n\tconst secure =\n\t\toptions.advanced?.useSecureCookies !== undefined\n\t\t\t? options.advanced?.useSecureCookies\n\t\t\t: options.baseURL !== undefined\n\t\t\t\t? options.baseURL.startsWith(\"https://\")\n\t\t\t\t\t? true\n\t\t\t\t\t: false\n\t\t\t\t: isProduction;\n\tconst secureCookiePrefix = secure ? \"__Secure-\" : \"\";\n\tconst crossSubdomainEnabled =\n\t\t!!options.advanced?.crossSubDomainCookies?.enabled;\n\tconst domain = crossSubdomainEnabled\n\t\t? options.advanced?.crossSubDomainCookies?.domain ||\n\t\t\t(options.baseURL ? new URL(options.baseURL).hostname : undefined)\n\t\t: undefined;\n\tif (crossSubdomainEnabled && !domain) {\n\t\tthrow new BetterAuthError(\n\t\t\t\"baseURL is required when crossSubdomainCookies are enabled\",\n\t\t);\n\t}\n\tfunction createCookie(\n\t\tcookieName: string,\n\t\toverrideAttributes: Partial<CookieOptions> = {},\n\t) {\n\t\tconst prefix = options.advanced?.cookiePrefix || \"better-auth\";\n\t\tconst name =\n\t\t\toptions.advanced?.cookies?.[cookieName as \"session_token\"]?.name ||\n\t\t\t`${prefix}.${cookieName}`;\n\n\t\tconst attributes =\n\t\t\toptions.advanced?.cookies?.[cookieName as \"session_token\"]?.attributes;\n\n\t\treturn {\n\t\t\tname: `${secureCookiePrefix}${name}`,\n\t\t\tattributes: {\n\t\t\t\tsecure: !!secureCookiePrefix,\n\t\t\t\tsameSite: \"lax\",\n\t\t\t\tpath: \"/\",\n\t\t\t\thttpOnly: true,\n\t\t\t\t...(crossSubdomainEnabled ? { domain } : {}),\n\t\t\t\t...options.advanced?.defaultCookieAttributes,\n\t\t\t\t...overrideAttributes,\n\t\t\t\t...attributes,\n\t\t\t} as CookieOptions,\n\t\t};\n\t}\n\treturn createCookie;\n}\n\nexport function getCookies(options: BetterAuthOptions) {\n\tconst createCookie = createCookieGetter(options);\n\tconst sessionMaxAge = options.session?.expiresIn || sec(\"7d\");\n\tconst sessionToken = createCookie(\"session_token\", {\n\t\tmaxAge: sessionMaxAge,\n\t});\n\tconst sessionData = createCookie(\"session_data\", {\n\t\tmaxAge: options.session?.cookieCache?.maxAge || 60 * 5,\n\t});\n\tconst accountData = createCookie(\"account_data\", {\n\t\tmaxAge: options.session?.cookieCache?.maxAge || 60 * 5,\n\t});\n\tconst dontRememberToken = createCookie(\"dont_remember\");\n\treturn {\n\t\tsessionToken: {\n\t\t\tname: sessionToken.name,\n\t\t\toptions: sessionToken.attributes,\n\t\t},\n\t\t/**\n\t\t * This cookie is used to store the session data in the cookie\n\t\t * This is useful for when you want to cache the session in the cookie\n\t\t */\n\t\tsessionData: {\n\t\t\tname: sessionData.name,\n\t\t\toptions: sessionData.attributes,\n\t\t},\n\t\tdontRememberToken: {\n\t\t\tname: dontRememberToken.name,\n\t\t\toptions: dontRememberToken.attributes,\n\t\t},\n\t\taccountData: {\n\t\t\tname: accountData.name,\n\t\t\toptions: accountData.attributes,\n\t\t},\n\t};\n}\n\nexport async function setCookieCache(\n\tctx: GenericEndpointContext,\n\tsession: {\n\t\tsession: Session & Record<string, any>;\n\t\tuser: User;\n\t},\n\tdontRememberMe: boolean,\n) {\n\tconst shouldStoreSessionDataInCookie =\n\t\tctx.context.options.session?.cookieCache?.enabled;\n\n\tif (shouldStoreSessionDataInCookie) {\n\t\tconst filteredSession = Object.entries(session.session).reduce(\n\t\t\t(acc, [key, value]) => {\n\t\t\t\tconst fieldConfig =\n\t\t\t\t\tctx.context.options.session?.additionalFields?.[key];\n\t\t\t\tif (!fieldConfig || fieldConfig.returned !== false) {\n\t\t\t\t\tacc[key] = value;\n\t\t\t\t}\n\t\t\t\treturn acc;\n\t\t\t},\n\t\t\t{} as Record<string, any>,\n\t\t);\n\n\t\t// Apply field filtering to user data\n\t\tconst filteredUser = parseUserOutput(ctx.context.options, session.user);\n\n\t\t// Compute version\n\t\tconst versionConfig = ctx.context.options.session?.cookieCache?.version;\n\t\tlet version = \"1\"; // default version\n\t\tif (versionConfig) {\n\t\t\tif (typeof versionConfig === \"string\") {\n\t\t\t\tversion = versionConfig;\n\t\t\t} else if (typeof versionConfig === \"function\") {\n\t\t\t\tconst result = versionConfig(session.session, session.user);\n\t\t\t\tversion = result instanceof Promise ? await result : result;\n\t\t\t}\n\t\t}\n\n\t\tconst sessionData = {\n\t\t\tsession: filteredSession,\n\t\t\tuser: filteredUser,\n\t\t\tupdatedAt: Date.now(),\n\t\t\tversion,\n\t\t};\n\n\t\tconst options = {\n\t\t\t...ctx.context.authCookies.sessionData.options,\n\t\t\tmaxAge: dontRememberMe\n\t\t\t\t? undefined\n\t\t\t\t: ctx.context.authCookies.sessionData.options.maxAge,\n\t\t};\n\n\t\tconst expiresAtDate = getDate(options.maxAge || 60, \"sec\").getTime();\n\t\tconst strategy =\n\t\t\tctx.context.options.session?.cookieCache?.strategy || \"compact\";\n\n\t\tlet data: string;\n\n\t\tif (strategy === \"jwe\") {\n\t\t\t// Use JWE strategy (JSON Web Encryption) with A256CBC-HS512 + HKDF\n\t\t\tdata = await symmetricEncodeJWT(\n\t\t\t\tsessionData,\n\t\t\t\tctx.context.secret,\n\t\t\t\t\"better-auth-session\",\n\t\t\t\toptions.maxAge || 60 * 5,\n\t\t\t);\n\t\t} else if (strategy === \"jwt\") {\n\t\t\t// Use JWT strategy with HMAC-SHA256 signature (HS256), no encryption\n\t\t\tdata = await signJWT(\n\t\t\t\tsessionData,\n\t\t\t\tctx.context.secret,\n\t\t\t\toptions.maxAge || 60 * 5,\n\t\t\t);\n\t\t} else {\n\t\t\t// Use compact strategy (base64url + HMAC, no JWT spec overhead)\n\t\t\t// Also handles legacy \"base64-hmac\" for backward compatibility\n\t\t\tdata = base64Url.encode(\n\t\t\t\tJSON.stringify({\n\t\t\t\t\tsession: sessionData,\n\t\t\t\t\texpiresAt: expiresAtDate,\n\t\t\t\t\tsignature: await createHMAC(\"SHA-256\", \"base64urlnopad\").sign(\n\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\t...sessionData,\n\t\t\t\t\t\t\texpiresAt: expiresAtDate,\n\t\t\t\t\t\t}),\n\t\t\t\t\t),\n\t\t\t\t}),\n\t\t\t\t{\n\t\t\t\t\tpadding: false,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\n\t\t// Check if we need to chunk the cookie (only if it exceeds 4093 bytes)\n\t\tif (data.length > 4093) {\n\t\t\tconst sessionStore = createSessionStore(\n\t\t\t\tctx.context.authCookies.sessionData.name,\n\t\t\t\toptions,\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tconst cookies = sessionStore.chunk(data, options);\n\t\t\tsessionStore.setCookies(cookies);\n\t\t} else {\n\t\t\tconst sessionStore = createSessionStore(\n\t\t\t\tctx.context.authCookies.sessionData.name,\n\t\t\t\toptions,\n\t\t\t\tctx,\n\t\t\t);\n\n\t\t\tif (sessionStore.hasChunks()) {\n\t\t\t\tconst cleanCookies = sessionStore.clean();\n\t\t\t\tsessionStore.setCookies(cleanCookies);\n\t\t\t}\n\n\t\t\tctx.setCookie(ctx.context.authCookies.sessionData.name, data, options);\n\t\t}\n\t}\n}\n\nexport async function setSessionCookie(\n\tctx: GenericEndpointContext,\n\tsession: {\n\t\tsession: Session & Record<string, any>;\n\t\tuser: User;\n\t},\n\tdontRememberMe?: boolean | undefined,\n\toverrides?: Partial<CookieOptions> | undefined,\n) {\n\tconst dontRememberMeCookie = await ctx.getSignedCookie(\n\t\tctx.context.authCookies.dontRememberToken.name,\n\t\tctx.context.secret,\n\t);\n\t// if dontRememberMe is not set, use the cookie value\n\tdontRememberMe =\n\t\tdontRememberMe !== undefined ? dontRememberMe : !!dontRememberMeCookie;\n\n\tconst options = ctx.context.authCookies.sessionToken.options;\n\tconst maxAge = dontRememberMe\n\t\t? undefined\n\t\t: ctx.context.sessionConfig.expiresIn;\n\tawait ctx.setSignedCookie(\n\t\tctx.context.authCookies.sessionToken.name,\n\t\tsession.session.token,\n\t\tctx.context.secret,\n\t\t{\n\t\t\t...options,\n\t\t\tmaxAge,\n\t\t\t...overrides,\n\t\t},\n\t);\n\n\tif (dontRememberMe) {\n\t\tawait ctx.setSignedCookie(\n\t\t\tctx.context.authCookies.dontRememberToken.name,\n\t\t\t\"true\",\n\t\t\tctx.context.secret,\n\t\t\tctx.context.authCookies.dontRememberToken.options,\n\t\t);\n\t}\n\tawait setCookieCache(ctx, session, dontRememberMe);\n\tctx.context.setNewSession(session);\n\t/**\n\t * If secondary storage is enabled, store the session data in the secondary storage\n\t * This is useful if the session got updated and we want to update the session data in the\n\t * secondary storage\n\t */\n\tif (ctx.context.options.secondaryStorage) {\n\t\tawait ctx.context.secondaryStorage?.set(\n\t\t\tsession.session.token,\n\t\t\tJSON.stringify({\n\t\t\t\tuser: session.user,\n\t\t\t\tsession: session.session,\n\t\t\t}),\n\t\t\tMath.floor(\n\t\t\t\t(new Date(session.session.expiresAt).getTime() - Date.now()) / 1000,\n\t\t\t),\n\t\t);\n\t}\n}\n\nexport function deleteSessionCookie(\n\tctx: GenericEndpointContext,\n\tskipDontRememberMe?: boolean | undefined,\n) {\n\tctx.setCookie(ctx.context.authCookies.sessionToken.name, \"\", {\n\t\t...ctx.context.authCookies.sessionToken.options,\n\t\tmaxAge: 0,\n\t});\n\n\tctx.setCookie(ctx.context.authCookies.sessionData.name, \"\", {\n\t\t...ctx.context.authCookies.sessionData.options,\n\t\tmaxAge: 0,\n\t});\n\n\tif (ctx.context.options.account?.storeAccountCookie) {\n\t\tctx.setCookie(ctx.context.authCookies.accountData.name, \"\", {\n\t\t\t...ctx.context.authCookies.accountData.options,\n\t\t\tmaxAge: 0,\n\t\t});\n\n\t\t//clean up the account data chunks\n\t\tconst accountStore = createAccountStore(\n\t\t\tctx.context.authCookies.accountData.name,\n\t\t\tctx.context.authCookies.accountData.options,\n\t\t\tctx,\n\t\t);\n\t\tconst cleanCookies = accountStore.clean();\n\t\taccountStore.setCookies(cleanCookies);\n\t}\n\n\tif (ctx.context.oauthConfig.storeStateStrategy === \"cookie\") {\n\t\tconst stateCookie = ctx.context.createAuthCookie(\"oauth_state\");\n\t\tctx.setCookie(stateCookie.name, \"\", {\n\t\t\t...stateCookie.attributes,\n\t\t\tmaxAge: 0,\n\t\t});\n\t}\n\n\t// Use createSessionStore to clean up all session data chunks\n\tconst sessionStore = createSessionStore(\n\t\tctx.context.authCookies.sessionData.name,\n\t\tctx.context.authCookies.sessionData.options,\n\t\tctx,\n\t);\n\tconst cleanCookies = sessionStore.clean();\n\tsessionStore.setCookies(cleanCookies);\n\n\tif (!skipDontRememberMe) {\n\t\tctx.setCookie(ctx.context.authCookies.dontRememberToken.name, \"\", {\n\t\t\t...ctx.context.authCookies.dontRememberToken.options,\n\t\t\tmaxAge: 0,\n\t\t});\n\t}\n}\n\nexport function parseCookies(cookieHeader: string) {\n\tconst cookies = cookieHeader.split(\"; \");\n\tconst cookieMap = new Map<string, string>();\n\n\tcookies.forEach((cookie) => {\n\t\tconst [name, value] = cookie.split(/=(.*)/s);\n\t\tcookieMap.set(name!, value!);\n\t});\n\treturn cookieMap;\n}\n\nexport type EligibleCookies = (string & {}) | (keyof BetterAuthCookies & {});\n\nexport const getSessionCookie = (\n\trequest: Request | Headers,\n\tconfig?:\n\t\t| {\n\t\t\t\tcookiePrefix?: string;\n\t\t\t\tcookieName?: string;\n\t\t\t\tpath?: string;\n\t\t }\n\t\t| undefined,\n) => {\n\tif (config?.cookiePrefix) {\n\t\tif (config.cookieName) {\n\t\t\tconfig.cookiePrefix = `${config.cookiePrefix}-`;\n\t\t} else {\n\t\t\tconfig.cookiePrefix = `${config.cookiePrefix}.`;\n\t\t}\n\t}\n\tconst headers = \"headers\" in request ? request.headers : request;\n\tconst cookies = headers.get(\"cookie\");\n\tif (!cookies) {\n\t\treturn null;\n\t}\n\tconst { cookieName = \"session_token\", cookiePrefix = \"better-auth.\" } =\n\t\tconfig || {};\n\tconst name = `${cookiePrefix}${cookieName}`;\n\tconst secureCookieName = `__Secure-${name}`;\n\tconst parsedCookie = parseCookies(cookies);\n\tconst sessionToken =\n\t\tparsedCookie.get(name) || parsedCookie.get(secureCookieName);\n\tif (sessionToken) {\n\t\treturn sessionToken;\n\t}\n\n\treturn null;\n};\n\nexport const getCookieCache = async <\n\tS extends {\n\t\tsession: Session & Record<string, any>;\n\t\tuser: User & Record<string, any>;\n\t\tupdatedAt: number;\n\t\tversion?: string;\n\t},\n>(\n\trequest: Request | Headers,\n\tconfig?:\n\t\t| {\n\t\t\t\tcookiePrefix?: string;\n\t\t\t\tcookieName?: string;\n\t\t\t\tisSecure?: boolean;\n\t\t\t\tsecret?: string;\n\t\t\t\tstrategy?: \"compact\" | \"jwt\" | \"jwe\"; // base64-hmac for backward compatibility\n\t\t\t\tversion?:\n\t\t\t\t\t| string\n\t\t\t\t\t| ((\n\t\t\t\t\t\t\tsession: Session & Record<string, any>,\n\t\t\t\t\t\t\tuser: User & Record<string, any>,\n\t\t\t\t\t ) => string)\n\t\t\t\t\t| ((\n\t\t\t\t\t\t\tsession: Session & Record<string, any>,\n\t\t\t\t\t\t\tuser: User & Record<string, any>,\n\t\t\t\t\t ) => Promise<string>);\n\t\t }\n\t\t| undefined,\n) => {\n\tconst headers = request instanceof Headers ? request : request.headers;\n\tconst cookies = headers.get(\"cookie\");\n\tif (!cookies) {\n\t\treturn null;\n\t}\n\tconst { cookieName = \"session_data\", cookiePrefix = \"better-auth\" } =\n\t\tconfig || {};\n\tconst name =\n\t\tconfig?.isSecure !== undefined\n\t\t\t? config.isSecure\n\t\t\t\t? `__Secure-${cookiePrefix}.${cookieName}`\n\t\t\t\t: `${cookiePrefix}.${cookieName}`\n\t\t\t: isProduction\n\t\t\t\t? `__Secure-${cookiePrefix}.${cookieName}`\n\t\t\t\t: `${cookiePrefix}.${cookieName}`;\n\tconst parsedCookie = parseCookies(cookies);\n\n\t// Check for chunked cookies\n\tlet sessionData = parsedCookie.get(name);\n\tif (!sessionData) {\n\t\t// Try to reconstruct from chunks\n\t\tconst chunks: Array<{ index: number; value: string }> = [];\n\t\tfor (const [cookieName, value] of parsedCookie.entries()) {\n\t\t\tif (cookieName.startsWith(name + \".\")) {\n\t\t\t\tconst parts = cookieName.split(\".\");\n\t\t\t\tconst indexStr = parts[parts.length - 1];\n\t\t\t\tconst index = parseInt(indexStr || \"0\", 10);\n\t\t\t\tif (!isNaN(index)) {\n\t\t\t\t\tchunks.push({ index, value });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (chunks.length > 0) {\n\t\t\t// Sort by index and join\n\t\t\tchunks.sort((a, b) => a.index - b.index);\n\t\t\tsessionData = chunks.map((c) => c.value).join(\"\");\n\t\t}\n\t}\n\n\tif (sessionData) {\n\t\tconst secret = config?.secret || env.BETTER_AUTH_SECRET;\n\t\tif (!secret) {\n\t\t\tthrow new BetterAuthError(\n\t\t\t\t\"getCookieCache requires a secret to be provided. Either pass it as an option or set the BETTER_AUTH_SECRET environment variable\",\n\t\t\t);\n\t\t}\n\n\t\tconst strategy = config?.strategy || \"compact\";\n\n\t\tif (strategy === \"jwe\") {\n\t\t\t// Use JWE strategy (encrypted)\n\t\t\tconst payload = await symmetricDecodeJWT<S>(\n\t\t\t\tsessionData,\n\t\t\t\tsecret,\n\t\t\t\t\"better-auth-session\",\n\t\t\t);\n\n\t\t\tif (payload && payload.session && payload.user) {\n\t\t\t\t// Validate version if provided\n\t\t\t\tif (config?.version) {\n\t\t\t\t\tconst cookieVersion = payload.version || \"1\";\n\t\t\t\t\tlet expectedVersion = \"1\";\n\t\t\t\t\tif (typeof config.version === \"string\") {\n\t\t\t\t\t\texpectedVersion = config.version;\n\t\t\t\t\t} else if (typeof config.version === \"function\") {\n\t\t\t\t\t\tconst result = config.version(payload.session, payload.user);\n\t\t\t\t\t\texpectedVersion = result instanceof Promise ? await result : result;\n\t\t\t\t\t}\n\t\t\t\t\tif (cookieVersion !== expectedVersion) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn payload;\n\t\t\t}\n\t\t\treturn null;\n\t\t} else if (strategy === \"jwt\") {\n\t\t\t// Use JWT strategy with HMAC signature (HS256), no encryption\n\t\t\tconst payload = await verifyJWT<S>(sessionData, secret);\n\n\t\t\tif (payload && payload.session && payload.user) {\n\t\t\t\t// Validate version if provided\n\t\t\t\tif (config?.version) {\n\t\t\t\t\tconst cookieVersion = payload.version || \"1\";\n\t\t\t\t\tlet expectedVersion = \"1\";\n\t\t\t\t\tif (typeof config.version === \"string\") {\n\t\t\t\t\t\texpectedVersion = config.version;\n\t\t\t\t\t} else if (typeof config.version === \"function\") {\n\t\t\t\t\t\tconst result = config.version(payload.session, payload.user);\n\t\t\t\t\t\texpectedVersion = result instanceof Promise ? await result : result;\n\t\t\t\t\t}\n\t\t\t\t\tif (cookieVersion !== expectedVersion) {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn payload;\n\t\t\t}\n\t\t\treturn null;\n\t\t} else {\n\t\t\t// Use compact strategy (or legacy base64-hmac)\n\t\t\tconst sessionDataPayload = safeJSONParse<{\n\t\t\t\tsession: S;\n\t\t\t\texpiresAt: number;\n\t\t\t\tsignature: string;\n\t\t\t}>(binary.decode(base64Url.decode(sessionData)));\n\t\t\tif (!sessionDataPayload) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst isValid = await createHMAC(\"SHA-256\", \"base64urlnopad\").verify(\n\t\t\t\tsecret,\n\t\t\t\tJSON.stringify({\n\t\t\t\t\t...sessionDataPayload.session,\n\t\t\t\t\texpiresAt: sessionDataPayload.expiresAt,\n\t\t\t\t}),\n\t\t\t\tsessionDataPayload.signature,\n\t\t\t);\n\t\t\tif (!isValid) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Validate version if provided\n\t\t\tif (config?.version && sessionDataPayload.session) {\n\t\t\t\tconst cookieVersion = sessionDataPayload.session.version || \"1\";\n\t\t\t\tlet expectedVersion = \"1\";\n\t\t\t\tif (typeof config.version === \"string\") {\n\t\t\t\t\texpectedVersion = config.version;\n\t\t\t\t} else if (typeof config.version === \"function\") {\n\t\t\t\t\tconst result = config.version(\n\t\t\t\t\t\tsessionDataPayload.session.session,\n\t\t\t\t\t\tsessionDataPayload.session.user,\n\t\t\t\t\t);\n\t\t\t\t\texpectedVersion = result instanceof Promise ? await result : result;\n\t\t\t\t}\n\t\t\t\tif (cookieVersion !== expectedVersion) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn sessionDataPayload.session;\n\t\t}\n\t}\n\treturn null;\n};\n\nexport * from \"./cookie-utils\";\nexport { createSessionStore, getChunkedCookie } from \"./session-store\";\n"],"mappings":"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"}