@warlock.js/auth 4.1.14 → 4.2.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.
Files changed (91) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/LICENSE +21 -0
  3. package/README.md +78 -37
  4. package/esm/commands/auth-cleanup-command.d.mts +1 -1
  5. package/esm/commands/auth-cleanup-command.d.mts.map +1 -1
  6. package/esm/commands/auth-cleanup-command.mjs +1 -1
  7. package/esm/commands/auth-cleanup-command.mjs.map +1 -1
  8. package/esm/commands/jwt-secret-generator-command.d.mts +1 -1
  9. package/esm/commands/jwt-secret-generator-command.d.mts.map +1 -1
  10. package/esm/commands/jwt-secret-generator-command.mjs +1 -1
  11. package/esm/commands/jwt-secret-generator-command.mjs.map +1 -1
  12. package/esm/contracts/auth-contract.d.mts +2 -2
  13. package/esm/contracts/auth-contract.d.mts.map +1 -1
  14. package/esm/contracts/index.d.mts +1 -1
  15. package/esm/contracts/types.d.mts +105 -76
  16. package/esm/contracts/types.d.mts.map +1 -1
  17. package/esm/contracts/types.mjs +2 -2
  18. package/esm/contracts/types.mjs.map +1 -1
  19. package/esm/index.d.mts +6 -5
  20. package/esm/index.mjs +4 -3
  21. package/esm/middleware/auth.middleware.d.mts +1 -1
  22. package/esm/middleware/auth.middleware.d.mts.map +1 -1
  23. package/esm/middleware/auth.middleware.mjs +9 -9
  24. package/esm/middleware/auth.middleware.mjs.map +1 -1
  25. package/esm/middleware/index.d.mts +2 -0
  26. package/esm/middleware/index.mjs +1 -0
  27. package/esm/middleware/login-throttle.middleware.d.mts +116 -0
  28. package/esm/middleware/login-throttle.middleware.d.mts.map +1 -0
  29. package/esm/middleware/login-throttle.middleware.mjs +105 -0
  30. package/esm/middleware/login-throttle.middleware.mjs.map +1 -0
  31. package/esm/models/access-token/access-token.model.d.mts +47 -5
  32. package/esm/models/access-token/access-token.model.d.mts.map +1 -1
  33. package/esm/models/access-token/access-token.model.mjs +70 -4
  34. package/esm/models/access-token/access-token.model.mjs.map +1 -1
  35. package/esm/models/access-token/index.d.mts +1 -1
  36. package/esm/models/access-token/index.mjs +1 -1
  37. package/esm/models/access-token/migration.mjs +2 -3
  38. package/esm/models/access-token/migration.mjs.map +1 -1
  39. package/esm/models/auth.model.d.mts +2 -2
  40. package/esm/models/auth.model.d.mts.map +1 -1
  41. package/esm/models/auth.model.mjs +1 -1
  42. package/esm/models/auth.model.mjs.map +1 -1
  43. package/esm/models/index.d.mts +4 -3
  44. package/esm/models/index.d.mts.map +1 -1
  45. package/esm/models/index.mjs +3 -3
  46. package/esm/models/index.mjs.map +1 -1
  47. package/esm/models/refresh-token/index.d.mts +1 -1
  48. package/esm/models/refresh-token/index.mjs +1 -1
  49. package/esm/models/refresh-token/migration.mjs +2 -1
  50. package/esm/models/refresh-token/migration.mjs.map +1 -1
  51. package/esm/models/refresh-token/refresh-token.model.d.mts +94 -16
  52. package/esm/models/refresh-token/refresh-token.model.d.mts.map +1 -1
  53. package/esm/models/refresh-token/refresh-token.model.mjs +152 -13
  54. package/esm/models/refresh-token/refresh-token.model.mjs.map +1 -1
  55. package/esm/services/auth-config.mjs +63 -0
  56. package/esm/services/auth-config.mjs.map +1 -0
  57. package/esm/services/auth-events.d.mts +2 -2
  58. package/esm/services/auth-events.d.mts.map +1 -1
  59. package/esm/services/auth-events.mjs +7 -2
  60. package/esm/services/auth-events.mjs.map +1 -1
  61. package/esm/services/auth.service.d.mts +48 -30
  62. package/esm/services/auth.service.d.mts.map +1 -1
  63. package/esm/services/auth.service.mjs +88 -102
  64. package/esm/services/auth.service.mjs.map +1 -1
  65. package/esm/services/generate-jwt-secret.d.mts +1 -1
  66. package/esm/services/generate-jwt-secret.d.mts.map +1 -1
  67. package/esm/services/generate-jwt-secret.mjs +4 -4
  68. package/esm/services/generate-jwt-secret.mjs.map +1 -1
  69. package/esm/services/index.d.mts +1 -1
  70. package/esm/services/jwt.d.mts +13 -4
  71. package/esm/services/jwt.d.mts.map +1 -1
  72. package/esm/services/jwt.mjs +30 -11
  73. package/esm/services/jwt.mjs.map +1 -1
  74. package/esm/utils/auth-error-codes.d.mts +9 -2
  75. package/esm/utils/auth-error-codes.d.mts.map +1 -1
  76. package/esm/utils/auth-error-codes.mjs +7 -1
  77. package/esm/utils/auth-error-codes.mjs.map +1 -1
  78. package/llms-full.txt +269 -20
  79. package/llms.txt +2 -0
  80. package/package.json +9 -11
  81. package/skills/auth-basics/SKILL.md +8 -7
  82. package/skills/customize-token-storage/SKILL.md +112 -0
  83. package/skills/customize-user-type/SKILL.md +7 -2
  84. package/skills/handle-login-and-logout/SKILL.md +1 -1
  85. package/skills/manage-tokens/SKILL.md +9 -9
  86. package/skills/overview/SKILL.md +7 -1
  87. package/skills/protect-routes/SKILL.md +1 -0
  88. package/skills/run-auth-commands/SKILL.md +1 -1
  89. package/skills/throttle-login-attempts/SKILL.md +116 -0
  90. package/cjs/index.cjs +0 -807
  91. package/cjs/index.cjs.map +0 -1
@@ -5,7 +5,7 @@ import { AuthErrorCodes } from "../utils/auth-error-codes.mjs";
5
5
  import { config, t } from "@warlock.js/core";
6
6
  import { log } from "@warlock.js/logger";
7
7
 
8
- //#region ../../@warlock.js/auth/src/middleware/auth.middleware.ts
8
+ //#region ../@warlock.js/auth/src/middleware/auth.middleware.ts
9
9
  /**
10
10
  * Build a route gate that always requires an authenticated request.
11
11
  *
@@ -32,31 +32,31 @@ function authMiddleware(allowedUserType) {
32
32
  error: t("auth.errors.missingAccessToken"),
33
33
  errorCode: "EC001"
34
34
  });
35
- const user = await jwt.verify(authorizationValue);
36
- request.decodedAccessToken = user;
37
- const accessToken = await AccessToken.first({ token: authorizationValue });
35
+ const decoded = await jwt.verify(authorizationValue);
36
+ request.decodedAccessToken = decoded;
37
+ const accessToken = await config.key("auth.accessToken.model", AccessToken).findByToken(authorizationValue);
38
38
  if (!accessToken) return response.unauthorized({
39
39
  error: t("auth.errors.invalidAccessToken"),
40
40
  errorCode: "EC002"
41
41
  });
42
- const userType = user.userType || accessToken.get("userType");
42
+ const userType = decoded.userType ?? accessToken.userType;
43
43
  if (allowedTypes.length && !allowedTypes.includes(userType)) return response.unauthorized({
44
44
  error: t("auth.errors.unauthorized"),
45
45
  errorCode: "EC003"
46
46
  });
47
47
  const UserModel = config.key(`auth.userType.${userType}`);
48
48
  if (!UserModel) throw new Error(`User type ${userType} is unknown type.`);
49
- const currentUser = await UserModel.find(user.id);
49
+ const currentUser = await UserModel.find(decoded.id);
50
50
  if (!currentUser) {
51
- accessToken.destroy();
51
+ await accessToken.destroy();
52
52
  return response.unauthorized({
53
53
  error: t("auth.errors.invalidAccessToken"),
54
54
  errorCode: "EC002"
55
55
  });
56
56
  }
57
57
  request.user = currentUser;
58
- } catch (err) {
59
- log.error("http", "auth", err);
58
+ } catch (error) {
59
+ log.error("http", "auth", error);
60
60
  request.clearCurrentUser();
61
61
  return response.unauthorized({
62
62
  error: t("auth.errors.invalidAccessToken"),
@@ -1 +1 @@
1
- {"version":3,"file":"auth.middleware.mjs","names":[],"sources":["../../../../../../@warlock.js/auth/src/middleware/auth.middleware.ts"],"sourcesContent":["import { config, t, type Middleware, type Request, type Response } from \"@warlock.js/core\";\r\nimport { log } from \"@warlock.js/logger\";\r\nimport { AccessToken } from \"../models/access-token\";\r\nimport { jwt } from \"../services/jwt\";\r\nimport { AuthErrorCodes } from \"../utils/auth-error-codes\";\r\n\r\n/**\r\n * Build a route gate that always requires an authenticated request.\r\n *\r\n * The argument is mandatory and selects which user types may pass:\r\n * - `[]` — any authenticated user (token required, type not checked).\r\n * - `\"admin\"` / `[\"admin\", \"staff\"]` — token required AND the user's\r\n * `userType` must be one of the listed types.\r\n *\r\n * There is no anonymous/optional mode: a request without a valid access\r\n * token is always rejected with `401`. Routes that should be public\r\n * simply omit the middleware.\r\n *\r\n * @example\r\n * router.get(\"/account\", authMiddleware([]), accountController);\r\n * router.get(\"/admin\", authMiddleware(\"admin\"), adminController);\r\n * router.get(\"/back-office\", authMiddleware([\"admin\", \"staff\"]), backOfficeController);\r\n */\r\nexport function authMiddleware(allowedUserType: string | string[]) {\r\n const allowedTypes = Array.isArray(allowedUserType) ? allowedUserType : [allowedUserType];\r\n\r\n const auth: Middleware = async (request: Request, response: Response) => {\r\n try {\r\n const authorizationValue = request.authorizationValue;\r\n\r\n if (!authorizationValue) {\r\n return response.unauthorized({\r\n error: t(\"auth.errors.missingAccessToken\"),\r\n errorCode: AuthErrorCodes.MissingAccessToken,\r\n });\r\n }\r\n\r\n // get current user jwt\r\n const user = await jwt.verify(authorizationValue);\r\n\r\n // store decoded access token object in request object\r\n request.decodedAccessToken = user;\r\n // use our own jwt verify to verify the token\r\n const accessToken = await AccessToken.first({\r\n token: authorizationValue,\r\n });\r\n\r\n if (!accessToken) {\r\n return response.unauthorized({\r\n error: t(\"auth.errors.invalidAccessToken\"),\r\n errorCode: AuthErrorCodes.InvalidAccessToken,\r\n });\r\n }\r\n\r\n // now, we need to get an instance of user using its corresponding model\r\n const userType = user.userType || accessToken.get(\"userType\");\r\n\r\n // check if the user type is allowed\r\n if (allowedTypes.length && !allowedTypes.includes(userType)) {\r\n return response.unauthorized({\r\n error: t(\"auth.errors.unauthorized\"),\r\n errorCode: AuthErrorCodes.Unauthorized,\r\n });\r\n }\r\n\r\n // get user model class\r\n const UserModel = config.key(`auth.userType.${userType}`);\r\n\r\n if (!UserModel) {\r\n throw new Error(`User type ${userType} is unknown type.`);\r\n }\r\n\r\n // get user model instance\r\n const currentUser = await UserModel.find(user.id);\r\n\r\n if (!currentUser) {\r\n accessToken.destroy();\r\n return response.unauthorized({\r\n error: t(\"auth.errors.invalidAccessToken\"),\r\n errorCode: AuthErrorCodes.InvalidAccessToken,\r\n });\r\n }\r\n\r\n // update last access\r\n // accessToken.set(\"lastAccess\", new Date());\r\n // await accessToken.save({ skipEvents: true });\r\n\r\n // set current user\r\n request.user = currentUser;\r\n } catch (err: any) {\r\n log.error(\"http\", \"auth\", err);\r\n\r\n request.clearCurrentUser();\r\n\r\n return response.unauthorized({\r\n error: t(\"auth.errors.invalidAccessToken\"),\r\n errorCode: AuthErrorCodes.InvalidAccessToken,\r\n });\r\n }\r\n };\r\n\r\n return auth;\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,eAAe,iBAAoC;CACjE,MAAM,eAAe,MAAM,QAAQ,eAAe,IAAI,kBAAkB,CAAC,eAAe;CAExF,MAAM,OAAmB,OAAO,SAAkB,aAAuB;EACvE,IAAI;GACF,MAAM,qBAAqB,QAAQ;GAEnC,IAAI,CAAC,oBACH,OAAO,SAAS,aAAa;IAC3B,OAAO,EAAE,gCAAgC;IACzC;GACF,CAAC;GAIH,MAAM,OAAO,MAAM,IAAI,OAAO,kBAAkB;GAGhD,QAAQ,qBAAqB;GAE7B,MAAM,cAAc,MAAM,YAAY,MAAM,EAC1C,OAAO,mBACT,CAAC;GAED,IAAI,CAAC,aACH,OAAO,SAAS,aAAa;IAC3B,OAAO,EAAE,gCAAgC;IACzC;GACF,CAAC;GAIH,MAAM,WAAW,KAAK,YAAY,YAAY,IAAI,UAAU;GAG5D,IAAI,aAAa,UAAU,CAAC,aAAa,SAAS,QAAQ,GACxD,OAAO,SAAS,aAAa;IAC3B,OAAO,EAAE,0BAA0B;IACnC;GACF,CAAC;GAIH,MAAM,YAAY,OAAO,IAAI,iBAAiB,UAAU;GAExD,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,aAAa,SAAS,kBAAkB;GAI1D,MAAM,cAAc,MAAM,UAAU,KAAK,KAAK,EAAE;GAEhD,IAAI,CAAC,aAAa;IAChB,YAAY,QAAQ;IACpB,OAAO,SAAS,aAAa;KAC3B,OAAO,EAAE,gCAAgC;KACzC;IACF,CAAC;GACH;GAOA,QAAQ,OAAO;EACjB,SAAS,KAAU;GACjB,IAAI,MAAM,QAAQ,QAAQ,GAAG;GAE7B,QAAQ,iBAAiB;GAEzB,OAAO,SAAS,aAAa;IAC3B,OAAO,EAAE,gCAAgC;IACzC;GACF,CAAC;EACH;CACF;CAEA,OAAO;AACT"}
1
+ {"version":3,"file":"auth.middleware.mjs","names":[],"sources":["../../../../../../../@warlock.js/auth/src/middleware/auth.middleware.ts"],"sourcesContent":["import { config, t, type Middleware, type Request, type Response } from \"@warlock.js/core\";\nimport { log } from \"@warlock.js/logger\";\nimport { AccessToken } from \"../models/access-token\";\nimport { jwt } from \"../services/jwt\";\nimport { AuthErrorCodes } from \"../utils/auth-error-codes\";\n\n/**\n * Decoded access-token claims the middleware reads. The full payload carries\n * more (`created_at`, `tokenType`, `iat`, `exp`) but only these drive routing.\n */\ntype DecodedAccessToken = {\n id: string | number;\n userType?: string;\n};\n\n/**\n * Build a route gate that always requires an authenticated request.\n *\n * The argument is mandatory and selects which user types may pass:\n * - `[]` — any authenticated user (token required, type not checked).\n * - `\"admin\"` / `[\"admin\", \"staff\"]` — token required AND the user's\n * `userType` must be one of the listed types.\n *\n * There is no anonymous/optional mode: a request without a valid access\n * token is always rejected with `401`. Routes that should be public\n * simply omit the middleware.\n *\n * @example\n * router.get(\"/account\", authMiddleware([]), accountController);\n * router.get(\"/admin\", authMiddleware(\"admin\"), adminController);\n * router.get(\"/back-office\", authMiddleware([\"admin\", \"staff\"]), backOfficeController);\n */\nexport function authMiddleware(allowedUserType: string | string[]) {\n const allowedTypes = Array.isArray(allowedUserType) ? allowedUserType : [allowedUserType];\n\n const auth: Middleware = async (request: Request, response: Response) => {\n try {\n const authorizationValue = request.authorizationValue;\n\n if (!authorizationValue) {\n return response.unauthorized({\n error: t(\"auth.errors.missingAccessToken\"),\n errorCode: AuthErrorCodes.MissingAccessToken,\n });\n }\n\n const decoded = await jwt.verify<DecodedAccessToken>(authorizationValue);\n\n request.decodedAccessToken = decoded;\n\n // A valid signature is not enough — the token must still exist in storage,\n // so deleting the row (logout) invalidates it before its JWT expiry.\n const AccessTokenModel = config.key(\"auth.accessToken.model\", AccessToken);\n const accessToken = await AccessTokenModel.findByToken(authorizationValue);\n\n if (!accessToken) {\n return response.unauthorized({\n error: t(\"auth.errors.invalidAccessToken\"),\n errorCode: AuthErrorCodes.InvalidAccessToken,\n });\n }\n\n const userType = decoded.userType ?? accessToken.userType;\n\n if (allowedTypes.length && !allowedTypes.includes(userType)) {\n return response.unauthorized({\n error: t(\"auth.errors.unauthorized\"),\n errorCode: AuthErrorCodes.Unauthorized,\n });\n }\n\n const UserModel = config.key(`auth.userType.${userType}`);\n\n if (!UserModel) {\n throw new Error(`User type ${userType} is unknown type.`);\n }\n\n const currentUser = await UserModel.find(decoded.id);\n\n if (!currentUser) {\n await accessToken.destroy();\n\n return response.unauthorized({\n error: t(\"auth.errors.invalidAccessToken\"),\n errorCode: AuthErrorCodes.InvalidAccessToken,\n });\n }\n\n request.user = currentUser;\n } catch (error: any) {\n log.error(\"http\", \"auth\", error);\n\n request.clearCurrentUser();\n\n return response.unauthorized({\n error: t(\"auth.errors.invalidAccessToken\"),\n errorCode: AuthErrorCodes.InvalidAccessToken,\n });\n }\n };\n\n return auth;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,eAAe,iBAAoC;CACjE,MAAM,eAAe,MAAM,QAAQ,eAAe,IAAI,kBAAkB,CAAC,eAAe;CAExF,MAAM,OAAmB,OAAO,SAAkB,aAAuB;EACvE,IAAI;GACF,MAAM,qBAAqB,QAAQ;GAEnC,IAAI,CAAC,oBACH,OAAO,SAAS,aAAa;IAC3B,OAAO,EAAE,gCAAgC;IACzC;GACF,CAAC;GAGH,MAAM,UAAU,MAAM,IAAI,OAA2B,kBAAkB;GAEvE,QAAQ,qBAAqB;GAK7B,MAAM,cAAc,MADK,OAAO,IAAI,0BAA0B,WACrB,CAAC,CAAC,YAAY,kBAAkB;GAEzE,IAAI,CAAC,aACH,OAAO,SAAS,aAAa;IAC3B,OAAO,EAAE,gCAAgC;IACzC;GACF,CAAC;GAGH,MAAM,WAAW,QAAQ,YAAY,YAAY;GAEjD,IAAI,aAAa,UAAU,CAAC,aAAa,SAAS,QAAQ,GACxD,OAAO,SAAS,aAAa;IAC3B,OAAO,EAAE,0BAA0B;IACnC;GACF,CAAC;GAGH,MAAM,YAAY,OAAO,IAAI,iBAAiB,UAAU;GAExD,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,aAAa,SAAS,kBAAkB;GAG1D,MAAM,cAAc,MAAM,UAAU,KAAK,QAAQ,EAAE;GAEnD,IAAI,CAAC,aAAa;IAChB,MAAM,YAAY,QAAQ;IAE1B,OAAO,SAAS,aAAa;KAC3B,OAAO,EAAE,gCAAgC;KACzC;IACF,CAAC;GACH;GAEA,QAAQ,OAAO;EACjB,SAAS,OAAY;GACnB,IAAI,MAAM,QAAQ,QAAQ,KAAK;GAE/B,QAAQ,iBAAiB;GAEzB,OAAO,SAAS,aAAa;IAC3B,OAAO,EAAE,gCAAgC;IACzC;GACF,CAAC;EACH;CACF;CAEA,OAAO;AACT"}
@@ -0,0 +1,2 @@
1
+ import { authMiddleware } from "./auth.middleware.mjs";
2
+ import { LoginThrottleIdentifier, LoginThrottleOptions, loginThrottleMiddleware } from "./login-throttle.middleware.mjs";
@@ -1,3 +1,4 @@
1
1
  import { authMiddleware } from "./auth.middleware.mjs";
2
+ import { loginThrottleMiddleware } from "./login-throttle.middleware.mjs";
2
3
 
3
4
  export { };
@@ -0,0 +1,116 @@
1
+ import { Middleware, Request, Response } from "@warlock.js/core";
2
+
3
+ //#region ../@warlock.js/auth/src/middleware/login-throttle.middleware.d.ts
4
+ /**
5
+ * Built-in identifier kinds the throttle knows how to extract from a request.
6
+ *
7
+ * - `"email"` tracks per-account, so an attacker can't spray one user from
8
+ * many IPs.
9
+ * - `"ip"` tracks per-source, so an attacker can't stuff many accounts from
10
+ * one IP.
11
+ *
12
+ * Lockout trips when ANY tracked identifier crosses `max` — defense-in-depth.
13
+ */
14
+ type LoginThrottleIdentifier = "email" | "ip";
15
+ /**
16
+ * Options for {@link loginThrottleMiddleware}.
17
+ *
18
+ * Failure-aware by design: only counts requests whose response is non-2xx
19
+ * (or whatever `isFailure` says) and resets on success, so a user who types
20
+ * a wrong password three times then the right one is not penalised.
21
+ *
22
+ * @example
23
+ * loginThrottleMiddleware({ max: 5, window: "15m", lockoutDuration: "15m" });
24
+ */
25
+ type LoginThrottleOptions = {
26
+ /**
27
+ * Failures allowed within the window before the identifier is locked.
28
+ *
29
+ * @default 5
30
+ */
31
+ max?: number;
32
+ /**
33
+ * Counting window. `ms`-format string (`"15m"`, `"1h"`) or seconds as a number.
34
+ *
35
+ * @default "15m"
36
+ */
37
+ window?: string | number;
38
+ /**
39
+ * Lockout duration once the threshold is tripped. `ms`-format string or seconds.
40
+ *
41
+ * @default "15m"
42
+ */
43
+ lockoutDuration?: string | number;
44
+ /**
45
+ * Identifiers tracked. Each is counted and locked independently.
46
+ *
47
+ * @default ["email", "ip"]
48
+ */
49
+ by?: LoginThrottleIdentifier[];
50
+ /**
51
+ * Credential field consulted when `"email"` is tracked. Switch to
52
+ * `"username"` (or any other input field) for systems that don't
53
+ * authenticate by email.
54
+ *
55
+ * @default "email"
56
+ */
57
+ identifierKey?: string;
58
+ /**
59
+ * Override the default error message returned with the 429 response.
60
+ */
61
+ errorMessage?: string;
62
+ /**
63
+ * Decide whether the response represents a failed login. Defaults to
64
+ * "non-2xx is a failure"; override if your controller signals failure
65
+ * through a 200-style body.
66
+ *
67
+ * @default (response) => !response.isOk
68
+ */
69
+ isFailure?: (response: Response) => boolean;
70
+ /**
71
+ * Compute the identifiers tracked for this request. Override to derive the
72
+ * account key from a header, a JWT subject, or any other shape that does
73
+ * not match the built-in `email` / `ip` extraction.
74
+ */
75
+ identify?: (request: Request) => string[];
76
+ };
77
+ /**
78
+ * Failure-aware login throttle. Locks an identifier (account, IP, or both)
79
+ * after too many failed login attempts within a window — without penalising
80
+ * a user who eventually types the right password.
81
+ *
82
+ * Mechanism (per identifier):
83
+ *
84
+ * 1. **Before the controller** — reject with `429` if a lock key is set,
85
+ * skipping the DB lookup AND the bcrypt verify (this also neutralises the
86
+ * CPU-DoS angle of brute-forcing).
87
+ * 2. **After the controller** — observe the outcome via `response.onSent`.
88
+ * On failure (non-2xx by default), bump a fixed-window counter; once it
89
+ * reaches `max`, set a lock key with `lockoutDuration` TTL. On success,
90
+ * clear both the counter and the lock for that identifier.
91
+ *
92
+ * Storage is `@warlock.js/cache` (a peer dependency of `@warlock.js/auth`,
93
+ * transitively guaranteed via `@warlock.js/core`). Lockout state is shared
94
+ * across replicas — correct security model, because an attacker can't bypass
95
+ * by round-robin to a different pod. Every cache call is guarded so a
96
+ * misconfigured driver fails open (a throttle outage must never escalate
97
+ * into a full auth outage).
98
+ *
99
+ * @example
100
+ * import { loginThrottleMiddleware } from "@warlock.js/auth";
101
+ * import { router } from "@warlock.js/core";
102
+ *
103
+ * router.post("/auth/login", loginController, {
104
+ * middleware: [loginThrottleMiddleware({ max: 5, window: "15m" })],
105
+ * });
106
+ *
107
+ * @example
108
+ * // Per-IP only — avoid account-lockout-as-DoS on anonymous-attack vectors
109
+ * router.post("/auth/refresh-token", refreshController, {
110
+ * middleware: [loginThrottleMiddleware({ by: ["ip"], max: 10, window: "1m" })],
111
+ * });
112
+ */
113
+ declare function loginThrottleMiddleware(options?: LoginThrottleOptions): Middleware;
114
+ //#endregion
115
+ export { LoginThrottleIdentifier, LoginThrottleOptions, loginThrottleMiddleware };
116
+ //# sourceMappingURL=login-throttle.middleware.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-throttle.middleware.d.mts","names":[],"sources":["../../../../../../../@warlock.js/auth/src/middleware/login-throttle.middleware.ts"],"mappings":";;;;;AAeA;;;;AAAmC;AAYnC;;;KAZY,uBAAA;;;;;;;;;;;KAYA,oBAAA;EAkDV;;;;;EA5CA,GAAA;EAmD4B;AAAA;AA6E9B;;;EAzHE,MAAA;EAyH+C;;;;AAAsC;EAlHrF,eAAA;;;;;;EAOA,EAAA,GAAK,uBAAA;;;;;;;;EASL,aAAA;;;;EAKA,YAAA;;;;;;;;EASA,SAAA,IAAa,QAAA,EAAU,QAAA;;;;;;EAOvB,QAAA,IAAY,OAAA,EAAS,OAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6EP,uBAAA,CAAwB,OAAA,GAAS,oBAAA,GAA4B,UAAU"}
@@ -0,0 +1,105 @@
1
+ import { AuthErrorCodes } from "../utils/auth-error-codes.mjs";
2
+ import { t } from "@warlock.js/core";
3
+ import { log } from "@warlock.js/logger";
4
+ import { cache } from "@warlock.js/cache";
5
+
6
+ //#region ../@warlock.js/auth/src/middleware/login-throttle.middleware.ts
7
+ const COUNTER_PREFIX = "auth.throttle.count.";
8
+ const LOCK_PREFIX = "auth.throttle.lock.";
9
+ const counterKey = (identifier) => `${COUNTER_PREFIX}${identifier}`;
10
+ const lockKey = (identifier) => `${LOCK_PREFIX}${identifier}`;
11
+ /**
12
+ * Built-in identifier extractor — reads the configured credential field from
13
+ * the request body for the per-account key, and `request.detectIp()` for the
14
+ * per-source key. Untruthy values are dropped; a request that yields zero
15
+ * identifiers is left untracked so the middleware no-ops for it.
16
+ */
17
+ function buildDefaultIdentifiers(request, tracked, credentialField) {
18
+ const identifiers = [];
19
+ if (tracked.includes("email")) {
20
+ const value = request.input(credentialField);
21
+ if (typeof value === "string" && value.length > 0) identifiers.push(`email.${value.toLowerCase()}`);
22
+ }
23
+ if (tracked.includes("ip")) {
24
+ const ip = request.detectIp();
25
+ if (ip) identifiers.push(`ip.${ip}`);
26
+ }
27
+ return identifiers;
28
+ }
29
+ /**
30
+ * Failure-aware login throttle. Locks an identifier (account, IP, or both)
31
+ * after too many failed login attempts within a window — without penalising
32
+ * a user who eventually types the right password.
33
+ *
34
+ * Mechanism (per identifier):
35
+ *
36
+ * 1. **Before the controller** — reject with `429` if a lock key is set,
37
+ * skipping the DB lookup AND the bcrypt verify (this also neutralises the
38
+ * CPU-DoS angle of brute-forcing).
39
+ * 2. **After the controller** — observe the outcome via `response.onSent`.
40
+ * On failure (non-2xx by default), bump a fixed-window counter; once it
41
+ * reaches `max`, set a lock key with `lockoutDuration` TTL. On success,
42
+ * clear both the counter and the lock for that identifier.
43
+ *
44
+ * Storage is `@warlock.js/cache` (a peer dependency of `@warlock.js/auth`,
45
+ * transitively guaranteed via `@warlock.js/core`). Lockout state is shared
46
+ * across replicas — correct security model, because an attacker can't bypass
47
+ * by round-robin to a different pod. Every cache call is guarded so a
48
+ * misconfigured driver fails open (a throttle outage must never escalate
49
+ * into a full auth outage).
50
+ *
51
+ * @example
52
+ * import { loginThrottleMiddleware } from "@warlock.js/auth";
53
+ * import { router } from "@warlock.js/core";
54
+ *
55
+ * router.post("/auth/login", loginController, {
56
+ * middleware: [loginThrottleMiddleware({ max: 5, window: "15m" })],
57
+ * });
58
+ *
59
+ * @example
60
+ * // Per-IP only — avoid account-lockout-as-DoS on anonymous-attack vectors
61
+ * router.post("/auth/refresh-token", refreshController, {
62
+ * middleware: [loginThrottleMiddleware({ by: ["ip"], max: 10, window: "1m" })],
63
+ * });
64
+ */
65
+ function loginThrottleMiddleware(options = {}) {
66
+ const max = options.max ?? 5;
67
+ const window = options.window ?? "15m";
68
+ const lockoutDuration = options.lockoutDuration ?? "15m";
69
+ const tracked = options.by ?? ["email", "ip"];
70
+ const credentialField = options.identifierKey ?? "email";
71
+ return async (request, response) => {
72
+ const identifiers = options.identify ? options.identify(request) : buildDefaultIdentifiers(request, tracked, credentialField);
73
+ if (identifiers.length === 0) return;
74
+ try {
75
+ for (const identifier of identifiers) if (await cache.get(lockKey(identifier))) return response.tooManyRequests({
76
+ error: options.errorMessage ?? t("auth.errors.tooManyAttempts"),
77
+ errorCode: "EC004"
78
+ });
79
+ } catch (error) {
80
+ log.error("auth", "login-throttle", error);
81
+ return;
82
+ }
83
+ response.onSent(async (resolvedResponse) => {
84
+ try {
85
+ if (!(options.isFailure ? options.isFailure(resolvedResponse) : !resolvedResponse.isOk)) {
86
+ for (const identifier of identifiers) {
87
+ await cache.remove(counterKey(identifier));
88
+ await cache.remove(lockKey(identifier));
89
+ }
90
+ return;
91
+ }
92
+ for (const identifier of identifiers) {
93
+ const ttlOption = await cache.get(counterKey(identifier)) === null ? { ttl: window } : void 0;
94
+ if ((await cache.update(counterKey(identifier), (current) => (current ?? 0) + 1, ttlOption) ?? 0) >= max) await cache.set(lockKey(identifier), true, { ttl: lockoutDuration });
95
+ }
96
+ } catch (error) {
97
+ log.error("auth", "login-throttle", error);
98
+ }
99
+ });
100
+ };
101
+ }
102
+
103
+ //#endregion
104
+ export { loginThrottleMiddleware };
105
+ //# sourceMappingURL=login-throttle.middleware.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-throttle.middleware.mjs","names":[],"sources":["../../../../../../../@warlock.js/auth/src/middleware/login-throttle.middleware.ts"],"sourcesContent":["import { cache } from \"@warlock.js/cache\";\nimport { t, type Middleware, type Request, type Response } from \"@warlock.js/core\";\nimport { log } from \"@warlock.js/logger\";\nimport { AuthErrorCodes } from \"../utils/auth-error-codes\";\n\n/**\n * Built-in identifier kinds the throttle knows how to extract from a request.\n *\n * - `\"email\"` tracks per-account, so an attacker can't spray one user from\n * many IPs.\n * - `\"ip\"` tracks per-source, so an attacker can't stuff many accounts from\n * one IP.\n *\n * Lockout trips when ANY tracked identifier crosses `max` — defense-in-depth.\n */\nexport type LoginThrottleIdentifier = \"email\" | \"ip\";\n\n/**\n * Options for {@link loginThrottleMiddleware}.\n *\n * Failure-aware by design: only counts requests whose response is non-2xx\n * (or whatever `isFailure` says) and resets on success, so a user who types\n * a wrong password three times then the right one is not penalised.\n *\n * @example\n * loginThrottleMiddleware({ max: 5, window: \"15m\", lockoutDuration: \"15m\" });\n */\nexport type LoginThrottleOptions = {\n /**\n * Failures allowed within the window before the identifier is locked.\n *\n * @default 5\n */\n max?: number;\n\n /**\n * Counting window. `ms`-format string (`\"15m\"`, `\"1h\"`) or seconds as a number.\n *\n * @default \"15m\"\n */\n window?: string | number;\n\n /**\n * Lockout duration once the threshold is tripped. `ms`-format string or seconds.\n *\n * @default \"15m\"\n */\n lockoutDuration?: string | number;\n\n /**\n * Identifiers tracked. Each is counted and locked independently.\n *\n * @default [\"email\", \"ip\"]\n */\n by?: LoginThrottleIdentifier[];\n\n /**\n * Credential field consulted when `\"email\"` is tracked. Switch to\n * `\"username\"` (or any other input field) for systems that don't\n * authenticate by email.\n *\n * @default \"email\"\n */\n identifierKey?: string;\n\n /**\n * Override the default error message returned with the 429 response.\n */\n errorMessage?: string;\n\n /**\n * Decide whether the response represents a failed login. Defaults to\n * \"non-2xx is a failure\"; override if your controller signals failure\n * through a 200-style body.\n *\n * @default (response) => !response.isOk\n */\n isFailure?: (response: Response) => boolean;\n\n /**\n * Compute the identifiers tracked for this request. Override to derive the\n * account key from a header, a JWT subject, or any other shape that does\n * not match the built-in `email` / `ip` extraction.\n */\n identify?: (request: Request) => string[];\n};\n\nconst COUNTER_PREFIX = \"auth.throttle.count.\";\nconst LOCK_PREFIX = \"auth.throttle.lock.\";\n\nconst counterKey = (identifier: string): string => `${COUNTER_PREFIX}${identifier}`;\nconst lockKey = (identifier: string): string => `${LOCK_PREFIX}${identifier}`;\n\n/**\n * Built-in identifier extractor — reads the configured credential field from\n * the request body for the per-account key, and `request.detectIp()` for the\n * per-source key. Untruthy values are dropped; a request that yields zero\n * identifiers is left untracked so the middleware no-ops for it.\n */\nfunction buildDefaultIdentifiers(\n request: Request,\n tracked: LoginThrottleIdentifier[],\n credentialField: string,\n): string[] {\n const identifiers: string[] = [];\n\n if (tracked.includes(\"email\")) {\n const value = request.input(credentialField);\n\n if (typeof value === \"string\" && value.length > 0) {\n identifiers.push(`email.${value.toLowerCase()}`);\n }\n }\n\n if (tracked.includes(\"ip\")) {\n const ip = request.detectIp();\n\n if (ip) {\n identifiers.push(`ip.${ip}`);\n }\n }\n\n return identifiers;\n}\n\n/**\n * Failure-aware login throttle. Locks an identifier (account, IP, or both)\n * after too many failed login attempts within a window — without penalising\n * a user who eventually types the right password.\n *\n * Mechanism (per identifier):\n *\n * 1. **Before the controller** — reject with `429` if a lock key is set,\n * skipping the DB lookup AND the bcrypt verify (this also neutralises the\n * CPU-DoS angle of brute-forcing).\n * 2. **After the controller** — observe the outcome via `response.onSent`.\n * On failure (non-2xx by default), bump a fixed-window counter; once it\n * reaches `max`, set a lock key with `lockoutDuration` TTL. On success,\n * clear both the counter and the lock for that identifier.\n *\n * Storage is `@warlock.js/cache` (a peer dependency of `@warlock.js/auth`,\n * transitively guaranteed via `@warlock.js/core`). Lockout state is shared\n * across replicas — correct security model, because an attacker can't bypass\n * by round-robin to a different pod. Every cache call is guarded so a\n * misconfigured driver fails open (a throttle outage must never escalate\n * into a full auth outage).\n *\n * @example\n * import { loginThrottleMiddleware } from \"@warlock.js/auth\";\n * import { router } from \"@warlock.js/core\";\n *\n * router.post(\"/auth/login\", loginController, {\n * middleware: [loginThrottleMiddleware({ max: 5, window: \"15m\" })],\n * });\n *\n * @example\n * // Per-IP only — avoid account-lockout-as-DoS on anonymous-attack vectors\n * router.post(\"/auth/refresh-token\", refreshController, {\n * middleware: [loginThrottleMiddleware({ by: [\"ip\"], max: 10, window: \"1m\" })],\n * });\n */\nexport function loginThrottleMiddleware(options: LoginThrottleOptions = {}): Middleware {\n const max = options.max ?? 5;\n const window = options.window ?? \"15m\";\n const lockoutDuration = options.lockoutDuration ?? \"15m\";\n const tracked = options.by ?? [\"email\", \"ip\"];\n const credentialField = options.identifierKey ?? \"email\";\n\n return async (request: Request, response: Response) => {\n const identifiers = options.identify\n ? options.identify(request)\n : buildDefaultIdentifiers(request, tracked, credentialField);\n\n if (identifiers.length === 0) return;\n\n try {\n for (const identifier of identifiers) {\n const locked = await cache.get(lockKey(identifier));\n\n if (locked) {\n return response.tooManyRequests({\n error: options.errorMessage ?? t(\"auth.errors.tooManyAttempts\"),\n errorCode: AuthErrorCodes.TooManyAttempts,\n });\n }\n }\n } catch (error) {\n // Fail open — a cache outage (driver not initialised, network blip)\n // must never escalate into a full auth outage. Log and let the request\n // through; the onSent recorder below is independently guarded so an\n // outage there is also harmless.\n log.error(\"auth\", \"login-throttle\", error);\n\n return;\n }\n\n response.onSent(async (resolvedResponse: Response) => {\n try {\n const failed = options.isFailure\n ? options.isFailure(resolvedResponse)\n : !resolvedResponse.isOk;\n\n if (!failed) {\n for (const identifier of identifiers) {\n await cache.remove(counterKey(identifier));\n await cache.remove(lockKey(identifier));\n }\n\n return;\n }\n\n for (const identifier of identifiers) {\n const existing = await cache.get(counterKey(identifier));\n const ttlOption = existing === null ? { ttl: window } : undefined;\n const count = await cache.update<number>(\n counterKey(identifier),\n (current) => (current ?? 0) + 1,\n ttlOption,\n );\n\n if ((count ?? 0) >= max) {\n await cache.set(lockKey(identifier), true, { ttl: lockoutDuration });\n }\n }\n } catch (error) {\n log.error(\"auth\", \"login-throttle\", error);\n }\n });\n };\n}\n"],"mappings":";;;;;;AAuFA,MAAM,iBAAiB;AACvB,MAAM,cAAc;AAEpB,MAAM,cAAc,eAA+B,GAAG,iBAAiB;AACvE,MAAM,WAAW,eAA+B,GAAG,cAAc;;;;;;;AAQjE,SAAS,wBACP,SACA,SACA,iBACU;CACV,MAAM,cAAwB,CAAC;CAE/B,IAAI,QAAQ,SAAS,OAAO,GAAG;EAC7B,MAAM,QAAQ,QAAQ,MAAM,eAAe;EAE3C,IAAI,OAAO,UAAU,YAAY,MAAM,SAAS,GAC9C,YAAY,KAAK,SAAS,MAAM,YAAY,GAAG;CAEnD;CAEA,IAAI,QAAQ,SAAS,IAAI,GAAG;EAC1B,MAAM,KAAK,QAAQ,SAAS;EAE5B,IAAI,IACF,YAAY,KAAK,MAAM,IAAI;CAE/B;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,SAAgB,wBAAwB,UAAgC,CAAC,GAAe;CACtF,MAAM,MAAM,QAAQ,OAAO;CAC3B,MAAM,SAAS,QAAQ,UAAU;CACjC,MAAM,kBAAkB,QAAQ,mBAAmB;CACnD,MAAM,UAAU,QAAQ,MAAM,CAAC,SAAS,IAAI;CAC5C,MAAM,kBAAkB,QAAQ,iBAAiB;CAEjD,OAAO,OAAO,SAAkB,aAAuB;EACrD,MAAM,cAAc,QAAQ,WACxB,QAAQ,SAAS,OAAO,IACxB,wBAAwB,SAAS,SAAS,eAAe;EAE7D,IAAI,YAAY,WAAW,GAAG;EAE9B,IAAI;GACF,KAAK,MAAM,cAAc,aAGvB,IAAI,MAFiB,MAAM,IAAI,QAAQ,UAAU,CAAC,GAGhD,OAAO,SAAS,gBAAgB;IAC9B,OAAO,QAAQ,gBAAgB,EAAE,6BAA6B;IAC9D;GACF,CAAC;EAGP,SAAS,OAAO;GAKd,IAAI,MAAM,QAAQ,kBAAkB,KAAK;GAEzC;EACF;EAEA,SAAS,OAAO,OAAO,qBAA+B;GACpD,IAAI;IAKF,IAAI,EAJW,QAAQ,YACnB,QAAQ,UAAU,gBAAgB,IAClC,CAAC,iBAAiB,OAET;KACX,KAAK,MAAM,cAAc,aAAa;MACpC,MAAM,MAAM,OAAO,WAAW,UAAU,CAAC;MACzC,MAAM,MAAM,OAAO,QAAQ,UAAU,CAAC;KACxC;KAEA;IACF;IAEA,KAAK,MAAM,cAAc,aAAa;KAEpC,MAAM,YAAY,MADK,MAAM,IAAI,WAAW,UAAU,CAAC,MACxB,OAAO,EAAE,KAAK,OAAO,IAAI;KAOxD,KAAK,MANe,MAAM,OACxB,WAAW,UAAU,IACpB,aAAa,WAAW,KAAK,GAC9B,SACF,KAEc,MAAM,KAClB,MAAM,MAAM,IAAI,QAAQ,UAAU,GAAG,MAAM,EAAE,KAAK,gBAAgB,CAAC;IAEvE;GACF,SAAS,OAAO;IACd,IAAI,MAAM,QAAQ,kBAAkB,KAAK;GAC3C;EACF,CAAC;CACH;AACF"}
@@ -1,13 +1,55 @@
1
+ import { Auth } from "../auth.model.mjs";
1
2
  import { Model } from "@warlock.js/cascade";
2
3
 
3
- //#region ../../@warlock.js/auth/src/models/access-token/access-token.model.d.ts
4
+ //#region ../@warlock.js/auth/src/models/access-token/access-token.model.d.ts
5
+ /**
6
+ * Seal schema for the persisted access-token record. Exported so an override
7
+ * can spread it and add columns (e.g. a tenant key) without re-declaring the
8
+ * base shape.
9
+ *
10
+ * `last_access` and the `is_active` soft-revoke flag were removed — neither was
11
+ * ever read; access tokens are revoked by deleting the row.
12
+ */
13
+ declare const accessTokenSchema: any;
14
+ /**
15
+ * Persisted access-token record + the data layer for access tokens.
16
+ *
17
+ * **Role.** Owns access-token persistence and lookup. The middleware checks a
18
+ * presented JWT against this table so deleting a row (logout) invalidates the
19
+ * token immediately, before its JWT expiry. The auth service goes through the
20
+ * named statics exclusively, so it never hard-codes a column name and an
21
+ * override can rename/add columns by registering under
22
+ * `config.auth.accessToken.model`.
23
+ */
4
24
  declare class AccessToken extends Model {
5
- /**
6
- * {@inheritDoc}
7
- */
8
25
  static table: string;
9
26
  static schema: any;
27
+ /** The user this token was issued for. */
28
+ get userId(): any;
29
+ /** The user-type slug this token was issued for. */
30
+ get userType(): string;
31
+ /**
32
+ * Persist a freshly-signed access token for the user.
33
+ */
34
+ static issue(user: Auth, token: string, expiresAt: Date): any;
35
+ /**
36
+ * Find an access-token row by its raw token string.
37
+ */
38
+ static findByToken(token: string): Promise<AccessToken | null>;
39
+ /**
40
+ * Delete a specific token that belongs to the given user.
41
+ */
42
+ static deleteForUser(user: Auth, token: string): any;
43
+ /**
44
+ * Delete every access token belonging to the user.
45
+ */
46
+ static deleteAllForUser(user: Auth): any;
47
+ /**
48
+ * Hard-delete every expired access-token row. Returns the number removed.
49
+ * Runs from the `auth.cleanup` CLI command (a cold batch path).
50
+ */
51
+ static purgeExpired(): Promise<number>;
10
52
  }
11
53
  //#endregion
12
- export { AccessToken };
54
+ export { AccessToken, accessTokenSchema };
13
55
  //# sourceMappingURL=access-token.model.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"access-token.model.d.mts","names":[],"sources":["../../../../../../../@warlock.js/auth/src/models/access-token/access-token.model.ts"],"mappings":";;;cAWa,WAAA,SAAoB,KAAK;;AAAtC;;SAIgB,KAAA;EAAA,OAEA,MAAA;AAAA"}
1
+ {"version":3,"file":"access-token.model.d.mts","names":[],"sources":["../../../../../../../../@warlock.js/auth/src/models/access-token/access-token.model.ts"],"mappings":";;;;;;AAYA;;;;AAKE;AAYF;cAjBa,iBAAA;;;;;;;;;;;cAiBA,WAAA,SAAoB,KAAA;EAAA,OACjB,KAAA;EAAA,OAEA,MAAA;EAAA;EAAA,IAGH,MAAA;EAKA;EAAA,IAAA,QAAA;EAOe;;;EAAA,OAAZ,KAAA,CAAM,IAAA,EAAM,IAAA,EAAM,KAAA,UAAe,SAAA,EAAW,IAAA;EAAX;;;EAAA,OAYjC,WAAA,CAAY,KAAA,WAAgB,OAAA,CAAQ,WAAA;EAAA;;;EAAA,OAOpC,aAAA,CAAc,IAAA,EAAM,IAAA,EAAM,KAAA;EAAA;;;EAAA,OAO1B,gBAAA,CAAiB,IAAA,EAAM,IAAA;EAQjB;;;AAAuB;EAAvB,OAAA,YAAA,IAAgB,OAAA;AAAA"}
@@ -1,14 +1,31 @@
1
1
  import { Model } from "@warlock.js/cascade";
2
2
  import { v } from "@warlock.js/seal";
3
3
 
4
- //#region ../../@warlock.js/auth/src/models/access-token/access-token.model.ts
4
+ //#region ../@warlock.js/auth/src/models/access-token/access-token.model.ts
5
+ /**
6
+ * Seal schema for the persisted access-token record. Exported so an override
7
+ * can spread it and add columns (e.g. a tenant key) without re-declaring the
8
+ * base shape.
9
+ *
10
+ * `last_access` and the `is_active` soft-revoke flag were removed — neither was
11
+ * ever read; access tokens are revoked by deleting the row.
12
+ */
5
13
  const accessTokenSchema = v.object({
6
14
  token: v.string().required(),
7
- last_access: v.date().defaultNow().optional(),
8
15
  user_id: v.scalar().required(),
9
16
  user_type: v.string().required(),
10
- is_active: v.boolean().default(true)
17
+ expires_at: v.date().required()
11
18
  });
19
+ /**
20
+ * Persisted access-token record + the data layer for access tokens.
21
+ *
22
+ * **Role.** Owns access-token persistence and lookup. The middleware checks a
23
+ * presented JWT against this table so deleting a row (logout) invalidates the
24
+ * token immediately, before its JWT expiry. The auth service goes through the
25
+ * named statics exclusively, so it never hard-codes a column name and an
26
+ * override can rename/add columns by registering under
27
+ * `config.auth.accessToken.model`.
28
+ */
12
29
  var AccessToken = class extends Model {
13
30
  static {
14
31
  this.table = "access_tokens";
@@ -16,8 +33,57 @@ var AccessToken = class extends Model {
16
33
  static {
17
34
  this.schema = accessTokenSchema;
18
35
  }
36
+ /** The user this token was issued for. */
37
+ get userId() {
38
+ return this.get("user_id");
39
+ }
40
+ /** The user-type slug this token was issued for. */
41
+ get userType() {
42
+ return this.get("user_type");
43
+ }
44
+ /**
45
+ * Persist a freshly-signed access token for the user.
46
+ */
47
+ static issue(user, token, expiresAt) {
48
+ return this.create({
49
+ token,
50
+ user_id: user.id,
51
+ user_type: user.userType,
52
+ expires_at: expiresAt
53
+ });
54
+ }
55
+ /**
56
+ * Find an access-token row by its raw token string.
57
+ */
58
+ static findByToken(token) {
59
+ return this.first({ token });
60
+ }
61
+ /**
62
+ * Delete a specific token that belongs to the given user.
63
+ */
64
+ static deleteForUser(user, token) {
65
+ return this.delete({
66
+ token,
67
+ user_id: user.id
68
+ });
69
+ }
70
+ /**
71
+ * Delete every access token belonging to the user.
72
+ */
73
+ static deleteAllForUser(user) {
74
+ return this.delete({ user_id: user.id });
75
+ }
76
+ /**
77
+ * Hard-delete every expired access-token row. Returns the number removed.
78
+ * Runs from the `auth.cleanup` CLI command (a cold batch path).
79
+ */
80
+ static async purgeExpired() {
81
+ const expiredTokens = await this.query().where("expires_at", "<", /* @__PURE__ */ new Date()).get();
82
+ for (const token of expiredTokens) await token.destroy();
83
+ return expiredTokens.length;
84
+ }
19
85
  };
20
86
 
21
87
  //#endregion
22
- export { AccessToken };
88
+ export { AccessToken, accessTokenSchema };
23
89
  //# sourceMappingURL=access-token.model.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"access-token.model.mjs","names":[],"sources":["../../../../../../../@warlock.js/auth/src/models/access-token/access-token.model.ts"],"sourcesContent":["import { Model } from \"@warlock.js/cascade\";\r\nimport { v } from \"@warlock.js/seal\";\r\n\r\nconst accessTokenSchema = v.object({\r\n token: v.string().required(),\r\n last_access: v.date().defaultNow().optional(),\r\n user_id: v.scalar().required(),\r\n user_type: v.string().required(),\r\n is_active: v.boolean().default(true),\r\n});\r\n\r\nexport class AccessToken extends Model {\r\n /**\r\n * {@inheritDoc}\r\n */\r\n public static table = \"access_tokens\";\r\n\r\n public static schema = accessTokenSchema;\r\n}\r\n"],"mappings":";;;;AAGA,MAAM,oBAAoB,EAAE,OAAO;CACjC,OAAO,EAAE,OAAO,EAAE,SAAS;CAC3B,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS;CAC5C,SAAS,EAAE,OAAO,EAAE,SAAS;CAC7B,WAAW,EAAE,OAAO,EAAE,SAAS;CAC/B,WAAW,EAAE,QAAQ,EAAE,QAAQ,IAAI;AACrC,CAAC;AAED,IAAa,cAAb,cAAiC,MAAM;;eAIf;;;gBAEC;;AACzB"}
1
+ {"version":3,"file":"access-token.model.mjs","names":[],"sources":["../../../../../../../../@warlock.js/auth/src/models/access-token/access-token.model.ts"],"sourcesContent":["import { Model } from \"@warlock.js/cascade\";\nimport { v } from \"@warlock.js/seal\";\nimport type { Auth } from \"../auth.model\";\n\n/**\n * Seal schema for the persisted access-token record. Exported so an override\n * can spread it and add columns (e.g. a tenant key) without re-declaring the\n * base shape.\n *\n * `last_access` and the `is_active` soft-revoke flag were removed — neither was\n * ever read; access tokens are revoked by deleting the row.\n */\nexport const accessTokenSchema = v.object({\n token: v.string().required(),\n user_id: v.scalar().required(),\n user_type: v.string().required(),\n expires_at: v.date().required(),\n});\n\n/**\n * Persisted access-token record + the data layer for access tokens.\n *\n * **Role.** Owns access-token persistence and lookup. The middleware checks a\n * presented JWT against this table so deleting a row (logout) invalidates the\n * token immediately, before its JWT expiry. The auth service goes through the\n * named statics exclusively, so it never hard-codes a column name and an\n * override can rename/add columns by registering under\n * `config.auth.accessToken.model`.\n */\nexport class AccessToken extends Model {\n public static table = \"access_tokens\";\n\n public static schema = accessTokenSchema;\n\n /** The user this token was issued for. */\n public get userId() {\n return this.get(\"user_id\");\n }\n\n /** The user-type slug this token was issued for. */\n public get userType(): string {\n return this.get(\"user_type\");\n }\n\n /**\n * Persist a freshly-signed access token for the user.\n */\n public static issue(user: Auth, token: string, expiresAt: Date) {\n return this.create({\n token,\n user_id: user.id,\n user_type: user.userType,\n expires_at: expiresAt,\n });\n }\n\n /**\n * Find an access-token row by its raw token string.\n */\n public static findByToken(token: string): Promise<AccessToken | null> {\n return this.first({ token });\n }\n\n /**\n * Delete a specific token that belongs to the given user.\n */\n public static deleteForUser(user: Auth, token: string) {\n return this.delete({ token, user_id: user.id });\n }\n\n /**\n * Delete every access token belonging to the user.\n */\n public static deleteAllForUser(user: Auth) {\n return this.delete({ user_id: user.id });\n }\n\n /**\n * Hard-delete every expired access-token row. Returns the number removed.\n * Runs from the `auth.cleanup` CLI command (a cold batch path).\n */\n public static async purgeExpired(): Promise<number> {\n const expiredTokens = await this.query().where(\"expires_at\", \"<\", new Date()).get();\n\n for (const token of expiredTokens) {\n await token.destroy();\n }\n\n return expiredTokens.length;\n }\n}\n"],"mappings":";;;;;;;;;;;;AAYA,MAAa,oBAAoB,EAAE,OAAO;CACxC,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAC3B,SAAS,EAAE,OAAO,CAAC,CAAC,SAAS;CAC7B,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS;CAC/B,YAAY,EAAE,KAAK,CAAC,CAAC,SAAS;AAChC,CAAC;;;;;;;;;;;AAYD,IAAa,cAAb,cAAiC,MAAM;;eACf;;;gBAEC;;;CAGvB,IAAW,SAAS;EAClB,OAAO,KAAK,IAAI,SAAS;CAC3B;;CAGA,IAAW,WAAmB;EAC5B,OAAO,KAAK,IAAI,WAAW;CAC7B;;;;CAKA,OAAc,MAAM,MAAY,OAAe,WAAiB;EAC9D,OAAO,KAAK,OAAO;GACjB;GACA,SAAS,KAAK;GACd,WAAW,KAAK;GAChB,YAAY;EACd,CAAC;CACH;;;;CAKA,OAAc,YAAY,OAA4C;EACpE,OAAO,KAAK,MAAM,EAAE,MAAM,CAAC;CAC7B;;;;CAKA,OAAc,cAAc,MAAY,OAAe;EACrD,OAAO,KAAK,OAAO;GAAE;GAAO,SAAS,KAAK;EAAG,CAAC;CAChD;;;;CAKA,OAAc,iBAAiB,MAAY;EACzC,OAAO,KAAK,OAAO,EAAE,SAAS,KAAK,GAAG,CAAC;CACzC;;;;;CAMA,aAAoB,eAAgC;EAClD,MAAM,gBAAgB,MAAM,KAAK,MAAM,CAAC,CAAC,MAAM,cAAc,qBAAK,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI;EAElF,KAAK,MAAM,SAAS,eAClB,MAAM,MAAM,QAAQ;EAGtB,OAAO,cAAc;CACvB;AACF"}
@@ -1 +1 @@
1
- import { AccessToken } from "./access-token.model.mjs";
1
+ import { AccessToken, accessTokenSchema } from "./access-token.model.mjs";
@@ -1,3 +1,3 @@
1
- import { AccessToken } from "./access-token.model.mjs";
1
+ import { AccessToken, accessTokenSchema } from "./access-token.model.mjs";
2
2
 
3
3
  export { };
@@ -1,17 +1,16 @@
1
1
  import { AccessToken } from "./access-token.model.mjs";
2
2
  import { migrate } from "@warlock.js/cascade";
3
3
 
4
- //#region ../../@warlock.js/auth/src/models/access-token/migration.ts
4
+ //#region ../@warlock.js/auth/src/models/access-token/migration.ts
5
5
  const AccessTokenMigration = migrate(AccessToken, {
6
6
  name: "accessToken",
7
7
  up() {
8
8
  this.createTableIfNotExists();
9
9
  this.primaryUuid();
10
10
  this.text("token").unique();
11
- this.timestamp("last_access").nullable();
12
11
  this.uuid("user_id").index();
13
12
  this.string("user_type", 50).nullable();
14
- this.boolean("is_active").nullable();
13
+ this.timestamp("expires_at").index().nullable();
15
14
  this.timestamps();
16
15
  },
17
16
  down() {
@@ -1 +1 @@
1
- {"version":3,"file":"migration.mjs","names":[],"sources":["../../../../../../../@warlock.js/auth/src/models/access-token/migration.ts"],"sourcesContent":["import { migrate } from \"@warlock.js/cascade\";\r\nimport { AccessToken } from \"./access-token.model\";\r\n\r\nexport const AccessTokenMigration = migrate(AccessToken, {\r\n name: \"accessToken\",\r\n up() {\r\n // Create table\r\n this.createTableIfNotExists();\r\n\r\n // Primary key\r\n this.primaryUuid();\r\n\r\n // Token field\r\n this.text(\"token\").unique();\r\n this.timestamp(\"last_access\").nullable();\r\n\r\n // User reference (flat columns for cross-driver compatibility)\r\n this.uuid(\"user_id\").index();\r\n this.string(\"user_type\", 50).nullable();\r\n\r\n // Status (for token revocation)\r\n this.boolean(\"is_active\").nullable();\r\n\r\n // Timestamps\r\n this.timestamps();\r\n },\r\n down() {\r\n this.dropTableIfExists();\r\n },\r\n});\r\n"],"mappings":";;;;AAGA,MAAa,uBAAuB,QAAQ,aAAa;CACvD,MAAM;CACN,KAAK;EAEH,KAAK,uBAAuB;EAG5B,KAAK,YAAY;EAGjB,KAAK,KAAK,OAAO,EAAE,OAAO;EAC1B,KAAK,UAAU,aAAa,EAAE,SAAS;EAGvC,KAAK,KAAK,SAAS,EAAE,MAAM;EAC3B,KAAK,OAAO,aAAa,EAAE,EAAE,SAAS;EAGtC,KAAK,QAAQ,WAAW,EAAE,SAAS;EAGnC,KAAK,WAAW;CAClB;CACA,OAAO;EACL,KAAK,kBAAkB;CACzB;AACF,CAAC"}
1
+ {"version":3,"file":"migration.mjs","names":[],"sources":["../../../../../../../../@warlock.js/auth/src/models/access-token/migration.ts"],"sourcesContent":["import { migrate } from \"@warlock.js/cascade\";\nimport { AccessToken } from \"./access-token.model\";\n\nexport const AccessTokenMigration = migrate(AccessToken, {\n name: \"accessToken\",\n up() {\n // Create table\n this.createTableIfNotExists();\n\n // Primary key\n this.primaryUuid();\n\n // Token field\n this.text(\"token\").unique();\n\n // User reference (flat columns for cross-driver compatibility)\n this.uuid(\"user_id\").index();\n this.string(\"user_type\", 50).nullable();\n\n // Expiry enables server-side cleanup of stale access-token rows\n this.timestamp(\"expires_at\").index().nullable();\n\n // Timestamps\n this.timestamps();\n },\n down() {\n this.dropTableIfExists();\n },\n});\n"],"mappings":";;;;AAGA,MAAa,uBAAuB,QAAQ,aAAa;CACvD,MAAM;CACN,KAAK;EAEH,KAAK,uBAAuB;EAG5B,KAAK,YAAY;EAGjB,KAAK,KAAK,OAAO,CAAC,CAAC,OAAO;EAG1B,KAAK,KAAK,SAAS,CAAC,CAAC,MAAM;EAC3B,KAAK,OAAO,aAAa,EAAE,CAAC,CAAC,SAAS;EAGtC,KAAK,UAAU,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,SAAS;EAG9C,KAAK,WAAW;CAClB;CACA,OAAO;EACL,KAAK,kBAAkB;CACzB;AACF,CAAC"}
@@ -1,9 +1,9 @@
1
- import { RefreshToken } from "./refresh-token/refresh-token.model.mjs";
2
1
  import { AccessTokenOutput, DeviceInfo, TokenPair } from "../contracts/types.mjs";
2
+ import { RefreshToken } from "./refresh-token/refresh-token.model.mjs";
3
3
  import { Authenticable } from "../contracts/auth-contract.mjs";
4
4
  import { ChildModel, Model, ModelSchema } from "@warlock.js/cascade";
5
5
 
6
- //#region ../../@warlock.js/auth/src/models/auth.model.d.ts
6
+ //#region ../@warlock.js/auth/src/models/auth.model.d.ts
7
7
  declare abstract class Auth<Schema extends ModelSchema = ModelSchema> extends Model<Schema> implements Authenticable {
8
8
  /**
9
9
  * Get user type
@@ -1 +1 @@
1
- {"version":3,"file":"auth.model.d.mts","names":[],"sources":["../../../../../../@warlock.js/auth/src/models/auth.model.ts"],"mappings":";;;;;;uBAMsB,IAAA,gBAAoB,WAAA,GAAc,WAAA,UAC9C,KAAA,CAAM,MAAA,aACH,aAAA;;AAFb;;eAOsB,QAAA,CAAA;EAPoB;;;EAYjC,kBAAA,CAAA;;;;;EAqBwC;;;EAdlC,eAAA,CAAgB,UAAA,GAAa,UAAA,GAAa,OAAA,CAAQ,SAAA;EA4Bf;;;EArBnC,mBAAA,CAAoB,IAAA,SAAa,OAAA,CAAQ,iBAAA;EA0CvB;;;EAnClB,oBAAA,CAAqB,UAAA,GAAa,UAAA,GAAa,OAAA,CAAQ,YAAA;EA0CJ;;;EAnCnD,iBAAA,CAAkB,KAAA,WAAgB,OAAA;EAtCvB;;;EA6CX,kBAAA,CAAmB,KAAA,WAAgB,OAAA;EA/CM;;;EAsDzC,qBAAA,CAAA,GAAyB,OAAA;EA/ClB;;;EAsDP,eAAA,CAAA,GAAmB,OAAA;;;;EAOnB,cAAA,CAAA,GAAkB,OAAA,CAAQ,YAAA;EAjDgB;;;EAAA,OAwDnC,OAAA,CAAQ,IAAA,EAAM,UAAA,CAAW,IAAA,GAAO,IAAA,QAAY,OAAA,CAAQ,IAAA;EAjD1B;;;EAwDjC,eAAA,CAAgB,QAAA,WAAmB,OAAA;AAAA"}
1
+ {"version":3,"file":"auth.model.d.mts","names":[],"sources":["../../../../../../../@warlock.js/auth/src/models/auth.model.ts"],"mappings":";;;;;;uBAMsB,IAAA,gBAAoB,WAAA,GAAc,WAAA,UAC9C,KAAA,CAAM,MAAA,aACH,aAAA;;AAFb;;eAOsB,QAAA;EAPoB;;;EAYjC,kBAAA;;;;;EAqBwC;;;EAdlC,eAAA,CAAgB,UAAA,GAAa,UAAA,GAAa,OAAA,CAAQ,SAAA;EA4Bf;;;EArBnC,mBAAA,CAAoB,IAAA,SAAa,OAAA,CAAQ,iBAAA;EA0CvB;;;EAnClB,oBAAA,CAAqB,UAAA,GAAa,UAAA,GAAa,OAAA,CAAQ,YAAA;EA0CJ;;;EAnCnD,iBAAA,CAAkB,KAAA,WAAgB,OAAA;EAtCvB;;;EA6CX,kBAAA,CAAmB,KAAA,WAAgB,OAAA;EA/CM;;;EAsDzC,qBAAA,IAAyB,OAAA;EA/ClB;;;EAsDP,eAAA,IAAmB,OAAA;;;;EAOnB,cAAA,IAAkB,OAAA,CAAQ,YAAA;EAjDgB;;;EAAA,OAwDnC,OAAA,CAAQ,IAAA,EAAM,UAAA,CAAW,IAAA,GAAO,IAAA,QAAY,OAAA,CAAQ,IAAA;EAjD1B;;;EAwDjC,eAAA,CAAgB,QAAA,WAAmB,OAAA;AAAA"}
@@ -2,7 +2,7 @@ import { authService } from "../services/auth.service.mjs";
2
2
  import "../services/index.mjs";
3
3
  import { Model } from "@warlock.js/cascade";
4
4
 
5
- //#region ../../@warlock.js/auth/src/models/auth.model.ts
5
+ //#region ../@warlock.js/auth/src/models/auth.model.ts
6
6
  var Auth = class extends Model {
7
7
  /**
8
8
  * Get access token payload
@@ -1 +1 @@
1
- {"version":3,"file":"auth.model.mjs","names":[],"sources":["../../../../../../@warlock.js/auth/src/models/auth.model.ts"],"sourcesContent":["import { type ChildModel, Model, type ModelSchema } from \"@warlock.js/cascade\";\r\nimport type { Authenticable } from \"../contracts/auth-contract\";\r\nimport type { AccessTokenOutput, DeviceInfo, TokenPair } from \"../contracts/types\";\r\nimport { authService } from \"../services\";\r\nimport type { RefreshToken } from \"./refresh-token/refresh-token.model\";\r\n\r\nexport abstract class Auth<Schema extends ModelSchema = ModelSchema>\r\n extends Model<Schema>\r\n implements Authenticable\r\n{\r\n /**\r\n * Get user type\r\n */\r\n public abstract get userType(): string;\r\n\r\n /**\r\n * Get access token payload\r\n */\r\n public accessTokenPayload() {\r\n return authService.buildAccessTokenPayload(this);\r\n }\r\n\r\n /**\r\n * Create both access and refresh tokens\r\n */\r\n public async createTokenPair(deviceInfo?: DeviceInfo): Promise<TokenPair> {\r\n return authService.createTokenPair(this, deviceInfo);\r\n }\r\n\r\n /**\r\n * Generate access token\r\n */\r\n public async generateAccessToken(data?: any): Promise<AccessTokenOutput> {\r\n return authService.generateAccessToken(this, data);\r\n }\r\n\r\n /**\r\n * Generate refresh token\r\n */\r\n public async generateRefreshToken(deviceInfo?: DeviceInfo): Promise<RefreshToken | undefined> {\r\n return authService.createRefreshToken(this, deviceInfo);\r\n }\r\n\r\n /**\r\n * Remove current access token\r\n */\r\n public async removeAccessToken(token: string): Promise<void> {\r\n return authService.removeAccessToken(this, token);\r\n }\r\n\r\n /**\r\n * Remove refresh token\r\n */\r\n public async removeRefreshToken(token: string): Promise<void> {\r\n return authService.removeRefreshToken(this, token);\r\n }\r\n\r\n /**\r\n * Remove all access tokens\r\n */\r\n public async removeAllAccessTokens(): Promise<void> {\r\n return authService.removeAllAccessTokens(this);\r\n }\r\n\r\n /**\r\n * Revoke all tokens (logout from all devices)\r\n */\r\n public async revokeAllTokens(): Promise<void> {\r\n return authService.revokeAllTokens(this);\r\n }\r\n\r\n /**\r\n * Get active sessions\r\n */\r\n public async activeSessions(): Promise<RefreshToken[]> {\r\n return authService.getActiveSessions(this);\r\n }\r\n\r\n /**\r\n * Attempt to login the user\r\n */\r\n public static async attempt(this: ChildModel<Auth>, data: any): Promise<Auth | null> {\r\n return authService.attemptLogin(this, data);\r\n }\r\n\r\n /**\r\n * Confirm password\r\n */\r\n public async confirmPassword(password: string): Promise<boolean> {\r\n return authService.verifyPassword(password, this.string(\"password\")!);\r\n }\r\n}\r\n"],"mappings":";;;;;AAMA,IAAsB,OAAtB,cACU,MAEV;;;;CASE,AAAO,qBAAqB;EAC1B,OAAO,YAAY,wBAAwB,IAAI;CACjD;;;;CAKA,MAAa,gBAAgB,YAA6C;EACxE,OAAO,YAAY,gBAAgB,MAAM,UAAU;CACrD;;;;CAKA,MAAa,oBAAoB,MAAwC;EACvE,OAAO,YAAY,oBAAoB,MAAM,IAAI;CACnD;;;;CAKA,MAAa,qBAAqB,YAA4D;EAC5F,OAAO,YAAY,mBAAmB,MAAM,UAAU;CACxD;;;;CAKA,MAAa,kBAAkB,OAA8B;EAC3D,OAAO,YAAY,kBAAkB,MAAM,KAAK;CAClD;;;;CAKA,MAAa,mBAAmB,OAA8B;EAC5D,OAAO,YAAY,mBAAmB,MAAM,KAAK;CACnD;;;;CAKA,MAAa,wBAAuC;EAClD,OAAO,YAAY,sBAAsB,IAAI;CAC/C;;;;CAKA,MAAa,kBAAiC;EAC5C,OAAO,YAAY,gBAAgB,IAAI;CACzC;;;;CAKA,MAAa,iBAA0C;EACrD,OAAO,YAAY,kBAAkB,IAAI;CAC3C;;;;CAKA,aAAoB,QAAgC,MAAiC;EACnF,OAAO,YAAY,aAAa,MAAM,IAAI;CAC5C;;;;CAKA,MAAa,gBAAgB,UAAoC;EAC/D,OAAO,YAAY,eAAe,UAAU,KAAK,OAAO,UAAU,CAAE;CACtE;AACF"}
1
+ {"version":3,"file":"auth.model.mjs","names":[],"sources":["../../../../../../../@warlock.js/auth/src/models/auth.model.ts"],"sourcesContent":["import { type ChildModel, Model, type ModelSchema } from \"@warlock.js/cascade\";\r\nimport type { Authenticable } from \"../contracts/auth-contract\";\r\nimport type { AccessTokenOutput, DeviceInfo, TokenPair } from \"../contracts/types\";\r\nimport { authService } from \"../services\";\r\nimport type { RefreshToken } from \"./refresh-token/refresh-token.model\";\r\n\r\nexport abstract class Auth<Schema extends ModelSchema = ModelSchema>\r\n extends Model<Schema>\r\n implements Authenticable\r\n{\r\n /**\r\n * Get user type\r\n */\r\n public abstract get userType(): string;\r\n\r\n /**\r\n * Get access token payload\r\n */\r\n public accessTokenPayload() {\r\n return authService.buildAccessTokenPayload(this);\r\n }\r\n\r\n /**\r\n * Create both access and refresh tokens\r\n */\r\n public async createTokenPair(deviceInfo?: DeviceInfo): Promise<TokenPair> {\r\n return authService.createTokenPair(this, deviceInfo);\r\n }\r\n\r\n /**\r\n * Generate access token\r\n */\r\n public async generateAccessToken(data?: any): Promise<AccessTokenOutput> {\r\n return authService.generateAccessToken(this, data);\r\n }\r\n\r\n /**\r\n * Generate refresh token\r\n */\r\n public async generateRefreshToken(deviceInfo?: DeviceInfo): Promise<RefreshToken | undefined> {\r\n return authService.createRefreshToken(this, deviceInfo);\r\n }\r\n\r\n /**\r\n * Remove current access token\r\n */\r\n public async removeAccessToken(token: string): Promise<void> {\r\n return authService.removeAccessToken(this, token);\r\n }\r\n\r\n /**\r\n * Remove refresh token\r\n */\r\n public async removeRefreshToken(token: string): Promise<void> {\r\n return authService.removeRefreshToken(this, token);\r\n }\r\n\r\n /**\r\n * Remove all access tokens\r\n */\r\n public async removeAllAccessTokens(): Promise<void> {\r\n return authService.removeAllAccessTokens(this);\r\n }\r\n\r\n /**\r\n * Revoke all tokens (logout from all devices)\r\n */\r\n public async revokeAllTokens(): Promise<void> {\r\n return authService.revokeAllTokens(this);\r\n }\r\n\r\n /**\r\n * Get active sessions\r\n */\r\n public async activeSessions(): Promise<RefreshToken[]> {\r\n return authService.getActiveSessions(this);\r\n }\r\n\r\n /**\r\n * Attempt to login the user\r\n */\r\n public static async attempt(this: ChildModel<Auth>, data: any): Promise<Auth | null> {\r\n return authService.attemptLogin(this, data);\r\n }\r\n\r\n /**\r\n * Confirm password\r\n */\r\n public async confirmPassword(password: string): Promise<boolean> {\r\n return authService.verifyPassword(password, this.string(\"password\")!);\r\n }\r\n}\r\n"],"mappings":";;;;;AAMA,IAAsB,OAAtB,cACU,MAEV;;;;CASE,AAAO,qBAAqB;EAC1B,OAAO,YAAY,wBAAwB,IAAI;CACjD;;;;CAKA,MAAa,gBAAgB,YAA6C;EACxE,OAAO,YAAY,gBAAgB,MAAM,UAAU;CACrD;;;;CAKA,MAAa,oBAAoB,MAAwC;EACvE,OAAO,YAAY,oBAAoB,MAAM,IAAI;CACnD;;;;CAKA,MAAa,qBAAqB,YAA4D;EAC5F,OAAO,YAAY,mBAAmB,MAAM,UAAU;CACxD;;;;CAKA,MAAa,kBAAkB,OAA8B;EAC3D,OAAO,YAAY,kBAAkB,MAAM,KAAK;CAClD;;;;CAKA,MAAa,mBAAmB,OAA8B;EAC5D,OAAO,YAAY,mBAAmB,MAAM,KAAK;CACnD;;;;CAKA,MAAa,wBAAuC;EAClD,OAAO,YAAY,sBAAsB,IAAI;CAC/C;;;;CAKA,MAAa,kBAAiC;EAC5C,OAAO,YAAY,gBAAgB,IAAI;CACzC;;;;CAKA,MAAa,iBAA0C;EACrD,OAAO,YAAY,kBAAkB,IAAI;CAC3C;;;;CAKA,aAAoB,QAAgC,MAAiC;EACnF,OAAO,YAAY,aAAa,MAAM,IAAI;CAC5C;;;;CAKA,MAAa,gBAAgB,UAAoC;EAC/D,OAAO,YAAY,eAAe,UAAU,KAAK,OAAO,UAAU,CAAE;CACtE;AACF"}
@@ -1,7 +1,8 @@
1
- import { RefreshToken } from "./refresh-token/refresh-token.model.mjs";
2
1
  import { Auth } from "./auth.model.mjs";
3
- import { AccessToken } from "./access-token/access-token.model.mjs";
4
- //#region ../../@warlock.js/auth/src/models/index.d.ts
2
+ import { AccessToken, accessTokenSchema } from "./access-token/access-token.model.mjs";
3
+ import { RefreshToken, RefreshTokenIssueOptions, refreshTokenSchema } from "./refresh-token/refresh-token.model.mjs";
4
+
5
+ //#region ../@warlock.js/auth/src/models/index.d.ts
5
6
  declare const authMigrations: any[];
6
7
  //#endregion
7
8
  export { authMigrations };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../@warlock.js/auth/src/models/index.ts"],"mappings":";;;;cAOa,cAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../../../../../../@warlock.js/auth/src/models/index.ts"],"mappings":";;;;;cAOa,cAAA"}