@warlock.js/auth 5.28.0 → 5.29.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 +6 -0
- package/llms-full.txt +73 -73
- package/llms.txt +10 -10
- package/package.json +8 -8
- 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 +4 -4
- package/skills/verify-email-and-reset-password/SKILL.md +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to `@warlock.js/auth` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
|
|
6
6
|
|
|
7
|
+
## 5.29.0 - 2026-10-01
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Lockstep release; package APIs are unchanged. Skills refer to other topics by name instead of file paths, so cross-references keep working after `agent-kit` copies them into an app.
|
|
12
|
+
|
|
7
13
|
## 5.28.0 - 2026-10-01
|
|
8
14
|
|
|
9
15
|
### Changed
|
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`
|
|
@@ -1968,16 +1968,16 @@ They compose. For a hard ceiling on request volume *and* failure-aware account l
|
|
|
1968
1968
|
|
|
1969
1969
|
## See also
|
|
1970
1970
|
|
|
1971
|
-
-
|
|
1972
|
-
-
|
|
1973
|
-
-
|
|
1971
|
+
- The `protect-routes` topic — gate a route behind a valid token (`authMiddleware`); stack the throttle in front of it on login routes.
|
|
1972
|
+
- The `handle-login-and-logout` topic — the login flow whose failures this middleware counts.
|
|
1973
|
+
- The `use-middleware` topic of the `warlock-js-core` skill — `middleware.rateLimit` and the built-in middleware suite.
|
|
1974
1974
|
|
|
1975
1975
|
|
|
1976
1976
|
## verify-email-and-reset-password `@warlock.js/auth/verify-email-and-reset-password/SKILL.md`
|
|
1977
1977
|
|
|
1978
1978
|
---
|
|
1979
1979
|
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 —
|
|
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 — the `register-user` topic; login throttling — the `throttle-login-attempts` topic; defining notifications — the `define-notification` topic of the `warlock-js-notifications` skill.'
|
|
1981
1981
|
---
|
|
1982
1982
|
|
|
1983
1983
|
# Verify email + reset password
|
|
@@ -2109,8 +2109,8 @@ The default notifications are `defineNotification` objects (`type: "auth.email-v
|
|
|
2109
2109
|
|
|
2110
2110
|
## See also
|
|
2111
2111
|
|
|
2112
|
-
-
|
|
2113
|
-
-
|
|
2114
|
-
-
|
|
2112
|
+
- The `register-user` topic — where `sendEmailVerification` is usually called
|
|
2113
|
+
- The `throttle-login-attempts` topic — the underlying throttle
|
|
2114
|
+
- The `configure-notifications` topic of the `warlock-js-notifications` skill — mail channel setup
|
|
2115
2115
|
|
|
2116
2116
|
|
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.29.0",
|
|
16
|
+
"@warlock.js/cache": "5.29.0",
|
|
17
|
+
"@warlock.js/cascade": "5.29.0",
|
|
18
|
+
"@warlock.js/core": "5.29.0",
|
|
19
|
+
"@warlock.js/logger": "5.29.0",
|
|
20
|
+
"@warlock.js/seal": "5.29.0",
|
|
21
|
+
"@warlock.js/notifications": "5.29.0",
|
|
22
22
|
"@simplewebauthn/server": "^13.1.0",
|
|
23
23
|
"jose": "^6.1.0"
|
|
24
24
|
},
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
],
|
|
48
48
|
"author": "hassanzohdy",
|
|
49
49
|
"license": "MIT",
|
|
50
|
-
"version": "5.
|
|
50
|
+
"version": "5.29.0",
|
|
51
51
|
"type": "module",
|
|
52
52
|
"main": "./esm/index.mjs",
|
|
53
53
|
"module": "./esm/index.mjs",
|
|
@@ -17,13 +17,13 @@ npm install @warlock.js/auth
|
|
|
17
17
|
|
|
18
18
|
## Foundations
|
|
19
19
|
|
|
20
|
-
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
|
|
20
|
+
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).
|
|
21
21
|
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.
|
|
22
22
|
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.
|
|
23
23
|
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.
|
|
24
24
|
5. **`authService.login(Model, credentials, deviceInfo?)` is the full happy path.** Verifies credentials, creates token pair (access + refresh), emits events, returns `{ user, tokens }`.
|
|
25
25
|
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.
|
|
26
|
-
7. **JWT secret lives in the env.** Generate with `warlock jwt.generate` (see
|
|
26
|
+
7. **JWT secret lives in the env.** Generate with `warlock jwt.generate` (see the `run-auth-commands` topic).
|
|
27
27
|
|
|
28
28
|
## Minimal wire-up
|
|
29
29
|
|
|
@@ -105,16 +105,16 @@ Two values that look right and are not:
|
|
|
105
105
|
|
|
106
106
|
| If the task is about… | Load |
|
|
107
107
|
| --- | --- |
|
|
108
|
-
| Gating routes with `authMiddleware(allowedUserType)`, any-authenticated vs typed access |
|
|
109
|
-
| `authService.login(...)`, `attemptLogin`, full credentials-to-tokens flow + logout |
|
|
110
|
-
| Token lifecycle — `generateAccessToken`, `createRefreshToken`, rotation, family revocation, max-per-user |
|
|
111
|
-
| Register a new user + issue tokens in one flow |
|
|
112
|
-
| Multi-user-type apps (`user`, `admin`, `client`), `config.auth.userType.<name>` mapping |
|
|
113
|
-
| `warlock jwt.generate` + `warlock auth.cleanup` CLI commands |
|
|
108
|
+
| Gating routes with `authMiddleware(allowedUserType)`, any-authenticated vs typed access | The `protect-routes` topic |
|
|
109
|
+
| `authService.login(...)`, `attemptLogin`, full credentials-to-tokens flow + logout | The `handle-login-and-logout` topic |
|
|
110
|
+
| Token lifecycle — `generateAccessToken`, `createRefreshToken`, rotation, family revocation, max-per-user | The `manage-tokens` topic |
|
|
111
|
+
| Register a new user + issue tokens in one flow | The `register-user` topic |
|
|
112
|
+
| Multi-user-type apps (`user`, `admin`, `client`), `config.auth.userType.<name>` mapping | The `customize-user-type` topic |
|
|
113
|
+
| `warlock jwt.generate` + `warlock auth.cleanup` CLI commands | The `run-auth-commands` topic |
|
|
114
114
|
|
|
115
115
|
## Things NOT to do
|
|
116
116
|
|
|
117
117
|
- Don't write your own JWT signing logic — use `authService` / `jwt` from this package so signature/secret/expiry stay consistent.
|
|
118
118
|
- Don't store the JWT secret in the model layer or anywhere user-modifiable. It lives in `.env` only.
|
|
119
|
-
- Don't return the raw `User` from a login endpoint without shaping output. Configure `static toJsonColumns` or `static resource` (see
|
|
120
|
-
- Don't run `auth.cleanup` from app boot. Schedule it (cron, scheduler) as a periodic task — see
|
|
119
|
+
- 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).
|
|
120
|
+
- 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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: customize-token-storage
|
|
3
|
-
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) —
|
|
3
|
+
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.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Customize token storage
|
|
@@ -110,7 +110,7 @@ The new column needs a real database column. Add it to your token-table migratio
|
|
|
110
110
|
this.uuid("organization_id").references("organizations").onDelete("cascade").index();
|
|
111
111
|
```
|
|
112
112
|
|
|
113
|
-
See
|
|
113
|
+
See the `write-migration` topic of the `warlock-js-cascade` skill for the migration mechanics.
|
|
114
114
|
|
|
115
115
|
## Why all three — the strict-mode trap
|
|
116
116
|
|
|
@@ -155,6 +155,6 @@ If you rename a column, override the statics **and these getters** so they map t
|
|
|
155
155
|
|
|
156
156
|
## See also
|
|
157
157
|
|
|
158
|
-
-
|
|
159
|
-
-
|
|
160
|
-
-
|
|
158
|
+
- The `customize-user-type` topic — multiple user *types* (a different axis from token *storage*).
|
|
159
|
+
- The `manage-tokens` topic — the token lifecycle your statics power.
|
|
160
|
+
- The `define-model` topic of the `warlock-js-cascade` skill — extending models and schemas.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: customize-user-type
|
|
3
|
-
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 —
|
|
3
|
+
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`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Customize user type (multi-user-type auth)
|
|
@@ -103,7 +103,7 @@ router.get("/back-office", backOfficeController, { middleware: [authMiddleware([
|
|
|
103
103
|
router.get("/dashboard", dashboardController, { middleware: [authMiddleware([])] }); // any logged-in
|
|
104
104
|
```
|
|
105
105
|
|
|
106
|
-
See
|
|
106
|
+
See the `protect-routes` topic.
|
|
107
107
|
|
|
108
108
|
## Login per user type — pass the right Model
|
|
109
109
|
|
|
@@ -163,6 +163,6 @@ abstract class Auth<TSchema> extends Model<TSchema> implements Authenticable {
|
|
|
163
163
|
|
|
164
164
|
## See also
|
|
165
165
|
|
|
166
|
-
-
|
|
167
|
-
-
|
|
168
|
-
-
|
|
166
|
+
- The `protect-routes` topic — `authMiddleware` semantics
|
|
167
|
+
- The `handle-login-and-logout` topic — passing the right Model to `login`
|
|
168
|
+
- The `define-model` topic of the `warlock-js-cascade` skill — `@RegisterModel`, models in general
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: handle-login-and-logout
|
|
3
|
-
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 —
|
|
3
|
+
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.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Login + logout
|
|
@@ -152,7 +152,7 @@ export const refreshController: RequestHandler = async ({ request, response }) =
|
|
|
152
152
|
};
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
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
|
|
155
|
+
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.
|
|
156
156
|
|
|
157
157
|
## Auth events
|
|
158
158
|
|
|
@@ -189,9 +189,9 @@ Full event list: `login.attempt`, `login.success`, `login.failed`, `logout`, `lo
|
|
|
189
189
|
|
|
190
190
|
## See also
|
|
191
191
|
|
|
192
|
-
-
|
|
193
|
-
-
|
|
194
|
-
-
|
|
192
|
+
- The `manage-tokens` topic — token lifecycle, rotation, family revocation
|
|
193
|
+
- The `register-user` topic — sign-up that issues tokens after creation
|
|
194
|
+
- The `protect-routes` topic — where the access token gets consumed
|
|
195
195
|
|
|
196
196
|
## Browser sessions for Web pages
|
|
197
197
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: login-with-providers
|
|
3
|
-
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 —
|
|
3
|
+
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.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Login with an OAuth provider, passkeys, or a phone code
|
|
@@ -278,6 +278,6 @@ router.post("/auth/otp/verify", async ({ request, response }) => {
|
|
|
278
278
|
|
|
279
279
|
## See also
|
|
280
280
|
|
|
281
|
-
-
|
|
282
|
-
-
|
|
283
|
-
-
|
|
281
|
+
- The `handle-login-and-logout` topic: password login and `setAuthCookie`
|
|
282
|
+
- The `throttle-login-attempts` topic: the throttle the OTP presets wrap
|
|
283
|
+
- The `define-channel` topic of the `warlock-js-notifications` skill: an SMS/WhatsApp channel
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: manage-tokens
|
|
3
|
-
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 —
|
|
3
|
+
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`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Manage tokens
|
|
@@ -155,7 +155,7 @@ scheduler.addJob(
|
|
|
155
155
|
);
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
-
Or use the bundled CLI command — see
|
|
158
|
+
Or use the bundled CLI command — see the `run-auth-commands` topic.
|
|
159
159
|
|
|
160
160
|
## JWT helpers
|
|
161
161
|
|
|
@@ -175,7 +175,7 @@ const decodedRefresh = await jwt.verifyRefreshToken(refreshToken);
|
|
|
175
175
|
|
|
176
176
|
**`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.
|
|
177
177
|
|
|
178
|
-
If tokens minted before 4.12.0 may lack an `exp`, see `warlock auth.purge-never-expiring` in
|
|
178
|
+
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.
|
|
179
179
|
|
|
180
180
|
`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.
|
|
181
181
|
|
|
@@ -191,6 +191,6 @@ The package signs access and refresh tokens with independent secrets — `config
|
|
|
191
191
|
|
|
192
192
|
## See also
|
|
193
193
|
|
|
194
|
-
-
|
|
195
|
-
-
|
|
196
|
-
-
|
|
194
|
+
- The `handle-login-and-logout` topic — full login/logout flow that uses these primitives
|
|
195
|
+
- The `run-auth-commands` topic — the bundled cleanup command
|
|
196
|
+
- The `scheduler-basics` topic of the `warlock-js-scheduler` skill — scheduling cleanup
|
package/skills/overview/SKILL.md
CHANGED
|
@@ -29,35 +29,35 @@ Nine task skills. Most apps need `auth-basics` + `protect-routes` + `handle-logi
|
|
|
29
29
|
|
|
30
30
|
### Foundations
|
|
31
31
|
|
|
32
|
-
####
|
|
32
|
+
#### `auth-basics`
|
|
33
33
|
Start here. The `Auth` base model, `authMiddleware` gate, `authService` (login/logout/refresh), AccessToken + RefreshToken persistence, multi-user-type support.
|
|
34
34
|
|
|
35
35
|
### The flows
|
|
36
36
|
|
|
37
|
-
####
|
|
37
|
+
#### `handle-login-and-logout`
|
|
38
38
|
`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.
|
|
39
39
|
|
|
40
|
-
####
|
|
40
|
+
#### `register-user`
|
|
41
41
|
Sign up a new user and issue the first token pair — `User.create({ ...password: await hashPassword(plain) })` then `authService.createTokenPair(user)`. For `POST /register`.
|
|
42
42
|
|
|
43
|
-
####
|
|
43
|
+
#### `protect-routes`
|
|
44
44
|
`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.
|
|
45
45
|
|
|
46
46
|
### Going deeper
|
|
47
47
|
|
|
48
|
-
####
|
|
48
|
+
#### `manage-tokens`
|
|
49
49
|
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.
|
|
50
50
|
|
|
51
|
-
####
|
|
51
|
+
#### `customize-user-type`
|
|
52
52
|
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.
|
|
53
53
|
|
|
54
|
-
####
|
|
54
|
+
#### `customize-token-storage`
|
|
55
55
|
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`.
|
|
56
56
|
|
|
57
|
-
####
|
|
57
|
+
#### `throttle-login-attempts`
|
|
58
58
|
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.
|
|
59
59
|
|
|
60
|
-
####
|
|
60
|
+
#### `run-auth-commands`
|
|
61
61
|
Two CLI commands — `warlock jwt.generate` (strong JWT secret → `.env`) and `warlock auth.cleanup` (remove expired refresh tokens). Register via `registerJWTSecretGeneratorCommand()` and `registerAuthCleanupCommand()`.
|
|
62
62
|
|
|
63
63
|
## What this package deliberately doesn't do
|
|
@@ -69,6 +69,6 @@ Two CLI commands — `warlock jwt.generate` (strong JWT secret → `.env`) and `
|
|
|
69
69
|
|
|
70
70
|
## See also
|
|
71
71
|
|
|
72
|
-
-
|
|
73
|
-
-
|
|
74
|
-
- `mongez-agent-kit-authoring-skills` (load via agent-kit sync) — how this `overview
|
|
72
|
+
- The `warlock-conventions` topic of the `warlock-js-core` skill — the framework auth runs inside (routing, middleware, config).
|
|
73
|
+
- The `cascade-basics` topic of the `warlock-js-cascade` skill — the ORM behind the `Auth`, `AccessToken`, and `RefreshToken` models.
|
|
74
|
+
- `mongez-agent-kit-authoring-skills` (load via agent-kit sync) — how this `overview` topic becomes `.claude/skills/warlock-js-auth/overview.md`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: protect-routes
|
|
3
|
-
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 —
|
|
3
|
+
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.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Gate routes with `authMiddleware`
|
|
@@ -49,7 +49,7 @@ router.get("/staff", staffController, {
|
|
|
49
49
|
});
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
The `userType` slug must match a key in `config.auth.userType.<name>` — see
|
|
52
|
+
The `userType` slug must match a key in `config.auth.userType.<name>` — see the `customize-user-type` topic.
|
|
53
53
|
|
|
54
54
|
## What the middleware does
|
|
55
55
|
|
|
@@ -172,7 +172,7 @@ The middleware uses the framework's `response.unauthorized({...})` shape. To ove
|
|
|
172
172
|
|
|
173
173
|
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.
|
|
174
174
|
|
|
175
|
-
Install
|
|
175
|
+
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:
|
|
176
176
|
|
|
177
177
|
```ts
|
|
178
178
|
import { router, type RequestHandler } from "@warlock.js/core";
|
|
@@ -297,7 +297,7 @@ This is an Origin check, not a double-submit CSRF token — cheaper, and closes
|
|
|
297
297
|
|
|
298
298
|
## See also
|
|
299
299
|
|
|
300
|
-
-
|
|
301
|
-
-
|
|
302
|
-
-
|
|
303
|
-
-
|
|
300
|
+
- The `customize-user-type` topic — config and multi-user-type semantics
|
|
301
|
+
- The `handle-login-and-logout` topic — where the access token gets issued in the first place
|
|
302
|
+
- The `throttle-login-attempts` topic — brute-force throttle to stack in front of the login route
|
|
303
|
+
- 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
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: register-user
|
|
3
|
-
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 —
|
|
3
|
+
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`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Register-and-issue-tokens flow
|
|
@@ -58,7 +58,7 @@ export const registerController: RequestHandler = async ({ request, response })
|
|
|
58
58
|
};
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
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
|
|
61
|
+
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.
|
|
62
62
|
|
|
63
63
|
## Hash the password on the way in
|
|
64
64
|
|
|
@@ -114,7 +114,7 @@ const tokens = await authService.createTokenPair(user);
|
|
|
114
114
|
return response.successCreate({ user, tokens });
|
|
115
115
|
```
|
|
116
116
|
|
|
117
|
-
Gate routes that need a confirmed address with `requireVerifiedEmail()` after `authMiddleware`. See
|
|
117
|
+
Gate routes that need a confirmed address with `requireVerifiedEmail()` after `authMiddleware`. See the `verify-email-and-reset-password` topic.
|
|
118
118
|
|
|
119
119
|
## Side effects via auth events
|
|
120
120
|
|
|
@@ -138,10 +138,10 @@ Cleaner alternative: emit your own `user.registered` event from the controller a
|
|
|
138
138
|
- Don't pass the plain password to `User.create()`. `await hashPassword(plain)` first.
|
|
139
139
|
- Don't return the user without `toJsonColumns` / `resource` shaping — the password hash will leak otherwise.
|
|
140
140
|
- Don't issue tokens before validating the user shape. `User.create` runs validation; let it throw on bad input before tokens get created.
|
|
141
|
-
- 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
|
|
141
|
+
- 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.
|
|
142
142
|
|
|
143
143
|
## See also
|
|
144
144
|
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
-
|
|
145
|
+
- The `handle-login-and-logout` topic — login flow (same `createTokenPair` step)
|
|
146
|
+
- The `manage-tokens` topic — token issuance internals
|
|
147
|
+
- The `define-model` topic of the `warlock-js-cascade` skill — `toJsonColumns` / `resource` for public output
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: run-auth-commands
|
|
3
|
-
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 —
|
|
3
|
+
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`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Run auth commands
|
|
@@ -66,7 +66,7 @@ scheduler.addJob(
|
|
|
66
66
|
scheduler.start();
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
-
In-process — no shell call. See
|
|
69
|
+
In-process — no shell call. See the `scheduler-basics` topic of the `warlock-js-scheduler` skill.
|
|
70
70
|
|
|
71
71
|
### Via system cron
|
|
72
72
|
|
|
@@ -155,5 +155,5 @@ Register it the same way as the bundled commands — call the factory inside `de
|
|
|
155
155
|
|
|
156
156
|
## See also
|
|
157
157
|
|
|
158
|
-
-
|
|
159
|
-
-
|
|
158
|
+
- The `manage-tokens` topic — `cleanupExpiredTokens` internals
|
|
159
|
+
- The `scheduler-basics` topic of the `warlock-js-scheduler` skill — in-process scheduling
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: throttle-login-attempts
|
|
3
|
-
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 —
|
|
3
|
+
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.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Throttle login attempts with `loginThrottleMiddleware`
|
|
@@ -112,6 +112,6 @@ They compose. For a hard ceiling on request volume *and* failure-aware account l
|
|
|
112
112
|
|
|
113
113
|
## See also
|
|
114
114
|
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
115
|
+
- The `protect-routes` topic — gate a route behind a valid token (`authMiddleware`); stack the throttle in front of it on login routes.
|
|
116
|
+
- The `handle-login-and-logout` topic — the login flow whose failures this middleware counts.
|
|
117
|
+
- The `use-middleware` topic of the `warlock-js-core` skill — `middleware.rateLimit` and the built-in middleware suite.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: verify-email-and-reset-password
|
|
3
|
-
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 —
|
|
3
|
+
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.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Verify email + reset password
|
|
@@ -132,6 +132,6 @@ The default notifications are `defineNotification` objects (`type: "auth.email-v
|
|
|
132
132
|
|
|
133
133
|
## See also
|
|
134
134
|
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
135
|
+
- The `register-user` topic — where `sendEmailVerification` is usually called
|
|
136
|
+
- The `throttle-login-attempts` topic — the underlying throttle
|
|
137
|
+
- The `configure-notifications` topic of the `warlock-js-notifications` skill — mail channel setup
|