@warlock.js/auth 4.0.174 → 4.1.2

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
@@ -0,0 +1,95 @@
1
+ import { RefreshToken } from "../models/refresh-token/refresh-token.model.mjs";
2
+ import { Auth } from "../models/auth.model.mjs";
3
+ import { AccessTokenOutput, DeviceInfo, LoginResult, TokenPair } from "../contracts/types.mjs";
4
+ import { ChildModel } from "@warlock.js/cascade";
5
+
6
+ //#region ../../@warlock.js/auth/src/services/auth.service.d.ts
7
+ declare class AuthService {
8
+ /**
9
+ * Build access token payload from user
10
+ */
11
+ buildAccessTokenPayload(user: Auth): {
12
+ id: any;
13
+ userType: string;
14
+ created_at: number;
15
+ };
16
+ /**
17
+ * Generate access token for user
18
+ */
19
+ generateAccessToken(user: Auth, payload?: any): Promise<AccessTokenOutput>;
20
+ /**
21
+ * Create refresh token for user
22
+ */
23
+ createRefreshToken(user: Auth, deviceInfo?: DeviceInfo): Promise<RefreshToken | undefined>;
24
+ /**
25
+ * Create both access and refresh tokens
26
+ */
27
+ createTokenPair(user: Auth, deviceInfo?: DeviceInfo): Promise<TokenPair>;
28
+ /**
29
+ * Refresh tokens using a refresh token
30
+ */
31
+ refreshTokens(refreshTokenString: string, deviceInfo?: DeviceInfo): Promise<TokenPair | null>;
32
+ /**
33
+ * Verify password
34
+ */
35
+ verifyPassword(plainPassword: string, hashedPassword: string): Promise<boolean>;
36
+ /**
37
+ * Hash password
38
+ */
39
+ hashPassword(password: string): Promise<string>;
40
+ /**
41
+ * Attempt to login user with given credentials
42
+ */
43
+ attemptLogin<T extends Auth>(Model: ChildModel<T>, data: any): Promise<T | null>;
44
+ /**
45
+ * Full login flow: validate credentials, create tokens, emit events
46
+ * Returns token pair on success, null on failure
47
+ */
48
+ login<T extends Auth>(Model: ChildModel<T>, credentials: any, deviceInfo?: DeviceInfo): Promise<LoginResult<T> | null>;
49
+ /**
50
+ * Logout user
51
+ * @param user - The authenticated user
52
+ * @param accessToken - Optional access token string to revoke
53
+ * @param refreshToken - Optional refresh token string to revoke
54
+ * If refresh token is not provided, behavior is determined by config:
55
+ * - "revoke-all" (default): Revoke ALL refresh tokens for security
56
+ * - "error": Throw error requiring refresh token
57
+ */
58
+ logout(user: Auth, accessToken?: string, refreshToken?: string): Promise<void>;
59
+ /**
60
+ * Remove specific access token
61
+ */
62
+ removeAccessToken(user: Auth, token: string): Promise<void>;
63
+ /**
64
+ * Remove all access tokens for a user
65
+ */
66
+ removeAllAccessTokens(user: Auth): Promise<void>;
67
+ /**
68
+ * Remove specific refresh token
69
+ */
70
+ removeRefreshToken(user: Auth, token: string): Promise<void>;
71
+ /**
72
+ * Revoke all tokens for a user
73
+ */
74
+ revokeAllTokens(user: Auth): Promise<void>;
75
+ /**
76
+ * Revoke entire token family (for rotation breach detection)
77
+ */
78
+ revokeTokenFamily(familyId: string): Promise<void>;
79
+ /**
80
+ * Cleanup expired tokens
81
+ */
82
+ cleanupExpiredTokens(): Promise<number>;
83
+ /**
84
+ * Enforce max refresh tokens per user
85
+ */
86
+ private enforceMaxRefreshTokens;
87
+ /**
88
+ * Get active sessions for user
89
+ */
90
+ getActiveSessions(user: Auth): Promise<RefreshToken[]>;
91
+ }
92
+ declare const authService: AuthService;
93
+ //#endregion
94
+ export { authService };
95
+ //# sourceMappingURL=auth.service.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.service.d.mts","names":[],"sources":["../../../../../../@warlock.js/auth/src/services/auth.service.ts"],"mappings":";;;;;;cAWM,WAAA;;;AAJiD;EAQ9C,uBAAA,CAAwB,IAAA,EAAM,IAAA;;;;;EAWwB;;;EAAhD,mBAAA,CAAoB,IAAA,EAAM,IAAA,EAAM,OAAA,SAAgB,OAAA,CAAQ,iBAAA;EA0BlE;;;EAHU,kBAAA,CACX,IAAA,EAAM,IAAA,EACN,UAAA,GAAa,UAAA,GACZ,OAAA,CAAQ,YAAA;EA2CwD;;;EAAtD,eAAA,CAAgB,IAAA,EAAM,IAAA,EAAM,UAAA,GAAa,UAAA,GAAa,OAAA,CAAQ,SAAA;EAqFC;;;EA1D/D,aAAA,CACX,kBAAA,UACA,UAAA,GAAa,UAAA,GACZ,OAAA,CAAQ,SAAA;EAqEsC;;;EAdpC,cAAA,CAAe,aAAA,UAAuB,cAAA,WAAyB,OAAA;EAwCxD;;;EAjCP,YAAA,CAAa,QAAA,WAAmB,OAAA;EAoClC;;;EA7BE,YAAA,WAAuB,IAAA,CAAA,CAAM,KAAA,EAAO,UAAA,CAAW,CAAA,GAAI,IAAA,QAAY,OAAA,CAAQ,CAAA;EAkG/C;;;;EAzExB,KAAA,WAAgB,IAAA,CAAA,CAC3B,KAAA,EAAO,UAAA,CAAW,CAAA,GAClB,WAAA,OACA,UAAA,GAAa,UAAA,GACZ,OAAA,CAAQ,WAAA,CAAY,CAAA;EAyFqC;;;;;;;;;EA3D/C,MAAA,CAAO,IAAA,EAAM,IAAA,EAAM,WAAA,WAAsB,YAAA,YAAwB,OAAA;EA9OvE;;;EAqRM,iBAAA,CAAkB,IAAA,EAAM,IAAA,EAAM,KAAA,WAAgB,OAAA;;;;EAU9C,qBAAA,CAAsB,IAAA,EAAM,IAAA,GAAO,OAAA;EApRf;;;EA8RpB,kBAAA,CAAmB,IAAA,EAAM,IAAA,EAAM,KAAA,WAAgB,OAAA;EAvQ/C;;;EAiRA,eAAA,CAAgB,IAAA,EAAM,IAAA,GAAO,OAAA;EA/QxC;;;EAsSW,iBAAA,CAAkB,QAAA,WAAmB,OAAA;EA1Pf;;;EA2QtB,oBAAA,CAAA,GAAwB,OAAA;EA3Q8B;;;EAAA,QA4RrD,uBAAA;EA/PC;;;EAuRF,iBAAA,CAAkB,IAAA,EAAM,IAAA,GAAO,OAAA,CAAQ,YAAA;AAAA;AAAA,cAazC,WAAA,EAAW,WAAoB"}
@@ -0,0 +1,275 @@
1
+ import { AccessToken } from "../models/access-token/access-token.model.mjs";
2
+ import "../models/access-token/index.mjs";
3
+ import { RefreshToken } from "../models/refresh-token/refresh-token.model.mjs";
4
+ import "../models/refresh-token/index.mjs";
5
+ import { authEvents } from "./auth-events.mjs";
6
+ import { jwt } from "./jwt.mjs";
7
+ import { config, hashPassword, verifyPassword } from "@warlock.js/core";
8
+ import { Random } from "@mongez/reinforcements";
9
+ import ms from "ms";
10
+
11
+ //#region ../../@warlock.js/auth/src/services/auth.service.ts
12
+ var AuthService = class {
13
+ /**
14
+ * Build access token payload from user
15
+ */
16
+ buildAccessTokenPayload(user) {
17
+ return {
18
+ id: user.id,
19
+ userType: user.userType,
20
+ created_at: Date.now()
21
+ };
22
+ }
23
+ /**
24
+ * Generate access token for user
25
+ */
26
+ async generateAccessToken(user, payload) {
27
+ const data = payload || this.buildAccessTokenPayload(user);
28
+ const expiresInConfig = config.key("auth.jwt.expiresIn");
29
+ const expiresIn = expiresInConfig ? ms(expiresInConfig) : 3600;
30
+ const token = await jwt.generate(data, { expiresIn });
31
+ const decoed = await jwt.verify(token);
32
+ await AccessToken.create({
33
+ token,
34
+ user_id: user.id,
35
+ user_type: user.userType
36
+ });
37
+ return {
38
+ token,
39
+ expiresAt: (/* @__PURE__ */ new Date(decoed.exp * 1e3)).toISOString()
40
+ };
41
+ }
42
+ /**
43
+ * Create refresh token for user
44
+ */
45
+ async createRefreshToken(user, deviceInfo) {
46
+ if (config.get("auth.jwt.refresh.enabled") === false) return;
47
+ const familyId = deviceInfo?.familyId || Random.string(32);
48
+ const payload = {
49
+ userId: user.id,
50
+ userType: user.userType,
51
+ familyId
52
+ };
53
+ const expiresIn = ms(config.get("auth.jwt.refresh.expiresIn", "7d"));
54
+ const token = await jwt.generateRefreshToken(payload, { expiresIn });
55
+ const expiresAt = new Date(Date.now() + expiresIn).toISOString();
56
+ await this.enforceMaxRefreshTokens(user);
57
+ return RefreshToken.create({
58
+ token,
59
+ user_id: user.id,
60
+ user_type: user.userType,
61
+ family_id: familyId,
62
+ expires_at: expiresAt,
63
+ device_info: deviceInfo ? {
64
+ userAgent: deviceInfo.userAgent,
65
+ ip: deviceInfo.ip,
66
+ deviceId: deviceInfo.deviceId
67
+ } : void 0
68
+ });
69
+ }
70
+ /**
71
+ * Create both access and refresh tokens
72
+ */
73
+ async createTokenPair(user, deviceInfo) {
74
+ const accessToken = await this.generateAccessToken(user, deviceInfo?.payload);
75
+ const refreshToken = await this.createRefreshToken(user, deviceInfo);
76
+ const tokenPair = {
77
+ accessToken,
78
+ refreshToken: refreshToken ? {
79
+ token: refreshToken.get("token"),
80
+ expiresAt: refreshToken.get("expires_at")
81
+ } : void 0
82
+ };
83
+ authEvents.emit("token.created", user, tokenPair);
84
+ if (refreshToken) authEvents.emit("session.created", user, refreshToken, deviceInfo);
85
+ return tokenPair;
86
+ }
87
+ /**
88
+ * Refresh tokens using a refresh token
89
+ */
90
+ async refreshTokens(refreshTokenString, deviceInfo) {
91
+ try {
92
+ const decoded = await jwt.verifyRefreshToken(refreshTokenString);
93
+ if (!decoded) return null;
94
+ const refreshToken = await RefreshToken.first({ token: refreshTokenString });
95
+ if (!refreshToken?.isValid) {
96
+ if (refreshToken) await this.revokeTokenFamily(refreshToken.get("family_id"));
97
+ return null;
98
+ }
99
+ const UserModel = config.key(`auth.userType.${decoded.userType}`);
100
+ if (!UserModel) return null;
101
+ const user = await UserModel.find(decoded.userId);
102
+ if (!user) return null;
103
+ if (config.key("auth.jwt.refresh.rotation", true)) await refreshToken.revoke();
104
+ else await refreshToken.markAsUsed();
105
+ const newTokenPair = await this.createTokenPair(user, {
106
+ ...deviceInfo,
107
+ familyId: refreshToken.get("family_id")
108
+ });
109
+ authEvents.emit("token.refreshed", user, newTokenPair, refreshToken);
110
+ return newTokenPair;
111
+ } catch {
112
+ return null;
113
+ }
114
+ }
115
+ /**
116
+ * Verify password
117
+ */
118
+ async verifyPassword(plainPassword, hashedPassword) {
119
+ return verifyPassword(plainPassword, hashedPassword);
120
+ }
121
+ /**
122
+ * Hash password
123
+ */
124
+ async hashPassword(password) {
125
+ return hashPassword(password);
126
+ }
127
+ /**
128
+ * Attempt to login user with given credentials
129
+ */
130
+ async attemptLogin(Model, data) {
131
+ const { password, ...otherData } = data;
132
+ authEvents.emit("login.attempt", otherData);
133
+ const user = await Model.first(otherData);
134
+ if (!user) {
135
+ authEvents.emit("login.failed", otherData, "User not found");
136
+ return null;
137
+ }
138
+ if (!await this.verifyPassword(password, user.string("password"))) {
139
+ authEvents.emit("login.failed", otherData, "Invalid password");
140
+ return null;
141
+ }
142
+ return user;
143
+ }
144
+ /**
145
+ * Full login flow: validate credentials, create tokens, emit events
146
+ * Returns token pair on success, null on failure
147
+ */
148
+ async login(Model, credentials, deviceInfo) {
149
+ const user = await this.attemptLogin(Model, credentials);
150
+ if (!user) return null;
151
+ if (!config.key("auth.jwt.refresh.enabled", true)) return {
152
+ user,
153
+ tokens: { accessToken: await this.generateAccessToken(user, deviceInfo?.payload) }
154
+ };
155
+ const tokens = await this.createTokenPair(user, deviceInfo);
156
+ authEvents.emit("login.success", user, tokens, deviceInfo);
157
+ return {
158
+ user,
159
+ tokens
160
+ };
161
+ }
162
+ /**
163
+ * Logout user
164
+ * @param user - The authenticated user
165
+ * @param accessToken - Optional access token string to revoke
166
+ * @param refreshToken - Optional refresh token string to revoke
167
+ * If refresh token is not provided, behavior is determined by config:
168
+ * - "revoke-all" (default): Revoke ALL refresh tokens for security
169
+ * - "error": Throw error requiring refresh token
170
+ */
171
+ async logout(user, accessToken, refreshToken) {
172
+ if (accessToken) await this.removeAccessToken(user, accessToken);
173
+ if (refreshToken) {
174
+ const token = await RefreshToken.first({
175
+ token: refreshToken,
176
+ userId: user.id
177
+ });
178
+ if (token) {
179
+ await token.revoke();
180
+ authEvents.emit("session.destroyed", user, token);
181
+ }
182
+ } else {
183
+ if (config.key("auth.jwt.refresh.logoutWithoutToken", "revoke-all") === "error") throw new Error("Refresh token required for logout");
184
+ await this.revokeAllTokens(user);
185
+ authEvents.emit("logout.failsafe", user);
186
+ }
187
+ authEvents.emit("logout", user);
188
+ }
189
+ /**
190
+ * Remove specific access token
191
+ */
192
+ async removeAccessToken(user, token) {
193
+ AccessToken.delete({
194
+ token,
195
+ userId: user.id
196
+ });
197
+ }
198
+ /**
199
+ * Remove all access tokens for a user
200
+ */
201
+ async removeAllAccessTokens(user) {
202
+ AccessToken.delete({ user_id: user.id });
203
+ }
204
+ /**
205
+ * Remove specific refresh token
206
+ */
207
+ async removeRefreshToken(user, token) {
208
+ RefreshToken.delete({
209
+ token,
210
+ userId: user.id
211
+ });
212
+ }
213
+ /**
214
+ * Revoke all tokens for a user
215
+ */
216
+ async revokeAllTokens(user) {
217
+ const refreshTokens = await RefreshToken.query().where("user_id", user.id).where("user_type", user.userType).where("revoked_at", null).get();
218
+ for (const token of refreshTokens) {
219
+ await token.revoke();
220
+ authEvents.emit("token.revoked", user, token);
221
+ }
222
+ await this.removeAllAccessTokens(user);
223
+ authEvents.emit("logout.all", user);
224
+ }
225
+ /**
226
+ * Revoke entire token family (for rotation breach detection)
227
+ */
228
+ async revokeTokenFamily(familyId) {
229
+ const tokens = await RefreshToken.query().where("family_id", familyId).where("revoked_at", null).get();
230
+ for (const token of tokens) await token.revoke();
231
+ authEvents.emit("token.familyRevoked", familyId, tokens);
232
+ }
233
+ /**
234
+ * Cleanup expired tokens
235
+ */
236
+ async cleanupExpiredTokens() {
237
+ const expiredTokens = await RefreshToken.query().where("expires_at", "<", /* @__PURE__ */ new Date()).get();
238
+ for (const token of expiredTokens) {
239
+ authEvents.emit("token.expired", token);
240
+ await token.destroy();
241
+ }
242
+ authEvents.emit("cleanup.completed", expiredTokens.length);
243
+ return expiredTokens.length;
244
+ }
245
+ /**
246
+ * Enforce max refresh tokens per user
247
+ */
248
+ async enforceMaxRefreshTokens(user) {
249
+ const maxPerUser = config.key("auth.jwt.refresh.maxPerUser", 5);
250
+ const activeTokens = await RefreshToken.query().where({
251
+ user_id: user.id,
252
+ user_type: user.userType,
253
+ revoked_at: null
254
+ }).orderBy("created_at", "asc").get();
255
+ if (activeTokens.length >= maxPerUser) {
256
+ const tokensToRevoke = activeTokens.slice(0, activeTokens.length - maxPerUser + 1);
257
+ for (const token of tokensToRevoke) await token.revoke();
258
+ }
259
+ }
260
+ /**
261
+ * Get active sessions for user
262
+ */
263
+ async getActiveSessions(user) {
264
+ return RefreshToken.query().where({
265
+ user_id: user.id,
266
+ user_type: user.userType,
267
+ revoked_at: null
268
+ }).where("expires_at", ">", /* @__PURE__ */ new Date()).orderBy("created_at", "desc").get();
269
+ }
270
+ };
271
+ const authService = new AuthService();
272
+
273
+ //#endregion
274
+ export { authService };
275
+ //# sourceMappingURL=auth.service.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.service.mjs","names":[],"sources":["../../../../../../@warlock.js/auth/src/services/auth.service.ts"],"sourcesContent":["import { Random } from \"@mongez/reinforcements\";\nimport type { ChildModel } from \"@warlock.js/cascade\";\nimport { config, hashPassword, verifyPassword } from \"@warlock.js/core\";\nimport ms from \"ms\";\nimport type { AccessTokenOutput, DeviceInfo, LoginResult, TokenPair } from \"../contracts/types\";\nimport { AccessToken } from \"../models/access-token\";\nimport type { Auth } from \"../models/auth.model\";\nimport { RefreshToken } from \"../models/refresh-token\";\nimport { authEvents } from \"./auth-events\";\nimport { jwt } from \"./jwt\";\n\nclass AuthService {\n /**\n * Build access token payload from user\n */\n public buildAccessTokenPayload(user: Auth) {\n return {\n id: user.id,\n userType: user.userType,\n created_at: Date.now(),\n };\n }\n\n /**\n * Generate access token for user\n */\n public async generateAccessToken(user: Auth, payload?: any): Promise<AccessTokenOutput> {\n const data = payload || this.buildAccessTokenPayload(user);\n const expiresInConfig = config.key(\"auth.jwt.expiresIn\");\n const expiresIn = expiresInConfig ? ms(expiresInConfig) : 3_600; // default 1 hour\n\n // If expiresIn is undefined, token never expires\n const token = await jwt.generate(data, { expiresIn });\n\n const decoed = await jwt.verify(token);\n\n // Store in database\n await AccessToken.create({\n token,\n user_id: user.id,\n user_type: user.userType,\n });\n\n return { token, expiresAt: new Date(decoed.exp * 1_000).toISOString() };\n }\n\n /**\n * Create refresh token for user\n */\n public async createRefreshToken(\n user: Auth,\n deviceInfo?: DeviceInfo,\n ): Promise<RefreshToken | undefined> {\n if (config.get(\"auth.jwt.refresh.enabled\") === false) return;\n\n const familyId = deviceInfo?.familyId || Random.string(32);\n\n const payload = {\n userId: user.id,\n userType: user.userType,\n familyId,\n };\n\n const epireTime = config.get(\"auth.jwt.refresh.expiresIn\", \"7d\");\n\n const expiresIn = ms(epireTime);\n\n const token = await jwt.generateRefreshToken(payload, { expiresIn });\n\n // Calculate expiration date (undefined means never expires, but we still set a far future date)\n const expiresAt = new Date(Date.now() + expiresIn).toISOString();\n\n // Enforce max tokens per user\n await this.enforceMaxRefreshTokens(user);\n\n // Store in database\n return RefreshToken.create({\n token,\n user_id: user.id,\n user_type: user.userType,\n family_id: familyId,\n expires_at: expiresAt,\n device_info: deviceInfo\n ? {\n userAgent: deviceInfo.userAgent,\n ip: deviceInfo.ip,\n deviceId: deviceInfo.deviceId,\n }\n : undefined,\n });\n }\n\n /**\n * Create both access and refresh tokens\n */\n public async createTokenPair(user: Auth, deviceInfo?: DeviceInfo): Promise<TokenPair> {\n const accessToken = await this.generateAccessToken(user, deviceInfo?.payload);\n const refreshToken = await this.createRefreshToken(user, deviceInfo);\n\n const tokenPair: TokenPair = {\n accessToken,\n refreshToken: refreshToken\n ? {\n token: refreshToken.get(\"token\"),\n expiresAt: refreshToken.get(\"expires_at\"),\n }\n : undefined,\n };\n\n // Emit events\n authEvents.emit(\"token.created\", user, tokenPair);\n\n if (refreshToken) {\n authEvents.emit(\"session.created\", user, refreshToken, deviceInfo);\n }\n\n return tokenPair;\n }\n\n /**\n * Refresh tokens using a refresh token\n */\n public async refreshTokens(\n refreshTokenString: string,\n deviceInfo?: DeviceInfo,\n ): Promise<TokenPair | null> {\n try {\n // 1. Verify JWT signature\n const decoded = await jwt.verifyRefreshToken<{\n userId: number;\n userType: string;\n familyId: string;\n }>(refreshTokenString);\n\n if (!decoded) return null;\n\n // 2. Find token in database\n const refreshToken = await RefreshToken.first({ token: refreshTokenString });\n\n if (!refreshToken?.isValid) {\n // If token was already used (rotation detection), revoke entire family\n if (refreshToken) {\n await this.revokeTokenFamily(refreshToken.get(\"family_id\"));\n }\n return null;\n }\n\n // 3. Get user model and find user\n const UserModel = config.key(`auth.userType.${decoded.userType}`);\n if (!UserModel) return null;\n\n const user = (await UserModel.find(decoded.userId)) as Auth | null;\n if (!user) return null;\n\n // 4. Rotate token if enabled (revoke old token)\n const rotationEnabled = config.key(\"auth.jwt.refresh.rotation\", true);\n if (rotationEnabled) {\n await refreshToken.revoke();\n } else {\n await refreshToken.markAsUsed();\n }\n\n // 5. Generate new token pair (keep same family)\n const newTokenPair = await this.createTokenPair(user, {\n ...deviceInfo,\n familyId: refreshToken.get(\"family_id\"),\n });\n\n // Emit token refreshed event\n authEvents.emit(\"token.refreshed\", user, newTokenPair, refreshToken);\n\n return newTokenPair;\n } catch {\n return null;\n }\n }\n\n /**\n * Verify password\n */\n public async verifyPassword(plainPassword: string, hashedPassword: string): Promise<boolean> {\n return verifyPassword(plainPassword, hashedPassword);\n }\n\n /**\n * Hash password\n */\n public async hashPassword(password: string): Promise<string> {\n return hashPassword(password);\n }\n\n /**\n * Attempt to login user with given credentials\n */\n public async attemptLogin<T extends Auth>(Model: ChildModel<T>, data: any): Promise<T | null> {\n const { password, ...otherData } = data;\n\n // Emit login attempt event\n authEvents.emit(\"login.attempt\", otherData);\n\n const user = (await Model.first(otherData)) as T | null;\n\n if (!user) {\n authEvents.emit(\"login.failed\", otherData, \"User not found\");\n return null;\n }\n\n if (!(await this.verifyPassword(password, user.string(\"password\")!))) {\n authEvents.emit(\"login.failed\", otherData, \"Invalid password\");\n return null;\n }\n\n return user;\n }\n\n /**\n * Full login flow: validate credentials, create tokens, emit events\n * Returns token pair on success, null on failure\n */\n public async login<T extends Auth>(\n Model: ChildModel<T>,\n credentials: any,\n deviceInfo?: DeviceInfo,\n ): Promise<LoginResult<T> | null> {\n const user = await this.attemptLogin(Model, credentials);\n\n if (!user) {\n return null;\n }\n\n // if no refresh token in config, then return user and access token only\n if (!config.key(\"auth.jwt.refresh.enabled\", true)) {\n const accessToken = await this.generateAccessToken(user, deviceInfo?.payload);\n return { user, tokens: { accessToken } };\n }\n\n const tokens = await this.createTokenPair(user, deviceInfo);\n\n // Emit login success event\n authEvents.emit(\"login.success\", user, tokens, deviceInfo);\n\n return { user, tokens } as LoginResult<T>;\n }\n\n /**\n * Logout user\n * @param user - The authenticated user\n * @param accessToken - Optional access token string to revoke\n * @param refreshToken - Optional refresh token string to revoke\n * If refresh token is not provided, behavior is determined by config:\n * - \"revoke-all\" (default): Revoke ALL refresh tokens for security\n * - \"error\": Throw error requiring refresh token\n */\n public async logout(user: Auth, accessToken?: string, refreshToken?: string): Promise<void> {\n // Remove access token if provided\n if (accessToken) {\n await this.removeAccessToken(user, accessToken);\n }\n\n if (refreshToken) {\n // Revoke specific refresh token\n const token = await RefreshToken.first({\n token: refreshToken,\n userId: user.id, // Security: ensure token belongs to this user\n });\n\n if (token) {\n await token.revoke();\n authEvents.emit(\"session.destroyed\", user, token);\n }\n } else {\n // No refresh token provided - check configured behavior\n const behavior = config.key(\"auth.jwt.refresh.logoutWithoutToken\", \"revoke-all\") as\n | \"revoke-all\"\n | \"error\";\n\n if (behavior === \"error\") {\n throw new Error(\"Refresh token required for logout\");\n }\n\n // Default: revoke-all (fail-safe)\n await this.revokeAllTokens(user);\n authEvents.emit(\"logout.failsafe\", user);\n }\n\n // Emit logout event\n authEvents.emit(\"logout\", user);\n }\n\n /**\n * Remove specific access token\n */\n public async removeAccessToken(user: Auth, token: string): Promise<void> {\n AccessToken.delete({\n token,\n userId: user.id,\n });\n }\n\n /**\n * Remove all access tokens for a user\n */\n public async removeAllAccessTokens(user: Auth): Promise<void> {\n // Delete access token\n AccessToken.delete({\n user_id: user.id,\n });\n }\n\n /**\n * Remove specific refresh token\n */\n public async removeRefreshToken(user: Auth, token: string): Promise<void> {\n RefreshToken.delete({\n token,\n userId: user.id,\n });\n }\n\n /**\n * Revoke all tokens for a user\n */\n public async revokeAllTokens(user: Auth): Promise<void> {\n // Revoke all refresh tokens\n const refreshTokens = await RefreshToken.query()\n .where(\"user_id\", user.id)\n .where(\"user_type\", user.userType)\n .where(\"revoked_at\", null)\n .get();\n\n for (const token of refreshTokens) {\n await token.revoke();\n authEvents.emit(\"token.revoked\", user, token);\n }\n\n // Delete all access tokens\n await this.removeAllAccessTokens(user);\n\n // Emit logout all event\n authEvents.emit(\"logout.all\", user);\n }\n\n /**\n * Revoke entire token family (for rotation breach detection)\n */\n public async revokeTokenFamily(familyId: string): Promise<void> {\n const tokens = await RefreshToken.query()\n .where(\"family_id\", familyId)\n .where(\"revoked_at\", null)\n .get();\n\n for (const token of tokens) {\n await token.revoke();\n }\n\n // Emit family revoked event\n authEvents.emit(\"token.familyRevoked\", familyId, tokens);\n }\n\n /**\n * Cleanup expired tokens\n */\n public async cleanupExpiredTokens(): Promise<number> {\n const expiredTokens = await RefreshToken.query().where(\"expires_at\", \"<\", new Date()).get();\n\n for (const token of expiredTokens) {\n authEvents.emit(\"token.expired\", token);\n await token.destroy();\n }\n\n // Emit cleanup completed event\n authEvents.emit(\"cleanup.completed\", expiredTokens.length);\n\n return expiredTokens.length;\n }\n\n /**\n * Enforce max refresh tokens per user\n */\n private async enforceMaxRefreshTokens(user: Auth): Promise<void> {\n const maxPerUser = config.key(\"auth.jwt.refresh.maxPerUser\", 5);\n\n const activeTokens = await RefreshToken.query()\n .where({\n user_id: user.id,\n user_type: user.userType,\n revoked_at: null,\n })\n .orderBy(\"created_at\", \"asc\")\n .get();\n\n // Revoke oldest tokens if exceeding limit\n if (activeTokens.length >= maxPerUser) {\n const tokensToRevoke = activeTokens.slice(0, activeTokens.length - maxPerUser + 1);\n for (const token of tokensToRevoke) {\n await token.revoke();\n }\n }\n }\n\n /**\n * Get active sessions for user\n */\n public async getActiveSessions(user: Auth): Promise<RefreshToken[]> {\n return RefreshToken.query()\n .where({\n user_id: user.id,\n user_type: user.userType,\n revoked_at: null,\n })\n .where(\"expires_at\", \">\", new Date())\n .orderBy(\"created_at\", \"desc\")\n .get();\n }\n}\n\nexport const authService = new AuthService();\n"],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ //#region ../../@warlock.js/auth/src/services/generate-jwt-secret.d.ts
2
+ declare function generateJWTSecret(): Promise<void>;
3
+ //#endregion
4
+ export { generateJWTSecret };
5
+ //# sourceMappingURL=generate-jwt-secret.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-jwt-secret.d.mts","names":[],"sources":["../../../../../../@warlock.js/auth/src/services/generate-jwt-secret.ts"],"mappings":";iBAKsB,iBAAA,CAAA,GAAiB,OAAA"}
@@ -0,0 +1,48 @@
1
+ import { environment, rootPath } from "@warlock.js/core";
2
+ import { Random } from "@mongez/reinforcements";
3
+ import { fileExistsAsync, getFileAsync, putFileAsync } from "@warlock.js/fs";
4
+ import { log } from "@warlock.js/logger";
5
+
6
+ //#region ../../@warlock.js/auth/src/services/generate-jwt-secret.ts
7
+ async function generateJWTSecret() {
8
+ let envFile = rootPath(".env");
9
+ log.info("jwt", "generating", "Generating JWT secrets");
10
+ const environmentMode = environment();
11
+ if (!await fileExistsAsync(envFile)) envFile = rootPath(environmentMode === "production" ? ".env.production" : ".env.development");
12
+ if (!await fileExistsAsync(envFile)) {
13
+ log.error("jwt", "error", ".env file not found");
14
+ return;
15
+ }
16
+ let contents = await getFileAsync(envFile);
17
+ const hasJwtSecret = contents.includes("JWT_SECRET");
18
+ const hasJwtRefreshSecret = contents.includes("JWT_REFRESH_SECRET");
19
+ if (hasJwtSecret && hasJwtRefreshSecret) {
20
+ log.warn("jwt", "exists", "JWT secrets already exist in the .env file.");
21
+ return;
22
+ }
23
+ let secretsToAdd = "";
24
+ if (!hasJwtSecret) {
25
+ const jwtSecret = Random.string(32);
26
+ secretsToAdd += `
27
+ # JWT Secret
28
+ JWT_SECRET=${jwtSecret}
29
+ `;
30
+ log.success("jwt", "generated", "JWT_SECRET generated and added to the .env file.");
31
+ } else log.info("jwt", "exists", "JWT_SECRET already exists in the .env file.");
32
+ if (!hasJwtRefreshSecret) {
33
+ const jwtRefreshSecret = Random.string(32);
34
+ secretsToAdd += `
35
+ # JWT Refresh Secret
36
+ JWT_REFRESH_SECRET=${jwtRefreshSecret}
37
+ `;
38
+ log.success("jwt", "generated", "JWT_REFRESH_SECRET generated and added to the .env file.");
39
+ } else log.info("jwt", "exists", "JWT_REFRESH_SECRET already exists in the .env file.");
40
+ if (secretsToAdd) {
41
+ contents += secretsToAdd;
42
+ await putFileAsync(envFile, contents);
43
+ }
44
+ }
45
+
46
+ //#endregion
47
+ export { generateJWTSecret };
48
+ //# sourceMappingURL=generate-jwt-secret.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-jwt-secret.mjs","names":[],"sources":["../../../../../../@warlock.js/auth/src/services/generate-jwt-secret.ts"],"sourcesContent":["import { fileExistsAsync, getFileAsync, putFileAsync } from \"@warlock.js/fs\";\r\nimport { Random } from \"@mongez/reinforcements\";\r\nimport { environment, rootPath } from \"@warlock.js/core\";\r\nimport { log } from \"@warlock.js/logger\";\r\n\r\nexport async function generateJWTSecret() {\r\n let envFile = rootPath(\".env\");\r\n\r\n log.info(\"jwt\", \"generating\", \"Generating JWT secrets\");\r\n\r\n const environmentMode = environment();\r\n\r\n if (!(await fileExistsAsync(envFile))) {\r\n const envFileType = environmentMode === \"production\" ? \".env.production\" : \".env.development\";\r\n envFile = rootPath(envFileType);\r\n }\r\n\r\n if (!(await fileExistsAsync(envFile))) {\r\n log.error(\"jwt\", \"error\", \".env file not found\");\r\n return;\r\n }\r\n\r\n let contents = await getFileAsync(envFile);\r\n\r\n const hasJwtSecret = contents.includes(\"JWT_SECRET\");\r\n const hasJwtRefreshSecret = contents.includes(\"JWT_REFRESH_SECRET\");\r\n\r\n if (hasJwtSecret && hasJwtRefreshSecret) {\r\n log.warn(\"jwt\", \"exists\", \"JWT secrets already exist in the .env file.\");\r\n return;\r\n }\r\n\r\n let secretsToAdd = \"\";\r\n\r\n if (!hasJwtSecret) {\r\n const jwtSecret = Random.string(32);\r\n secretsToAdd += `\r\n# JWT Secret\r\nJWT_SECRET=${jwtSecret}\r\n`;\r\n log.success(\"jwt\", \"generated\", \"JWT_SECRET generated and added to the .env file.\");\r\n } else {\r\n log.info(\"jwt\", \"exists\", \"JWT_SECRET already exists in the .env file.\");\r\n }\r\n\r\n if (!hasJwtRefreshSecret) {\r\n const jwtRefreshSecret = Random.string(32);\r\n secretsToAdd += `\r\n# JWT Refresh Secret\r\nJWT_REFRESH_SECRET=${jwtRefreshSecret}\r\n`;\r\n log.success(\"jwt\", \"generated\", \"JWT_REFRESH_SECRET generated and added to the .env file.\");\r\n } else {\r\n log.info(\"jwt\", \"exists\", \"JWT_REFRESH_SECRET already exists in the .env file.\");\r\n }\r\n\r\n if (secretsToAdd) {\r\n contents += secretsToAdd;\r\n await putFileAsync(envFile, contents);\r\n }\r\n}\r\n"],"mappings":";;;;;;AAKA,eAAsB,oBAAoB;CACxC,IAAI,UAAU,SAAS,MAAM;CAE7B,IAAI,KAAK,OAAO,cAAc,wBAAwB;CAEtD,MAAM,kBAAkB,YAAY;CAEpC,IAAI,CAAE,MAAM,gBAAgB,OAAO,GAEjC,UAAU,SADU,oBAAoB,eAAe,oBAAoB,kBAC7C;CAGhC,IAAI,CAAE,MAAM,gBAAgB,OAAO,GAAI;EACrC,IAAI,MAAM,OAAO,SAAS,qBAAqB;EAC/C;CACF;CAEA,IAAI,WAAW,MAAM,aAAa,OAAO;CAEzC,MAAM,eAAe,SAAS,SAAS,YAAY;CACnD,MAAM,sBAAsB,SAAS,SAAS,oBAAoB;CAElE,IAAI,gBAAgB,qBAAqB;EACvC,IAAI,KAAK,OAAO,UAAU,6CAA6C;EACvE;CACF;CAEA,IAAI,eAAe;CAEnB,IAAI,CAAC,cAAc;EACjB,MAAM,YAAY,OAAO,OAAO,EAAE;EAClC,gBAAgB;;aAEP,UAAU;;EAEnB,IAAI,QAAQ,OAAO,aAAa,kDAAkD;CACpF,OACE,IAAI,KAAK,OAAO,UAAU,6CAA6C;CAGzE,IAAI,CAAC,qBAAqB;EACxB,MAAM,mBAAmB,OAAO,OAAO,EAAE;EACzC,gBAAgB;;qBAEC,iBAAiB;;EAElC,IAAI,QAAQ,OAAO,aAAa,0DAA0D;CAC5F,OACE,IAAI,KAAK,OAAO,UAAU,qDAAqD;CAGjF,IAAI,cAAc;EAChB,YAAY;EACZ,MAAM,aAAa,SAAS,QAAQ;CACtC;AACF"}
@@ -0,0 +1,4 @@
1
+ import { AuthEventCallback, AuthEventName, AuthEventPayloads, authEvents } from "./auth-events.mjs";
2
+ import { authService } from "./auth.service.mjs";
3
+ import { generateJWTSecret } from "./generate-jwt-secret.mjs";
4
+ import { jwt } from "./jwt.mjs";
@@ -0,0 +1,6 @@
1
+ import { authEvents } from "./auth-events.mjs";
2
+ import { jwt } from "./jwt.mjs";
3
+ import { authService } from "./auth.service.mjs";
4
+ import { generateJWTSecret } from "./generate-jwt-secret.mjs";
5
+
6
+ export { };
@@ -0,0 +1,52 @@
1
+ import { SignerOptions, VerifierOptions } from "fast-jwt";
2
+
3
+ //#region ../../@warlock.js/auth/src/services/jwt.d.ts
4
+ declare const jwt: {
5
+ /**
6
+ * Generate a new JWT token for the user.
7
+ * @param payload The payload to encode in the JWT token.
8
+ */
9
+ generate(payload: any, {
10
+ key,
11
+ algorithm,
12
+ ...options
13
+ }?: SignerOptions & {
14
+ key?: string;
15
+ }): Promise<string>;
16
+ /**
17
+ * Verify the given token.
18
+ * @param token The JWT token to verify.
19
+ * @returns The decoded token payload if verification is successful.
20
+ */
21
+ verify<T = any>(token: string, {
22
+ key,
23
+ algorithms,
24
+ ...options
25
+ }?: VerifierOptions & {
26
+ key?: string;
27
+ }): Promise<T>;
28
+ /**
29
+ * Generate a new refresh token for the user.
30
+ */
31
+ generateRefreshToken(payload: any, {
32
+ key,
33
+ expiresIn,
34
+ algorithm,
35
+ ...options
36
+ }?: SignerOptions & {
37
+ key?: string;
38
+ }): Promise<string>;
39
+ /**
40
+ * Verify the given refresh token.
41
+ */
42
+ verifyRefreshToken<T = any>(token: string, {
43
+ key,
44
+ algorithms,
45
+ ...options
46
+ }?: VerifierOptions & {
47
+ key?: string;
48
+ }): Promise<T>;
49
+ };
50
+ //#endregion
51
+ export { jwt };
52
+ //# sourceMappingURL=jwt.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.mts","names":[],"sources":["../../../../../../@warlock.js/auth/src/services/jwt.ts"],"mappings":";;;cA0Ba,GAAA;;AAAb;;;;IAMgB,GAAA;IAAA,SAAA;IAAA,GAAA;EAAA,IAKT,aAAA;IAAkB,GAAA;EAAA,IACpB,OAAA;EAAA;;;;;kBAaW,KAAA;IACC,GAAA;IAAA,UAAA;IAAA,GAAA;EAAA,IAKV,eAAA;IAAoB,GAAA;EAAA,IACtB,OAAA,CAAQ,CAAA;EAUG;;;;IAAA,GAAA;IAAA,SAAA;IAAA,SAAA;IAAA,GAAA;EAAA,IAMT,aAAA;IAAkB,GAAA;EAAA,IACpB,OAAA;EAeQ;;;8BAPe,KAAA;IACX,GAAA;IAAA,UAAA;IAAA,GAAA;EAAA,IAKV,eAAA;IAAoB,GAAA;EAAA,IACtB,OAAA,CAAQ,CAAA;AAAA"}
@@ -0,0 +1,58 @@
1
+ import { config } from "@warlock.js/core";
2
+ import ms from "ms";
3
+ import { createSigner, createVerifier } from "fast-jwt";
4
+
5
+ //#region ../../@warlock.js/auth/src/services/jwt.ts
6
+ const getSecretKey = () => config.key("auth.jwt.secret");
7
+ const getAlgorithm = () => config.key("auth.jwt.algorithm", "HS256");
8
+ const getRefreshSecretKey = () => config.key("auth.jwt.refresh.secret") || getSecretKey();
9
+ const jwt = {
10
+ /**
11
+ * Generate a new JWT token for the user.
12
+ * @param payload The payload to encode in the JWT token.
13
+ */
14
+ async generate(payload, { key = getSecretKey(), algorithm = getAlgorithm(), ...options } = {}) {
15
+ return await createSigner({
16
+ key,
17
+ ...options,
18
+ algorithm
19
+ })({ ...payload });
20
+ },
21
+ /**
22
+ * Verify the given token.
23
+ * @param token The JWT token to verify.
24
+ * @returns The decoded token payload if verification is successful.
25
+ */
26
+ async verify(token, { key = getSecretKey(), algorithms = getAlgorithm() ? [getAlgorithm()] : void 0, ...options } = {}) {
27
+ return await createVerifier({
28
+ key,
29
+ ...options,
30
+ algorithms
31
+ })(token);
32
+ },
33
+ /**
34
+ * Generate a new refresh token for the user.
35
+ */
36
+ async generateRefreshToken(payload, { key = getRefreshSecretKey(), expiresIn, algorithm = getAlgorithm(), ...options } = {}) {
37
+ return createSigner({
38
+ key,
39
+ expiresIn,
40
+ algorithm,
41
+ ...options
42
+ })({ ...payload });
43
+ },
44
+ /**
45
+ * Verify the given refresh token.
46
+ */
47
+ async verifyRefreshToken(token, { key = getRefreshSecretKey(), algorithms = [getAlgorithm()], ...options } = {}) {
48
+ return await createVerifier({
49
+ key,
50
+ algorithms,
51
+ ...options
52
+ })(token);
53
+ }
54
+ };
55
+
56
+ //#endregion
57
+ export { jwt };
58
+ //# sourceMappingURL=jwt.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.mjs","names":[],"sources":["../../../../../../@warlock.js/auth/src/services/jwt.ts"],"sourcesContent":["import { config } from \"@warlock.js/core\";\r\nimport {\r\n createSigner,\r\n createVerifier,\r\n type Algorithm,\r\n type SignerOptions,\r\n type VerifierOptions,\r\n} from \"fast-jwt\";\r\nimport ms from \"ms\";\r\n\r\nconst getSecretKey = () => config.key(\"auth.jwt.secret\") as string;\r\nconst getAlgorithm = () => config.key(\"auth.jwt.algorithm\", \"HS256\") as Algorithm;\r\n\r\n// Refresh tokens may declare their own secret. When `auth.jwt.refresh.secret`\r\n// is unset/empty we fall back to the main JWT secret, matching the documented\r\n// optional behavior in `contracts/types.ts`.\r\nconst getRefreshSecretKey = () =>\r\n (config.key(\"auth.jwt.refresh.secret\") || getSecretKey()) as string;\r\n// Refresh token validity — defaults to 7d when not configured. Opt in to\r\n// no-expiry semantics with `NO_EXPIRATION` (100y) from `contracts/types.ts`.\r\nconst getRefreshTokenValidity = () => {\r\n const expiresIn = config.key(\"auth.jwt.refresh.expiresIn\") || \"7d\";\r\n\r\n return ms(expiresIn);\r\n};\r\n\r\nexport const jwt = {\r\n /**\r\n * Generate a new JWT token for the user.\r\n * @param payload The payload to encode in the JWT token.\r\n */\r\n async generate(\r\n payload: any,\r\n {\r\n key = getSecretKey(),\r\n algorithm = getAlgorithm(),\r\n ...options\r\n }: SignerOptions & { key?: string } = {},\r\n ): Promise<string> {\r\n // Create a signer function with predefined options\r\n const sign = createSigner({ key, ...options, algorithm });\r\n\r\n const token = await sign({ ...payload });\r\n return token;\r\n },\r\n\r\n /**\r\n * Verify the given token.\r\n * @param token The JWT token to verify.\r\n * @returns The decoded token payload if verification is successful.\r\n */\r\n async verify<T = any>(\r\n token: string,\r\n {\r\n key = getSecretKey(),\r\n algorithms = getAlgorithm() ? [getAlgorithm()] : undefined,\r\n ...options\r\n }: VerifierOptions & { key?: string } = {},\r\n ): Promise<T> {\r\n const verify = createVerifier({ key, ...options, algorithms });\r\n\r\n return await verify(token as string);\r\n },\r\n\r\n /**\r\n * Generate a new refresh token for the user.\r\n */\r\n async generateRefreshToken(\r\n payload: any,\r\n {\r\n key = getRefreshSecretKey(),\r\n expiresIn,\r\n algorithm = getAlgorithm(),\r\n ...options\r\n }: SignerOptions & { key?: string } = {},\r\n ): Promise<string> {\r\n const sign = createSigner({ key, expiresIn, algorithm, ...options });\r\n return sign({ ...payload });\r\n },\r\n\r\n /**\r\n * Verify the given refresh token.\r\n */\r\n async verifyRefreshToken<T = any>(\r\n token: string,\r\n {\r\n key = getRefreshSecretKey(),\r\n algorithms = [getAlgorithm()],\r\n ...options\r\n }: VerifierOptions & { key?: string } = {},\r\n ): Promise<T> {\r\n const verify = createVerifier({ key, algorithms, ...options });\r\n return await verify(token);\r\n },\r\n};\r\n"],"mappings":";;;;;AAUA,MAAM,qBAAqB,OAAO,IAAI,iBAAiB;AACvD,MAAM,qBAAqB,OAAO,IAAI,sBAAsB,OAAO;AAKnE,MAAM,4BACH,OAAO,IAAI,yBAAyB,KAAK,aAAa;AASzD,MAAa,MAAM;;;;;CAKjB,MAAM,SACJ,SACA,EACE,MAAM,aAAa,GACnB,YAAY,aAAa,GACzB,GAAG,YACiC,CAAC,GACtB;EAKjB,OAAO,MAHM,aAAa;GAAE;GAAK,GAAG;GAAS;EAAU,CAEhC,EAAE,EAAE,GAAG,QAAQ,CAAC;CAEzC;;;;;;CAOA,MAAM,OACJ,OACA,EACE,MAAM,aAAa,GACnB,aAAa,aAAa,IAAI,CAAC,aAAa,CAAC,IAAI,QACjD,GAAG,YACmC,CAAC,GAC7B;EAGZ,OAAO,MAFQ,eAAe;GAAE;GAAK,GAAG;GAAS;EAAW,CAE1C,EAAE,KAAe;CACrC;;;;CAKA,MAAM,qBACJ,SACA,EACE,MAAM,oBAAoB,GAC1B,WACA,YAAY,aAAa,GACzB,GAAG,YACiC,CAAC,GACtB;EAEjB,OADa,aAAa;GAAE;GAAK;GAAW;GAAW,GAAG;EAAQ,CACxD,EAAE,EAAE,GAAG,QAAQ,CAAC;CAC5B;;;;CAKA,MAAM,mBACJ,OACA,EACE,MAAM,oBAAoB,GAC1B,aAAa,CAAC,aAAa,CAAC,GAC5B,GAAG,YACmC,CAAC,GAC7B;EAEZ,OAAO,MADQ,eAAe;GAAE;GAAK;GAAY,GAAG;EAAQ,CAC1C,EAAE,KAAK;CAC3B;AACF"}
@@ -0,0 +1,23 @@
1
+ //#region ../../@warlock.js/auth/src/utils/auth-error-codes.d.ts
2
+ declare enum AuthErrorCodes {
3
+ /**
4
+ * Missing Access Token Error Code EC001
5
+ * EC001 = Missing Access Token
6
+ */
7
+ MissingAccessToken = "EC001",
8
+ // Error Code 001
9
+ /**
10
+ * Invalid Access Token Error Code EC002
11
+ * EC002 = Invalid Access Token
12
+ */
13
+ InvalidAccessToken = "EC002",
14
+ // Error Code 002
15
+ /**
16
+ * Unauthorized Error Code EC003
17
+ * EC003 = Unauthorized
18
+ */
19
+ Unauthorized = "EC003"
20
+ }
21
+ //#endregion
22
+ export { AuthErrorCodes };
23
+ //# sourceMappingURL=auth-error-codes.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-error-codes.d.mts","names":[],"sources":["../../../../../../@warlock.js/auth/src/utils/auth-error-codes.ts"],"mappings":";aAAY,cAAA;EAAA;;;;EAKV,kBAAA;EAAA;EAKA;;;AAKY;EALZ,kBAAA;EAAA;;;;;EAKA,YAAA;AAAA"}