@warlock.js/auth 5.28.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 +18 -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 +76 -74
- package/llms.txt +10 -10
- package/package.json +10 -9
- package/skills/auth-basics/SKILL.md +10 -10
- package/skills/customize-token-storage/SKILL.md +5 -5
- package/skills/customize-user-type/SKILL.md +5 -5
- package/skills/handle-login-and-logout/SKILL.md +5 -5
- package/skills/login-with-providers/SKILL.md +4 -4
- package/skills/manage-tokens/SKILL.md +6 -6
- package/skills/overview/SKILL.md +12 -12
- package/skills/protect-routes/SKILL.md +7 -7
- package/skills/register-user/SKILL.md +7 -7
- package/skills/run-auth-commands/SKILL.md +4 -4
- package/skills/throttle-login-attempts/SKILL.md +7 -5
- package/skills/verify-email-and-reset-password/SKILL.md +4 -4
|
@@ -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
|
@@ -25,13 +25,13 @@ npm install @warlock.js/auth
|
|
|
25
25
|
|
|
26
26
|
## Foundations
|
|
27
27
|
|
|
28
|
-
1. **Users extend `Auth`.** Your `User`, `Admin`, etc. extend the shared base model that knows how to issue tokens and verify passwords. Multiple user types coexist (see
|
|
28
|
+
1. **Users extend `Auth`.** Your `User`, `Admin`, etc. extend the shared base model that knows how to issue tokens and verify passwords. Multiple user types coexist (see the `customize-user-type` topic).
|
|
29
29
|
2. **`auth.userType.<name>` config maps a user-type slug to the model class.** The middleware uses this to hydrate the right model from a token.
|
|
30
30
|
3. **Tokens persist.** Both `AccessToken` and `RefreshToken` are Cascade models — issuing a token writes a row; logout / revoke deletes or marks-revoked. Stateless JWT verification + stateful revocation list.
|
|
31
31
|
4. **`authMiddleware` gates or resolves routes.** Use `authMiddleware("user")` for a typed hard gate, `authMiddleware()` for the configured/default user type, or `authMiddleware({ source: "cookie", key: "token", optional: true })` for a public route that receives a valid user when present. The object form can set a page-local login redirect. Legacy `authMiddleware(userType, "cookie:name")` remains valid.
|
|
32
32
|
5. **`authService.login(Model, credentials, deviceInfo?)` is the full happy path.** Verifies credentials, creates token pair (access + refresh), emits events, returns `{ user, tokens }`.
|
|
33
33
|
6. **Refresh-token rotation is on by default.** Each refresh consumes the old token and issues new ones from the same "family" — replay detection revokes the family.
|
|
34
|
-
7. **JWT secret lives in the env.** Generate with `warlock jwt.generate` (see
|
|
34
|
+
7. **JWT secret lives in the env.** Generate with `warlock jwt.generate` (see the `run-auth-commands` topic).
|
|
35
35
|
|
|
36
36
|
## Minimal wire-up
|
|
37
37
|
|
|
@@ -113,26 +113,26 @@ Two values that look right and are not:
|
|
|
113
113
|
|
|
114
114
|
| If the task is about… | Load |
|
|
115
115
|
| --- | --- |
|
|
116
|
-
| Gating routes with `authMiddleware(allowedUserType)`, any-authenticated vs typed access |
|
|
117
|
-
| `authService.login(...)`, `attemptLogin`, full credentials-to-tokens flow + logout |
|
|
118
|
-
| Token lifecycle — `generateAccessToken`, `createRefreshToken`, rotation, family revocation, max-per-user |
|
|
119
|
-
| Register a new user + issue tokens in one flow |
|
|
120
|
-
| Multi-user-type apps (`user`, `admin`, `client`), `config.auth.userType.<name>` mapping |
|
|
121
|
-
| `warlock jwt.generate` + `warlock auth.cleanup` CLI commands |
|
|
116
|
+
| Gating routes with `authMiddleware(allowedUserType)`, any-authenticated vs typed access | The `protect-routes` topic |
|
|
117
|
+
| `authService.login(...)`, `attemptLogin`, full credentials-to-tokens flow + logout | The `handle-login-and-logout` topic |
|
|
118
|
+
| Token lifecycle — `generateAccessToken`, `createRefreshToken`, rotation, family revocation, max-per-user | The `manage-tokens` topic |
|
|
119
|
+
| Register a new user + issue tokens in one flow | The `register-user` topic |
|
|
120
|
+
| Multi-user-type apps (`user`, `admin`, `client`), `config.auth.userType.<name>` mapping | The `customize-user-type` topic |
|
|
121
|
+
| `warlock jwt.generate` + `warlock auth.cleanup` CLI commands | The `run-auth-commands` topic |
|
|
122
122
|
|
|
123
123
|
## Things NOT to do
|
|
124
124
|
|
|
125
125
|
- Don't write your own JWT signing logic — use `authService` / `jwt` from this package so signature/secret/expiry stay consistent.
|
|
126
126
|
- Don't store the JWT secret in the model layer or anywhere user-modifiable. It lives in `.env` only.
|
|
127
|
-
- Don't return the raw `User` from a login endpoint without shaping output. Configure `static toJsonColumns` or `static resource` (see
|
|
128
|
-
- Don't run `auth.cleanup` from app boot. Schedule it (cron, scheduler) as a periodic task — see
|
|
127
|
+
- Don't return the raw `User` from a login endpoint without shaping output. Configure `static toJsonColumns` or `static resource` (see the `define-model` topic of the `warlock-js-cascade` skill).
|
|
128
|
+
- Don't run `auth.cleanup` from app boot. Schedule it (cron, scheduler) as a periodic task — see the `scheduler-basics` topic of the `warlock-js-scheduler` skill.
|
|
129
129
|
|
|
130
130
|
|
|
131
131
|
## customize-token-storage `@warlock.js/auth/customize-token-storage/SKILL.md`
|
|
132
132
|
|
|
133
133
|
---
|
|
134
134
|
name: customize-token-storage
|
|
135
|
-
description: 'Override the persisted AccessToken / RefreshToken models to add columns (multi-tenant `organization_id`, device metadata), rename, or change storage — without forking the package. Extend the model + `schema.extend(...)`, register it under `config.auth.accessToken.model` / `config.auth.refreshToken.model`, override `issue()` to populate the new column, and add a migration. Triggers: `accessToken.model`, `refreshToken.model`, `AccessToken.issue`, `RefreshToken.issue`, `accessTokenSchema`, `refreshTokenSchema`, "add a column to the token table", "multi-tenant tokens", "organization_id on access token", "override the token model", "custom token storage"; typical import `import { AccessToken, accessTokenSchema } from "@warlock.js/auth"`. Skip: multiple user TYPES (not token storage) —
|
|
135
|
+
description: 'Override the persisted AccessToken / RefreshToken models to add columns (multi-tenant `organization_id`, device metadata), rename, or change storage — without forking the package. Extend the model + `schema.extend(...)`, register it under `config.auth.accessToken.model` / `config.auth.refreshToken.model`, override `issue()` to populate the new column, and add a migration. Triggers: `accessToken.model`, `refreshToken.model`, `AccessToken.issue`, `RefreshToken.issue`, `accessTokenSchema`, `refreshTokenSchema`, "add a column to the token table", "multi-tenant tokens", "organization_id on access token", "override the token model", "custom token storage"; typical import `import { AccessToken, accessTokenSchema } from "@warlock.js/auth"`. Skip: multiple user TYPES (not token storage) — the `customize-user-type` topic; the token lifecycle API — the `manage-tokens` topic; the config blocks themselves — the `auth-basics` topic.'
|
|
136
136
|
---
|
|
137
137
|
|
|
138
138
|
# Customize token storage
|
|
@@ -242,7 +242,7 @@ The new column needs a real database column. Add it to your token-table migratio
|
|
|
242
242
|
this.uuid("organization_id").references("organizations").onDelete("cascade").index();
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
-
See
|
|
245
|
+
See the `write-migration` topic of the `warlock-js-cascade` skill for the migration mechanics.
|
|
246
246
|
|
|
247
247
|
## Why all three — the strict-mode trap
|
|
248
248
|
|
|
@@ -287,16 +287,16 @@ If you rename a column, override the statics **and these getters** so they map t
|
|
|
287
287
|
|
|
288
288
|
## See also
|
|
289
289
|
|
|
290
|
-
-
|
|
291
|
-
-
|
|
292
|
-
-
|
|
290
|
+
- The `customize-user-type` topic — multiple user *types* (a different axis from token *storage*).
|
|
291
|
+
- The `manage-tokens` topic — the token lifecycle your statics power.
|
|
292
|
+
- The `define-model` topic of the `warlock-js-cascade` skill — extending models and schemas.
|
|
293
293
|
|
|
294
294
|
|
|
295
295
|
## customize-user-type `@warlock.js/auth/customize-user-type/SKILL.md`
|
|
296
296
|
|
|
297
297
|
---
|
|
298
298
|
name: customize-user-type
|
|
299
|
-
description: 'Support multiple user types (user / admin / client / staff) in one auth system — each Auth subclass overrides userType, config.auth.userType.<slug> maps slug to model class, authMiddleware(''admin'') gates per type. Triggers: `Auth`, `userType`, `config.auth.userType`, `Authenticable`, `@RegisterModel`, `confirmPassword`; "add admins and users", "multiple user types", "separate client and vendor personas", "per-type login"; typical import `import { Auth } from "@warlock.js/auth"`. Skip: `authMiddleware` semantics —
|
|
299
|
+
description: 'Support multiple user types (user / admin / client / staff) in one auth system — each Auth subclass overrides userType, config.auth.userType.<slug> maps slug to model class, authMiddleware(''admin'') gates per type. Triggers: `Auth`, `userType`, `config.auth.userType`, `Authenticable`, `@RegisterModel`, `confirmPassword`; "add admins and users", "multiple user types", "separate client and vendor personas", "per-type login"; typical import `import { Auth } from "@warlock.js/auth"`. Skip: `authMiddleware` semantics — the `protect-routes` topic; login flow — the `handle-login-and-logout` topic; RBAC libs `casl`, `accesscontrol`, `rbac`.'
|
|
300
300
|
---
|
|
301
301
|
|
|
302
302
|
# Customize user type (multi-user-type auth)
|
|
@@ -399,7 +399,7 @@ router.get("/back-office", backOfficeController, { middleware: [authMiddleware([
|
|
|
399
399
|
router.get("/dashboard", dashboardController, { middleware: [authMiddleware([])] }); // any logged-in
|
|
400
400
|
```
|
|
401
401
|
|
|
402
|
-
See
|
|
402
|
+
See the `protect-routes` topic.
|
|
403
403
|
|
|
404
404
|
## Login per user type — pass the right Model
|
|
405
405
|
|
|
@@ -459,16 +459,16 @@ abstract class Auth<TSchema> extends Model<TSchema> implements Authenticable {
|
|
|
459
459
|
|
|
460
460
|
## See also
|
|
461
461
|
|
|
462
|
-
-
|
|
463
|
-
-
|
|
464
|
-
-
|
|
462
|
+
- The `protect-routes` topic — `authMiddleware` semantics
|
|
463
|
+
- The `handle-login-and-logout` topic — passing the right Model to `login`
|
|
464
|
+
- The `define-model` topic of the `warlock-js-cascade` skill — `@RegisterModel`, models in general
|
|
465
465
|
|
|
466
466
|
|
|
467
467
|
## handle-login-and-logout `@warlock.js/auth/handle-login-and-logout/SKILL.md`
|
|
468
468
|
|
|
469
469
|
---
|
|
470
470
|
name: handle-login-and-logout
|
|
471
|
-
description: 'Run the full login flow via authService.login(Model, credentials, deviceInfo?) — verify password, create access + refresh token pair, fire events. Logout via authService.logout(user, accessToken?, refreshToken?) revokes tokens. Triggers: `authService.login`, `authService.logout`, `authService.attemptLogin`, `authService.refreshTokens`, `authService.revokeAllTokens`, `authEvents`; "build a login endpoint", "POST /login controller", "logout from all devices", "verify credentials and issue tokens"; typical import `import { authService, authEvents } from "@warlock.js/auth"`. Skip: token internals —
|
|
471
|
+
description: 'Run the full login flow via authService.login(Model, credentials, deviceInfo?) — verify password, create access + refresh token pair, fire events. Logout via authService.logout(user, accessToken?, refreshToken?) revokes tokens. Triggers: `authService.login`, `authService.logout`, `authService.attemptLogin`, `authService.refreshTokens`, `authService.revokeAllTokens`, `authEvents`; "build a login endpoint", "POST /login controller", "logout from all devices", "verify credentials and issue tokens"; typical import `import { authService, authEvents } from "@warlock.js/auth"`. Skip: token internals — the `manage-tokens` topic; sign-up — the `register-user` topic; competing libs `passport-local`, `next-auth` credentials.'
|
|
472
472
|
---
|
|
473
473
|
|
|
474
474
|
# Login + logout
|
|
@@ -620,7 +620,7 @@ export const refreshController: RequestHandler = async ({ request, response }) =
|
|
|
620
620
|
};
|
|
621
621
|
```
|
|
622
622
|
|
|
623
|
-
Returns a new token pair or `null` (token expired, revoked, or replay-detected). With rotation enabled (default), the old refresh token is consumed; the new pair stays in the same "family." Replay → revoke the whole family. See
|
|
623
|
+
Returns a new token pair or `null` (token expired, revoked, or replay-detected). With rotation enabled (default), the old refresh token is consumed; the new pair stays in the same "family." Replay → revoke the whole family. See the `manage-tokens` topic.
|
|
624
624
|
|
|
625
625
|
## Auth events
|
|
626
626
|
|
|
@@ -657,9 +657,9 @@ Full event list: `login.attempt`, `login.success`, `login.failed`, `logout`, `lo
|
|
|
657
657
|
|
|
658
658
|
## See also
|
|
659
659
|
|
|
660
|
-
-
|
|
661
|
-
-
|
|
662
|
-
-
|
|
660
|
+
- The `manage-tokens` topic — token lifecycle, rotation, family revocation
|
|
661
|
+
- The `register-user` topic — sign-up that issues tokens after creation
|
|
662
|
+
- The `protect-routes` topic — where the access token gets consumed
|
|
663
663
|
|
|
664
664
|
## Browser sessions for Web pages
|
|
665
665
|
|
|
@@ -670,7 +670,7 @@ For cookie sessions read by `@warlock.js/web` pages, log in with `authService.lo
|
|
|
670
670
|
|
|
671
671
|
---
|
|
672
672
|
name: login-with-providers
|
|
673
|
-
description: 'Log in without a password — Google (OIDC code + PKCE + state + nonce, id_token verified with `jose`), GitHub, Discord, LinkedIn, Apple (`form_post` callback), Facebook and X, passkeys (WebAuthn via `@simplewebauthn/server`) and phone one-time codes (delivered through a `@warlock.js/notifications` channel). Every method ends in `authService.completeLogin(user)`, the same tokens, rows and events as password login. `startProviderLogin`, `completeProviderLogin`, `generatePasskeyRegistrationOptions`, `verifyPasskeyRegistration`, `generatePasskeyAuthenticationOptions`, `verifyPasskeyAuthentication`, `requestOtp`, `verifyOtp`, `otpRequestThrottleMiddleware`, `otpVerifyThrottleMiddleware`, `AuthProvider`. Triggers: "login with Google", "sign in with Google", "login with GitHub", "Sign in with Apple", "login with Discord", "login with LinkedIn", "login with Facebook", "login with X", `auth.providers.github`, "OAuth", "OIDC", "passkeys", "WebAuthn", "passwordless", "SMS code login", "WhatsApp OTP", "phone login", `warlock add auth-google`, `warlock add auth-passkeys`, `AuthProviderSdkMissingError`, `InvalidProviderCallbackError`, `ProviderEmailNotVerifiedError`, `InvalidPasskeyError`, `EC009`, `EC010`, `EC011`, `auth.providers`, `auth.passkeys`, `auth.otp`; typical import `import { completeProviderLogin, verifyOtp } from "@warlock.js/auth"`. Skip: password login —
|
|
673
|
+
description: 'Log in without a password — Google (OIDC code + PKCE + state + nonce, id_token verified with `jose`), GitHub, Discord, LinkedIn, Apple (`form_post` callback), Facebook and X, passkeys (WebAuthn via `@simplewebauthn/server`) and phone one-time codes (delivered through a `@warlock.js/notifications` channel). Every method ends in `authService.completeLogin(user)`, the same tokens, rows and events as password login. `startProviderLogin`, `completeProviderLogin`, `generatePasskeyRegistrationOptions`, `verifyPasskeyRegistration`, `generatePasskeyAuthenticationOptions`, `verifyPasskeyAuthentication`, `requestOtp`, `verifyOtp`, `otpRequestThrottleMiddleware`, `otpVerifyThrottleMiddleware`, `AuthProvider`. Triggers: "login with Google", "sign in with Google", "login with GitHub", "Sign in with Apple", "login with Discord", "login with LinkedIn", "login with Facebook", "login with X", `auth.providers.github`, "OAuth", "OIDC", "passkeys", "WebAuthn", "passwordless", "SMS code login", "WhatsApp OTP", "phone login", `warlock add auth-google`, `warlock add auth-passkeys`, `AuthProviderSdkMissingError`, `InvalidProviderCallbackError`, `ProviderEmailNotVerifiedError`, `InvalidPasskeyError`, `EC009`, `EC010`, `EC011`, `auth.providers`, `auth.passkeys`, `auth.otp`; typical import `import { completeProviderLogin, verifyOtp } from "@warlock.js/auth"`. Skip: password login — the `handle-login-and-logout` topic; email tokens — the `verify-email-and-reset-password` topic; defining an SMS channel — the `define-channel` topic of the `warlock-js-notifications` skill.'
|
|
674
674
|
---
|
|
675
675
|
|
|
676
676
|
# Login with an OAuth provider, passkeys, or a phone code
|
|
@@ -948,16 +948,16 @@ router.post("/auth/otp/verify", async ({ request, response }) => {
|
|
|
948
948
|
|
|
949
949
|
## See also
|
|
950
950
|
|
|
951
|
-
-
|
|
952
|
-
-
|
|
953
|
-
-
|
|
951
|
+
- The `handle-login-and-logout` topic: password login and `setAuthCookie`
|
|
952
|
+
- The `throttle-login-attempts` topic: the throttle the OTP presets wrap
|
|
953
|
+
- The `define-channel` topic of the `warlock-js-notifications` skill: an SMS/WhatsApp channel
|
|
954
954
|
|
|
955
955
|
|
|
956
956
|
## manage-tokens `@warlock.js/auth/manage-tokens/SKILL.md`
|
|
957
957
|
|
|
958
958
|
---
|
|
959
959
|
name: manage-tokens
|
|
960
|
-
description: 'Token lifecycle — generateAccessToken, createRefreshToken, createTokenPair, refreshTokens (with rotation + replay detection), revokeAllTokens, revokeTokenFamily, cleanupExpiredTokens, getActiveSessions. Triggers: `createTokenPair`, `refreshTokens`, `revokeTokenFamily`, `cleanupExpiredTokens`, `getActiveSessions`, `jwt.generate`, `jwt.verify`, `AccessToken`, `RefreshToken`; "rotate refresh tokens", "detect token replay", "logout from all devices", "list active sessions", "clean up expired tokens"; typical import `import { authService, jwt } from "@warlock.js/auth"`. Skip: login flow —
|
|
960
|
+
description: 'Token lifecycle — generateAccessToken, createRefreshToken, createTokenPair, refreshTokens (with rotation + replay detection), revokeAllTokens, revokeTokenFamily, cleanupExpiredTokens, getActiveSessions. Triggers: `createTokenPair`, `refreshTokens`, `revokeTokenFamily`, `cleanupExpiredTokens`, `getActiveSessions`, `jwt.generate`, `jwt.verify`, `AccessToken`, `RefreshToken`; "rotate refresh tokens", "detect token replay", "logout from all devices", "list active sessions", "clean up expired tokens"; typical import `import { authService, jwt } from "@warlock.js/auth"`. Skip: login flow — the `handle-login-and-logout` topic; CLI cleanup — the `run-auth-commands` topic; competing libs `jsonwebtoken`, `jose`, `fast-jwt`.'
|
|
961
961
|
---
|
|
962
962
|
|
|
963
963
|
# Manage tokens
|
|
@@ -1112,7 +1112,7 @@ scheduler.addJob(
|
|
|
1112
1112
|
);
|
|
1113
1113
|
```
|
|
1114
1114
|
|
|
1115
|
-
Or use the bundled CLI command — see
|
|
1115
|
+
Or use the bundled CLI command — see the `run-auth-commands` topic.
|
|
1116
1116
|
|
|
1117
1117
|
## JWT helpers
|
|
1118
1118
|
|
|
@@ -1132,7 +1132,7 @@ const decodedRefresh = await jwt.verifyRefreshToken(refreshToken);
|
|
|
1132
1132
|
|
|
1133
1133
|
**`jwt.verify` / `jwt.verifyRefreshToken` require an `exp` claim** (since 4.12.0) and reject a token without one. There is no deadline to check on such a token, so verification would otherwise succeed indefinitely. `requiredClaims` is additive — `jwt.verify(token, { requiredClaims: ["iat"] })` requires `iat` **and** `exp`; you cannot opt out of `exp`. If you want a token that effectively never expires, use `expiresIn: NO_EXPIRATION` (`"100y"`), which stamps a real `exp` about a century out.
|
|
1134
1134
|
|
|
1135
|
-
If tokens minted before 4.12.0 may lack an `exp`, see `warlock auth.purge-never-expiring` in
|
|
1135
|
+
If tokens minted before 4.12.0 may lack an `exp`, see `warlock auth.purge-never-expiring` in the `run-auth-commands` topic — they are still in your token tables and no date-based cleanup can reach them.
|
|
1136
1136
|
|
|
1137
1137
|
`authService.generateAccessToken` / `createRefreshToken` read `config.auth.accessToken.expiresIn` / `config.auth.refreshToken.expiresIn`, validate them, and throw naming the key if the value is not a positive `ms` duration — before anything is signed, persisted, or capped.
|
|
1138
1138
|
|
|
@@ -1148,9 +1148,9 @@ The package signs access and refresh tokens with independent secrets — `config
|
|
|
1148
1148
|
|
|
1149
1149
|
## See also
|
|
1150
1150
|
|
|
1151
|
-
-
|
|
1152
|
-
-
|
|
1153
|
-
-
|
|
1151
|
+
- The `handle-login-and-logout` topic — full login/logout flow that uses these primitives
|
|
1152
|
+
- The `run-auth-commands` topic — the bundled cleanup command
|
|
1153
|
+
- The `scheduler-basics` topic of the `warlock-js-scheduler` skill — scheduling cleanup
|
|
1154
1154
|
|
|
1155
1155
|
|
|
1156
1156
|
## overview `@warlock.js/auth/overview/SKILL.md`
|
|
@@ -1186,35 +1186,35 @@ Nine task skills. Most apps need `auth-basics` + `protect-routes` + `handle-logi
|
|
|
1186
1186
|
|
|
1187
1187
|
### Foundations
|
|
1188
1188
|
|
|
1189
|
-
####
|
|
1189
|
+
#### `auth-basics`
|
|
1190
1190
|
Start here. The `Auth` base model, `authMiddleware` gate, `authService` (login/logout/refresh), AccessToken + RefreshToken persistence, multi-user-type support.
|
|
1191
1191
|
|
|
1192
1192
|
### The flows
|
|
1193
1193
|
|
|
1194
|
-
####
|
|
1194
|
+
#### `handle-login-and-logout`
|
|
1195
1195
|
`authService.login(Model, credentials, deviceInfo?)` — verify password, issue the token pair, fire events. `authService.logout(user, accessToken?, refreshToken?)` — revoke tokens. For your `POST /login` and `POST /logout` controllers.
|
|
1196
1196
|
|
|
1197
|
-
####
|
|
1197
|
+
#### `register-user`
|
|
1198
1198
|
Sign up a new user and issue the first token pair — `User.create({ ...password: await hashPassword(plain) })` then `authService.createTokenPair(user)`. For `POST /register`.
|
|
1199
1199
|
|
|
1200
|
-
####
|
|
1200
|
+
#### `protect-routes`
|
|
1201
1201
|
`authMiddleware` object and legacy overloads: typed/default hard gates, optional resolution, credential source, and page-local redirects. Sets `request.locals.user` + `request.decodedAccessToken` when a user resolves.
|
|
1202
1202
|
|
|
1203
1203
|
### Going deeper
|
|
1204
1204
|
|
|
1205
|
-
####
|
|
1205
|
+
#### `manage-tokens`
|
|
1206
1206
|
The token lifecycle — `generateAccessToken`, `createRefreshToken`, `createTokenPair`, `refreshTokens` (rotation + replay detection), `revokeAllTokens`, `revokeTokenFamily`, `cleanupExpiredTokens`, `getActiveSessions`. For custom login/registration, token revocation, "logout everywhere", and scheduled cleanup.
|
|
1207
1207
|
|
|
1208
|
-
####
|
|
1208
|
+
#### `customize-user-type`
|
|
1209
1209
|
Support multiple user types in one system — each `Auth` subclass overrides `userType`, `config.auth.userType.<slug>` maps the slug to a model class, `authMiddleware("admin")` / `authMiddleware(["admin", "staff"])` gates per type.
|
|
1210
1210
|
|
|
1211
|
-
####
|
|
1211
|
+
#### `customize-token-storage`
|
|
1212
1212
|
Override the persisted `AccessToken` / `RefreshToken` models to add columns (multi-tenant `organization_id`), rename, or change storage — extend the model + schema and register it under `config.auth.accessToken.model` / `config.auth.refreshToken.model`.
|
|
1213
1213
|
|
|
1214
|
-
####
|
|
1214
|
+
#### `throttle-login-attempts`
|
|
1215
1215
|
Brute-force / credential-stuffing protection — `loginThrottleMiddleware()` counts only failed logins, resets on success, locks per-account + per-IP after a threshold, and rejects pre-controller with 429. Cache-backed, fails open on a cache outage.
|
|
1216
1216
|
|
|
1217
|
-
####
|
|
1217
|
+
#### `run-auth-commands`
|
|
1218
1218
|
Two CLI commands — `warlock jwt.generate` (strong JWT secret → `.env`) and `warlock auth.cleanup` (remove expired refresh tokens). Register via `registerJWTSecretGeneratorCommand()` and `registerAuthCleanupCommand()`.
|
|
1219
1219
|
|
|
1220
1220
|
## What this package deliberately doesn't do
|
|
@@ -1226,16 +1226,16 @@ Two CLI commands — `warlock jwt.generate` (strong JWT secret → `.env`) and `
|
|
|
1226
1226
|
|
|
1227
1227
|
## See also
|
|
1228
1228
|
|
|
1229
|
-
-
|
|
1230
|
-
-
|
|
1231
|
-
- `mongez-agent-kit-authoring-skills` (load via agent-kit sync) — how this `overview
|
|
1229
|
+
- The `warlock-conventions` topic of the `warlock-js-core` skill — the framework auth runs inside (routing, middleware, config).
|
|
1230
|
+
- The `cascade-basics` topic of the `warlock-js-cascade` skill — the ORM behind the `Auth`, `AccessToken`, and `RefreshToken` models.
|
|
1231
|
+
- `mongez-agent-kit-authoring-skills` (load via agent-kit sync) — how this `overview` topic becomes `.claude/skills/warlock-js-auth/overview.md`.
|
|
1232
1232
|
|
|
1233
1233
|
|
|
1234
1234
|
## protect-routes `@warlock.js/auth/protect-routes/SKILL.md`
|
|
1235
1235
|
|
|
1236
1236
|
---
|
|
1237
1237
|
name: protect-routes
|
|
1238
|
-
description: 'Gate HTTP routes via authMiddleware(allowedUserType) — the argument is required and a valid token is always required: [] allows any authenticated user, a user-type restricts to those types. Sets request.locals.user + request.decodedAccessToken on success, 401 on failure. Triggers: `authMiddleware`, `request.locals.user`, `request.decodedAccessToken`, `AuthErrorCodes`, `MissingAccessToken`, `InvalidAccessToken`; "how do I protect a route", "restrict route by user type", "require any logged-in user"; typical import `import { authMiddleware } from "@warlock.js/auth"`. Skip: multi-user-type config —
|
|
1238
|
+
description: 'Gate HTTP routes via authMiddleware(allowedUserType) — the argument is required and a valid token is always required: [] allows any authenticated user, a user-type restricts to those types. Sets request.locals.user + request.decodedAccessToken on success, 401 on failure. Triggers: `authMiddleware`, `request.locals.user`, `request.decodedAccessToken`, `AuthErrorCodes`, `MissingAccessToken`, `InvalidAccessToken`; "how do I protect a route", "restrict route by user type", "require any logged-in user"; typical import `import { authMiddleware } from "@warlock.js/auth"`. Skip: multi-user-type config — the `customize-user-type` topic; issuing the token — the `handle-login-and-logout` topic; competing libs `passport`, `express-jwt`, `next-auth` middleware.'
|
|
1239
1239
|
---
|
|
1240
1240
|
|
|
1241
1241
|
# Gate routes with `authMiddleware`
|
|
@@ -1284,7 +1284,7 @@ router.get("/staff", staffController, {
|
|
|
1284
1284
|
});
|
|
1285
1285
|
```
|
|
1286
1286
|
|
|
1287
|
-
The `userType` slug must match a key in `config.auth.userType.<name>` — see
|
|
1287
|
+
The `userType` slug must match a key in `config.auth.userType.<name>` — see the `customize-user-type` topic.
|
|
1288
1288
|
|
|
1289
1289
|
## What the middleware does
|
|
1290
1290
|
|
|
@@ -1407,7 +1407,7 @@ The middleware uses the framework's `response.unauthorized({...})` shape. To ove
|
|
|
1407
1407
|
|
|
1408
1408
|
That last one is the difference between a role check and an authorization model: without it, an account-creation endpoint gated on `authMiddleware("admin")` is a privilege-escalation path.
|
|
1409
1409
|
|
|
1410
|
-
Install
|
|
1410
|
+
Install `@warlock.js/access` (the `overview` topic of the `warlock-js-access` skill) for that. It layers RBAC plus per-permission ABAC policies over the same authenticated user:
|
|
1411
1411
|
|
|
1412
1412
|
```ts
|
|
1413
1413
|
import { router, type RequestHandler } from "@warlock.js/core";
|
|
@@ -1532,17 +1532,17 @@ This is an Origin check, not a double-submit CSRF token — cheaper, and closes
|
|
|
1532
1532
|
|
|
1533
1533
|
## See also
|
|
1534
1534
|
|
|
1535
|
-
-
|
|
1536
|
-
-
|
|
1537
|
-
-
|
|
1538
|
-
-
|
|
1535
|
+
- The `customize-user-type` topic — config and multi-user-type semantics
|
|
1536
|
+
- The `handle-login-and-logout` topic — where the access token gets issued in the first place
|
|
1537
|
+
- The `throttle-login-attempts` topic — brute-force throttle to stack in front of the login route
|
|
1538
|
+
- The `overview` topic of the `warlock-js-access` skill — permissions, roles, and who-may-act-on-whom policies, when user-type gating isn't enough
|
|
1539
1539
|
|
|
1540
1540
|
|
|
1541
1541
|
## register-user `@warlock.js/auth/register-user/SKILL.md`
|
|
1542
1542
|
|
|
1543
1543
|
---
|
|
1544
1544
|
name: register-user
|
|
1545
|
-
description: 'Sign up a new user and issue the initial token pair — User.create({...password: await hashPassword(plain)}) then authService.createTokenPair(user). Triggers: `User.create`, `hashPassword`, `verifyPassword`, `authService.createTokenPair`, `toJsonColumns`, `strongPassword`, `authEvents`; "build a register endpoint", "POST /register controller", "sign up a new user", "hash password on signup", "email verification flow"; typical import `import { authService } from "@warlock.js/auth"; import { hashPassword } from "@warlock.js/core"`. Skip: login —
|
|
1545
|
+
description: 'Sign up a new user and issue the initial token pair — User.create({...password: await hashPassword(plain)}) then authService.createTokenPair(user). Triggers: `User.create`, `hashPassword`, `verifyPassword`, `authService.createTokenPair`, `toJsonColumns`, `strongPassword`, `authEvents`; "build a register endpoint", "POST /register controller", "sign up a new user", "hash password on signup", "email verification flow"; typical import `import { authService } from "@warlock.js/auth"; import { hashPassword } from "@warlock.js/core"`. Skip: login — the `handle-login-and-logout` topic; token internals — the `manage-tokens` topic; competing libs `bcrypt`, `bcryptjs`, `argon2`.'
|
|
1546
1546
|
---
|
|
1547
1547
|
|
|
1548
1548
|
# Register-and-issue-tokens flow
|
|
@@ -1600,7 +1600,7 @@ export const registerController: RequestHandler = async ({ request, response })
|
|
|
1600
1600
|
};
|
|
1601
1601
|
```
|
|
1602
1602
|
|
|
1603
|
-
That's the whole flow. `User.create({...})` runs the schema validation (including `.email()`, `.min()`, etc. on each field), so you don't need a separate validation pass — see
|
|
1603
|
+
That's the whole flow. `User.create({...})` runs the schema validation (including `.email()`, `.min()`, etc. on each field), so you don't need a separate validation pass — see the `handle-seal-errors` topic of the `warlock-js-seal` skill for catching validation failures.
|
|
1604
1604
|
|
|
1605
1605
|
## Hash the password on the way in
|
|
1606
1606
|
|
|
@@ -1656,7 +1656,7 @@ const tokens = await authService.createTokenPair(user);
|
|
|
1656
1656
|
return response.successCreate({ user, tokens });
|
|
1657
1657
|
```
|
|
1658
1658
|
|
|
1659
|
-
Gate routes that need a confirmed address with `requireVerifiedEmail()` after `authMiddleware`. See
|
|
1659
|
+
Gate routes that need a confirmed address with `requireVerifiedEmail()` after `authMiddleware`. See the `verify-email-and-reset-password` topic.
|
|
1660
1660
|
|
|
1661
1661
|
## Side effects via auth events
|
|
1662
1662
|
|
|
@@ -1680,20 +1680,20 @@ Cleaner alternative: emit your own `user.registered` event from the controller a
|
|
|
1680
1680
|
- Don't pass the plain password to `User.create()`. `await hashPassword(plain)` first.
|
|
1681
1681
|
- Don't return the user without `toJsonColumns` / `resource` shaping — the password hash will leak otherwise.
|
|
1682
1682
|
- Don't issue tokens before validating the user shape. `User.create` runs validation; let it throw on bad input before tokens get created.
|
|
1683
|
-
- Don't run "send welcome email" inline in the controller. Push it to a queue or run it after-commit via the outbox pattern — see
|
|
1683
|
+
- Don't run "send welcome email" inline in the controller. Push it to a queue or run it after-commit via the outbox pattern — see the `manage-transactions` topic of the `warlock-js-cascade` skill.
|
|
1684
1684
|
|
|
1685
1685
|
## See also
|
|
1686
1686
|
|
|
1687
|
-
-
|
|
1688
|
-
-
|
|
1689
|
-
-
|
|
1687
|
+
- The `handle-login-and-logout` topic — login flow (same `createTokenPair` step)
|
|
1688
|
+
- The `manage-tokens` topic — token issuance internals
|
|
1689
|
+
- The `define-model` topic of the `warlock-js-cascade` skill — `toJsonColumns` / `resource` for public output
|
|
1690
1690
|
|
|
1691
1691
|
|
|
1692
1692
|
## run-auth-commands `@warlock.js/auth/run-auth-commands/SKILL.md`
|
|
1693
1693
|
|
|
1694
1694
|
---
|
|
1695
1695
|
name: run-auth-commands
|
|
1696
|
-
description: 'Three bundled CLI commands — warlock jwt.generate (creates strong JWT secret + writes to .env), warlock auth.cleanup (removes expired refresh tokens), and warlock auth.purge-never-expiring (finds and revokes token rows that can never expire). Register via registerJWTSecretGeneratorCommand(), registerAuthCleanupCommand() and registerAuthPurgeNeverExpiringCommand(). Triggers: `registerJWTSecretGeneratorCommand`, `registerAuthCleanupCommand`, `registerAuthPurgeNeverExpiringCommand`, `warlock jwt.generate`, `warlock auth.cleanup`, `warlock auth.purge-never-expiring`, `cleanupExpiredTokens`, `purgeNeverExpiringTokens`, `command`; "token never expires", "no exp claim", "Invalid Date expires_at"; "generate JWT secret", "bootstrap .env JWT_SECRET", "cron job for expired tokens", "schedule auth cleanup"; typical import `import { registerJWTSecretGeneratorCommand, registerAuthCleanupCommand, registerAuthPurgeNeverExpiringCommand } from "@warlock.js/auth"`. Skip: programmatic cleanup —
|
|
1696
|
+
description: 'Three bundled CLI commands — warlock jwt.generate (creates strong JWT secret + writes to .env), warlock auth.cleanup (removes expired refresh tokens), and warlock auth.purge-never-expiring (finds and revokes token rows that can never expire). Register via registerJWTSecretGeneratorCommand(), registerAuthCleanupCommand() and registerAuthPurgeNeverExpiringCommand(). Triggers: `registerJWTSecretGeneratorCommand`, `registerAuthCleanupCommand`, `registerAuthPurgeNeverExpiringCommand`, `warlock jwt.generate`, `warlock auth.cleanup`, `warlock auth.purge-never-expiring`, `cleanupExpiredTokens`, `purgeNeverExpiringTokens`, `command`; "token never expires", "no exp claim", "Invalid Date expires_at"; "generate JWT secret", "bootstrap .env JWT_SECRET", "cron job for expired tokens", "schedule auth cleanup"; typical import `import { registerJWTSecretGeneratorCommand, registerAuthCleanupCommand, registerAuthPurgeNeverExpiringCommand } from "@warlock.js/auth"`. Skip: programmatic cleanup — the `manage-tokens` topic; in-process scheduling — the `scheduler-basics` topic of the `warlock-js-scheduler` skill; competing tools `dotenv-cli`, `node-cron`.'
|
|
1697
1697
|
---
|
|
1698
1698
|
|
|
1699
1699
|
# Run auth commands
|
|
@@ -1759,7 +1759,7 @@ scheduler.addJob(
|
|
|
1759
1759
|
scheduler.start();
|
|
1760
1760
|
```
|
|
1761
1761
|
|
|
1762
|
-
In-process — no shell call. See
|
|
1762
|
+
In-process — no shell call. See the `scheduler-basics` topic of the `warlock-js-scheduler` skill.
|
|
1763
1763
|
|
|
1764
1764
|
### Via system cron
|
|
1765
1765
|
|
|
@@ -1848,15 +1848,15 @@ Register it the same way as the bundled commands — call the factory inside `de
|
|
|
1848
1848
|
|
|
1849
1849
|
## See also
|
|
1850
1850
|
|
|
1851
|
-
-
|
|
1852
|
-
-
|
|
1851
|
+
- The `manage-tokens` topic — `cleanupExpiredTokens` internals
|
|
1852
|
+
- The `scheduler-basics` topic of the `warlock-js-scheduler` skill — in-process scheduling
|
|
1853
1853
|
|
|
1854
1854
|
|
|
1855
1855
|
## throttle-login-attempts `@warlock.js/auth/throttle-login-attempts/SKILL.md`
|
|
1856
1856
|
|
|
1857
1857
|
---
|
|
1858
1858
|
name: throttle-login-attempts
|
|
1859
|
-
description: 'Brute-force / credential-stuffing protection via `loginThrottleMiddleware` — a failure-aware route gate that counts only failed logins (resets on success), locks per-account and per-source after a threshold, and rejects pre-controller with 429 so the DB lookup and bcrypt verify are skipped. Cache-backed (shared across replicas), fixed-window, fails open on a cache outage. Triggers: `loginThrottleMiddleware`, `AuthErrorCodes.TooManyAttempts`, `EC004`, "rate limit login", "brute force protection", "lock account after failed logins", "throttle login attempts", "too many login attempts 429"; typical import `import { loginThrottleMiddleware } from "@warlock.js/auth"`. Skip: generic per-route request rate limiting that counts every request (use core `middleware.rateLimit`); gating a route by auth —
|
|
1859
|
+
description: 'Brute-force / credential-stuffing protection via `loginThrottleMiddleware` — a failure-aware route gate that counts only failed logins (resets on success), locks per-account and per-source after a threshold, and rejects pre-controller with 429 so the DB lookup and bcrypt verify are skipped. Cache-backed (shared across replicas), fixed-window, fails open on a cache outage. Triggers: `loginThrottleMiddleware`, `AuthErrorCodes.TooManyAttempts`, `EC004`, "rate limit login", "brute force protection", "lock account after failed logins", "throttle login attempts", "too many login attempts 429"; typical import `import { loginThrottleMiddleware } from "@warlock.js/auth"`. Skip: generic per-route request rate limiting that counts every request (use core `middleware.rateLimit`); gating a route by auth — the `protect-routes` topic; issuing tokens — the `handle-login-and-logout` topic.'
|
|
1860
1860
|
---
|
|
1861
1861
|
|
|
1862
1862
|
# Throttle login attempts with `loginThrottleMiddleware`
|
|
@@ -1962,22 +1962,24 @@ 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
|
|
|
1969
1971
|
## See also
|
|
1970
1972
|
|
|
1971
|
-
-
|
|
1972
|
-
-
|
|
1973
|
-
-
|
|
1973
|
+
- The `protect-routes` topic — gate a route behind a valid token (`authMiddleware`); stack the throttle in front of it on login routes.
|
|
1974
|
+
- The `handle-login-and-logout` topic — the login flow whose failures this middleware counts.
|
|
1975
|
+
- The `use-middleware` topic of the `warlock-js-core` skill — `middleware.rateLimit` and the built-in middleware suite.
|
|
1974
1976
|
|
|
1975
1977
|
|
|
1976
1978
|
## verify-email-and-reset-password `@warlock.js/auth/verify-email-and-reset-password/SKILL.md`
|
|
1977
1979
|
|
|
1978
1980
|
---
|
|
1979
1981
|
name: verify-email-and-reset-password
|
|
1980
|
-
description: 'Email verification and password reset with hashed, single-use, expiring, purpose-bound tokens delivered through `@warlock.js/notifications` — `sendEmailVerification(user)`, `verifyEmail(token)`, `requestPasswordReset(Model, email)`, `resetPassword(token, newPassword)`, the `requireVerifiedEmail()` guard, and the `tokenIssueThrottleMiddleware` / `tokenConsumeThrottleMiddleware` presets. Reset revokes every access token, refresh token and cookie session; a reset request answers the same for unknown emails. Triggers: `sendEmailVerification`, `verifyEmail`, `requestPasswordReset`, `resetPassword`, `requireVerifiedEmail`, `isEmailVerified`, `OneTimeToken`, `InvalidOneTimeTokenError`, `EmailNotVerifiedError`, `NotificationsUnavailableError`, `EC007`, `EC008`, `auth.verification`, `auth.passwordReset`; "verify email", "confirm email address", "forgot password", "reset password link", "block unverified users", "resend verification email"; typical import `import { requestPasswordReset, resetPassword } from "@warlock.js/auth"`. Skip: sign-up itself —
|
|
1982
|
+
description: 'Email verification and password reset with hashed, single-use, expiring, purpose-bound tokens delivered through `@warlock.js/notifications` — `sendEmailVerification(user)`, `verifyEmail(token)`, `requestPasswordReset(Model, email)`, `resetPassword(token, newPassword)`, the `requireVerifiedEmail()` guard, and the `tokenIssueThrottleMiddleware` / `tokenConsumeThrottleMiddleware` presets. Reset revokes every access token, refresh token and cookie session; a reset request answers the same for unknown emails. Triggers: `sendEmailVerification`, `verifyEmail`, `requestPasswordReset`, `resetPassword`, `requireVerifiedEmail`, `isEmailVerified`, `OneTimeToken`, `InvalidOneTimeTokenError`, `EmailNotVerifiedError`, `NotificationsUnavailableError`, `EC007`, `EC008`, `auth.verification`, `auth.passwordReset`; "verify email", "confirm email address", "forgot password", "reset password link", "block unverified users", "resend verification email"; typical import `import { requestPasswordReset, resetPassword } from "@warlock.js/auth"`. Skip: sign-up itself — the `register-user` topic; login throttling — the `throttle-login-attempts` topic; defining notifications — the `define-notification` topic of the `warlock-js-notifications` skill.'
|
|
1981
1983
|
---
|
|
1982
1984
|
|
|
1983
1985
|
# Verify email + reset password
|
|
@@ -2109,8 +2111,8 @@ The default notifications are `defineNotification` objects (`type: "auth.email-v
|
|
|
2109
2111
|
|
|
2110
2112
|
## See also
|
|
2111
2113
|
|
|
2112
|
-
-
|
|
2113
|
-
-
|
|
2114
|
-
-
|
|
2114
|
+
- The `register-user` topic — where `sendEmailVerification` is usually called
|
|
2115
|
+
- The `throttle-login-attempts` topic — the underlying throttle
|
|
2116
|
+
- The `configure-notifications` topic of the `warlock-js-notifications` skill — mail channel setup
|
|
2115
2117
|
|
|
2116
2118
|
|
package/llms.txt
CHANGED
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
## Skills
|
|
8
8
|
|
|
9
9
|
- [auth-basics](@warlock.js/auth/auth-basics/SKILL.md): Start here for @warlock.js/auth configuration, user models, credentials, and application session flows.
|
|
10
|
-
- [customize-token-storage](@warlock.js/auth/customize-token-storage/SKILL.md): Override the persisted AccessToken / RefreshToken models to add columns (multi-tenant `organization_id`, device metadata), rename, or change storage — without forking the package. Extend the model + `schema.extend(...)`, register it under `config.auth.accessToken.model` / `config.auth.refreshToken.model`, override `issue()` to populate the new column, and add a migration. Triggers: `accessToken.model`, `refreshToken.model`, `AccessToken.issue`, `RefreshToken.issue`, `accessTokenSchema`, `refreshTokenSchema`, "add a column to the token table", "multi-tenant tokens", "organization_id on access token", "override the token model", "custom token storage"; typical import `import { AccessToken, accessTokenSchema } from "@warlock.js/auth"`. Skip: multiple user TYPES (not token storage) —
|
|
11
|
-
- [customize-user-type](@warlock.js/auth/customize-user-type/SKILL.md): Support multiple user types (user / admin / client / staff) in one auth system — each Auth subclass overrides userType, config.auth.userType.<slug> maps slug to model class, authMiddleware('admin') gates per type. Triggers: `Auth`, `userType`, `config.auth.userType`, `Authenticable`, `@RegisterModel`, `confirmPassword`; "add admins and users", "multiple user types", "separate client and vendor personas", "per-type login"; typical import `import { Auth } from "@warlock.js/auth"`. Skip: `authMiddleware` semantics —
|
|
12
|
-
- [handle-login-and-logout](@warlock.js/auth/handle-login-and-logout/SKILL.md): Run the full login flow via authService.login(Model, credentials, deviceInfo?) — verify password, create access + refresh token pair, fire events. Logout via authService.logout(user, accessToken?, refreshToken?) revokes tokens. Triggers: `authService.login`, `authService.logout`, `authService.attemptLogin`, `authService.refreshTokens`, `authService.revokeAllTokens`, `authEvents`; "build a login endpoint", "POST /login controller", "logout from all devices", "verify credentials and issue tokens"; typical import `import { authService, authEvents } from "@warlock.js/auth"`. Skip: token internals —
|
|
13
|
-
- [login-with-providers](@warlock.js/auth/login-with-providers/SKILL.md): Log in without a password — Google (OIDC code + PKCE + state + nonce, id_token verified with `jose`), GitHub, Discord, LinkedIn, Apple (`form_post` callback), Facebook and X, passkeys (WebAuthn via `@simplewebauthn/server`) and phone one-time codes (delivered through a `@warlock.js/notifications` channel). Every method ends in `authService.completeLogin(user)`, the same tokens, rows and events as password login. `startProviderLogin`, `completeProviderLogin`, `generatePasskeyRegistrationOptions`, `verifyPasskeyRegistration`, `generatePasskeyAuthenticationOptions`, `verifyPasskeyAuthentication`, `requestOtp`, `verifyOtp`, `otpRequestThrottleMiddleware`, `otpVerifyThrottleMiddleware`, `AuthProvider`. Triggers: "login with Google", "sign in with Google", "login with GitHub", "Sign in with Apple", "login with Discord", "login with LinkedIn", "login with Facebook", "login with X", `auth.providers.github`, "OAuth", "OIDC", "passkeys", "WebAuthn", "passwordless", "SMS code login", "WhatsApp OTP", "phone login", `warlock add auth-google`, `warlock add auth-passkeys`, `AuthProviderSdkMissingError`, `InvalidProviderCallbackError`, `ProviderEmailNotVerifiedError`, `InvalidPasskeyError`, `EC009`, `EC010`, `EC011`, `auth.providers`, `auth.passkeys`, `auth.otp`; typical import `import { completeProviderLogin, verifyOtp } from "@warlock.js/auth"`. Skip: password login —
|
|
14
|
-
- [manage-tokens](@warlock.js/auth/manage-tokens/SKILL.md): Token lifecycle — generateAccessToken, createRefreshToken, createTokenPair, refreshTokens (with rotation + replay detection), revokeAllTokens, revokeTokenFamily, cleanupExpiredTokens, getActiveSessions. Triggers: `createTokenPair`, `refreshTokens`, `revokeTokenFamily`, `cleanupExpiredTokens`, `getActiveSessions`, `jwt.generate`, `jwt.verify`, `AccessToken`, `RefreshToken`; "rotate refresh tokens", "detect token replay", "logout from all devices", "list active sessions", "clean up expired tokens"; typical import `import { authService, jwt } from "@warlock.js/auth"`. Skip: login flow —
|
|
10
|
+
- [customize-token-storage](@warlock.js/auth/customize-token-storage/SKILL.md): Override the persisted AccessToken / RefreshToken models to add columns (multi-tenant `organization_id`, device metadata), rename, or change storage — without forking the package. Extend the model + `schema.extend(...)`, register it under `config.auth.accessToken.model` / `config.auth.refreshToken.model`, override `issue()` to populate the new column, and add a migration. Triggers: `accessToken.model`, `refreshToken.model`, `AccessToken.issue`, `RefreshToken.issue`, `accessTokenSchema`, `refreshTokenSchema`, "add a column to the token table", "multi-tenant tokens", "organization_id on access token", "override the token model", "custom token storage"; typical import `import { AccessToken, accessTokenSchema } from "@warlock.js/auth"`. Skip: multiple user TYPES (not token storage) — the `customize-user-type` topic; the token lifecycle API — the `manage-tokens` topic; the config blocks themselves — the `auth-basics` topic.
|
|
11
|
+
- [customize-user-type](@warlock.js/auth/customize-user-type/SKILL.md): Support multiple user types (user / admin / client / staff) in one auth system — each Auth subclass overrides userType, config.auth.userType.<slug> maps slug to model class, authMiddleware('admin') gates per type. Triggers: `Auth`, `userType`, `config.auth.userType`, `Authenticable`, `@RegisterModel`, `confirmPassword`; "add admins and users", "multiple user types", "separate client and vendor personas", "per-type login"; typical import `import { Auth } from "@warlock.js/auth"`. Skip: `authMiddleware` semantics — the `protect-routes` topic; login flow — the `handle-login-and-logout` topic; RBAC libs `casl`, `accesscontrol`, `rbac`.
|
|
12
|
+
- [handle-login-and-logout](@warlock.js/auth/handle-login-and-logout/SKILL.md): Run the full login flow via authService.login(Model, credentials, deviceInfo?) — verify password, create access + refresh token pair, fire events. Logout via authService.logout(user, accessToken?, refreshToken?) revokes tokens. Triggers: `authService.login`, `authService.logout`, `authService.attemptLogin`, `authService.refreshTokens`, `authService.revokeAllTokens`, `authEvents`; "build a login endpoint", "POST /login controller", "logout from all devices", "verify credentials and issue tokens"; typical import `import { authService, authEvents } from "@warlock.js/auth"`. Skip: token internals — the `manage-tokens` topic; sign-up — the `register-user` topic; competing libs `passport-local`, `next-auth` credentials.
|
|
13
|
+
- [login-with-providers](@warlock.js/auth/login-with-providers/SKILL.md): Log in without a password — Google (OIDC code + PKCE + state + nonce, id_token verified with `jose`), GitHub, Discord, LinkedIn, Apple (`form_post` callback), Facebook and X, passkeys (WebAuthn via `@simplewebauthn/server`) and phone one-time codes (delivered through a `@warlock.js/notifications` channel). Every method ends in `authService.completeLogin(user)`, the same tokens, rows and events as password login. `startProviderLogin`, `completeProviderLogin`, `generatePasskeyRegistrationOptions`, `verifyPasskeyRegistration`, `generatePasskeyAuthenticationOptions`, `verifyPasskeyAuthentication`, `requestOtp`, `verifyOtp`, `otpRequestThrottleMiddleware`, `otpVerifyThrottleMiddleware`, `AuthProvider`. Triggers: "login with Google", "sign in with Google", "login with GitHub", "Sign in with Apple", "login with Discord", "login with LinkedIn", "login with Facebook", "login with X", `auth.providers.github`, "OAuth", "OIDC", "passkeys", "WebAuthn", "passwordless", "SMS code login", "WhatsApp OTP", "phone login", `warlock add auth-google`, `warlock add auth-passkeys`, `AuthProviderSdkMissingError`, `InvalidProviderCallbackError`, `ProviderEmailNotVerifiedError`, `InvalidPasskeyError`, `EC009`, `EC010`, `EC011`, `auth.providers`, `auth.passkeys`, `auth.otp`; typical import `import { completeProviderLogin, verifyOtp } from "@warlock.js/auth"`. Skip: password login — the `handle-login-and-logout` topic; email tokens — the `verify-email-and-reset-password` topic; defining an SMS channel — the `define-channel` topic of the `warlock-js-notifications` skill.
|
|
14
|
+
- [manage-tokens](@warlock.js/auth/manage-tokens/SKILL.md): Token lifecycle — generateAccessToken, createRefreshToken, createTokenPair, refreshTokens (with rotation + replay detection), revokeAllTokens, revokeTokenFamily, cleanupExpiredTokens, getActiveSessions. Triggers: `createTokenPair`, `refreshTokens`, `revokeTokenFamily`, `cleanupExpiredTokens`, `getActiveSessions`, `jwt.generate`, `jwt.verify`, `AccessToken`, `RefreshToken`; "rotate refresh tokens", "detect token replay", "logout from all devices", "list active sessions", "clean up expired tokens"; typical import `import { authService, jwt } from "@warlock.js/auth"`. Skip: login flow — the `handle-login-and-logout` topic; CLI cleanup — the `run-auth-commands` topic; competing libs `jsonwebtoken`, `jose`, `fast-jwt`.
|
|
15
15
|
- [overview](@warlock.js/auth/overview/SKILL.md): @warlock.js/auth overview for orientation and choosing the focused skill for the task.
|
|
16
|
-
- [protect-routes](@warlock.js/auth/protect-routes/SKILL.md): Gate HTTP routes via authMiddleware(allowedUserType) — the argument is required and a valid token is always required: [] allows any authenticated user, a user-type restricts to those types. Sets request.locals.user + request.decodedAccessToken on success, 401 on failure. Triggers: `authMiddleware`, `request.locals.user`, `request.decodedAccessToken`, `AuthErrorCodes`, `MissingAccessToken`, `InvalidAccessToken`; "how do I protect a route", "restrict route by user type", "require any logged-in user"; typical import `import { authMiddleware } from "@warlock.js/auth"`. Skip: multi-user-type config —
|
|
17
|
-
- [register-user](@warlock.js/auth/register-user/SKILL.md): Sign up a new user and issue the initial token pair — User.create({...password: await hashPassword(plain)}) then authService.createTokenPair(user). Triggers: `User.create`, `hashPassword`, `verifyPassword`, `authService.createTokenPair`, `toJsonColumns`, `strongPassword`, `authEvents`; "build a register endpoint", "POST /register controller", "sign up a new user", "hash password on signup", "email verification flow"; typical import `import { authService } from "@warlock.js/auth"; import { hashPassword } from "@warlock.js/core"`. Skip: login —
|
|
18
|
-
- [run-auth-commands](@warlock.js/auth/run-auth-commands/SKILL.md): Three bundled CLI commands — warlock jwt.generate (creates strong JWT secret + writes to .env), warlock auth.cleanup (removes expired refresh tokens), and warlock auth.purge-never-expiring (finds and revokes token rows that can never expire). Register via registerJWTSecretGeneratorCommand(), registerAuthCleanupCommand() and registerAuthPurgeNeverExpiringCommand(). Triggers: `registerJWTSecretGeneratorCommand`, `registerAuthCleanupCommand`, `registerAuthPurgeNeverExpiringCommand`, `warlock jwt.generate`, `warlock auth.cleanup`, `warlock auth.purge-never-expiring`, `cleanupExpiredTokens`, `purgeNeverExpiringTokens`, `command`; "token never expires", "no exp claim", "Invalid Date expires_at"; "generate JWT secret", "bootstrap .env JWT_SECRET", "cron job for expired tokens", "schedule auth cleanup"; typical import `import { registerJWTSecretGeneratorCommand, registerAuthCleanupCommand, registerAuthPurgeNeverExpiringCommand } from "@warlock.js/auth"`. Skip: programmatic cleanup —
|
|
19
|
-
- [throttle-login-attempts](@warlock.js/auth/throttle-login-attempts/SKILL.md): Brute-force / credential-stuffing protection via `loginThrottleMiddleware` — a failure-aware route gate that counts only failed logins (resets on success), locks per-account and per-source after a threshold, and rejects pre-controller with 429 so the DB lookup and bcrypt verify are skipped. Cache-backed (shared across replicas), fixed-window, fails open on a cache outage. Triggers: `loginThrottleMiddleware`, `AuthErrorCodes.TooManyAttempts`, `EC004`, "rate limit login", "brute force protection", "lock account after failed logins", "throttle login attempts", "too many login attempts 429"; typical import `import { loginThrottleMiddleware } from "@warlock.js/auth"`. Skip: generic per-route request rate limiting that counts every request (use core `middleware.rateLimit`); gating a route by auth —
|
|
20
|
-
- [verify-email-and-reset-password](@warlock.js/auth/verify-email-and-reset-password/SKILL.md): Email verification and password reset with hashed, single-use, expiring, purpose-bound tokens delivered through `@warlock.js/notifications` — `sendEmailVerification(user)`, `verifyEmail(token)`, `requestPasswordReset(Model, email)`, `resetPassword(token, newPassword)`, the `requireVerifiedEmail()` guard, and the `tokenIssueThrottleMiddleware` / `tokenConsumeThrottleMiddleware` presets. Reset revokes every access token, refresh token and cookie session; a reset request answers the same for unknown emails. Triggers: `sendEmailVerification`, `verifyEmail`, `requestPasswordReset`, `resetPassword`, `requireVerifiedEmail`, `isEmailVerified`, `OneTimeToken`, `InvalidOneTimeTokenError`, `EmailNotVerifiedError`, `NotificationsUnavailableError`, `EC007`, `EC008`, `auth.verification`, `auth.passwordReset`; "verify email", "confirm email address", "forgot password", "reset password link", "block unverified users", "resend verification email"; typical import `import { requestPasswordReset, resetPassword } from "@warlock.js/auth"`. Skip: sign-up itself —
|
|
16
|
+
- [protect-routes](@warlock.js/auth/protect-routes/SKILL.md): Gate HTTP routes via authMiddleware(allowedUserType) — the argument is required and a valid token is always required: [] allows any authenticated user, a user-type restricts to those types. Sets request.locals.user + request.decodedAccessToken on success, 401 on failure. Triggers: `authMiddleware`, `request.locals.user`, `request.decodedAccessToken`, `AuthErrorCodes`, `MissingAccessToken`, `InvalidAccessToken`; "how do I protect a route", "restrict route by user type", "require any logged-in user"; typical import `import { authMiddleware } from "@warlock.js/auth"`. Skip: multi-user-type config — the `customize-user-type` topic; issuing the token — the `handle-login-and-logout` topic; competing libs `passport`, `express-jwt`, `next-auth` middleware.
|
|
17
|
+
- [register-user](@warlock.js/auth/register-user/SKILL.md): Sign up a new user and issue the initial token pair — User.create({...password: await hashPassword(plain)}) then authService.createTokenPair(user). Triggers: `User.create`, `hashPassword`, `verifyPassword`, `authService.createTokenPair`, `toJsonColumns`, `strongPassword`, `authEvents`; "build a register endpoint", "POST /register controller", "sign up a new user", "hash password on signup", "email verification flow"; typical import `import { authService } from "@warlock.js/auth"; import { hashPassword } from "@warlock.js/core"`. Skip: login — the `handle-login-and-logout` topic; token internals — the `manage-tokens` topic; competing libs `bcrypt`, `bcryptjs`, `argon2`.
|
|
18
|
+
- [run-auth-commands](@warlock.js/auth/run-auth-commands/SKILL.md): Three bundled CLI commands — warlock jwt.generate (creates strong JWT secret + writes to .env), warlock auth.cleanup (removes expired refresh tokens), and warlock auth.purge-never-expiring (finds and revokes token rows that can never expire). Register via registerJWTSecretGeneratorCommand(), registerAuthCleanupCommand() and registerAuthPurgeNeverExpiringCommand(). Triggers: `registerJWTSecretGeneratorCommand`, `registerAuthCleanupCommand`, `registerAuthPurgeNeverExpiringCommand`, `warlock jwt.generate`, `warlock auth.cleanup`, `warlock auth.purge-never-expiring`, `cleanupExpiredTokens`, `purgeNeverExpiringTokens`, `command`; "token never expires", "no exp claim", "Invalid Date expires_at"; "generate JWT secret", "bootstrap .env JWT_SECRET", "cron job for expired tokens", "schedule auth cleanup"; typical import `import { registerJWTSecretGeneratorCommand, registerAuthCleanupCommand, registerAuthPurgeNeverExpiringCommand } from "@warlock.js/auth"`. Skip: programmatic cleanup — the `manage-tokens` topic; in-process scheduling — the `scheduler-basics` topic of the `warlock-js-scheduler` skill; competing tools `dotenv-cli`, `node-cron`.
|
|
19
|
+
- [throttle-login-attempts](@warlock.js/auth/throttle-login-attempts/SKILL.md): Brute-force / credential-stuffing protection via `loginThrottleMiddleware` — a failure-aware route gate that counts only failed logins (resets on success), locks per-account and per-source after a threshold, and rejects pre-controller with 429 so the DB lookup and bcrypt verify are skipped. Cache-backed (shared across replicas), fixed-window, fails open on a cache outage. Triggers: `loginThrottleMiddleware`, `AuthErrorCodes.TooManyAttempts`, `EC004`, "rate limit login", "brute force protection", "lock account after failed logins", "throttle login attempts", "too many login attempts 429"; typical import `import { loginThrottleMiddleware } from "@warlock.js/auth"`. Skip: generic per-route request rate limiting that counts every request (use core `middleware.rateLimit`); gating a route by auth — the `protect-routes` topic; issuing tokens — the `handle-login-and-logout` topic.
|
|
20
|
+
- [verify-email-and-reset-password](@warlock.js/auth/verify-email-and-reset-password/SKILL.md): Email verification and password reset with hashed, single-use, expiring, purpose-bound tokens delivered through `@warlock.js/notifications` — `sendEmailVerification(user)`, `verifyEmail(token)`, `requestPasswordReset(Model, email)`, `resetPassword(token, newPassword)`, the `requireVerifiedEmail()` guard, and the `tokenIssueThrottleMiddleware` / `tokenConsumeThrottleMiddleware` presets. Reset revokes every access token, refresh token and cookie session; a reset request answers the same for unknown emails. Triggers: `sendEmailVerification`, `verifyEmail`, `requestPasswordReset`, `resetPassword`, `requireVerifiedEmail`, `isEmailVerified`, `OneTimeToken`, `InvalidOneTimeTokenError`, `EmailNotVerifiedError`, `NotificationsUnavailableError`, `EC007`, `EC008`, `auth.verification`, `auth.passwordReset`; "verify email", "confirm email address", "forgot password", "reset password link", "block unverified users", "resend verification email"; typical import `import { requestPasswordReset, resetPassword } from "@warlock.js/auth"`. Skip: sign-up itself — the `register-user` topic; login throttling — the `throttle-login-attempts` topic; defining notifications — the `define-notification` topic of the `warlock-js-notifications` skill.
|
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",
|