UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

1 lines • 35.2 kB
{"version":3,"file":"session.mjs","names":["sessionDataPayload: {\n\t\t\t\t\tsession: {\n\t\t\t\t\t\tsession: Session;\n\t\t\t\t\t\tuser: User;\n\t\t\t\t\t\tupdatedAt: number;\n\t\t\t\t\t\tversion?: string;\n\t\t\t\t\t};\n\t\t\t\t\texpiresAt: number;\n\t\t\t\t} | null","session","e: any"],"sources":["../../../src/api/routes/session.ts"],"sourcesContent":["import type {\n\tBetterAuthOptions,\n\tGenericEndpointContext,\n} from \"@better-auth/core\";\nimport {\n\tcreateAuthEndpoint,\n\tcreateAuthMiddleware,\n} from \"@better-auth/core/api\";\nimport { BASE_ERROR_CODES } 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 { APIError } from \"better-call\";\nimport * as z from \"zod\";\nimport {\n\tdeleteSessionCookie,\n\tgetChunkedCookie,\n\tsetCookieCache,\n\tsetSessionCookie,\n} from \"../../cookies\";\nimport { getSessionQuerySchema } from \"../../cookies/session-store\";\nimport { symmetricDecodeJWT, verifyJWT } from \"../../crypto\";\nimport { parseSessionOutput, parseUserOutput } from \"../../db\";\nimport type { InferSession, InferUser, Session, User } from \"../../types\";\nimport type { Prettify } from \"../../types/helper\";\nimport { getDate } from \"../../utils/date\";\n\nexport const getSession = <Option extends BetterAuthOptions>() =>\n\tcreateAuthEndpoint(\n\t\t\"/get-session\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\toperationId: \"getSession\",\n\t\t\tquery: getSessionQuerySchema,\n\t\t\trequireHeaders: true,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"getSession\",\n\t\t\t\t\tdescription: \"Get the current session\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tnullable: true,\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tsession: {\n\t\t\t\t\t\t\t\t\t\t\t\t$ref: \"#/components/schemas/Session\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\tuser: {\n\t\t\t\t\t\t\t\t\t\t\t\t$ref: \"#/components/schemas/User\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\trequired: [\"session\", \"user\"],\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (\n\t\t\tctx,\n\t\t): Promise<{\n\t\t\tsession: InferSession<Option>;\n\t\t\tuser: InferUser<Option>;\n\t\t} | null> => {\n\t\t\ttry {\n\t\t\t\tconst sessionCookieToken = await ctx.getSignedCookie(\n\t\t\t\t\tctx.context.authCookies.sessionToken.name,\n\t\t\t\t\tctx.context.secret,\n\t\t\t\t);\n\n\t\t\t\tif (!sessionCookieToken) {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\n\t\t\t\tconst sessionDataCookie = getChunkedCookie(\n\t\t\t\t\tctx,\n\t\t\t\t\tctx.context.authCookies.sessionData.name,\n\t\t\t\t);\n\n\t\t\t\tlet sessionDataPayload: {\n\t\t\t\t\tsession: {\n\t\t\t\t\t\tsession: Session;\n\t\t\t\t\t\tuser: User;\n\t\t\t\t\t\tupdatedAt: number;\n\t\t\t\t\t\tversion?: string;\n\t\t\t\t\t};\n\t\t\t\t\texpiresAt: number;\n\t\t\t\t} | null = null;\n\n\t\t\t\tif (sessionDataCookie) {\n\t\t\t\t\tconst strategy =\n\t\t\t\t\t\tctx.context.options.session?.cookieCache?.strategy || \"compact\";\n\n\t\t\t\t\tif (strategy === \"jwe\") {\n\t\t\t\t\t\t// Decode JWE (encrypted)\n\t\t\t\t\t\tconst payload = await symmetricDecodeJWT<{\n\t\t\t\t\t\t\tsession: Session;\n\t\t\t\t\t\t\tuser: User;\n\t\t\t\t\t\t\tupdatedAt: number;\n\t\t\t\t\t\t\tversion?: string;\n\t\t\t\t\t\t\texp?: number;\n\t\t\t\t\t\t}>(sessionDataCookie, ctx.context.secret, \"better-auth-session\");\n\n\t\t\t\t\t\tif (payload && payload.session && payload.user) {\n\t\t\t\t\t\t\tsessionDataPayload = {\n\t\t\t\t\t\t\t\tsession: {\n\t\t\t\t\t\t\t\t\tsession: payload.session,\n\t\t\t\t\t\t\t\t\tuser: payload.user,\n\t\t\t\t\t\t\t\t\tupdatedAt: payload.updatedAt,\n\t\t\t\t\t\t\t\t\tversion: payload.version,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\texpiresAt: payload.exp ? payload.exp * 1000 : Date.now(),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst dataCookie = ctx.context.authCookies.sessionData.name;\n\t\t\t\t\t\t\tctx.setCookie(dataCookie, \"\", {\n\t\t\t\t\t\t\t\tmaxAge: 0,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\treturn ctx.json(null);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (strategy === \"jwt\") {\n\t\t\t\t\t\t// Decode JWT (signed with HMAC, not encrypted)\n\t\t\t\t\t\tconst payload = await verifyJWT<{\n\t\t\t\t\t\t\tsession: Session;\n\t\t\t\t\t\t\tuser: User;\n\t\t\t\t\t\t\tupdatedAt: number;\n\t\t\t\t\t\t\tversion?: string;\n\t\t\t\t\t\t\texp?: number;\n\t\t\t\t\t\t}>(sessionDataCookie, ctx.context.secret);\n\n\t\t\t\t\t\tif (payload && payload.session && payload.user) {\n\t\t\t\t\t\t\tsessionDataPayload = {\n\t\t\t\t\t\t\t\tsession: {\n\t\t\t\t\t\t\t\t\tsession: payload.session,\n\t\t\t\t\t\t\t\t\tuser: payload.user,\n\t\t\t\t\t\t\t\t\tupdatedAt: payload.updatedAt,\n\t\t\t\t\t\t\t\t\tversion: payload.version,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\texpiresAt: payload.exp ? payload.exp * 1000 : Date.now(),\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst dataCookie = ctx.context.authCookies.sessionData.name;\n\t\t\t\t\t\t\tctx.setCookie(dataCookie, \"\", {\n\t\t\t\t\t\t\t\tmaxAge: 0,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\treturn ctx.json(null);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Decode compact format (or legacy base64-hmac)\n\t\t\t\t\t\tconst parsed = safeJSONParse<{\n\t\t\t\t\t\t\tsession: {\n\t\t\t\t\t\t\t\tsession: Session;\n\t\t\t\t\t\t\t\tuser: User;\n\t\t\t\t\t\t\t\tupdatedAt: number;\n\t\t\t\t\t\t\t\tversion?: string;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tsignature: string;\n\t\t\t\t\t\t\texpiresAt: number;\n\t\t\t\t\t\t}>(binary.decode(base64Url.decode(sessionDataCookie)));\n\n\t\t\t\t\t\tif (parsed) {\n\t\t\t\t\t\t\tconst isValid = await createHMAC(\n\t\t\t\t\t\t\t\t\"SHA-256\",\n\t\t\t\t\t\t\t\t\"base64urlnopad\",\n\t\t\t\t\t\t\t).verify(\n\t\t\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t\t\tJSON.stringify({\n\t\t\t\t\t\t\t\t\t...parsed.session,\n\t\t\t\t\t\t\t\t\texpiresAt: parsed.expiresAt,\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t\tparsed.signature,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (isValid) {\n\t\t\t\t\t\t\t\tsessionDataPayload = parsed;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tconst dataCookie = ctx.context.authCookies.sessionData.name;\n\t\t\t\t\t\t\t\tctx.setCookie(dataCookie, \"\", {\n\t\t\t\t\t\t\t\t\tmaxAge: 0,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\treturn ctx.json(null);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst dontRememberMe = await ctx.getSignedCookie(\n\t\t\t\t\tctx.context.authCookies.dontRememberToken.name,\n\t\t\t\t\tctx.context.secret,\n\t\t\t\t);\n\n\t\t\t\t/**\n\t\t\t\t * If session data is present in the cookie, check if it should be used or refreshed\n\t\t\t\t */\n\t\t\t\tif (\n\t\t\t\t\tsessionDataPayload?.session &&\n\t\t\t\t\tctx.context.options.session?.cookieCache?.enabled &&\n\t\t\t\t\t!ctx.query?.disableCookieCache\n\t\t\t\t) {\n\t\t\t\t\tconst session = sessionDataPayload.session;\n\n\t\t\t\t\tconst versionConfig =\n\t\t\t\t\t\tctx.context.options.session?.cookieCache?.version;\n\t\t\t\t\tlet expectedVersion = \"1\";\n\t\t\t\t\tif (versionConfig) {\n\t\t\t\t\t\tif (typeof versionConfig === \"string\") {\n\t\t\t\t\t\t\texpectedVersion = versionConfig;\n\t\t\t\t\t\t} else if (typeof versionConfig === \"function\") {\n\t\t\t\t\t\t\tconst result = versionConfig(session.session, session.user);\n\t\t\t\t\t\t\texpectedVersion =\n\t\t\t\t\t\t\t\tresult instanceof Promise ? await result : result;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tconst cookieVersion = session.version || \"1\";\n\t\t\t\t\tif (cookieVersion !== expectedVersion) {\n\t\t\t\t\t\t// Version mismatch - invalidate the cookie cache\n\t\t\t\t\t\tconst dataCookie = ctx.context.authCookies.sessionData.name;\n\t\t\t\t\t\tctx.setCookie(dataCookie, \"\", {\n\t\t\t\t\t\t\tmaxAge: 0,\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst cachedSessionExpiresAt = new Date(\n\t\t\t\t\t\t\tsession.session.expiresAt as unknown as string | number | Date,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst hasExpired =\n\t\t\t\t\t\t\tsessionDataPayload.expiresAt < Date.now() ||\n\t\t\t\t\t\t\tcachedSessionExpiresAt < new Date();\n\n\t\t\t\t\t\tif (hasExpired) {\n\t\t\t\t\t\t\t// When the session data cookie has expired, delete it;\n\t\t\t\t\t\t\t// then we try to fetch from DB\n\t\t\t\t\t\t\tconst dataCookie = ctx.context.authCookies.sessionData.name;\n\t\t\t\t\t\t\tctx.setCookie(dataCookie, \"\", {\n\t\t\t\t\t\t\t\tmaxAge: 0,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Check if the cookie cache needs to be refreshed based on refreshCache\n\t\t\t\t\t\t\tconst cookieRefreshCache =\n\t\t\t\t\t\t\t\tctx.context.sessionConfig.cookieRefreshCache;\n\n\t\t\t\t\t\t\tif (cookieRefreshCache === false) {\n\t\t\t\t\t\t\t\t// If refreshCache is disabled, return the session from cookie as-is\n\t\t\t\t\t\t\t\tctx.context.session = session;\n\t\t\t\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\t\t\t\tsession: session.session,\n\t\t\t\t\t\t\t\t\tuser: session.user,\n\t\t\t\t\t\t\t\t} as {\n\t\t\t\t\t\t\t\t\tsession: InferSession<Option>;\n\t\t\t\t\t\t\t\t\tuser: InferUser<Option>;\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tconst timeUntilExpiry = sessionDataPayload.expiresAt - Date.now();\n\t\t\t\t\t\t\tconst updateAge = cookieRefreshCache.updateAge * 1000; // Convert to milliseconds\n\n\t\t\t\t\t\t\tif (timeUntilExpiry < updateAge) {\n\t\t\t\t\t\t\t\tconst cookieMaxAge =\n\t\t\t\t\t\t\t\t\tctx.context.options.session?.cookieCache?.maxAge || 60 * 5;\n\t\t\t\t\t\t\t\tconst newExpiresAt = getDate(cookieMaxAge, \"sec\");\n\t\t\t\t\t\t\t\tconst refreshedSession = {\n\t\t\t\t\t\t\t\t\tsession: {\n\t\t\t\t\t\t\t\t\t\t...session.session,\n\t\t\t\t\t\t\t\t\t\texpiresAt: newExpiresAt,\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tuser: session.user,\n\t\t\t\t\t\t\t\t\tupdatedAt: Date.now(),\n\t\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t\t// Set the refreshed cookie cache\n\t\t\t\t\t\t\t\tawait setCookieCache(ctx, refreshedSession, false);\n\n\t\t\t\t\t\t\t\t// Parse session and user to ensure additionalFields are included\n\t\t\t\t\t\t\t\t// Rehydrate date fields from JSON strings before parsing\n\t\t\t\t\t\t\t\tconst parsedRefreshedSession = parseSessionOutput(\n\t\t\t\t\t\t\t\t\tctx.context.options,\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t...refreshedSession.session,\n\t\t\t\t\t\t\t\t\t\texpiresAt: new Date(refreshedSession.session.expiresAt),\n\t\t\t\t\t\t\t\t\t\tcreatedAt: new Date(refreshedSession.session.createdAt),\n\t\t\t\t\t\t\t\t\t\tupdatedAt: new Date(refreshedSession.session.updatedAt),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tconst parsedRefreshedUser = parseUserOutput(\n\t\t\t\t\t\t\t\t\tctx.context.options,\n\t\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\t\t...refreshedSession.user,\n\t\t\t\t\t\t\t\t\t\tcreatedAt: new Date(refreshedSession.user.createdAt),\n\t\t\t\t\t\t\t\t\t\tupdatedAt: new Date(refreshedSession.user.updatedAt),\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\tctx.context.session = {\n\t\t\t\t\t\t\t\t\tsession: parsedRefreshedSession,\n\t\t\t\t\t\t\t\t\tuser: parsedRefreshedUser,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\t\t\t\tsession: parsedRefreshedSession,\n\t\t\t\t\t\t\t\t\tuser: parsedRefreshedUser,\n\t\t\t\t\t\t\t\t} as {\n\t\t\t\t\t\t\t\t\tsession: InferSession<Option>;\n\t\t\t\t\t\t\t\t\tuser: InferUser<Option>;\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t// Parse session and user to ensure additionalFields are included\n\t\t\t\t\t\t\tconst parsedSession = parseSessionOutput(ctx.context.options, {\n\t\t\t\t\t\t\t\t...session.session,\n\t\t\t\t\t\t\t\texpiresAt: new Date(session.session.expiresAt),\n\t\t\t\t\t\t\t\tcreatedAt: new Date(session.session.createdAt),\n\t\t\t\t\t\t\t\tupdatedAt: new Date(session.session.updatedAt),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tconst parsedUser = parseUserOutput(ctx.context.options, {\n\t\t\t\t\t\t\t\t...session.user,\n\t\t\t\t\t\t\t\tcreatedAt: new Date(session.user.createdAt),\n\t\t\t\t\t\t\t\tupdatedAt: new Date(session.user.updatedAt),\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tctx.context.session = {\n\t\t\t\t\t\t\t\tsession: parsedSession,\n\t\t\t\t\t\t\t\tuser: parsedUser,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\t\t\tsession: parsedSession,\n\t\t\t\t\t\t\t\tuser: parsedUser,\n\t\t\t\t\t\t\t} as {\n\t\t\t\t\t\t\t\tsession: InferSession<Option>;\n\t\t\t\t\t\t\t\tuser: InferUser<Option>;\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst session =\n\t\t\t\t\tawait ctx.context.internalAdapter.findSession(sessionCookieToken);\n\t\t\t\tctx.context.session = session;\n\t\t\t\tif (!session || session.session.expiresAt < new Date()) {\n\t\t\t\t\tdeleteSessionCookie(ctx);\n\t\t\t\t\tif (session) {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * if session expired clean up the session\n\t\t\t\t\t\t */\n\t\t\t\t\t\tawait ctx.context.internalAdapter.deleteSession(\n\t\t\t\t\t\t\tsession.session.token,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\treturn ctx.json(null);\n\t\t\t\t}\n\t\t\t\t/**\n\t\t\t\t * We don't need to update the session if the user doesn't want to be remembered\n\t\t\t\t * or if the session refresh is disabled\n\t\t\t\t */\n\t\t\t\tif (dontRememberMe || ctx.query?.disableRefresh) {\n\t\t\t\t\t// Parse session and user to ensure additionalFields are included\n\t\t\t\t\tconst parsedSession = parseSessionOutput(\n\t\t\t\t\t\tctx.context.options,\n\t\t\t\t\t\tsession.session,\n\t\t\t\t\t);\n\t\t\t\t\tconst parsedUser = parseUserOutput(ctx.context.options, session.user);\n\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\tsession: parsedSession,\n\t\t\t\t\t\tuser: parsedUser,\n\t\t\t\t\t} as {\n\t\t\t\t\t\tsession: InferSession<Option>;\n\t\t\t\t\t\tuser: InferUser<Option>;\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst expiresIn = ctx.context.sessionConfig.expiresIn;\n\t\t\t\tconst updateAge = ctx.context.sessionConfig.updateAge;\n\t\t\t\t/**\n\t\t\t\t * Calculate last updated date to throttle write updates to database\n\t\t\t\t * Formula: ({expiry date} - sessionMaxAge) + sessionUpdateAge\n\t\t\t\t *\n\t\t\t\t * e.g. ({expiry date} - 30 days) + 1 hour\n\t\t\t\t *\n\t\t\t\t * inspired by: https://github.com/nextauthjs/next-auth/blob/main/packages/core/src/lib/actions/session.ts\n\t\t\t\t */\n\t\t\t\tconst sessionIsDueToBeUpdatedDate =\n\t\t\t\t\tsession.session.expiresAt.valueOf() -\n\t\t\t\t\texpiresIn * 1000 +\n\t\t\t\t\tupdateAge * 1000;\n\t\t\t\tconst shouldBeUpdated = sessionIsDueToBeUpdatedDate <= Date.now();\n\n\t\t\t\tif (\n\t\t\t\t\tshouldBeUpdated &&\n\t\t\t\t\t(!ctx.query?.disableRefresh ||\n\t\t\t\t\t\t!ctx.context.options.session?.disableSessionRefresh)\n\t\t\t\t) {\n\t\t\t\t\tconst updatedSession =\n\t\t\t\t\t\tawait ctx.context.internalAdapter.updateSession(\n\t\t\t\t\t\t\tsession.session.token,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\texpiresAt: getDate(ctx.context.sessionConfig.expiresIn, \"sec\"),\n\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\tif (!updatedSession) {\n\t\t\t\t\t\t/**\n\t\t\t\t\t\t * Handle case where session update fails (e.g., concurrent deletion)\n\t\t\t\t\t\t */\n\t\t\t\t\t\tdeleteSessionCookie(ctx);\n\t\t\t\t\t\treturn ctx.json(null, { status: 401 });\n\t\t\t\t\t}\n\t\t\t\t\tconst maxAge =\n\t\t\t\t\t\t(updatedSession.expiresAt.valueOf() - Date.now()) / 1000;\n\t\t\t\t\tawait setSessionCookie(\n\t\t\t\t\t\tctx,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tsession: updatedSession,\n\t\t\t\t\t\t\tuser: session.user,\n\t\t\t\t\t\t},\n\t\t\t\t\t\tfalse,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmaxAge,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\n\t\t\t\t\t// Parse session and user to ensure additionalFields are included\n\t\t\t\t\tconst parsedUpdatedSession = parseSessionOutput(\n\t\t\t\t\t\tctx.context.options,\n\t\t\t\t\t\tupdatedSession,\n\t\t\t\t\t);\n\t\t\t\t\tconst parsedUser = parseUserOutput(ctx.context.options, session.user);\n\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\tsession: parsedUpdatedSession,\n\t\t\t\t\t\tuser: parsedUser,\n\t\t\t\t\t} as unknown as {\n\t\t\t\t\t\tsession: InferSession<Option>;\n\t\t\t\t\t\tuser: InferUser<Option>;\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tawait setCookieCache(ctx, session, !!dontRememberMe);\n\t\t\t\treturn ctx.json(\n\t\t\t\t\tsession as unknown as {\n\t\t\t\t\t\tsession: InferSession<Option>;\n\t\t\t\t\t\tuser: InferUser<Option>;\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\tctx.context.logger.error(\"INTERNAL_SERVER_ERROR\", error);\n\t\t\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\t\tmessage: BASE_ERROR_CODES.FAILED_TO_GET_SESSION,\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t);\n\nexport const getSessionFromCtx = async <\n\tU extends Record<string, any> = Record<string, any>,\n\tS extends Record<string, any> = Record<string, any>,\n>(\n\tctx: GenericEndpointContext,\n\tconfig?:\n\t\t| {\n\t\t\t\tdisableCookieCache?: boolean;\n\t\t\t\tdisableRefresh?: boolean;\n\t\t }\n\t\t| undefined,\n) => {\n\tif (ctx.context.session) {\n\t\treturn ctx.context.session as {\n\t\t\tsession: S & Session;\n\t\t\tuser: U & User;\n\t\t};\n\t}\n\n\tconst session = await getSession()({\n\t\t...ctx,\n\t\tasResponse: false,\n\t\theaders: ctx.headers!,\n\t\treturnHeaders: false,\n\t\treturnStatus: false,\n\t\tquery: {\n\t\t\t...config,\n\t\t\t...ctx.query,\n\t\t},\n\t}).catch((e) => {\n\t\treturn null;\n\t});\n\tctx.context.session = session;\n\treturn session as {\n\t\tsession: S & Session;\n\t\tuser: U & User;\n\t} | null;\n};\n\n/**\n * The middleware forces the endpoint to require a valid session.\n */\nexport const sessionMiddleware = createAuthMiddleware(async (ctx) => {\n\tconst session = await getSessionFromCtx(ctx);\n\tif (!session?.session) {\n\t\tthrow new APIError(\"UNAUTHORIZED\");\n\t}\n\treturn {\n\t\tsession,\n\t};\n});\n\n/**\n * This middleware forces the endpoint to require a valid session and ignores cookie cache.\n * This should be used for sensitive operations like password changes, account deletion, etc.\n * to ensure that revoked sessions cannot be used even if they're still cached in cookies.\n */\nexport const sensitiveSessionMiddleware = createAuthMiddleware(async (ctx) => {\n\tconst session = await getSessionFromCtx(ctx, { disableCookieCache: true });\n\tif (!session?.session) {\n\t\tthrow new APIError(\"UNAUTHORIZED\");\n\t}\n\treturn {\n\t\tsession,\n\t};\n});\n\n/**\n * This middleware allows you to call the endpoint on the client if session is valid.\n * However, if called on the server, no session is required.\n */\nexport const requestOnlySessionMiddleware = createAuthMiddleware(\n\tasync (ctx) => {\n\t\tconst session = await getSessionFromCtx(ctx);\n\t\tif (!session?.session && (ctx.request || ctx.headers)) {\n\t\t\tthrow new APIError(\"UNAUTHORIZED\");\n\t\t}\n\t\treturn { session };\n\t},\n);\n\n/**\n * This middleware forces the endpoint to require a valid session,\n * as well as making sure the session is fresh before proceeding.\n *\n * Session freshness check will be skipped if the session config's freshAge\n * is set to 0\n */\nexport const freshSessionMiddleware = createAuthMiddleware(async (ctx) => {\n\tconst session = await getSessionFromCtx(ctx);\n\tif (!session?.session) {\n\t\tthrow new APIError(\"UNAUTHORIZED\");\n\t}\n\tif (ctx.context.sessionConfig.freshAge === 0) {\n\t\treturn {\n\t\t\tsession,\n\t\t};\n\t}\n\tconst freshAge = ctx.context.sessionConfig.freshAge;\n\tconst lastUpdated = new Date(\n\t\tsession.session.updatedAt || session.session.createdAt,\n\t).getTime();\n\tconst now = Date.now();\n\tconst isFresh = now - lastUpdated < freshAge * 1000;\n\tif (!isFresh) {\n\t\tthrow new APIError(\"FORBIDDEN\", {\n\t\t\tmessage: \"Session is not fresh\",\n\t\t});\n\t}\n\treturn {\n\t\tsession,\n\t};\n});\n/**\n * user active sessions list\n */\nexport const listSessions = <Option extends BetterAuthOptions>() =>\n\tcreateAuthEndpoint(\n\t\t\"/list-sessions\",\n\t\t{\n\t\t\tmethod: \"GET\",\n\t\t\toperationId: \"listUserSessions\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\trequireHeaders: true,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\toperationId: \"listUserSessions\",\n\t\t\t\t\tdescription: \"List all active sessions for the user\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\"200\": {\n\t\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\t\t\t\t\t$ref: \"#/components/schemas/Session\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\ttry {\n\t\t\t\tconst sessions = await ctx.context.internalAdapter.listSessions(\n\t\t\t\t\tctx.context.session.user.id,\n\t\t\t\t);\n\t\t\t\tconst activeSessions = sessions.filter((session) => {\n\t\t\t\t\treturn session.expiresAt > new Date();\n\t\t\t\t});\n\t\t\t\treturn ctx.json(\n\t\t\t\t\tactiveSessions as unknown as Prettify<InferSession<Option>>[],\n\t\t\t\t);\n\t\t\t} catch (e: any) {\n\t\t\t\tctx.context.logger.error(e);\n\t\t\t\tthrow ctx.error(\"INTERNAL_SERVER_ERROR\");\n\t\t\t}\n\t\t},\n\t);\n\n/**\n * revoke a single session\n */\nexport const revokeSession = createAuthEndpoint(\n\t\"/revoke-session\",\n\t{\n\t\tmethod: \"POST\",\n\t\tbody: z.object({\n\t\t\ttoken: z.string().meta({\n\t\t\t\tdescription: \"The token to revoke\",\n\t\t\t}),\n\t\t}),\n\t\tuse: [sensitiveSessionMiddleware],\n\t\trequireHeaders: true,\n\t\tmetadata: {\n\t\t\topenapi: {\n\t\t\t\tdescription: \"Revoke a single session\",\n\t\t\t\trequestBody: {\n\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\ttoken: {\n\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\tdescription: \"The token to revoke\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\trequired: [\"token\"],\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tresponses: {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\"Indicates if the session was revoked successfully\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"status\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\tasync (ctx) => {\n\t\tconst token = ctx.body.token;\n\t\tconst session = await ctx.context.internalAdapter.findSession(token);\n\n\t\tif (session?.session.userId === ctx.context.session.user.id) {\n\t\t\ttry {\n\t\t\t\tawait ctx.context.internalAdapter.deleteSession(token);\n\t\t\t} catch (error) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\terror && typeof error === \"object\" && \"name\" in error\n\t\t\t\t\t\t? (error.name as string)\n\t\t\t\t\t\t: \"\",\n\t\t\t\t\terror,\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\");\n\t\t\t}\n\t\t}\n\t\treturn ctx.json({\n\t\t\tstatus: true,\n\t\t});\n\t},\n);\n/**\n * revoke all user sessions\n */\nexport const revokeSessions = createAuthEndpoint(\n\t\"/revoke-sessions\",\n\t{\n\t\tmethod: \"POST\",\n\t\tuse: [sensitiveSessionMiddleware],\n\t\trequireHeaders: true,\n\t\tmetadata: {\n\t\t\topenapi: {\n\t\t\t\tdescription: \"Revoke all sessions for the user\",\n\t\t\t\tresponses: {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\"Indicates if all sessions were revoked successfully\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"status\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\tasync (ctx) => {\n\t\ttry {\n\t\t\tawait ctx.context.internalAdapter.deleteSessions(\n\t\t\t\tctx.context.session.user.id,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tctx.context.logger.error(\n\t\t\t\terror && typeof error === \"object\" && \"name\" in error\n\t\t\t\t\t? (error.name as string)\n\t\t\t\t\t: \"\",\n\t\t\t\terror,\n\t\t\t);\n\t\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\");\n\t\t}\n\t\treturn ctx.json({\n\t\t\tstatus: true,\n\t\t});\n\t},\n);\n\nexport const revokeOtherSessions = createAuthEndpoint(\n\t\"/revoke-other-sessions\",\n\t{\n\t\tmethod: \"POST\",\n\t\trequireHeaders: true,\n\t\tuse: [sensitiveSessionMiddleware],\n\t\tmetadata: {\n\t\t\topenapi: {\n\t\t\t\tdescription:\n\t\t\t\t\t\"Revoke all other sessions for the user except the current one\",\n\t\t\t\tresponses: {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\tdescription: \"Success\",\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\tdescription:\n\t\t\t\t\t\t\t\t\t\t\t\t\"Indicates if all other sessions were revoked successfully\",\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\trequired: [\"status\"],\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\tasync (ctx) => {\n\t\tconst session = ctx.context.session;\n\t\tif (!session.user) {\n\t\t\tthrow new APIError(\"UNAUTHORIZED\");\n\t\t}\n\t\tconst sessions = await ctx.context.internalAdapter.listSessions(\n\t\t\tsession.user.id,\n\t\t);\n\t\tconst activeSessions = sessions.filter((session) => {\n\t\t\treturn session.expiresAt > new Date();\n\t\t});\n\t\tconst otherSessions = activeSessions.filter(\n\t\t\t(session) => session.token !== ctx.context.session.session.token,\n\t\t);\n\t\tawait Promise.all(\n\t\t\totherSessions.map((session) =>\n\t\t\t\tctx.context.internalAdapter.deleteSession(session.token),\n\t\t\t),\n\t\t);\n\t\treturn ctx.json({\n\t\t\tstatus: true,\n\t\t});\n\t},\n);\n"],"mappings":"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"}