password-expiry-lite
v1.0.0
Published
Dependency-free, adapter-driven password expiry engine for Node.js.
Downloads
13
Maintainers
Readme
password-expiry-lite
A zero-dependency, adapter-driven password expiry engine for Node.js. It is framework-agnostic, database-agnostic, and authentication-agnostic. All IO is delegated to adapters so you can embed it into an existing application without rewriting your auth or persistence layers.
Why It Exists
Most apps need password expiration, warnings, and forced resets. Existing solutions are often tied to a framework or database, or require heavy dependencies. This library provides the logic only, with clean adapters for storage, notifications, and lifecycle hooks.
Installation
npm install password-expiry-liteQuick Start
import { PasswordExpiry } from 'password-expiry-lite';
const expiry = new PasswordExpiry({
expiresInDays: 90,
gracePeriodDays: 3,
warningWindowDays: 7,
store,
notifier,
});
const result = await expiry.check(userId);
if (result.shouldBlock) {
throw new Error(result.message);
}Core Concepts
- Statuses:
VALID,EXPIRING_SOON,EXPIRED,FORCE_CHANGE - Deterministic evaluation: all decisions are made from the stored record and configuration
- Adapters for storage, notifications, and events
Recording Password Changes
await expiry.recordPasswordChange(userId);Forcing A Password Change
await expiry.forcePasswordChange(userId);Configuration
import { PasswordExpiryConfig } from 'password-expiry-lite';
const config: PasswordExpiryConfig = {
expiresInDays: 90,
gracePeriodDays: 5,
warningWindowDays: 7,
forceChange: false,
requireRecord: false,
notifyOnCheck: true,
store,
notifier,
events,
messages: {
VALID: () => 'Password is valid.',
EXPIRING_SOON: ({ daysLeft }) => `Password expires in ${daysLeft} days.`,
EXPIRED: ({ daysOverdue }) => `Password expired ${daysOverdue} days ago.`,
FORCE_CHANGE: () => 'Password change required.',
},
};Adapter Implementation Guide
Store Adapter
import { PasswordRecordStore } from 'password-expiry-lite';
const store: PasswordRecordStore = {
async get(userId) {
return db.passwordRecords.find(userId) ?? null;
},
async update(userId, record) {
await db.passwordRecords.upsert(userId, record);
},
};Notifier Adapter (Optional)
import { PasswordExpiryNotifier } from 'password-expiry-lite';
const notifier: PasswordExpiryNotifier = {
async onExpiringSoon(payload) {
await email.send(payload.userId, payload.message);
},
async onExpired(payload) {
await email.send(payload.userId, payload.message);
},
async onForcedChange(payload) {
await email.send(payload.userId, payload.message);
},
};Events Adapter (Optional)
import { PasswordExpiryEvents } from 'password-expiry-lite';
const events: PasswordExpiryEvents = {
onExpired: ({ userId }) => audit.log(userId, 'password_expired'),
onForcedChange: ({ userId }) => audit.log(userId, 'password_forced_change'),
onValidated: ({ userId }) => audit.log(userId, 'password_validated'),
};Example Integration
See example/ for a realistic app integration with an in-memory store and fake login flow. The example imports the package by name, so run npm run build before npm run example.
Security Notes
- Use a strong password hashing scheme in your authentication layer.
- Keep password change timestamps in a durable store.
- Treat
FORCE_CHANGEas a blocking state. - Consider shorter expiration windows for privileged accounts.
License
MIT
