@warlock.js/auth 5.29.0 → 5.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/esm/commands/auth-cleanup-command.d.mts +1 -1
- package/esm/commands/auth-cleanup-command.d.mts.map +1 -1
- package/esm/commands/auth-cleanup-command.mjs +1 -1
- package/esm/commands/auth-cleanup-command.mjs.map +1 -1
- package/esm/commands/auth-purge-never-expiring-command.d.mts +1 -1
- package/esm/commands/auth-purge-never-expiring-command.d.mts.map +1 -1
- package/esm/commands/auth-purge-never-expiring-command.mjs +1 -1
- package/esm/commands/auth-purge-never-expiring-command.mjs.map +1 -1
- package/esm/commands/jwt-secret-generator-command.d.mts +1 -1
- package/esm/commands/jwt-secret-generator-command.d.mts.map +1 -1
- package/esm/commands/jwt-secret-generator-command.mjs +1 -1
- package/esm/commands/jwt-secret-generator-command.mjs.map +1 -1
- package/esm/contracts/auth-contract.d.mts +1 -1
- package/esm/contracts/auth-contract.d.mts.map +1 -1
- package/esm/contracts/providers.d.mts +1 -1
- package/esm/contracts/providers.d.mts.map +1 -1
- package/esm/contracts/request-locals.d.mts +1 -1
- package/esm/contracts/request-locals.d.mts.map +1 -1
- package/esm/contracts/types.d.mts +1 -1
- package/esm/contracts/types.d.mts.map +1 -1
- package/esm/contracts/types.mjs +1 -1
- package/esm/contracts/types.mjs.map +1 -1
- package/esm/errors/auth-provider-sdk-missing.error.d.mts +1 -1
- package/esm/errors/auth-provider-sdk-missing.error.d.mts.map +1 -1
- package/esm/errors/auth-provider-sdk-missing.error.mjs +1 -1
- package/esm/errors/auth-provider-sdk-missing.error.mjs.map +1 -1
- package/esm/errors/email-not-verified.error.d.mts +1 -1
- package/esm/errors/email-not-verified.error.d.mts.map +1 -1
- package/esm/errors/email-not-verified.error.mjs +1 -1
- package/esm/errors/email-not-verified.error.mjs.map +1 -1
- package/esm/errors/invalid-one-time-token.error.d.mts +1 -1
- package/esm/errors/invalid-one-time-token.error.d.mts.map +1 -1
- package/esm/errors/invalid-one-time-token.error.mjs +1 -1
- package/esm/errors/invalid-one-time-token.error.mjs.map +1 -1
- package/esm/errors/invalid-passkey.error.d.mts +1 -1
- package/esm/errors/invalid-passkey.error.d.mts.map +1 -1
- package/esm/errors/invalid-passkey.error.mjs +1 -1
- package/esm/errors/invalid-passkey.error.mjs.map +1 -1
- package/esm/errors/invalid-provider-callback.error.d.mts +1 -1
- package/esm/errors/invalid-provider-callback.error.d.mts.map +1 -1
- package/esm/errors/invalid-provider-callback.error.mjs +1 -1
- package/esm/errors/invalid-provider-callback.error.mjs.map +1 -1
- package/esm/errors/notifications-unavailable.error.d.mts +1 -1
- package/esm/errors/notifications-unavailable.error.d.mts.map +1 -1
- package/esm/errors/notifications-unavailable.error.mjs +1 -1
- package/esm/errors/notifications-unavailable.error.mjs.map +1 -1
- package/esm/errors/provider-email-not-verified.error.d.mts +1 -1
- package/esm/errors/provider-email-not-verified.error.d.mts.map +1 -1
- package/esm/errors/provider-email-not-verified.error.mjs +1 -1
- package/esm/errors/provider-email-not-verified.error.mjs.map +1 -1
- package/esm/index.d.mts +2 -2
- package/esm/index.mjs +2 -2
- package/esm/localization/ar.translations.mjs +1 -1
- package/esm/localization/ar.translations.mjs.map +1 -1
- package/esm/localization/auth-default-translations.d.mts +1 -1
- package/esm/localization/auth-default-translations.d.mts.map +1 -1
- package/esm/localization/auth-default-translations.mjs +1 -1
- package/esm/localization/auth-default-translations.mjs.map +1 -1
- package/esm/localization/auth-translations.type.d.mts +1 -1
- package/esm/localization/auth-translations.type.d.mts.map +1 -1
- package/esm/localization/en.translations.mjs +1 -1
- package/esm/localization/en.translations.mjs.map +1 -1
- package/esm/localization/init.mjs +1 -1
- package/esm/localization/init.mjs.map +1 -1
- package/esm/localization/register-auth-translations.d.mts +1 -1
- package/esm/localization/register-auth-translations.d.mts.map +1 -1
- package/esm/localization/register-auth-translations.mjs +1 -1
- package/esm/localization/register-auth-translations.mjs.map +1 -1
- package/esm/middleware/auth.middleware.d.mts +1 -1
- package/esm/middleware/auth.middleware.d.mts.map +1 -1
- package/esm/middleware/auth.middleware.mjs +1 -1
- package/esm/middleware/auth.middleware.mjs.map +1 -1
- package/esm/middleware/csrf-origin-check.d.mts +1 -1
- package/esm/middleware/csrf-origin-check.d.mts.map +1 -1
- package/esm/middleware/csrf-origin-check.mjs +1 -1
- package/esm/middleware/csrf-origin-check.mjs.map +1 -1
- package/esm/middleware/index.d.mts +1 -1
- package/esm/middleware/index.mjs +1 -1
- package/esm/middleware/localized-login-path.mjs +1 -1
- package/esm/middleware/localized-login-path.mjs.map +1 -1
- package/esm/middleware/login-throttle.middleware.d.mts +17 -2
- package/esm/middleware/login-throttle.middleware.d.mts.map +1 -1
- package/esm/middleware/login-throttle.middleware.mjs +84 -6
- package/esm/middleware/login-throttle.middleware.mjs.map +1 -1
- package/esm/middleware/one-time-token-throttle.middleware.d.mts +1 -1
- package/esm/middleware/one-time-token-throttle.middleware.d.mts.map +1 -1
- package/esm/middleware/one-time-token-throttle.middleware.mjs +1 -1
- package/esm/middleware/one-time-token-throttle.middleware.mjs.map +1 -1
- package/esm/middleware/otp-throttle.middleware.d.mts +1 -1
- package/esm/middleware/otp-throttle.middleware.d.mts.map +1 -1
- package/esm/middleware/otp-throttle.middleware.mjs +1 -1
- package/esm/middleware/otp-throttle.middleware.mjs.map +1 -1
- package/esm/middleware/require-verified-email.middleware.d.mts +1 -1
- package/esm/middleware/require-verified-email.middleware.d.mts.map +1 -1
- package/esm/middleware/require-verified-email.middleware.mjs +1 -1
- package/esm/middleware/require-verified-email.middleware.mjs.map +1 -1
- package/esm/models/access-token/access-token.model.d.mts +27 -35
- package/esm/models/access-token/access-token.model.d.mts.map +1 -1
- package/esm/models/access-token/access-token.model.mjs +1 -1
- package/esm/models/access-token/access-token.model.mjs.map +1 -1
- package/esm/models/access-token/family-association.migration.mjs +1 -1
- package/esm/models/access-token/family-association.migration.mjs.map +1 -1
- package/esm/models/access-token/migration.mjs +1 -1
- package/esm/models/access-token/migration.mjs.map +1 -1
- package/esm/models/auth-token-family/auth-token-family.model.d.mts +27 -43
- package/esm/models/auth-token-family/auth-token-family.model.d.mts.map +1 -1
- package/esm/models/auth-token-family/auth-token-family.model.mjs +1 -1
- package/esm/models/auth-token-family/auth-token-family.model.mjs.map +1 -1
- package/esm/models/auth-token-family/migration.mjs +1 -1
- package/esm/models/auth-token-family/migration.mjs.map +1 -1
- package/esm/models/auth.model.d.mts +1 -1
- package/esm/models/auth.model.d.mts.map +1 -1
- package/esm/models/auth.model.mjs +1 -1
- package/esm/models/auth.model.mjs.map +1 -1
- package/esm/models/index.d.mts +1 -1
- package/esm/models/index.d.mts.map +1 -1
- package/esm/models/index.mjs +1 -1
- package/esm/models/index.mjs.map +1 -1
- package/esm/models/one-time-token/migration.mjs +1 -1
- package/esm/models/one-time-token/migration.mjs.map +1 -1
- package/esm/models/one-time-token/one-time-token.model.d.mts +35 -67
- package/esm/models/one-time-token/one-time-token.model.d.mts.map +1 -1
- package/esm/models/one-time-token/one-time-token.model.mjs +1 -1
- package/esm/models/one-time-token/one-time-token.model.mjs.map +1 -1
- package/esm/models/passkey-credential/migration.mjs +1 -1
- package/esm/models/passkey-credential/migration.mjs.map +1 -1
- package/esm/models/passkey-credential/passkey-credential.model.d.mts +31 -47
- package/esm/models/passkey-credential/passkey-credential.model.d.mts.map +1 -1
- package/esm/models/passkey-credential/passkey-credential.model.mjs +1 -1
- package/esm/models/passkey-credential/passkey-credential.model.mjs.map +1 -1
- package/esm/models/provider-account/migration.mjs +1 -1
- package/esm/models/provider-account/migration.mjs.map +1 -1
- package/esm/models/provider-account/provider-account.model.d.mts +27 -35
- package/esm/models/provider-account/provider-account.model.d.mts.map +1 -1
- package/esm/models/provider-account/provider-account.model.mjs +1 -1
- package/esm/models/provider-account/provider-account.model.mjs.map +1 -1
- package/esm/models/refresh-token/migration.mjs +1 -1
- package/esm/models/refresh-token/migration.mjs.map +1 -1
- package/esm/models/refresh-token/refresh-token.model.d.mts +47 -95
- package/esm/models/refresh-token/refresh-token.model.d.mts.map +1 -1
- package/esm/models/refresh-token/refresh-token.model.mjs +1 -1
- package/esm/models/refresh-token/refresh-token.model.mjs.map +1 -1
- package/esm/models/refresh-token/successor-association.migration.mjs +1 -1
- package/esm/models/refresh-token/successor-association.migration.mjs.map +1 -1
- package/esm/otp/otp.d.mts +1 -1
- package/esm/otp/otp.d.mts.map +1 -1
- package/esm/otp/otp.mjs +1 -1
- package/esm/otp/otp.mjs.map +1 -1
- package/esm/passkeys/passkey-authentication.d.mts +1 -1
- package/esm/passkeys/passkey-authentication.d.mts.map +1 -1
- package/esm/passkeys/passkey-authentication.mjs +1 -1
- package/esm/passkeys/passkey-authentication.mjs.map +1 -1
- package/esm/passkeys/passkey-challenges.mjs +1 -1
- package/esm/passkeys/passkey-challenges.mjs.map +1 -1
- package/esm/passkeys/passkey-registration.d.mts +1 -1
- package/esm/passkeys/passkey-registration.d.mts.map +1 -1
- package/esm/passkeys/passkey-registration.mjs +1 -1
- package/esm/passkeys/passkey-registration.mjs.map +1 -1
- package/esm/passkeys/simplewebauthn.d.mts +1 -1
- package/esm/passkeys/simplewebauthn.d.mts.map +1 -1
- package/esm/passkeys/simplewebauthn.mjs +1 -1
- package/esm/passkeys/simplewebauthn.mjs.map +1 -1
- package/esm/providers/apple-provider.d.mts +1 -1
- package/esm/providers/apple-provider.d.mts.map +1 -1
- package/esm/providers/apple-provider.mjs +1 -1
- package/esm/providers/apple-provider.mjs.map +1 -1
- package/esm/providers/discord-provider.d.mts +1 -1
- package/esm/providers/discord-provider.d.mts.map +1 -1
- package/esm/providers/discord-provider.mjs +1 -1
- package/esm/providers/discord-provider.mjs.map +1 -1
- package/esm/providers/facebook-provider.d.mts +1 -1
- package/esm/providers/facebook-provider.d.mts.map +1 -1
- package/esm/providers/facebook-provider.mjs +1 -1
- package/esm/providers/facebook-provider.mjs.map +1 -1
- package/esm/providers/github-provider.d.mts +1 -1
- package/esm/providers/github-provider.d.mts.map +1 -1
- package/esm/providers/github-provider.mjs +1 -1
- package/esm/providers/github-provider.mjs.map +1 -1
- package/esm/providers/google-provider.d.mts +1 -1
- package/esm/providers/google-provider.d.mts.map +1 -1
- package/esm/providers/google-provider.mjs +1 -1
- package/esm/providers/google-provider.mjs.map +1 -1
- package/esm/providers/linkedin-provider.d.mts +1 -1
- package/esm/providers/linkedin-provider.d.mts.map +1 -1
- package/esm/providers/linkedin-provider.mjs +1 -1
- package/esm/providers/linkedin-provider.mjs.map +1 -1
- package/esm/providers/provider-login.d.mts +1 -1
- package/esm/providers/provider-login.d.mts.map +1 -1
- package/esm/providers/provider-login.mjs +1 -1
- package/esm/providers/provider-login.mjs.map +1 -1
- package/esm/providers/provider-state-cookie.mjs +1 -1
- package/esm/providers/provider-state-cookie.mjs.map +1 -1
- package/esm/providers/resolve-provider-user.mjs +1 -1
- package/esm/providers/resolve-provider-user.mjs.map +1 -1
- package/esm/providers/x-provider.d.mts +1 -1
- package/esm/providers/x-provider.d.mts.map +1 -1
- package/esm/providers/x-provider.mjs +1 -1
- package/esm/providers/x-provider.mjs.map +1 -1
- package/esm/services/auth-config.mjs +1 -1
- package/esm/services/auth-config.mjs.map +1 -1
- package/esm/services/auth-events.d.mts +1 -1
- package/esm/services/auth-events.d.mts.map +1 -1
- package/esm/services/auth-events.mjs +1 -1
- package/esm/services/auth-events.mjs.map +1 -1
- package/esm/services/auth.service.d.mts +1 -1
- package/esm/services/auth.service.d.mts.map +1 -1
- package/esm/services/auth.service.mjs +1 -1
- package/esm/services/auth.service.mjs.map +1 -1
- package/esm/services/current-user.d.mts +1 -1
- package/esm/services/current-user.d.mts.map +1 -1
- package/esm/services/current-user.mjs +1 -1
- package/esm/services/current-user.mjs.map +1 -1
- package/esm/services/email-verification.d.mts +1 -1
- package/esm/services/email-verification.d.mts.map +1 -1
- package/esm/services/email-verification.mjs +1 -1
- package/esm/services/email-verification.mjs.map +1 -1
- package/esm/services/generate-jwt-secret.d.mts +1 -1
- package/esm/services/generate-jwt-secret.d.mts.map +1 -1
- package/esm/services/generate-jwt-secret.mjs +1 -1
- package/esm/services/generate-jwt-secret.mjs.map +1 -1
- package/esm/services/jwt.d.mts +1 -1
- package/esm/services/jwt.d.mts.map +1 -1
- package/esm/services/jwt.mjs +1 -1
- package/esm/services/jwt.mjs.map +1 -1
- package/esm/services/one-time-tokens.d.mts +1 -1
- package/esm/services/one-time-tokens.d.mts.map +1 -1
- package/esm/services/one-time-tokens.mjs +1 -1
- package/esm/services/one-time-tokens.mjs.map +1 -1
- package/esm/services/optional-peer.mjs +1 -1
- package/esm/services/optional-peer.mjs.map +1 -1
- package/esm/services/password-reset.d.mts +1 -1
- package/esm/services/password-reset.d.mts.map +1 -1
- package/esm/services/password-reset.mjs +1 -1
- package/esm/services/password-reset.mjs.map +1 -1
- package/esm/services/resolve-request-user.d.mts +1 -1
- package/esm/services/resolve-request-user.d.mts.map +1 -1
- package/esm/services/resolve-request-user.mjs +1 -1
- package/esm/services/resolve-request-user.mjs.map +1 -1
- package/esm/services/token-family-operation.mjs +1 -1
- package/esm/services/token-family-operation.mjs.map +1 -1
- package/esm/services/token-notifications.mjs +1 -1
- package/esm/services/token-notifications.mjs.map +1 -1
- package/esm/session/page-session.d.mts +1 -1
- package/esm/session/page-session.d.mts.map +1 -1
- package/esm/session/page-session.mjs +1 -1
- package/esm/session/page-session.mjs.map +1 -1
- package/esm/utils/auth-error-codes.d.mts +1 -1
- package/esm/utils/auth-error-codes.d.mts.map +1 -1
- package/esm/utils/auth-error-codes.mjs +1 -1
- package/esm/utils/auth-error-codes.mjs.map +1 -1
- package/esm/utils/safe-equal.d.mts +1 -1
- package/esm/utils/safe-equal.d.mts.map +1 -1
- package/esm/utils/safe-equal.mjs +1 -1
- package/esm/utils/safe-equal.mjs.map +1 -1
- package/esm/utils/token-expiry.d.mts +1 -1
- package/esm/utils/token-expiry.d.mts.map +1 -1
- package/esm/utils/token-expiry.mjs +1 -1
- package/esm/utils/token-expiry.mjs.map +1 -1
- package/llms-full.txt +3 -1
- package/package.json +10 -9
- package/skills/throttle-login-attempts/SKILL.md +3 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createDecoder } from "fast-jwt";
|
|
2
2
|
|
|
3
|
-
//#region
|
|
3
|
+
//#region ../../packages/auth/src/utils/token-expiry.ts
|
|
4
4
|
/**
|
|
5
5
|
* Payload decoder. Deliberately signature-*unaware*: these helpers answer
|
|
6
6
|
* "does this token carry a deadline at all", which is a property of the claims,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-expiry.mjs","names":[],"sources":["
|
|
1
|
+
{"version":3,"file":"token-expiry.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/utils/token-expiry.ts"],"sourcesContent":["import { createDecoder } from \"fast-jwt\";\n\n/**\n * Payload decoder. Deliberately signature-*unaware*: these helpers answer\n * \"does this token carry a deadline at all\", which is a property of the claims,\n * not of the signature. Every caller that acts on the answer (the middleware,\n * the purge command) has already verified the signature or is about to delete\n * the row anyway.\n */\nconst decodePayload = createDecoder();\n\n/**\n * Whether a persisted `expires_at` can serve as a deadline at all.\n *\n * Answers `false` for absent, empty, and — the case that matters — `Invalid\n * Date`, which is what a pre-4.12.0 unparseable `expiresIn` wrote to the token\n * row. An `Invalid Date` compares `false` against *every* other date, so a row\n * holding one satisfies neither `expires_at < now` (cleanup never purges it)\n * nor `expires_at > now` (it never shows as an active session): it is outside\n * the reach of every date predicate rather than merely wrong.\n */\nexport function isUsableExpiry(value: unknown): boolean {\n if (value === undefined || value === null || value === \"\") return false;\n\n return !Number.isNaN(new Date(value as string | number | Date).getTime());\n}\n\n/**\n * Whether the JWT carries a finite numeric `exp` claim.\n *\n * This is the store-independent signal for a never-expiring credential: a\n * token with no `exp` has nothing for a verifier to check, so it verifies\n * indefinitely no matter what the row beside it says. An undecodable string\n * answers `false` — it cannot be shown to expire, and a row whose token cannot\n * even be parsed is unusable regardless.\n */\nexport function tokenHasExpClaim(token: unknown): boolean {\n if (typeof token !== \"string\" || token === \"\") return false;\n\n try {\n const payload = decodePayload(token) as { exp?: unknown } | null;\n\n return typeof payload?.exp === \"number\" && Number.isFinite(payload.exp);\n } catch {\n return false;\n }\n}\n\n/**\n * Whether a token row can ever stop being accepted on its own.\n *\n * Two independent ways to answer \"no\", either of which is enough:\n * - the persisted expiry is unusable, so no date check can ever retire the row;\n * - the token itself carries no `exp`, so signature verification never retires it.\n *\n * This is the predicate the `auth.purge-never-expiring` remediation selects on.\n */\nexport function isNeverExpiring(token: unknown, expiresAt: unknown): boolean {\n return !isUsableExpiry(expiresAt) || !tokenHasExpClaim(token);\n}\n"],"mappings":";;;;;;;;;;AASA,MAAM,gBAAgB,cAAc;;;;;;;;;;;AAYpC,SAAgB,eAAe,OAAyB;CACtD,IAAI,UAAU,UAAa,UAAU,QAAQ,UAAU,IAAI,OAAO;CAElE,OAAO,CAAC,OAAO,MAAM,IAAI,KAAK,KAA+B,CAAC,CAAC,QAAQ,CAAC;AAC1E;;;;;;;;;;AAWA,SAAgB,iBAAiB,OAAyB;CACxD,IAAI,OAAO,UAAU,YAAY,UAAU,IAAI,OAAO;CAEtD,IAAI;EACF,MAAM,UAAU,cAAc,KAAK;EAEnC,OAAO,OAAO,SAAS,QAAQ,YAAY,OAAO,SAAS,QAAQ,GAAG;CACxE,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;AAWA,SAAgB,gBAAgB,OAAgB,WAA6B;CAC3E,OAAO,CAAC,eAAe,SAAS,KAAK,CAAC,iBAAiB,KAAK;AAC9D"}
|
package/llms-full.txt
CHANGED
|
@@ -1962,7 +1962,9 @@ They compose. For a hard ceiling on request volume *and* failure-aware account l
|
|
|
1962
1962
|
- **Needs an initialised cache driver.** Storage is `@warlock.js/cache` (a peer dep, transitively present via core). If the driver isn't configured at runtime, the middleware **fails open** — it logs and lets the request through, because a throttle outage must never become an auth outage. That also means: no cache, no protection. Verify your cache config in production.
|
|
1963
1963
|
- **Account lockout can be weaponised.** An attacker who knows a victim's email can lock them out by spamming failures. The per-IP counter mitigates this (the attacker's IP locks too), but for purely anonymous endpoints prefer `by: ["ip"]`. For high-value accounts, consider a CAPTCHA step instead of a hard lock.
|
|
1964
1964
|
- **Counts are atomic and exact across servers (redis/pg).** Each failure does a create-only `cache.set(key, 0, { ttl: window })` (only the first failure opens the window and sets the TTL) followed by an atomic `cache.increment(key)`, so concurrent failures on different servers are never lost. The memory cache driver is per-process — counts and locks are then per-process, so use redis/pg when running more than one instance.
|
|
1965
|
-
- **
|
|
1965
|
+
- **Account and IP keys are hashed.** The built-in extractor keys both by an HMAC of the trimmed, lower-cased value (`email.<hmac>`, `ip.<hmac>`), keyed from `auth.accessToken.secret`, so cache logs never show the address or IP and a list of known emails cannot reverse the key. Without a configured secret it falls back to SHA-256. A custom `identify` is used as is — hash personal values yourself (`throttleIdentifierHash(kind, value)` is exported).
|
|
1966
|
+
- **Fixed window, not sliding.** The window is anchored at the first failure and does not extend on each subsequent one — predictable lockout timing. The lock key is independent and always lasts `lockoutDuration`: the lock stores its own deadline, so even a sliding-expiry driver (`memoryExtended`) cannot extend it.
|
|
1967
|
+
- **Needs a real cache driver.** The `null` driver stores nothing, so the throttle never locks; it warns once when that happens. On Redis, any version from 6 works.
|
|
1966
1968
|
- **Defense-in-depth, not a WAF.** This is application-layer. It won't stop a volumetric L3/L4 flood — pair it with an edge/CDN rate limit for that.
|
|
1967
1969
|
- **Don't call it per-request.** Like every middleware, call it once at route definition; it returns the function that runs per request.
|
|
1968
1970
|
|
package/package.json
CHANGED
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
"ms": "^2.1.3"
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
|
-
"@warlock.js/fs": "5.
|
|
16
|
-
"@warlock.js/cache": "5.
|
|
17
|
-
"@warlock.js/cascade": "5.
|
|
18
|
-
"@warlock.js/core": "5.
|
|
19
|
-
"@warlock.js/logger": "5.
|
|
20
|
-
"@warlock.js/seal": "5.
|
|
21
|
-
"@warlock.js/notifications": "5.
|
|
15
|
+
"@warlock.js/fs": "5.30.0",
|
|
16
|
+
"@warlock.js/cache": "5.30.0",
|
|
17
|
+
"@warlock.js/cascade": "5.30.0",
|
|
18
|
+
"@warlock.js/core": "5.30.0",
|
|
19
|
+
"@warlock.js/logger": "5.30.0",
|
|
20
|
+
"@warlock.js/seal": "5.30.0",
|
|
21
|
+
"@warlock.js/notifications": "5.30.0",
|
|
22
22
|
"@simplewebauthn/server": "^13.1.0",
|
|
23
23
|
"jose": "^6.1.0"
|
|
24
24
|
},
|
|
@@ -35,7 +35,8 @@
|
|
|
35
35
|
},
|
|
36
36
|
"repository": {
|
|
37
37
|
"type": "git",
|
|
38
|
-
"url": "https://github.com/warlockjs/
|
|
38
|
+
"url": "git+https://github.com/warlockjs/framework.git",
|
|
39
|
+
"directory": "packages/auth"
|
|
39
40
|
},
|
|
40
41
|
"keywords": [
|
|
41
42
|
"nodejs",
|
|
@@ -47,7 +48,7 @@
|
|
|
47
48
|
],
|
|
48
49
|
"author": "hassanzohdy",
|
|
49
50
|
"license": "MIT",
|
|
50
|
-
"version": "5.
|
|
51
|
+
"version": "5.30.0",
|
|
51
52
|
"type": "module",
|
|
52
53
|
"main": "./esm/index.mjs",
|
|
53
54
|
"module": "./esm/index.mjs",
|
|
@@ -106,7 +106,9 @@ They compose. For a hard ceiling on request volume *and* failure-aware account l
|
|
|
106
106
|
- **Needs an initialised cache driver.** Storage is `@warlock.js/cache` (a peer dep, transitively present via core). If the driver isn't configured at runtime, the middleware **fails open** — it logs and lets the request through, because a throttle outage must never become an auth outage. That also means: no cache, no protection. Verify your cache config in production.
|
|
107
107
|
- **Account lockout can be weaponised.** An attacker who knows a victim's email can lock them out by spamming failures. The per-IP counter mitigates this (the attacker's IP locks too), but for purely anonymous endpoints prefer `by: ["ip"]`. For high-value accounts, consider a CAPTCHA step instead of a hard lock.
|
|
108
108
|
- **Counts are atomic and exact across servers (redis/pg).** Each failure does a create-only `cache.set(key, 0, { ttl: window })` (only the first failure opens the window and sets the TTL) followed by an atomic `cache.increment(key)`, so concurrent failures on different servers are never lost. The memory cache driver is per-process — counts and locks are then per-process, so use redis/pg when running more than one instance.
|
|
109
|
-
- **
|
|
109
|
+
- **Account and IP keys are hashed.** The built-in extractor keys both by an HMAC of the trimmed, lower-cased value (`email.<hmac>`, `ip.<hmac>`), keyed from `auth.accessToken.secret`, so cache logs never show the address or IP and a list of known emails cannot reverse the key. Without a configured secret it falls back to SHA-256. A custom `identify` is used as is — hash personal values yourself (`throttleIdentifierHash(kind, value)` is exported).
|
|
110
|
+
- **Fixed window, not sliding.** The window is anchored at the first failure and does not extend on each subsequent one — predictable lockout timing. The lock key is independent and always lasts `lockoutDuration`: the lock stores its own deadline, so even a sliding-expiry driver (`memoryExtended`) cannot extend it.
|
|
111
|
+
- **Needs a real cache driver.** The `null` driver stores nothing, so the throttle never locks; it warns once when that happens. On Redis, any version from 6 works.
|
|
110
112
|
- **Defense-in-depth, not a WAF.** This is application-layer. It won't stop a volumetric L3/L4 flood — pair it with an edge/CDN rate limit for that.
|
|
111
113
|
- **Don't call it per-request.** Like every middleware, call it once at route definition; it returns the function that runs per request.
|
|
112
114
|
|