Skip to main content

API Reference

@authlock/core

The engine

ExportKindPurpose
LockoutManagerclasscheck() (read-only pre-auth gate), recordFailure(), recordSuccess(), reset() (administrative unlock), pruneExpired(); multi-key evaluation + onLockout hook
InMemoryLockoutStoreclasssingle-instance store, ships in the box
deriveKeysfunctionresolve an identity to its storage keys (sha256 of the dimension/value pairs — raw values never reach a store)
cooloffFor / effectiveWindowMs / evaluateRecordfunctionsthe pure policy maths, exported for custom stores and tooling
VERSIONconstthe package version

Types

TypePurpose
Identifiersthe identity dimensions (username, ip, userAgent, custom…)
LockoutParameterone dimension combination that can trip a lock, e.g. ['ip', 'userAgent']
FailureRecorda stored counter: {key, failures, firstFailureAt, lastFailureAt} — lock state is derived, never stored. The window is measured from firstFailureAt; the cooloff from lastFailureAt.
CooloffTier{atFailures, cooloffMs} — escalating cooloff by failure count
FailMode'open' (allow + log on store error, default) or 'closed' (deny)
LockoutPolicylimit, cooloff, window, tiers, parameters, whitelist, resetOnSuccess, failMode
LockoutDecision{locked, retryAfterMs, trippedParameter}
LockoutManagerOptionsthe policy plus store, onLockout, logger, and an injectable now clock
LockoutStorethe persistence seam: increment / get / clear / clearExpired (each sync or async)

Policy options

OptionMeaning
limitfailures allowed before a key locks (locks at exactly limit)
cooloffMsbase lock duration once locked
windowMs?failure-counting window; defaults to the effective cooloff
tiers?escalating cooloff by failure count, e.g. [{atFailures: 10, cooloffMs: 3_600_000}]
parametersdimension combinations to evaluate; a lock trips if any trips
whitelist?(id) => boolean | Promise<boolean> — identities never counted or locked
resetOnSuccess?clear failures on success (default true)
failMode?'open' (default) or 'closed'

Store subpaths

ImportContents
@authlock/core/drizzleall three stores + table factories (needs drizzle-orm, no driver)
@authlock/core/postgresPostgresLockoutStore + pgLockoutTable()
@authlock/core/sqliteSqliteLockoutStore + sqliteLockoutTable()
@authlock/core/mysqlMysqlLockoutStore + mysqlLockoutTable()

Every dialect uses the same table shape — key (primary key), failures, and first_failure_at (a numeric epoch in ms) — and the same semantics: the increment is a single atomic create-or-increment-with-window-reset statement, so concurrent attempts across app instances count exactly once each. Postgres and SQLite read the result back with RETURNING; MySQL (which has no RETURNING) re-selects the row, which can only ever report an equal-or-higher count — never an undercount an attacker could slip through.

drizzle-orm is an optional peer: the root @authlock/core import pulls no Drizzle at all and stays zero-dependency.

@nest-native/lockout

ExportKindPurpose
LockoutModuledynamic moduleforRoot(options) / forRootAsync({useFactory, inject, imports}); global by default
LockoutGuardCanActivatereject-if-locked, applied before authentication — HTTP 429 + Retry-After (Express and Fastify responses)
LockoutServiceprovidercheck() / reportFailure() / reportSuccess() / reset() — the explicit call site for your login handler and admin unlock
defaultExtractorfunctionusername from the body, ip from req.ip, userAgent from the header — deliberately no X-Forwarded-For trust
LOCKOUT_MANAGER / LOCKOUT_OPTIONStokensSymbol.for DI tokens (the manager is injectable directly)
LockoutModuleOptionstypeeverything LockoutManagerOptions takes, plus extractor and isGlobal
IdentifierExtractortype(context: ExecutionContext) => Identifiers
VERSIONconstthe adapter version

The adapter builds only on stable Nest primitives (CanActivate, DynamicModule, HttpException, ExecutionContext) and supports NestJS 10, 11, and 12.

:::tip Identity extraction is your trust decision The default extractor reads req.ip — the connection address. Behind a proxy, configure your platform's trust proxy (or supply your own extractor) so the IP dimension reflects a source you actually trust; the library never reads proxy headers for you. :::

Security & operations

  • Do not trust X-Forwarded-For. This is the vulnerability class that hit django-axes and other tools: if the IP you key on comes from a spoofable header, an attacker can rotate it to bypass IP lockout, or forge a victim's IP to lock them out. The default extractor uses req.ip, not X-Forwarded-For — behind a proxy, configure trust proxy so req.ip reflects a source you trust. Because a lock trips if any parameter trips, a ['username'] parameter still catches a single-target brute force even when the IP is spoofable. And never whitelist on a dimension an attacker can forge.
  • Normalize identity dimensions. Alice and alice (and unicode variants) hash to different counters. If your auth is case-insensitive, lowercase / canonicalize the username before passing it, or the limit is per-spelling.
  • Bound store growth. Every distinct identity creates a record. Schedule pruneExpired() (it drops records past their window, capping the store to identities active within windowMs), put a rate limiter (@nestjs/throttler) in front, and use a Drizzle store (not in-memory) for hostile or multi-instance deployments.
  • fail-open vs. availability. With the default failMode: 'open', a store error allows the attempt and logs — so brute-force protection is off while the store is down. Use failMode: 'closed' to deny during an outage instead.