AbsoluteJS

First-party Plugins

A small bundle of opinionated, ~20-LOC helpers for the things you'd otherwise write twice — Slack / Discord / PagerDuty audit sinks, disposable-email and geo deny lists, PostHog identify. Each is a plain function returning a shape the package already accepts; no plugin registry, no lifecycle.

#Philosophy

The benefit of this package is that the hooks already let you do anything. A "plugin" is just a function you pass to one of those hooks. So /plugins is exactly that — small named functions that return the same primitives (AuditSink, (headers) => boolean, (email) => verdict) your config already consumes. If you want one we don't ship, write the same shape and pass it to the same hook.

Deliberately tiny
Each plugin is a small named function (20-40 LOC), and there is no plugin registry, no lifecycle, no install command, no DI container. The hooks are the contract; the plugins package is just our own opinionated, batteries-included examples for the things you'd otherwise look up a Stack Overflow answer to write yourself.

#Alert sinks

slackAlertPlugin, discordAlertPlugin, and pagerdutyAlertPlugin each return an AuditSink tuned for that destination's payload format. Filter to the events you actually want to wake up for — impersonation, MFA-disabled, abuse blocks, lockouts — and forward everything else only to your audit table.

TS
import {
  discordAlertPlugin,
  pagerdutyAlertPlugin,
  slackAlertPlugin
} from '@absolutejs/auth/plugins';

// Three audit sinks for the events you actually wake up for — login_failed_lockout,
// password_changed, mfa_disabled, impersonation_started, abuse_blocked, etc. Each
// plugin is a thin function (~20 LOC) that returns an AuditSink; you decide which
// events to forward by filtering in the auditSinks pipeline.
const slack = slackAlertPlugin({
  webhookUrl: process.env.SLACK_SECURITY_WEBHOOK,
  events: ['impersonation_started', 'mfa_disabled', 'login_failed_lockout']
});
const pagerduty = pagerdutyAlertPlugin({
  routingKey: process.env.PAGERDUTY_ROUTING_KEY,
  events: ['abuse_blocked'] // page on hostile traffic
});
const discord = discordAlertPlugin({
  webhookUrl: process.env.DISCORD_SECURITY_WEBHOOK
});

await auth<User>({
  providersConfiguration: {},
  audit: {
    auditStore: createNeonAuditStore(process.env.DATABASE_URL),
    sinks: [slack, pagerduty, discord]
  }
});

#Geo block & disposable email

denyDisposableEmailPluginBlocks signups from throwaway mail providers.
geoBlockPluginTakes an allow- or deny-list of ISO-3166 country codes and reads whichever header your CDN already populates.

Both compose with the existing credentials and abuse hooks — no separate middleware pipeline.

TS
import {
  denyDisposableEmailPlugin,
  geoBlockPlugin
} from '@absolutejs/auth/plugins';

// denyDisposableEmail: drop signups from throwaway-mail providers (mailinator,
// 10minutemail, ...) — returns { allow, reason? }. Plug it into your register hook:
const checkEmail = denyDisposableEmailPlugin();
credentials: {
  // ...the rest of your credentials config
  onCreateCredentialUser: ({ email, ...extra }) => {
    const verdict = checkEmail(email);
    if (!verdict.allow) {
      throw new Error(verdict.reason ?? 'disposable_email');
    }
    return createUser({ email, ...extra });
  }
}

// geoBlock: allow / deny by ISO-3166 country code from any header signal you
// already have (cf-ipcountry, x-vercel-ip-country, x-client-country, ...). Returns
// (headers) => boolean — true means block. Pair with the abuse block's onRequest:
const blockedCountry = geoBlockPlugin({
  denyCountries: ['RU', 'KP', 'IR'] // or allowCountries: ['US', 'CA', 'GB']
});
abuse: {
  // ...the rest of your abuse config
  shouldBlock: (request) => blockedCountry(request.headers)
}

#PostHog identify

posthogIdentifyPlugin streams every audit event as $identify, keyed by the audit event's userId. Same sink interface as the alert plugins, so you can chain it alongside Slack/PagerDuty.

TS
import { posthogIdentifyPlugin } from '@absolutejs/auth/plugins';

// posthogIdentify: stream every audit event into PostHog as $identify — login,
// MFA enrollment, password reset, impersonation, etc. — keyed by the audit
// event's userId. Returns an AuditSink, so it composes with the alert plugins.
await auth<User>({
  providersConfiguration: {},
  audit: {
    auditStore: createNeonAuditStore(process.env.DATABASE_URL),
    sinks: [
      slackAlertPlugin({ webhookUrl: process.env.SLACK_WEBHOOK }),
      posthogIdentifyPlugin({
        host: 'https://us.i.posthog.com',
        projectApiKey: process.env.POSTHOG_KEY
      })
    ]
  }
});