UNPKG

better-auth-abuse-detection

Version:

AI-powered abuse detection plugin for Better Auth - Detect and prevent account takeover attempts

146 lines (144 loc) 4.9 kB
"use strict"; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { abuseDetection: () => abuseDetection, default: () => src_default }); module.exports = __toCommonJS(src_exports); function abuseDetection(options = {}) { const { strategies = { credentialStuffing: { enabled: true, threshold: 5, windowMinutes: 10 }, velocityCheck: { enabled: true, maxSignIns: 10, windowMinutes: 5 }, impossibleTravel: { enabled: true, speedKmh: 1e3 }, deviceAnomaly: { enabled: true }, behavioralAnalysis: { enabled: false } }, riskScoring = { enabled: true, blockThreshold: 0.9, challengeThreshold: 0.7 }, actions = { block: { duration: 3600 }, challenge: { types: ["captcha"] }, notify: { user: true, admin: false } } } = options; const calculateRiskScore = (factors) => { let score = 0; const weights = riskScoring.factors || {}; for (const [factor, value] of Object.entries(factors)) { score += value * (weights[factor] || 0.1); } return Math.min(score, 1); }; const detectImpossibleTravel = (lat1, lon1, lat2, lon2, timeDiffHours) => { const R = 6371; const dLat = (lat2 - lat1) * Math.PI / 180; const dLon = (lon2 - lon1) * Math.PI / 180; const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); const distance = R * c; const speed = distance / timeDiffHours; return speed > (strategies.impossibleTravel?.speedKmh || 1e3); }; return { id: "abuse-detection", schema: { threatEvent: { modelName: "threatEvent", fields: { id: { type: "string" }, userId: { type: "string", references: { model: "user", field: "id", onDelete: "cascade" } }, type: { type: "string" }, // credential_stuffing, impossible_travel, etc. severity: { type: "string" }, // low, medium, high, critical riskScore: { type: "number" }, ipAddress: { type: "string" }, userAgent: { type: "string" }, location: { type: "string" }, // JSON with lat, lon, country, city deviceFingerprint: { type: "string" }, action: { type: "string" }, // blocked, challenged, allowed metadata: { type: "string" }, // JSON with additional data detectedAt: { type: "date", defaultValue: /* @__PURE__ */ new Date() } } }, deviceTrust: { modelName: "deviceTrust", fields: { id: { type: "string" }, userId: { type: "string", references: { model: "user", field: "id", onDelete: "cascade" } }, fingerprint: { type: "string" }, trustScore: { type: "number" }, lastSeen: { type: "date" }, firstSeen: { type: "date", defaultValue: /* @__PURE__ */ new Date() }, metadata: { type: "string" } // JSON with device info } }, blockedEntity: { modelName: "blockedEntity", fields: { id: { type: "string" }, type: { type: "string" }, // ip, email, fingerprint value: { type: "string" }, reason: { type: "string" }, expiresAt: { type: "date" }, createdAt: { type: "date", defaultValue: /* @__PURE__ */ new Date() } } } }, // Hooks to analyze authentication attempts hooks: { before: [ { matcher: (ctx) => ctx.path === "/sign-in" || ctx.path === "/sign-up", handler: async (ctx) => { } } ], after: [ { matcher: (ctx) => ctx.path === "/sign-in", handler: async (ctx) => { } } ] } }; } var src_default = abuseDetection; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { abuseDetection });