UNPKG

better-auth

Version:

The most comprehensive authentication framework for TypeScript.

98 lines (97 loc) 3.4 kB
import { PACKAGE_VERSION } from "../../version.mjs"; import { createAuthMiddleware } from "@better-auth/core/api"; //#region src/plugins/last-login-method/index.ts /** * Plugin to track the last used login method */ const lastLoginMethod = (userConfig) => { const defaultResolveMethod = (ctx) => { const path = ctx.path; if (!path) return null; if (path.startsWith("/callback/") || path.startsWith("/oauth2/callback/")) return ctx.params?.id || ctx.params?.providerId || path.split("/").pop(); if (path === "/sign-in/email" || path === "/sign-up/email") return "email"; if (path.includes("siwe")) return "siwe"; if (path.includes("/passkey/verify-authentication")) return "passkey"; if (path.startsWith("/magic-link/verify")) return "magic-link"; return null; }; const getResolveContext = (ctx) => { return ctx.path ? ctx : { ...ctx, path: "" }; }; const resolveMethod = (ctx) => { const resolveContext = getResolveContext(ctx); return config.customResolveMethod?.(resolveContext) ?? defaultResolveMethod(resolveContext); }; const config = { cookieName: "better-auth.last_used_login_method", maxAge: 3600 * 24 * 30, ...userConfig }; return { id: "last-login-method", version: PACKAGE_VERSION, init(ctx) { return { options: { databaseHooks: { user: { create: { async before(user, context) { if (!config.storeInDatabase) return; if (!context) return; const lastUsedLoginMethod = resolveMethod(context); if (lastUsedLoginMethod) return { data: { ...user, lastLoginMethod: lastUsedLoginMethod } }; } } }, session: { create: { async after(session, context) { if (!config.storeInDatabase) return; if (!context) return; const lastUsedLoginMethod = resolveMethod(context); if (lastUsedLoginMethod && session?.userId) try { await ctx.internalAdapter.updateUser(session.userId, { lastLoginMethod: lastUsedLoginMethod }); } catch (error) { ctx.logger.error("Failed to update lastLoginMethod", error); } } } } } } }; }, hooks: { after: [{ matcher() { return true; }, handler: createAuthMiddleware(async (ctx) => { const lastUsedLoginMethod = resolveMethod(ctx); if (lastUsedLoginMethod) { const setCookieHeaders = ctx.context.responseHeaders?.getSetCookie?.() || []; const sessionTokenName = ctx.context.authCookies.sessionToken.name; if (setCookieHeaders.some((cookie) => cookie.includes(sessionTokenName))) { const cookieAttributes = { ...ctx.context.authCookies.sessionToken.attributes, maxAge: config.maxAge, httpOnly: false }; let isPermitted = true; if (config.beforeStoreCookie) try { isPermitted = await config.beforeStoreCookie(ctx, lastUsedLoginMethod); } catch (error) { if (ctx.context.logger) ctx.context.logger.error?.("[LastLoginMethod] Error in beforeStoreCookie hook", error); isPermitted = false; } if (!isPermitted) return; ctx.setCookie(config.cookieName, lastUsedLoginMethod, cookieAttributes); } } }) }] }, schema: config.storeInDatabase ? { user: { fields: { lastLoginMethod: { type: "string", input: false, required: false, fieldName: config.schema?.user?.lastLoginMethod || "lastLoginMethod" } } } } : void 0, options: userConfig }; }; //#endregion export { lastLoginMethod };