UNPKG

@zpg6-test-pkgs/better-auth

Version:

The most comprehensive authentication library for TypeScript.

527 lines (519 loc) 15.9 kB
'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;