Back to Better Auth

Audit Logs

docs/content/docs/infrastructure/plugins/audit-logs.mdx

1.6.1811.3 KB
Original Source

Better Auth Infrastructure automatically collects audit logs for authentication events in your application. Once you've added the dash() plugin, events are tracked without any additional configuration.

How Audit Logs Are Collected

The dash() plugin hooks into your Better Auth instance and automatically records events as they happen. No manual instrumentation is needed — sign-ups, sign-ins, password changes, and more are all captured.

ts
import { betterAuth } from "better-auth";
import { dash } from "@better-auth/infra";

export const auth = betterAuth({
  plugins: [
    dash(),
  ],
});

That's it. Once dash() is active, audit logs are collected automatically.

Tracked Events

<Tabs items={["User", "Session", "Account", "Verification", "Organization", "Security"]}> <Tab value="User"> | Event | Trigger | | ---------------------------- | ---------------------------- | | user_signed_up | New user registration | | user_profile_updated | User updates their profile | | user_profile_image_updated | User changes their avatar | | user_email_verified | Email verification completed | | user_banned | User is banned | | user_unbanned | User is unbanned | | user_deleted | User account deleted | </Tab>

<Tab value="Session"> | Event | Trigger | | ---------------------------- | ------------------------------- | | `user_signed_in` | Successful sign-in | | `user_signed_out` | User signs out | | `session_created` | New session created | | `session_revoked` | Single session revoked | | `sessions_revoked_all` | All sessions revoked | | `user_impersonated` | Admin starts impersonating user | | `user_impersonation_stopped` | Admin stops impersonating | </Tab> <Tab value="Account"> | Event | Trigger | | ------------------ | ----------------------- | | `account_linked` | Social account linked | | `account_unlinked` | Social account unlinked | | `password_changed` | Password updated | </Tab> <Tab value="Verification"> | Event | Trigger | | -------------------------- | ------------------------ | | `password_reset_requested` | Password reset initiated | | `password_reset_completed` | Password reset finished | | `email_verification_sent` | Verification email sent | </Tab> <Tab value="Organization"> Tracked when using the [organization plugin](/docs/plugins/organization).
| Event                  | Trigger                          |
| ---------------------- | -------------------------------- |
| `organization_created` | New organization created         |
| `organization_updated` | Organization settings changed    |
| `member_added`         | Member added to organization     |
| `member_removed`       | Member removed from organization |
| `member_role_updated`  | Member role changed              |
| `member_invited`       | Invitation sent                  |
| `invite_accepted`      | Invitation accepted              |
| `invite_rejected`      | Invitation rejected              |
| `invite_cancelled`     | Invitation cancelled             |
| `team_created`         | Team created                     |
| `team_updated`         | Team updated                     |
| `team_deleted`         | Team deleted                     |
| `team_member_added`    | Member added to team             |
| `team_member_removed`  | Member removed from team         |
</Tab> <Tab value="Security"> Tracked when using the [Sentinel](/docs/infrastructure/plugins/sentinel) plugin.
| Event                           | Trigger                             |
| ------------------------------- | ----------------------------------- |
| `security_blocked`              | Request was blocked                 |
| `security_allowed`              | Request was allowed after challenge |
| `security_credential_stuffing`  | Credential stuffing detected        |
| `security_impossible_travel`    | Impossible travel detected          |
| `security_geo_blocked`          | Geo-blocking triggered              |
| `security_bot_blocked`          | Bot detected and blocked            |
| `security_suspicious_ip`        | Suspicious IP detected              |
| `security_velocity_exceeded`    | Rate limit exceeded                 |
| `security_free_trial_abuse`     | Free trial abuse detected           |
| `security_compromised_password` | Compromised password detected       |
| `security_stale_account`        | Stale account reactivation          |
</Tab> </Tabs>

Fetching Audit Logs

Client setup

Add dashClient() to your auth client:

ts
import { createAuthClient } from "better-auth/client";
import { dashClient } from "@better-auth/infra/client";

export const authClient = createAuthClient({
  plugins: [dashClient()],
});

Get current user's audit logs

Returns audit events for the current user. Use this for end-user activity and org views where the caller is a normal member.

Query parameters

ParameterTypeDescription
limitnumberResults per page (max 100, default 50)
offsetnumberPagination offset (default 0)
organizationIdstringFilter by organization
identifierstringFilter by identifier
eventTypestringFilter by event type
userIdstringFilter by user ID
userobjectUser object with ID
sessionobjectSession object with user

Basic query

ts
const session = await authClient.getSession();

const logs = await authClient.dash.getAuditLogs({
  session: session.data,
  limit: 50,
  offset: 0,
});

logs.data?.events;  // Array of audit log events
logs.data?.total;   // Total count
logs.data?.limit;   // Page size
logs.data?.offset;  // Current offset

Filter by event type

Returns the current user's signed-in audit logs.

ts
const signIns = await authClient.dash.getAuditLogs({
  session: session.data,
  eventType: "user_signed_in",
});

Filter by organization

Returns the current user's audit logs restricted to the given organization.

ts
const orgLogs = await authClient.dash.getAuditLogs({
  session: session.data,
  organizationId: "org_123",
});

Combined filters

You can combine filters to get more specific results (e.g. all current user's signed-in events for a specific organization).

ts
const orgSignIns = await authClient.dash.getAuditLogs({
  session: session.data,
  organizationId: "org_123",
  eventType: "user_signed_in",
});

You can also limit results to a specific identifier (e.g all current user's events for a specific org and email).

ts
const orgSignIns = await authClient.dash.getAuditLogs({
  session: session.data,
  organizationId: "org_123",
  identifier: "[email protected]",
});

Note: An identifier is a unique value for an event. For example, an email address, username, etc.

Pagination

Results are paginated. If you need to consume more than one page, you can use the limit and offset parameters to paginate the results:

ts
async function fetchAllUserAuditLogEvents(session: unknown) {
  const limit = 100;
  let offset = 0;
  const allEvents = [];

  while (true) {
    const result = await authClient.dash.getAuditLogs({
      session: session.data,
      limit,
      offset,
    });
    
    const events = result.data?.events ?? [];
    allEvents.push(...events);

    if (events.length < limit) break;
    offset += limit;
  }

  return allEvents;
}

Get all audit logs

Returns all audit events for organizations the current user has admin or owner access to. Use it for admin-style dashboards that need activity across organizations you manage. You must use the organization plugin so membership roles can be evaluated.

Query parameters

ParameterTypeDescription
limitnumberResults per page (max 100, default 50)
offsetnumberPagination offset (default 0)
organizationIdstringLimit results to one organization (you must be owner or admin)
userIdstringLimit to one user’s activity across organizations you administer as owner or admin
eventTypestringFilter by event type
identifierstringMatch eventData.identifier (organization-scoped actor identity)
sessionobjectSession object with user

Basic query

ts
const session = await authClient.getSession();

const activity = await authClient.dash.getAllAuditLogs({
  session: session.data,
  limit: 50,
  offset: 0,
});

activity.data?.events;
activity.data?.total;
activity.data?.limit;
activity.data?.offset;

Filter by organization

Returns all audit events for the given organization:

ts
const orgActivity = await authClient.dash.getAllAuditLogs({
  session: session.data,
  organizationId: "org_123",
});

Filter by user

Returns all audit events for the given user across organizations:

ts
const userActivity = await authClient.dash.getAllAuditLogs({
  session: session.data,
  userId: "user_456",
});

Filter by event type

Returns all audit events across organizations for the given event type:

ts
const events = await authClient.dash.getAllAuditLogs({
  session: session.data,
  eventType: "member_invited",
});

Combined filters

You can combine filters to get more specific results (e.g. all audit events for a specific organization and event type).

ts
const orgEvents = await authClient.dash.getAllAuditLogs({
  session: session.data,
  organizationId: "org_123",
  eventType: "member_invited",
});

Pagination

Results are paginated. If you need to consume more than one page, you can use the limit and offset parameters to paginate the results:

ts
async function fetchAllAuditLogs(session: unknown) {
  const limit = 100;
  let offset = 0;
  const allEvents = [];

  while (true) {
    const result = await authClient.dash.getAllAuditLogs({
      session: session.data,
      limit,
      offset,
    });
    
    const events = result.data?.events ?? [];
    allEvents.push(...events);

    if (events.length < limit) break;
    offset += limit;
  }

  return allEvents;
}