@warlock.js/auth 4.0.174 → 4.1.1

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.
Files changed (164) hide show
  1. package/README.md +37 -0
  2. package/cjs/index.cjs +807 -0
  3. package/cjs/index.cjs.map +1 -0
  4. package/esm/commands/auth-cleanup-command.d.mts +13 -0
  5. package/esm/commands/auth-cleanup-command.d.mts.map +1 -0
  6. package/esm/commands/auth-cleanup-command.mjs +34 -0
  7. package/esm/commands/auth-cleanup-command.mjs.map +1 -0
  8. package/esm/commands/jwt-secret-generator-command.d.mts +5 -0
  9. package/esm/commands/jwt-secret-generator-command.d.mts.map +1 -0
  10. package/esm/commands/jwt-secret-generator-command.mjs +15 -0
  11. package/esm/commands/jwt-secret-generator-command.mjs.map +1 -0
  12. package/esm/contracts/auth-contract.d.mts +40 -0
  13. package/esm/contracts/auth-contract.d.mts.map +1 -0
  14. package/esm/contracts/index.d.mts +2 -0
  15. package/esm/contracts/types.d.mts +170 -0
  16. package/esm/contracts/types.d.mts.map +1 -0
  17. package/esm/contracts/types.mjs +25 -0
  18. package/esm/contracts/types.mjs.map +1 -0
  19. package/esm/index.d.mts +15 -0
  20. package/esm/index.mjs +17 -0
  21. package/esm/middleware/auth.middleware.d.mts +22 -0
  22. package/esm/middleware/auth.middleware.d.mts.map +1 -0
  23. package/esm/middleware/auth.middleware.mjs +72 -0
  24. package/esm/middleware/auth.middleware.mjs.map +1 -0
  25. package/esm/middleware/index.mjs +3 -0
  26. package/esm/models/access-token/access-token.model.d.mts +13 -0
  27. package/esm/models/access-token/access-token.model.d.mts.map +1 -0
  28. package/esm/models/access-token/access-token.model.mjs +23 -0
  29. package/esm/models/access-token/access-token.model.mjs.map +1 -0
  30. package/esm/models/access-token/index.d.mts +1 -0
  31. package/esm/models/access-token/index.mjs +3 -0
  32. package/esm/models/access-token/migration.mjs +24 -0
  33. package/esm/models/access-token/migration.mjs.map +1 -0
  34. package/esm/models/auth.model.d.mts +63 -0
  35. package/esm/models/auth.model.d.mts.map +1 -0
  36. package/esm/models/auth.model.mjs +77 -0
  37. package/esm/models/auth.model.mjs.map +1 -0
  38. package/esm/models/index.d.mts +8 -0
  39. package/esm/models/index.d.mts.map +1 -0
  40. package/esm/models/index.mjs +14 -0
  41. package/esm/models/index.mjs.map +1 -0
  42. package/esm/models/refresh-token/index.d.mts +1 -0
  43. package/esm/models/refresh-token/index.mjs +3 -0
  44. package/esm/models/refresh-token/migration.mjs +27 -0
  45. package/esm/models/refresh-token/migration.mjs.map +1 -0
  46. package/esm/models/refresh-token/refresh-token.model.d.mts +36 -0
  47. package/esm/models/refresh-token/refresh-token.model.d.mts.map +1 -0
  48. package/esm/models/refresh-token/refresh-token.model.mjs +58 -0
  49. package/esm/models/refresh-token/refresh-token.model.mjs.map +1 -0
  50. package/esm/services/auth-events.d.mts +89 -0
  51. package/esm/services/auth-events.d.mts.map +1 -0
  52. package/esm/services/auth-events.mjs +68 -0
  53. package/esm/services/auth-events.mjs.map +1 -0
  54. package/esm/services/auth.service.d.mts +95 -0
  55. package/esm/services/auth.service.d.mts.map +1 -0
  56. package/esm/services/auth.service.mjs +275 -0
  57. package/esm/services/auth.service.mjs.map +1 -0
  58. package/esm/services/generate-jwt-secret.d.mts +5 -0
  59. package/esm/services/generate-jwt-secret.d.mts.map +1 -0
  60. package/esm/services/generate-jwt-secret.mjs +48 -0
  61. package/esm/services/generate-jwt-secret.mjs.map +1 -0
  62. package/esm/services/index.d.mts +4 -0
  63. package/esm/services/index.mjs +6 -0
  64. package/esm/services/jwt.d.mts +52 -0
  65. package/esm/services/jwt.d.mts.map +1 -0
  66. package/esm/services/jwt.mjs +58 -0
  67. package/esm/services/jwt.mjs.map +1 -0
  68. package/esm/utils/auth-error-codes.d.mts +23 -0
  69. package/esm/utils/auth-error-codes.d.mts.map +1 -0
  70. package/esm/utils/auth-error-codes.mjs +23 -0
  71. package/esm/utils/auth-error-codes.mjs.map +1 -0
  72. package/llms-full.txt +1023 -0
  73. package/llms.txt +16 -0
  74. package/package.json +47 -36
  75. package/skills/auth-basics/SKILL.md +88 -0
  76. package/skills/customize-user-type/SKILL.md +137 -0
  77. package/skills/handle-login-and-logout/SKILL.md +160 -0
  78. package/skills/manage-tokens/SKILL.md +169 -0
  79. package/skills/overview/SKILL.md +66 -0
  80. package/skills/protect-routes/SKILL.md +105 -0
  81. package/skills/register-user/SKILL.md +135 -0
  82. package/skills/run-auth-commands/SKILL.md +125 -0
  83. package/esm/commands/auth-cleanup-command.d.ts +0 -10
  84. package/esm/commands/auth-cleanup-command.d.ts.map +0 -1
  85. package/esm/commands/auth-cleanup-command.js +0 -29
  86. package/esm/commands/auth-cleanup-command.js.map +0 -1
  87. package/esm/commands/jwt-secret-generator-command.d.ts +0 -2
  88. package/esm/commands/jwt-secret-generator-command.d.ts.map +0 -1
  89. package/esm/commands/jwt-secret-generator-command.js +0 -7
  90. package/esm/commands/jwt-secret-generator-command.js.map +0 -1
  91. package/esm/contracts/auth-contract.d.ts +0 -23
  92. package/esm/contracts/auth-contract.d.ts.map +0 -1
  93. package/esm/contracts/index.d.ts +0 -3
  94. package/esm/contracts/index.d.ts.map +0 -1
  95. package/esm/contracts/types.d.ts +0 -167
  96. package/esm/contracts/types.d.ts.map +0 -1
  97. package/esm/contracts/types.js +0 -20
  98. package/esm/contracts/types.js.map +0 -1
  99. package/esm/index.d.ts +0 -8
  100. package/esm/index.d.ts.map +0 -1
  101. package/esm/index.js +0 -1
  102. package/esm/index.js.map +0 -1
  103. package/esm/middleware/auth.middleware.d.ts +0 -2
  104. package/esm/middleware/auth.middleware.d.ts.map +0 -1
  105. package/esm/middleware/auth.middleware.js +0 -72
  106. package/esm/middleware/auth.middleware.js.map +0 -1
  107. package/esm/middleware/index.d.ts +0 -2
  108. package/esm/middleware/index.d.ts.map +0 -1
  109. package/esm/models/access-token/access-token.model.d.ts +0 -9
  110. package/esm/models/access-token/access-token.model.d.ts.map +0 -1
  111. package/esm/models/access-token/access-token.model.js +0 -14
  112. package/esm/models/access-token/access-token.model.js.map +0 -1
  113. package/esm/models/access-token/index.d.ts +0 -2
  114. package/esm/models/access-token/index.d.ts.map +0 -1
  115. package/esm/models/access-token/migration.d.ts +0 -2
  116. package/esm/models/access-token/migration.d.ts.map +0 -1
  117. package/esm/models/access-token/migration.js +0 -22
  118. package/esm/models/access-token/migration.js.map +0 -1
  119. package/esm/models/auth.model.d.ts +0 -58
  120. package/esm/models/auth.model.d.ts.map +0 -1
  121. package/esm/models/auth.model.js +0 -68
  122. package/esm/models/auth.model.js.map +0 -1
  123. package/esm/models/index.d.ts +0 -5
  124. package/esm/models/index.d.ts.map +0 -1
  125. package/esm/models/index.js +0 -1
  126. package/esm/models/index.js.map +0 -1
  127. package/esm/models/refresh-token/index.d.ts +0 -2
  128. package/esm/models/refresh-token/index.d.ts.map +0 -1
  129. package/esm/models/refresh-token/migration.d.ts +0 -2
  130. package/esm/models/refresh-token/migration.d.ts.map +0 -1
  131. package/esm/models/refresh-token/migration.js +0 -23
  132. package/esm/models/refresh-token/migration.js.map +0 -1
  133. package/esm/models/refresh-token/refresh-token.model.d.ts +0 -32
  134. package/esm/models/refresh-token/refresh-token.model.d.ts.map +0 -1
  135. package/esm/models/refresh-token/refresh-token.model.js +0 -53
  136. package/esm/models/refresh-token/refresh-token.model.js.map +0 -1
  137. package/esm/services/auth-events.d.ts +0 -85
  138. package/esm/services/auth-events.d.ts.map +0 -1
  139. package/esm/services/auth-events.js +0 -65
  140. package/esm/services/auth-events.js.map +0 -1
  141. package/esm/services/auth.service.d.ts +0 -92
  142. package/esm/services/auth.service.d.ts.map +0 -1
  143. package/esm/services/auth.service.js +0 -322
  144. package/esm/services/auth.service.js.map +0 -1
  145. package/esm/services/generate-jwt-secret.d.ts +0 -2
  146. package/esm/services/generate-jwt-secret.d.ts.map +0 -1
  147. package/esm/services/generate-jwt-secret.js +0 -47
  148. package/esm/services/generate-jwt-secret.js.map +0 -1
  149. package/esm/services/index.d.ts +0 -5
  150. package/esm/services/index.d.ts.map +0 -1
  151. package/esm/services/jwt.d.ts +0 -23
  152. package/esm/services/jwt.d.ts.map +0 -1
  153. package/esm/services/jwt.js +0 -40
  154. package/esm/services/jwt.js.map +0 -1
  155. package/esm/utils/auth-error-codes.d.ts +0 -18
  156. package/esm/utils/auth-error-codes.d.ts.map +0 -1
  157. package/esm/utils/auth-error-codes.js +0 -18
  158. package/esm/utils/auth-error-codes.js.map +0 -1
  159. package/esm/utils/duration.d.ts +0 -45
  160. package/esm/utils/duration.d.ts.map +0 -1
  161. package/esm/utils/duration.js +0 -93
  162. package/esm/utils/duration.js.map +0 -1
  163. package/esm/utils/index.d.ts +0 -3
  164. package/esm/utils/index.d.ts.map +0 -1
package/llms.txt ADDED
@@ -0,0 +1,16 @@
1
+ # Warlock Auth
2
+
3
+ > Package: `@warlock.js/auth`
4
+
5
+ > Authentication system for Warlock.js applications
6
+
7
+ ## Skills
8
+
9
+ - [auth-basics](@warlock.js/auth/auth-basics/SKILL.md): Start with @warlock.js/auth — JWT auth, Auth base model, authMiddleware route gate, authService (login / logout / refresh), AccessToken + RefreshToken persistence, multi-user-type support. Triggers: `Auth`, `authMiddleware`, `authService`, `AccessToken`, `RefreshToken`, `authMigrations`; "set up auth in a new app", "which auth skill do I need", "JWT authentication overview", "wire warlock auth"; typical import `import { authMiddleware, authService, Auth, authMigrations } from "@warlock.js/auth"`. Skip: routing — `@warlock.js/auth/protect-routes/SKILL.md`; login — `@warlock.js/auth/handle-login-and-logout/SKILL.md`; competing libs `passport`, `next-auth`, `lucia-auth`, `auth0`.
10
+ - [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 — `@warlock.js/auth/protect-routes/SKILL.md`; login flow — `@warlock.js/auth/handle-login-and-logout/SKILL.md`; RBAC libs `casl`, `accesscontrol`, `rbac`.
11
+ - [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 — `@warlock.js/auth/manage-tokens/SKILL.md`; sign-up — `@warlock.js/auth/register-user/SKILL.md`; competing libs `passport-local`, `next-auth` credentials.
12
+ - [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 — `@warlock.js/auth/handle-login-and-logout/SKILL.md`; CLI cleanup — `@warlock.js/auth/run-auth-commands/SKILL.md`; competing libs `jsonwebtoken`, `jose`, `fast-jwt`.
13
+ - [overview](@warlock.js/auth/overview/SKILL.md): Front-door orientation for `@warlock.js/auth` — JWT authentication for Warlock apps: the `Auth` base model, `authMiddleware` route gate, `authService` (login / logout / refresh with token rotation + replay detection), persisted AccessToken + RefreshToken, multi-user-type support, auth lifecycle events, and two CLI commands. Coupled to `@warlock.js/core`. TRIGGER when: code imports anything from `@warlock.js/auth`; user asks "what does @warlock.js/auth do", "how do I add login to my Warlock app", "JWT auth in Warlock", "protect a route", "multiple user types / admin + user", "refresh token rotation"; package.json adds `@warlock.js/auth`. Skip: specific task already known — load the matching task skill directly (`auth-basics`, `protect-routes`, `handle-login-and-logout`, `register-user`, `manage-tokens`, `customize-user-type`, `run-auth-commands`); non-Warlock apps (this package depends on core); session-cookie auth (this is JWT/token-based).
14
+ - [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.user + request.decodedAccessToken on success, 401 on failure. Triggers: `authMiddleware`, `request.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 — `@warlock.js/auth/customize-user-type/SKILL.md`; issuing the token — `@warlock.js/auth/handle-login-and-logout/SKILL.md`; competing libs `passport`, `express-jwt`, `next-auth` middleware.
15
+ - [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 — `@warlock.js/auth/handle-login-and-logout/SKILL.md`; token internals — `@warlock.js/auth/manage-tokens/SKILL.md`; competing libs `bcrypt`, `bcryptjs`, `argon2`.
16
+ - [run-auth-commands](@warlock.js/auth/run-auth-commands/SKILL.md): Two bundled CLI commands — warlock jwt.generate (creates strong JWT secret + writes to .env) and warlock auth.cleanup (removes expired refresh tokens). Register via registerJWTSecretGeneratorCommand() and registerAuthCleanupCommand(). Triggers: `registerJWTSecretGeneratorCommand`, `registerAuthCleanupCommand`, `warlock jwt.generate`, `warlock auth.cleanup`, `cleanupExpiredTokens`, `command`; "generate JWT secret", "bootstrap .env JWT_SECRET", "cron job for expired tokens", "schedule auth cleanup"; typical import `import { registerJWTSecretGeneratorCommand, registerAuthCleanupCommand } from "@warlock.js/auth"`. Skip: programmatic cleanup — `@warlock.js/auth/manage-tokens/SKILL.md`; in-process scheduling — `@warlock.js/scheduler/scheduler-basics/SKILL.md`; competing tools `dotenv-cli`, `node-cron`.
package/package.json CHANGED
@@ -1,37 +1,48 @@
1
1
  {
2
- "name": "@warlock.js/auth",
3
- "version": "4.0.174",
4
- "description": "Authentication system for Warlock.js applications",
5
- "main": "./esm/index.js",
6
- "dependencies": {
7
- "@mongez/password": "^1.0.2",
8
- "fast-jwt": "^6.1.0"
9
- },
10
- "peerDependencies": {
11
- "@mongez/copper": "^1.0.1",
12
- "@mongez/events": "^2.1.0",
13
- "@mongez/fs": "^3.0.5",
14
- "@mongez/reinforcements": "^2.3.17",
15
- "@warlock.js/cascade": "4.0.174",
16
- "@warlock.js/core": "4.0.174",
17
- "@warlock.js/logger": "4.0.174",
18
- "@warlock.js/seal": "4.0.174"
19
- },
20
- "repository": {
21
- "type": "git",
22
- "url": "https://github.com/warlockjs/auth"
23
- },
24
- "keywords": [
25
- "nodejs",
26
- "auth",
27
- "auth.js",
28
- "authentication",
29
- "authorization",
30
- "jwt"
31
- ],
32
- "author": "hassanzohdy",
33
- "license": "MIT",
34
- "module": "./esm/index.js",
35
- "typings": "./esm/index.d.ts",
36
- "type": "module"
37
- }
2
+ "name": "@warlock.js/auth",
3
+ "description": "Authentication system for Warlock.js applications",
4
+ "keywords": [
5
+ "nodejs",
6
+ "auth",
7
+ "auth.js",
8
+ "authentication",
9
+ "authorization",
10
+ "jwt"
11
+ ],
12
+ "author": "hassanzohdy",
13
+ "license": "MIT",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/warlockjs/auth"
17
+ },
18
+ "dependencies": {
19
+ "fast-jwt": "^6.1.0",
20
+ "ms": "^2.1.3"
21
+ },
22
+ "peerDependencies": {
23
+ "@mongez/copper": "^2.1.2",
24
+ "@mongez/events": "^2.2.6",
25
+ "@warlock.js/fs": "*",
26
+ "@mongez/reinforcements": "^3.2.0",
27
+ "@warlock.js/cascade": "*",
28
+ "@warlock.js/core": "*",
29
+ "@warlock.js/logger": "*",
30
+ "@warlock.js/seal": "*"
31
+ },
32
+ "version": "4.1.1",
33
+ "main": "./cjs/index.cjs",
34
+ "module": "./esm/index.mjs",
35
+ "types": "./esm/index.d.mts",
36
+ "exports": {
37
+ ".": {
38
+ "import": {
39
+ "types": "./esm/index.d.mts",
40
+ "default": "./esm/index.mjs"
41
+ },
42
+ "require": {
43
+ "types": "./esm/index.d.mts",
44
+ "default": "./cjs/index.cjs"
45
+ }
46
+ }
47
+ }
48
+ }
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: auth-basics
3
+ description: 'Start with @warlock.js/auth — JWT auth, Auth base model, authMiddleware route gate, authService (login / logout / refresh), AccessToken + RefreshToken persistence, multi-user-type support. Triggers: `Auth`, `authMiddleware`, `authService`, `AccessToken`, `RefreshToken`, `authMigrations`; "set up auth in a new app", "which auth skill do I need", "JWT authentication overview", "wire warlock auth"; typical import `import { authMiddleware, authService, Auth, authMigrations } from "@warlock.js/auth"`. Skip: routing — `@warlock.js/auth/protect-routes/SKILL.md`; login — `@warlock.js/auth/handle-login-and-logout/SKILL.md`; competing libs `passport`, `next-auth`, `lucia-auth`, `auth0`.'
4
+ ---
5
+
6
+ # Auth basics
7
+
8
+ JWT-based authentication for Warlock. `Auth` base model + `authMiddleware` gate + `authService` for login/logout/refresh + `AccessToken` / `RefreshToken` persistence + multi-user-type support.
9
+
10
+ > This skill is the auth **map** — read it first, then load the specific skill for the task.
11
+
12
+ ## Install
13
+
14
+ ```bash
15
+ yarn add @warlock.js/auth
16
+ ```
17
+
18
+ ## Foundations
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 [`@warlock.js/auth/customize-user-type/SKILL.md`](@warlock.js/auth/customize-user-type/SKILL.md)).
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
+ 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
+ 4. **`authMiddleware(allowedUserType)` gates routes.** The argument is required and a valid token is always required. `[]` → any authenticated user; a user-type → required auth scoped to those types. Public routes omit the middleware entirely.
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
+ 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 [`@warlock.js/auth/run-auth-commands/SKILL.md`](@warlock.js/auth/run-auth-commands/SKILL.md)).
27
+
28
+ ## Minimal wire-up
29
+
30
+ ```ts title="warlock.config.ts"
31
+ import {
32
+ authMigrations,
33
+ registerAuthCleanupCommand,
34
+ registerJWTSecretGeneratorCommand,
35
+ } from "@warlock.js/auth";
36
+ import { defineConfig } from "@warlock.js/core";
37
+
38
+ export default defineConfig({
39
+ cli: {
40
+ commands: [
41
+ registerJWTSecretGeneratorCommand(),
42
+ registerAuthCleanupCommand(),
43
+ ],
44
+ },
45
+ database: {
46
+ migrations: authMigrations,
47
+ },
48
+ });
49
+ ```
50
+
51
+ ```ts title="src/config/auth.ts"
52
+ import { User } from "@/app/users/models/user.model";
53
+
54
+ export default {
55
+ userType: {
56
+ user: User,
57
+ // admin: Admin, // for multi-user-type
58
+ },
59
+ jwt: {
60
+ secret: env("JWT_SECRET"),
61
+ expiresIn: "1h",
62
+ refresh: {
63
+ enabled: true,
64
+ expiresIn: "30d",
65
+ rotation: true,
66
+ maxPerUser: 5,
67
+ },
68
+ },
69
+ };
70
+ ```
71
+
72
+ ## Pick a skill
73
+
74
+ | If the task is about… | Load |
75
+ | --- | --- |
76
+ | Gating routes with `authMiddleware(allowedUserType)`, any-authenticated vs typed access | [`@warlock.js/auth/protect-routes/SKILL.md`](@warlock.js/auth/protect-routes/SKILL.md) |
77
+ | `authService.login(...)`, `attemptLogin`, full credentials-to-tokens flow + logout | [`@warlock.js/auth/handle-login-and-logout/SKILL.md`](@warlock.js/auth/handle-login-and-logout/SKILL.md) |
78
+ | Token lifecycle — `generateAccessToken`, `createRefreshToken`, rotation, family revocation, max-per-user | [`@warlock.js/auth/manage-tokens/SKILL.md`](@warlock.js/auth/manage-tokens/SKILL.md) |
79
+ | Register a new user + issue tokens in one flow | [`@warlock.js/auth/register-user/SKILL.md`](@warlock.js/auth/register-user/SKILL.md) |
80
+ | Multi-user-type apps (`user`, `admin`, `client`), `config.auth.userType.<name>` mapping | [`@warlock.js/auth/customize-user-type/SKILL.md`](@warlock.js/auth/customize-user-type/SKILL.md) |
81
+ | `warlock jwt.generate` + `warlock auth.cleanup` CLI commands | [`@warlock.js/auth/run-auth-commands/SKILL.md`](@warlock.js/auth/run-auth-commands/SKILL.md) |
82
+
83
+ ## Things NOT to do
84
+
85
+ - Don't write your own JWT signing logic — use `authService` / `jwt` from this package so signature/secret/expiry stay consistent.
86
+ - Don't store the JWT secret in the model layer or anywhere user-modifiable. It lives in `.env` only.
87
+ - Don't return the raw `User` from a login endpoint without shaping output. Configure `static toJsonColumns` or `static resource` (see [`@warlock.js/cascade/define-model/SKILL.md`](@warlock.js/cascade/define-model/SKILL.md)).
88
+ - Don't run `auth.cleanup` from app boot. Schedule it (cron, scheduler) as a periodic task — see [`@warlock.js/scheduler/scheduler-basics/SKILL.md`](@warlock.js/scheduler/scheduler-basics/SKILL.md).
@@ -0,0 +1,137 @@
1
+ ---
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 — `@warlock.js/auth/protect-routes/SKILL.md`; login flow — `@warlock.js/auth/handle-login-and-logout/SKILL.md`; RBAC libs `casl`, `accesscontrol`, `rbac`.'
4
+ ---
5
+
6
+ # Customize user type (multi-user-type auth)
7
+
8
+ The `Auth` base class has a `userType` slot. Subclass it once per type, register each class under `config.auth.userType.<slug>`, and the auth flow handles the rest.
9
+
10
+ ## Define a model per user type
11
+
12
+ ```ts title="src/app/users/models/user/user.model.ts"
13
+ import { Auth } from "@warlock.js/auth";
14
+ import { RegisterModel } from "@warlock.js/cascade";
15
+
16
+ @RegisterModel()
17
+ export class User extends Auth<UserSchema> {
18
+ public static table = "users";
19
+ public static schema = userSchema;
20
+
21
+ public get userType(): string {
22
+ return "user";
23
+ }
24
+ }
25
+ ```
26
+
27
+ ```ts title="src/app/admins/models/admin/admin.model.ts"
28
+ @RegisterModel()
29
+ export class Admin extends Auth<AdminSchema> {
30
+ public static table = "admins";
31
+ public static schema = adminSchema;
32
+
33
+ public get userType(): string {
34
+ return "admin";
35
+ }
36
+ }
37
+ ```
38
+
39
+ Each gets its own table, its own schema, its own `userType` slug. They DON'T share table — they're separate models.
40
+
41
+ ## Register them in `config.auth`
42
+
43
+ ```ts title="src/config/auth.ts"
44
+ import { User } from "@/app/users/models/user.model";
45
+ import { Admin } from "@/app/admins/models/admin.model";
46
+
47
+ export default {
48
+ userType: {
49
+ user: User,
50
+ admin: Admin,
51
+ // staff: Staff,
52
+ // client: Client,
53
+ },
54
+ jwt: {
55
+ secret: env("JWT_SECRET"),
56
+ expiresIn: "1h",
57
+ refresh: { enabled: true, expiresIn: "30d", rotation: true },
58
+ },
59
+ };
60
+ ```
61
+
62
+ The keys (`"user"`, `"admin"`) are the **userType slugs** that flow through every token, middleware call, and event payload.
63
+
64
+ ## Gate routes per user type
65
+
66
+ ```ts
67
+ import { authMiddleware } from "@warlock.js/auth";
68
+
69
+ router.get("/account", userAccountController, { middleware: [authMiddleware("user")] });
70
+ router.get("/admin/users", listUsersController, { middleware: [authMiddleware("admin")] });
71
+ router.get("/back-office", backOfficeController, { middleware: [authMiddleware(["admin", "staff"])] });
72
+ router.get("/dashboard", dashboardController, { middleware: [authMiddleware([])] }); // any logged-in
73
+ ```
74
+
75
+ See [`@warlock.js/auth/protect-routes/SKILL.md`](@warlock.js/auth/protect-routes/SKILL.md).
76
+
77
+ ## Login per user type — pass the right Model
78
+
79
+ `authService.login(Model, credentials, deviceInfo?)` is keyed off the model you pass:
80
+
81
+ ```ts
82
+ // User login endpoint
83
+ const result = await authService.login(User, credentials);
84
+ // Issues tokens with userType "user"; middleware will route them to User model.
85
+
86
+ // Admin login endpoint
87
+ const result = await authService.login(Admin, credentials);
88
+ // Issues tokens with userType "admin"; middleware will route them to Admin model.
89
+ ```
90
+
91
+ The middleware then uses `config.auth.userType[token.userType]` to know which model to hydrate.
92
+
93
+ ## Cross-type behavior
94
+
95
+ - **Tokens are scoped to their issuing user-type.** A user-type token doesn't unlock admin-type routes.
96
+ - **AccessToken / RefreshToken rows carry the `user_type` column.** Same model classes, different rows per type.
97
+ - **`authMiddleware(["admin", "user"])`** allows either — useful for endpoints shared between roles.
98
+
99
+ ## When NOT to use multi-user-type
100
+
101
+ If the distinction is **permissions/roles within one user shape**, use a `role` column on a single User model instead. Multi-user-type is right when:
102
+
103
+ - Different tables / schemas (admins have an `admin_level`; users have a `subscription_tier`).
104
+ - Separate registration flows (admins are created via an admin panel; users self-register).
105
+ - Truly separate concepts at the data layer (clients vs vendors in a marketplace).
106
+
107
+ If users and admins differ only in a `role` field, stick with one `User` model + a role check at the controller layer.
108
+
109
+ ## `Auth` base — what your subclass inherits
110
+
111
+ ```ts
112
+ abstract class Auth<TSchema> extends Model<TSchema> implements Authenticable {
113
+ // ...all the Model<> methods
114
+ public abstract get userType(): string;
115
+ public generateAccessToken(payload?: Record<string, unknown>): Promise<AccessTokenOutput>;
116
+ public generateRefreshToken(deviceInfo?: DeviceInfo): Promise<RefreshToken | undefined>;
117
+ public createTokenPair(deviceInfo?: DeviceInfo): Promise<TokenPair>;
118
+ public confirmPassword(password: string): Promise<boolean>;
119
+ }
120
+ ```
121
+
122
+ `userType` is the only required override (an abstract getter — return the type slug). Override `generateAccessToken` if you need a non-default payload.
123
+
124
+ `Auth` implements the `Authenticable` contract — that interface mirrors exactly these methods (`userType`, `generateAccessToken`, `generateRefreshToken`, `createTokenPair`, `confirmPassword`), so the class fails to compile if it drifts from the contract. Use `confirmPassword(plaintext)` to check a password against the stored hash (e.g. a "confirm current password" step).
125
+
126
+ ## Things NOT to do
127
+
128
+ - Don't use multi-user-type for what's really role-based access control. Use a `role` column on a single User model when the data shape is shared.
129
+ - Don't forget the `public get userType(): string` override. It's an abstract getter on `Auth` — a subclass without it won't compile, and middleware lookups key off its return value.
130
+ - Don't reuse the same `userType` slug across two models — the `config.auth.userType` map can only point one slug at one model.
131
+ - Don't put admins and users in the same table differentiated by a flag. Separate tables means migrations don't coupling, queries don't accidentally cross, and audit logs are cleaner.
132
+
133
+ ## See also
134
+
135
+ - [`@warlock.js/auth/protect-routes/SKILL.md`](@warlock.js/auth/protect-routes/SKILL.md) — `authMiddleware` semantics
136
+ - [`@warlock.js/auth/handle-login-and-logout/SKILL.md`](@warlock.js/auth/handle-login-and-logout/SKILL.md) — passing the right Model to `login`
137
+ - [`@warlock.js/cascade/define-model/SKILL.md`](@warlock.js/cascade/define-model/SKILL.md) — `@RegisterModel`, models in general
@@ -0,0 +1,160 @@
1
+ ---
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 — `@warlock.js/auth/manage-tokens/SKILL.md`; sign-up — `@warlock.js/auth/register-user/SKILL.md`; competing libs `passport-local`, `next-auth` credentials.'
4
+ ---
5
+
6
+ # Login + logout
7
+
8
+ `authService` exposes the full flow. Pass the model class so the service knows which user-type to look up.
9
+
10
+ ## Login — `authService.login(Model, credentials, deviceInfo?)`
11
+
12
+ ```ts
13
+ import { authService } from "@warlock.js/auth";
14
+ import { User } from "@/app/users/models/user.model";
15
+
16
+ async function loginController(request: Request, response: Response) {
17
+ const result = await authService.login(User, {
18
+ email: request.input("email"),
19
+ password: request.input("password"),
20
+ }, {
21
+ userAgent: request.header("user-agent"),
22
+ ip: request.ip,
23
+ });
24
+
25
+ if (!result) {
26
+ return response.unauthorized({ error: "Invalid credentials" });
27
+ }
28
+
29
+ return response.success({
30
+ user: result.user,
31
+ tokens: result.tokens,
32
+ });
33
+ }
34
+ ```
35
+
36
+ The returned shape:
37
+
38
+ ```ts
39
+ {
40
+ user: T, // your User subclass, hydrated
41
+ tokens: {
42
+ accessToken: { token: string, expiresAt: string },
43
+ refreshToken?: { token: string, expiresAt: string }, // omitted if refresh tokens disabled
44
+ },
45
+ }
46
+ ```
47
+
48
+ Returns `null` on failure (wrong password, user not found). The service emits `login.attempt` → `login.success` or `login.failed` events as it goes — subscribe via the auth event bus if you need an audit trail.
49
+
50
+ ## What `credentials` looks like
51
+
52
+ The shape is **arbitrary** — every key except `password` is used as a `where(...)` filter against the model. The password is verified separately via bcrypt.
53
+
54
+ ```ts
55
+ // Email + password
56
+ authService.login(User, { email: "ada@example.com", password: "..." });
57
+
58
+ // Username + password
59
+ authService.login(User, { username: "ada", password: "..." });
60
+
61
+ // Phone-based OTP (where password is the OTP hash)
62
+ authService.login(User, { phone: "+1...", password: hashedOTP });
63
+ ```
64
+
65
+ For lower-level credential verification (just check, don't issue tokens), use `authService.attemptLogin(Model, credentials)` — returns the user or null without creating tokens.
66
+
67
+ ## Device info
68
+
69
+ The optional `deviceInfo` carries metadata into the refresh token row:
70
+
71
+ ```ts
72
+ authService.login(User, credentials, {
73
+ userAgent: request.header("user-agent"),
74
+ ip: request.ip,
75
+ deviceId: "...", // your client-side device fingerprint
76
+ familyId: "...", // pre-existing family for token rotation, usually omitted
77
+ });
78
+ ```
79
+
80
+ Useful for "show active sessions" UIs — see `authService.getActiveSessions(user)`.
81
+
82
+ ## Logout — `authService.logout(user, accessToken?, refreshToken?)`
83
+
84
+ ```ts
85
+ async function logoutController(request: Request, response: Response) {
86
+ await authService.logout(
87
+ request.user!,
88
+ request.authorizationValue, // access token from the Authorization header
89
+ request.input("refreshToken"), // refresh token from the request body
90
+ );
91
+
92
+ return response.success({ message: "Logged out" });
93
+ }
94
+ ```
95
+
96
+ The contract:
97
+ - **Pass the access token** → that specific access-token row is deleted.
98
+ - **Pass the refresh token** → that specific refresh-token row is revoked.
99
+ - **Omit refresh token** → behavior depends on `config.auth.jwt.refresh.logoutWithoutToken`:
100
+ - `"revoke-all"` (default) — every refresh token for this user is revoked. Fail-safe.
101
+ - `"error"` — throws. Force the client to send the refresh token.
102
+
103
+ The `revoke-all` default is the right call for most apps. If a client loses track of the refresh token, logout still works and the user has to log in fresh on every device.
104
+
105
+ ## Logout-everywhere
106
+
107
+ ```ts
108
+ await authService.revokeAllTokens(user);
109
+ // Revokes every refresh token + deletes every access token for this user.
110
+ ```
111
+
112
+ Useful for "logout from all devices" buttons. Fires `token.revoked` per token + `logout.all` once.
113
+
114
+ ## Refresh tokens — `authService.refreshTokens(refreshTokenString, deviceInfo?)`
115
+
116
+ ```ts
117
+ async function refreshController(request: Request, response: Response) {
118
+ const tokens = await authService.refreshTokens(
119
+ request.input("refreshToken"),
120
+ { userAgent: request.header("user-agent"), ip: request.ip },
121
+ );
122
+
123
+ if (!tokens) {
124
+ return response.unauthorized({ error: "Invalid refresh token" });
125
+ }
126
+
127
+ return response.success({ tokens });
128
+ }
129
+ ```
130
+
131
+ 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 [`@warlock.js/auth/manage-tokens/SKILL.md`](@warlock.js/auth/manage-tokens/SKILL.md).
132
+
133
+ ## Auth events
134
+
135
+ `authEvents` is a type-safe event bus (over `@mongez/events`) that fires on every meaningful auth moment. Subscribe with `on` / `subscribe`, unsubscribe with `off` / `unsubscribeAll`:
136
+
137
+ ```ts
138
+ import { authEvents } from "@warlock.js/auth";
139
+
140
+ authEvents.on("login.success", (user, tokens, deviceInfo) => { /* audit */ });
141
+ authEvents.on("login.failed", (credentials, reason) => { /* alert on brute force */ });
142
+ authEvents.on("logout", (user) => { /* clear server-side session, if any */ });
143
+ authEvents.on("token.refreshed", (user, newPair, oldToken) => { /* track rotation */ });
144
+ authEvents.on("cleanup.completed", (count) => { /* metrics */ });
145
+ ```
146
+
147
+ Full event list: `login.attempt`, `login.success`, `login.failed`, `logout`, `logout.all`, `logout.failsafe`, `token.created`, `token.refreshed`, `token.revoked`, `token.expired`, `token.familyRevoked`, `session.created`, `session.destroyed`, `cleanup.completed`.
148
+
149
+ ## Things NOT to do
150
+
151
+ - Don't `authService.login(User, { password })` without other credentials — the password is the secret; the other fields are the lookup. A login with only a password is a logic bug.
152
+ - Don't return the password hash in the response. `static toJsonColumns` on the User model should explicitly exclude it.
153
+ - Don't store the refresh token in localStorage. Use an httpOnly secure cookie for refresh tokens; the access token can sit in memory.
154
+ - Don't issue a new token pair without revoking the old one when rotation is enabled. `refreshTokens` does this for you — don't bypass it.
155
+
156
+ ## See also
157
+
158
+ - [`@warlock.js/auth/manage-tokens/SKILL.md`](@warlock.js/auth/manage-tokens/SKILL.md) — token lifecycle, rotation, family revocation
159
+ - [`@warlock.js/auth/register-user/SKILL.md`](@warlock.js/auth/register-user/SKILL.md) — sign-up that issues tokens after creation
160
+ - [`@warlock.js/auth/protect-routes/SKILL.md`](@warlock.js/auth/protect-routes/SKILL.md) — where the access token gets consumed