{"_id":"@stotles/better-auth-audit-logs","_rev":"6-1d2a75903dc778c3257005dd4db0edb4","name":"@stotles/better-auth-audit-logs","dist-tags":{"latest":"0.6.0"},"versions":{"0.4.0-rc.1":{"name":"@stotles/better-auth-audit-logs","version":"0.4.0-rc.1","keywords":["better-auth","better-auth-plugin","audit-log","audit-trail","auth","authentication","security","logging","compliance","pii-redaction","session-tracking"],"author":{"name":"Ejiro Asiuwhu","email":"ejiroasiuwhu10@gmail.com"},"license":"MIT","_id":"@stotles/better-auth-audit-logs@0.4.0-rc.1","maintainers":[{"name":"dabstotler","email":"dominic@stotles.com"},{"name":"lambarchie-stotles","email":"archie@stotles.com"}],"homepage":"https://github.com/Stotles/better-auth-audit-logs#readme","bugs":{"url":"https://github.com/Stotles/better-auth-audit-logs/issues"},"dist":{"shasum":"42393c9c9e29cc56651bcd1af83cd4e98f1e3d37","tarball":"https://registry.npmjs.org/@stotles/better-auth-audit-logs/-/better-auth-audit-logs-0.4.0-rc.1.tgz","fileCount":55,"integrity":"sha512-7oaR6tfORrIfABjl0qp9V2BXuSE/itFJ2nWC2OeGGTD8F6BLWq/8+kX54u23/6s+mihHM5UXQSQigBnxGSIxYQ==","signatures":[{"sig":"MEUCIQD+ob19nv3uLvGl9qXLrlhuPFHhCdScqwLLsRpS8i0h9wIgE5aFdbk7r/nJlfVGZ20CDkN72/meqZXhV06c+9UQtjY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":101364},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./client":{"types":"./dist/client.d.ts","import":"./dist/client.js","require":"./dist/client.cjs"}},"gitHead":"48952247702e7de5f4148026473dda195dfb149f","scripts":{"test":"bun test","build":"bun build src/index.ts src/client.ts --outdir dist --target node --external better-auth --external zod && bun build src/index.ts src/client.ts --outdir dist --target node --format cjs --entry-naming '[dir]/[name].cjs' --external better-auth --external zod && tsc -p tsconfig.build.json","check":"tsc --noEmit && bun test","typecheck":"tsc --noEmit"},"_npmUser":{"name":"lambarchie-stotles","email":"archie@stotles.com"},"repository":{"url":"git+https://github.com/Stotles/better-auth-audit-logs.git","type":"git"},"_npmVersion":"11.16.0","description":"Audit log plugin for Better Auth. Captures auth lifecycle events, stores structured log entries, and exposes query endpoints with PII redaction and custom storage backends.","directories":{},"_nodeVersion":"22.22.2","publishConfig":{"access":"public"},"typesVersions":{"*":{"client":["./dist/client.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.3.6","@types/bun":"latest","better-auth":"^1.5.5"},"peerDependencies":{"zod":">=3.0.0","typescript":"^5","better-auth":">=1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/better-auth-audit-logs_0.4.0-rc.1_1783608045182_0.8570723660112614","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@stotles/better-auth-audit-logs","version":"0.4.0","keywords":["better-auth","better-auth-plugin","audit-log","audit-trail","auth","authentication","security","logging","compliance","pii-redaction","session-tracking"],"author":{"name":"Ejiro Asiuwhu","email":"ejiroasiuwhu10@gmail.com"},"license":"MIT","_id":"@stotles/better-auth-audit-logs@0.4.0","maintainers":[{"name":"dabstotler","email":"dominic@stotles.com"},{"name":"lambarchie-stotles","email":"archie@stotles.com"}],"homepage":"https://github.com/Stotles/better-auth-audit-logs#readme","bugs":{"url":"https://github.com/Stotles/better-auth-audit-logs/issues"},"dist":{"shasum":"04b81502cf8ca3814c88d41dc32feb4ae4dd9b11","tarball":"https://registry.npmjs.org/@stotles/better-auth-audit-logs/-/better-auth-audit-logs-0.4.0.tgz","fileCount":31,"integrity":"sha512-4KjZB2fd2Cb13ETZo41bKOGJcOIggoqcf5P6qz33KJLGEKsD07myHdPoj0uJJGBWEtPIPJhMkYt7oB4B6X/nJw==","signatures":[{"sig":"MEUCIQD5oRM2x7QXTnvAaO9ls9kazSwHvpF8mANjIHOTum5qRwIgNvRIfP9+LpIZFnj9xFKy54iIT0YytOCpiojMZStHhYs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@stotles%2fbetter-auth-audit-logs@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":89917},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./client":{"types":"./dist/client.d.ts","import":"./dist/client.js","require":"./dist/client.cjs"}},"gitHead":"a1a4d7812ff60c0c1cc59622314248c2c1535cd1","scripts":{"test":"bun test","build":"bun build src/index.ts src/client.ts --outdir dist --target node --external better-auth --external zod && bun build src/index.ts src/client.ts --outdir dist --target node --format cjs --entry-naming '[dir]/[name].cjs' --external better-auth --external zod && tsc -p tsconfig.build.json","check":"tsc --noEmit && bun test","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","approver":{"name":"lambarchie-stotles","email":"archie@stotles.com"},"trustedPublisher":{"id":"github","oidcConfigId":"oidc:b75797f3-ae82-4dd2-b104-f430fdb08c2c"}},"repository":{"url":"git+https://github.com/Stotles/better-auth-audit-logs.git","type":"git"},"_npmVersion":"11.16.0","description":"Audit log plugin for Better Auth. Captures auth lifecycle events, stores structured log entries, and exposes query endpoints with PII redaction and custom storage backends.","directories":{},"_nodeVersion":"24.18.0","publishConfig":{"access":"public","provenance":true},"typesVersions":{"*":{"client":["./dist/client.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.3.6","@types/bun":"latest","better-auth":"^1.7.0-rc.1"},"peerDependencies":{"zod":">=3.0.0","typescript":"^5","better-auth":">=1.0.0"},"_npmOperationalInternal":{"tmp":"tmp/better-auth-audit-logs_0.4.0_1783608238140_0.4518487392672612","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@stotles/better-auth-audit-logs","version":"0.4.1","keywords":["better-auth","better-auth-plugin","audit-log","audit-trail","auth","authentication","security","logging","compliance","pii-redaction","session-tracking"],"license":"MIT","_id":"@stotles/better-auth-audit-logs@0.4.1","maintainers":[{"name":"dabstotler","email":"dominic@stotles.com"},{"name":"lambarchie-stotles","email":"archie@stotles.com"}],"homepage":"https://github.com/Stotles/better-auth-audit-logs#readme","bugs":{"url":"https://github.com/Stotles/better-auth-audit-logs/issues"},"dist":{"shasum":"ed5122d73fc6e648358b0118b3878e47ec607678","tarball":"https://registry.npmjs.org/@stotles/better-auth-audit-logs/-/better-auth-audit-logs-0.4.1.tgz","fileCount":31,"integrity":"sha512-yU87fqTRrdTai3YFd7Zf723R1PHpyRW0C28VdbaassBQsRY+K+O2bnktZ+Myz9oep5Ua3js9DL0uGaljFwPYOA==","signatures":[{"sig":"MEYCIQCiQKhr4aqUilneZoqSfwAG/9ELSpuBbVbWrmKbemMrKQIhALmmAwG0ecRluOfk1GUO4bmTTA9bdDQFdy5R7zTjBPFJ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@stotles%2fbetter-auth-audit-logs@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":92208},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./client":{"types":"./dist/client.d.ts","import":"./dist/client.js","require":"./dist/client.cjs"}},"gitHead":"781f7134ed9c2513136a12b4437e5ead4d42ef88","scripts":{"test":"bun test","build":"bun build src/index.ts src/client.ts --outdir dist --target node --external better-auth --external zod && bun build src/index.ts src/client.ts --outdir dist --target node --format cjs --entry-naming '[dir]/[name].cjs' --external better-auth --external zod && tsc -p tsconfig.build.json","check":"tsc --noEmit && bun test","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","approver":{"name":"lambarchie-stotles","email":"archie@stotles.com"},"trustedPublisher":{"id":"github","oidcConfigId":"oidc:b75797f3-ae82-4dd2-b104-f430fdb08c2c"}},"repository":{"url":"git+https://github.com/Stotles/better-auth-audit-logs.git","type":"git"},"_npmVersion":"11.16.0","description":"Audit log plugin for Better Auth. Captures auth lifecycle events, stores structured log entries, and exposes query endpoints with PII redaction and custom storage backends.","directories":{},"_nodeVersion":"24.18.0","publishConfig":{"access":"public","provenance":true},"typesVersions":{"*":{"client":["./dist/client.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.3.6","@types/bun":"latest","better-auth":"^1.7.0-rc.1"},"peerDependencies":{"zod":">=3.0.0","typescript":"^5","better-auth":">=1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/better-auth-audit-logs_0.4.1_1783676566824_0.8253593516664461","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@stotles/better-auth-audit-logs","version":"0.5.0","keywords":["better-auth","better-auth-plugin","audit-log","audit-trail","auth","authentication","security","logging","compliance","pii-redaction","session-tracking"],"license":"MIT","_id":"@stotles/better-auth-audit-logs@0.5.0","maintainers":[{"name":"dabstotler","email":"dominic@stotles.com"},{"name":"lambarchie-stotles","email":"archie@stotles.com"}],"homepage":"https://github.com/Stotles/better-auth-audit-logs#readme","bugs":{"url":"https://github.com/Stotles/better-auth-audit-logs/issues"},"dist":{"shasum":"46bcb41f2e51597705b8f42ee37ca9697856d008","tarball":"https://registry.npmjs.org/@stotles/better-auth-audit-logs/-/better-auth-audit-logs-0.5.0.tgz","fileCount":31,"integrity":"sha512-MOks6hyzwM4NuRM/Kw/FxpnKmPj1bWRAMUlIDkUbhm8hGuw+4jNL3pPhyqau7uD2JLVC+wM0BtcUng+XO3iycw==","signatures":[{"sig":"MEYCIQDboB+zrbts9XF5DhASClaqItOub64sFLmWqOj+1dedIwIhALmgdABjk2i28xU0CzSqzITJCp1QnfLyiJiGsI1edUua","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@stotles%2fbetter-auth-audit-logs@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":104597},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./client":{"types":"./dist/client.d.ts","import":"./dist/client.js","require":"./dist/client.cjs"}},"gitHead":"b809d92ee41bcc7fc8283b731afbd31aebe9cc4e","scripts":{"test":"bun test","build":"bun build src/index.ts src/client.ts --outdir dist --target node --external better-auth --external zod && bun build src/index.ts src/client.ts --outdir dist --target node --format cjs --entry-naming '[dir]/[name].cjs' --external better-auth --external zod && tsc -p tsconfig.build.json","check":"tsc --noEmit && bun test","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","approver":{"name":"lambarchie-stotles","email":"archie@stotles.com"},"trustedPublisher":{"id":"github","oidcConfigId":"oidc:b75797f3-ae82-4dd2-b104-f430fdb08c2c"}},"repository":{"url":"git+https://github.com/Stotles/better-auth-audit-logs.git","type":"git"},"_npmVersion":"11.16.0","description":"Audit log plugin for Better Auth. Captures auth lifecycle events, stores structured log entries, and exposes query endpoints with PII redaction and custom storage backends.","directories":{},"_nodeVersion":"24.18.0","publishConfig":{"access":"public","provenance":true},"typesVersions":{"*":{"client":["./dist/client.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.3.6","@types/bun":"latest","better-auth":"^1.7.0-rc.1"},"peerDependencies":{"zod":">=3.0.0","typescript":"^5","better-auth":">=1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/better-auth-audit-logs_0.5.0_1784125484365_0.7912391074593161","host":"s3://npm-registry-packages-npm-production"}},"0.5.1":{"name":"@stotles/better-auth-audit-logs","version":"0.5.1","keywords":["better-auth","better-auth-plugin","audit-log","audit-trail","auth","authentication","security","logging","compliance","pii-redaction","session-tracking"],"license":"MIT","_id":"@stotles/better-auth-audit-logs@0.5.1","maintainers":[{"name":"dabstotler","email":"dominic@stotles.com"},{"name":"lambarchie-stotles","email":"archie@stotles.com"}],"homepage":"https://github.com/Stotles/better-auth-audit-logs#readme","bugs":{"url":"https://github.com/Stotles/better-auth-audit-logs/issues"},"dist":{"shasum":"c8d3a8d0da08b95d9762cf639a920880be4ca045","tarball":"https://registry.npmjs.org/@stotles/better-auth-audit-logs/-/better-auth-audit-logs-0.5.1.tgz","fileCount":31,"integrity":"sha512-ts+sOrBowg3+XO/yC2OB5fwFmQiQwR9f2FkPoc7mtbzDxPf7BEB0l2YTonqauf5XLXGTXi7qY89ornJlbFzkxg==","signatures":[{"sig":"MEQCIAFusLSF6c9B+F192LOT8CyR1geQ/DWUYa8ejCytk1S+AiBSzfI2/jRGp8ow99fYmvTvvJ5oR4s41g+PnQB97pXDZw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@stotles%2fbetter-auth-audit-logs@0.5.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":104925},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./client":{"types":"./dist/client.d.ts","import":"./dist/client.js","require":"./dist/client.cjs"}},"gitHead":"388d3c08e09d018f9c16a4e1431a49328b7d9a91","scripts":{"test":"bun test","build":"bun build src/index.ts src/client.ts --outdir dist --target node --external better-auth --external zod && bun build src/index.ts src/client.ts --outdir dist --target node --format cjs --entry-naming '[dir]/[name].cjs' --external better-auth --external zod && tsc -p tsconfig.build.json","check":"tsc --noEmit && bun test","typecheck":"tsc --noEmit"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","approver":{"name":"lambarchie-stotles","email":"archie@stotles.com"},"trustedPublisher":{"id":"github","oidcConfigId":"oidc:b75797f3-ae82-4dd2-b104-f430fdb08c2c"}},"repository":{"url":"git+https://github.com/Stotles/better-auth-audit-logs.git","type":"git"},"_npmVersion":"11.16.0","description":"Audit log plugin for Better Auth. Captures auth lifecycle events, stores structured log entries, and exposes query endpoints with PII redaction and custom storage backends.","directories":{},"_nodeVersion":"24.18.0","publishConfig":{"access":"public","provenance":true},"typesVersions":{"*":{"client":["./dist/client.d.ts"]}},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.3.6","@types/bun":"latest","better-auth":"^1.7.0-rc.1"},"peerDependencies":{"zod":">=3.0.0","typescript":"^5","better-auth":">=1.5.0"},"_npmOperationalInternal":{"tmp":"tmp/better-auth-audit-logs_0.5.1_1784134166390_0.33559914256518897","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"_id":"@stotles/better-auth-audit-logs@0.6.0","bugs":{"url":"https://github.com/Stotles/better-auth-audit-logs/issues"},"dist":{"shasum":"9065d082800bfc4e2184e261b76cfb8706d11c89","tarball":"https://registry.npmjs.org/@stotles/better-auth-audit-logs/-/better-auth-audit-logs-0.6.0.tgz","integrity":"sha512-XlsSfVmib673k9z1S63CYNFCkYVUoqi8S/FhoVYASvFwCz0Z0DRfsDdVsmPkQQp0QODD/PAsbFq7iOMpkVvwxg==","fileCount":31,"unpackedSize":106969,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@stotles%2fbetter-auth-audit-logs@0.6.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDMLEr+XzT0/MQv/tK+6Lxe+469p6VRMKx9JmC3kYdJewIgOiXQA8S/9CkYW6Vvra6llr1UGR/cCyPXm2N0EIsS/mM="}]},"main":"./dist/index.cjs","name":"@stotles/better-auth-audit-logs","type":"module","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./client":{"types":"./dist/client.d.ts","import":"./dist/client.js","require":"./dist/client.cjs"}},"gitHead":"49056f089b574b8d72121adacf5ce2e95844988d","license":"MIT","scripts":{"test":"bun test","build":"bun build src/index.ts src/client.ts --outdir dist --target node --external better-auth --external zod && bun build src/index.ts src/client.ts --outdir dist --target node --format cjs --entry-naming '[dir]/[name].cjs' --external better-auth --external zod && tsc -p tsconfig.build.json","check":"tsc --noEmit && bun test","typecheck":"tsc --noEmit"},"version":"0.6.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b75797f3-ae82-4dd2-b104-f430fdb08c2c"},"approver":{"name":"lambarchie-stotles","email":"archie@stotles.com"}},"homepage":"https://github.com/Stotles/better-auth-audit-logs#readme","keywords":["better-auth","better-auth-plugin","audit-log","audit-trail","auth","authentication","security","logging","compliance","pii-redaction","session-tracking"],"repository":{"url":"git+https://github.com/Stotles/better-auth-audit-logs.git","type":"git"},"_npmVersion":"11.19.0","description":"Audit log plugin for Better Auth. Captures auth lifecycle events, stores structured log entries, and exposes query endpoints with PII redaction and custom storage backends.","directories":{},"maintainers":[{"name":"dabstotler","email":"dominic@stotles.com"},{"name":"lambarchie-stotles","email":"archie@stotles.com"}],"_nodeVersion":"24.20.0","publishConfig":{"access":"public","provenance":true},"typesVersions":{"*":{"client":["./dist/client.d.ts"]}},"devDependencies":{"zod":"^4.3.6","@types/bun":"latest","better-auth":"^1.7.0-rc.1"},"peerDependencies":{"zod":">=3.0.0","typescript":"^5","better-auth":">=1.5.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/better-auth-audit-logs_0.6.0_1789665230357_0.2672303374749616"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-09T14:40:45.000Z","modified":"2026-09-17T17:13:50.756Z","0.4.0-rc.1":"2026-07-09T14:40:45.323Z","0.4.0":"2026-07-09T14:43:58.220Z","0.4.1":"2026-07-10T09:42:46.900Z","0.5.0":"2026-07-15T14:24:44.497Z","0.5.1":"2026-07-15T16:49:26.502Z","0.6.0":"2026-09-17T17:13:50.462Z"},"bugs":{"url":"https://github.com/Stotles/better-auth-audit-logs/issues"},"license":"MIT","homepage":"https://github.com/Stotles/better-auth-audit-logs#readme","keywords":["better-auth","better-auth-plugin","audit-log","audit-trail","auth","authentication","security","logging","compliance","pii-redaction","session-tracking"],"repository":{"url":"git+https://github.com/Stotles/better-auth-audit-logs.git","type":"git"},"description":"Audit log plugin for Better Auth. Captures auth lifecycle events, stores structured log entries, and exposes query endpoints with PII redaction and custom storage backends.","maintainers":[{"name":"dabstotler","email":"dominic@stotles.com"},{"name":"lambarchie-stotles","email":"archie@stotles.com"}],"readme":"# @stotles/better-auth-audit-logs\n\n[![npm version](https://img.shields.io/npm/v/@stotles/better-auth-audit-logs)](https://www.npmjs.com/package/@stotles/better-auth-audit-logs)\n[![npm downloads](https://img.shields.io/npm/dm/@stotles/better-auth-audit-logs)](https://www.npmjs.com/package/@stotles/better-auth-audit-logs)\n[![license](https://img.shields.io/npm/l/@stotles/better-auth-audit-logs)](https://github.com/Stotles/better-auth-audit-logs/blob/main/LICENSE)\n\nAudit log plugin for [Better Auth](https://better-auth.com). Automatically captures auth events with IP, user agent, and severity — zero config required.\n\n**Requires** `better-auth >= 1.5.0` and `typescript >= 5`.\n\n## Quick start\n\n```bash\nnpm install @stotles/better-auth-audit-logs\n```\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { auditLog } from \"@stotles/better-auth-audit-logs\";\n\nexport const auth = betterAuth({\n  plugins: [auditLog()],\n});\n```\n\nThen generate and run the migration:\n\n```bash\nnpx @better-auth/cli generate\n```\n\nThat's it. All auth events are now logged automatically.\n\n## Schema\n\nThe plugin adds an `auditLog` table. If you prefer to manage your schema manually, copy the relevant definition:\n\n<details>\n<summary>Prisma</summary>\n\n```prisma\nmodel AuditLog {\n  id        String   @id @default(cuid())\n  userId    String?\n  action    String\n  status    String\n  severity  String\n  ipAddress String?\n  userAgent String?\n  metadata  String?\n  createdAt DateTime @default(now())\n\n  user User? @relation(fields: [userId], references: [id], onDelete: SetNull)\n\n  @@index([userId])\n  @@index([action])\n  @@index([createdAt])\n  @@map(\"auditLog\")\n}\n```\n\n</details>\n\n<details>\n<summary>Drizzle</summary>\n\n```ts\nimport { sqliteTable, text, integer } from \"drizzle-orm/sqlite-core\";\nimport { user } from \"./auth-schema\"; // your existing user table\n\nexport const auditLog = sqliteTable(\"auditLog\", {\n  id: text(\"id\").primaryKey(),\n  userId: text(\"userId\").references(() => user.id, { onDelete: \"set null\" }),\n  action: text(\"action\").notNull(),\n  status: text(\"status\").notNull(),\n  severity: text(\"severity\").notNull(),\n  ipAddress: text(\"ipAddress\"),\n  userAgent: text(\"userAgent\"),\n  metadata: text(\"metadata\"),\n  createdAt: integer(\"createdAt\", { mode: \"timestamp\" }).notNull(),\n});\n```\n\n</details>\n\n<details>\n<summary>MongoDB</summary>\n\n```ts\n// Collection: auditLog\n{\n  _id: ObjectId,\n  userId: String | null,       // references user collection\n  action: String,              // e.g. \"sign-in:email\"\n  status: String,              // \"success\" | \"failed\" | \"requested\"\n  severity: String,            // \"low\" | \"medium\" | \"high\" | \"critical\"\n  ipAddress: String | null,\n  userAgent: String | null,\n  metadata: String | null,     // JSON string\n  createdAt: Date\n}\n\n// Recommended indexes\ndb.auditLog.createIndex({ userId: 1 })\ndb.auditLog.createIndex({ action: 1 })\ndb.auditLog.createIndex({ createdAt: 1 })\n```\n\n</details>\n\n## Client plugin\n\n```ts\nimport { createAuthClient } from \"better-auth/client\";\nimport { auditLogClient } from \"@stotles/better-auth-audit-logs/client\";\n\nexport const authClient = createAuthClient({\n  plugins: [auditLogClient()],\n});\n```\n\n```ts\n// List recent failed sign-ins\nconst { data } = await authClient.auditLog.listAuditLogs({\n  query: { status: \"failed\", limit: 20 },\n});\n\n// Single entry by ID\nconst { data: entry } = await authClient.auditLog.getAuditLog({\n  params: { id: \"log-entry-id\" },\n});\n\n// Manually log custom events (admin actions, data exports, etc.)\nawait authClient.auditLog.insertAuditLog({\n  action: \"admin:user-export\",\n  status: \"success\",\n  severity: \"high\",\n  metadata: { exportedCount: 500 },\n});\n```\n\n## What gets logged\n\nAll auth `POST` endpoints are captured by default:\n\n| Event | Path | Hook |\n|---|---|---|\n| Sign in | `/sign-in/email`, `/sign-in/social` | after |\n| Sign up | `/sign-up/email` | after |\n| Change/reset password | `/change-password`, `/reset-password` | after |\n| Change email | `/change-email` | after |\n| Two-factor | `/two-factor/*` | after |\n| OAuth callback | `/oauth/callback` | after |\n| Sign out | `/sign-out` | **before** |\n| Delete account | `/delete-user` | **before** |\n| Revoke session | `/revoke-session`, `/revoke-sessions`, `/revoke-other-sessions` | **before** |\n\n\"Before\" hooks fire for destructive events where the session would be lost after execution. Because the write happens *before* the action runs, these entries are recorded with `status: \"requested\"` — the request was captured, but its outcome is not (the entry is never updated). `after`-hook events, by contrast, record the observed `\"success\"` or `\"failed\"`.\n\n> **before-hook events produce exactly one `\"requested\"` entry, even if the action then fails.** The before and after hooks are mutually exclusive, so a failed `delete-user`/`sign-out`/`revoke-session` is *not* separately logged as `\"failed\"` — only the `\"requested\"` record exists. (This is the trade-off for capturing the userId before the session is torn down.) Regular `after`-hook paths do record failures: a thrown `APIError` is captured as `\"failed\"`. A handler that throws a non-`APIError` bypasses the after hook entirely and isn't logged at all (see the limitation below).\n>\n> **This applies to *every* path routed through the before hook — including when you invert the timing with [`afterPaths`](#configuration).** In that mode most paths log before the handler runs, so most of your entries will be `\"requested\"` with no observed outcome, and only the paths listed in `afterPaths` will record `\"success\"`/`\"failed\"`. Reach for `afterPaths` only when capturing the *request* (not its outcome) is what you want for the bulk of your paths.\n\nSeverity is inferred automatically (`critical` for ban/impersonate, `high` for delete/revoke/failed sign-in/OAuth client changes, `medium` for sign-in/out and the OIDC provider flows, `low` for everything else) and can be overridden per-path with [`pathConfig`](#configuration). A `medium` action recorded as `failed` is promoted to `high`.\n\n### Limitation: unexpected (non-`APIError`) failures aren't logged\n\nThe after hook only sees failures that better-auth surfaces as an `APIError` (invalid credentials, rate limits, validation, etc.). If a handler throws something else — a raw database driver error, a `TypeError`, any unhandled bug — the current better-auth re-throws it *before* the after-hook stage runs, so **no audit entry is written** for it. There is no plugin-level error hook that can catch this, so the plugin cannot close the gap on its own. (The after hook does keep a defensive guard that records a `\"failed\"` entry should a future version ever surface such an error as the endpoint result instead of re-throwing — but you can't rely on that today.)\n\nThese are unexpected `500`s rather than auth outcomes, so they usually belong in your error monitoring (Sentry, structured logs). If you *do* want them in the audit trail, wire better-auth's global `onAPIError.onError` — it fires for any escaped error and write the non-`APIError` case yourself via your storage backend:\n\n```ts\nimport { betterAuth } from \"better-auth\";\nimport { APIError } from \"better-auth/api\";\n\nexport const auth = betterAuth({\n  plugins: [auditLog({ storage })],\n  onAPIError: {\n    onError: async (error, ctx) => {\n      if (error instanceof APIError) return; // already captured by the after hook\n      await storage.write({\n        id: crypto.randomUUID(),\n        userId: null,\n        action: \"unexpected-error\",\n        status: \"failed\",\n        severity: \"high\",\n        ipAddress: null,\n        userAgent: null,\n        metadata: { error: error instanceof Error ? error.message : String(error) },\n        createdAt: new Date(),\n      });\n    },\n  },\n});\n```\n\nNote the callback only receives better-auth's shared `AuthContext`, **not** the per-request context: better-auth doesn't forward the `Request` to `onAPIError.onError`, and the request-scoped context is already torn down by the time an unhandled throw reaches it. So there's no reliable `path`/IP/headers here — the entry is necessarily coarse.\n\n## Configuration\n\nAll options are optional:\n\n```ts\nauditLog({\n  enabled: true,                 // disable without removing the plugin\n  writeMode: \"sync-best-effort\", // how the write relates to the auth request (see Design decisions)\n\n  // restrict to specific paths (empty = capture all). This is an allowlist and nothing else: once\n  // it's non-empty, every path not listed stops being audited.\n  paths: [\"/sign-in/email\", \"/delete-user\"],\n\n  // per-path settings, which never narrow what is captured. Keys are exact request paths, the\n  // same format as `paths`. Listing a path here does not make it captured — that's `paths`.\n  pathConfig: {\n    \"/oauth2/authorize\": { severity: \"medium\" },\n    \"/delete-user\": { severity: \"high\", capture: { requestBody: true } },\n    \"/change-email\": { capture: { requestBody: true } },\n  },\n\n  // paths logged *before* the handler runs (needed where the session is torn down mid-request,\n  // so the userId can still be captured). Defaults to the session-destroying paths.\n  // This can be combined with `paths`\n  beforePaths: [\"/sign-out\", \"/delete-user\", \"/revoke-session\"],\n\n  // OR invert it: log *all* paths before the handler, except these which log after.\n  // Mutually exclusive with beforePaths. Don't list session-destroying paths here.\n  // This can be combined with `paths`\n  // afterPaths: [\"/callback\"],\n\n  capture: {\n    ipAddress: true,         // capture client IP\n    userAgent: true,         // capture User-Agent header\n    requestBody: false,      // include request body in metadata\n  },\n\n  piiRedaction: {\n    enabled: false,          // redact sensitive fields when requestBody is captured\n    strategy: \"mask\",        // \"mask\" (***) | \"hash\" (SHA-256) | \"remove\" (delete key)\n    fields: [\"password\"],    // defaults: password, token, secret, apiKey, otp, etc.\n  },\n\n  // intercept before write — return null to suppress. Receives the endpoint\n  // ctx as a second argument, so you can resolve the session or read the request.\n  beforeLog: async (entry, ctx) => {\n    if (entry.userId === \"service-account\") return null;\n    return entry;\n  },\n\n  // called after each successful write\n  afterLog: async (entry) => {\n    await analytics.track(\"auth.event\", entry);\n  },\n\n  storage: undefined,        // custom storage backend (see below)\n})\n```\n\nTo override the DB model name, pass `schema: { auditLog: { modelName: \"your_table_name\" } }`.\n\n## Adding additional metadata to log entries\n\n`beforeLog` is the injection point for extra per-entry data. Because it receives the\nendpoint `ctx`, you can resolve the session and stash a value such as the active\norganization into `metadata` — which is stored as JSON and returned intact:\n\n```ts\nimport { getSessionFromCtx } from \"better-auth/api\";\n\nauditLog({\n  beforeLog: async (entry, ctx) => {\n    const session = await getSessionFromCtx(ctx);\n    return {\n      ...entry,\n      metadata: {\n        ...entry.metadata,\n        activeOrganizationId: session?.session?.activeOrganizationId ?? null,\n      },\n    };\n  },\n});\n```\n\n## Custom storage\n\nRoute writes to any external backend instead of Better Auth's database:\n\n```ts\nimport { auditLog, type AuditLogStorage } from \"@stotles/better-auth-audit-logs\";\n\nconst clickhouse: AuditLogStorage = {\n  async write(entry) {\n    await fetch(\"https://ch.example.com/insert\", {\n      method: \"POST\",\n      body: JSON.stringify(entry),\n    });\n  },\n  // Optional — enables the query endpoints to work with your backend\n  async read(options) { /* ... */ },\n  async readById(id) { /* ... */ },\n};\n\nauditLog({ storage: clickhouse })\n```\n\nA `MemoryStorage` adapter is included for testing:\n\n```ts\nimport { auditLog, MemoryStorage } from \"@stotles/better-auth-audit-logs\";\n\nconst storage = new MemoryStorage();\nconst auth = betterAuth({ plugins: [auditLog({ storage })] });\n\n// assert in tests\nexpect(storage.entries).toHaveLength(1);\nexpect(storage.entries[0].action).toBe(\"sign-in:email\");\n```\n\n## API endpoints\n\nThree endpoints are registered under `/audit-log/`, all requiring an active session. Rate limited to 60 req/min.\n\n| Endpoint | Method | Description |\n|---|---|---|\n| `/audit-log/list` | `GET` | Paginated entries |\n| `/audit-log/:id` | `GET` | Single entry by ID |\n| `/audit-log/insert` | `POST` | Manually insert a custom event |\n\n**Query parameters** for `GET /audit-log/list`:\n\n| Parameter | Type | Default |\n|---|---|---|\n| `userId` | `string` | session user |\n| `action` | `string` | — |\n| `status` | `\"success\" \\| \"failed\" \\| \"requested\"` | — |\n| `from` | ISO date string | — |\n| `to` | ISO date string | — |\n| `limit` | `number` | `50` (max 500) |\n| `offset` | `number` | `0` |\n\n## Design decisions\n\n- **Entries survive user deletion** — `userId` uses `ON DELETE SET NULL`. Deleting a user does not erase their audit trail.\n- **`userAgent` is not returned in API responses** — stored for forensics but excluded from client queries by default.\n- **Failed sign-ins have `userId: null`** — the user isn't authenticated yet, so there's no session to pull from.\n- **`writeMode` trades audit integrity against auth availability** — the write can relate to the auth request in three ways:\n  - `\"sync-best-effort\"` **(default)** — the write is awaited before responding (so it isn't dropped on runtimes without a reliable background mechanism), but a failure after retries is logged and passed to `onWriteError` **without** failing the auth request. Auth always succeeds.\n  - `\"sync-strict\"` — same, but a failure (storage after retries, or a throw from `beforeLog`/`afterLog`) is **rethrown**, turning the audit failure into the auth response. This does **not** roll the action back: `after` hooks run once the auth action has already executed and committed (better-auth does not wrap the request and its hooks in a shared transaction), so strict mode surfaces an error *after the fact*. To actually gate an action on a durable audit record, log its path in the *before* hook so the write runs before the action — a failed write then blocks it. The robust way to do this broadly is `afterPaths`, which inverts the default so **every** path logs before except the ones you list. Prefer it over `beforePaths` here: you only have to enumerate the non-mutating paths (which don't need gating), rather than remembering to add every mutating path. However, this means you lose the observed outcome of those paths (they all log as `\"requested\"`).\n  - `\"background\"` — fire-and-forget via `runInBackground`; lowest latency, failures never touch the response. Requires the runtime to keep the task alive after responding (e.g. `waitUntil`), or writes may be lost.\n\n## Recommended production config\n\n```ts\nauditLog({\n  writeMode: \"sync-best-effort\", // the default: never fails auth, and the write isn't dropped post-response.\n                                 // See above for trade-offs between modes as all are reasonable.\n  piiRedaction: { enabled: true, strategy: \"hash\" },\n  afterLog: async (entry) => {\n    if (entry.severity === \"critical\" || entry.severity === \"high\") {\n      await alerting.emit(entry);\n    }\n  },\n})\n```\n\n## Acknowledgments\n\nThis plugin was inspired by the audit log design shared by [@Re4GD](https://github.com/Re4GD) in [better-auth/better-auth#1184](https://github.com/better-auth/better-auth/issues/1184). Additional inspiration from [@issamwahbi](https://github.com/issamwahbi) ([#3592](https://github.com/better-auth/better-auth/discussions/3592)) and [@ItsProless](https://github.com/ItsProless) ([#7952](https://github.com/better-auth/better-auth/discussions/7952)).\n\nThis plugin was originally maintained by [@ejirocodes](https://github.com/ejirocodes/better-auth-audit-logs).\n\nThis fork is maintained by [@Stotles](https://github.com/Stotles).\n\n## License\n\n[MIT](./LICENSE)\n","readmeFilename":"README.md"}