@tachybase/auth 1.6.0 → 1.6.1-alpha.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.
@@ -4,6 +4,7 @@ import { Auth, AuthExtend } from './auth';
4
4
  import { JwtOptions, JwtService } from './base/jwt-service';
5
5
  import { ITokenBlacklistService } from './base/token-blacklist-service';
6
6
  import { ITokenControlService } from './base/token-control-service';
7
+ import { IUserStatusService } from './base/user-status-service';
7
8
  export interface Authenticator {
8
9
  authType: string;
9
10
  options: Record<string, any>;
@@ -25,6 +26,7 @@ type AuthConfig = {
25
26
  export declare class AuthManager {
26
27
  jwt: JwtService;
27
28
  tokenController: ITokenControlService;
29
+ userStatusService: IUserStatusService;
28
30
  protected options: AuthManagerOptions;
29
31
  protected authTypes: Registry<AuthConfig>;
30
32
  protected storer: Storer;
@@ -32,6 +34,7 @@ export declare class AuthManager {
32
34
  setStorer(storer: Storer): void;
33
35
  setTokenBlacklistService(service: ITokenBlacklistService): void;
34
36
  setTokenControlService(service: ITokenControlService): void;
37
+ setUserStatusService(service: IUserStatusService): void;
35
38
  /**
36
39
  * registerTypes
37
40
  * @description Add a new authenticate type and the corresponding authenticator.
@@ -38,6 +38,9 @@ const _AuthManager = class _AuthManager {
38
38
  setTokenControlService(service) {
39
39
  this.tokenController = service;
40
40
  }
41
+ setUserStatusService(service) {
42
+ this.userStatusService = service;
43
+ }
41
44
  /**
42
45
  * registerTypes
43
46
  * @description Add a new authenticate type and the corresponding authenticator.
package/lib/auth.d.ts CHANGED
@@ -17,6 +17,7 @@ export declare const AuthErrorCode: {
17
17
  EXPIRED_SESSION: "EXPIRED_SESSION";
18
18
  NOT_EXIST_USER: "NOT_EXIST_USER";
19
19
  SKIP_TOKEN_RENEW: "SKIP_TOKEN_RENEW";
20
+ USER_STATUS_NOT_ALLOW_LOGIN: "USER_STATUS_NOT_ALLOW_LOGIN";
20
21
  };
21
22
  export type AuthErrorType = keyof typeof AuthErrorCode;
22
23
  export declare class AuthError extends Error {
package/lib/auth.js CHANGED
@@ -31,7 +31,8 @@ const AuthErrorCode = {
31
31
  BLOCKED_TOKEN: "BLOCKED_TOKEN",
32
32
  EXPIRED_SESSION: "EXPIRED_SESSION",
33
33
  NOT_EXIST_USER: "NOT_EXIST_USER",
34
- SKIP_TOKEN_RENEW: "SKIP_TOKEN_RENEW"
34
+ SKIP_TOKEN_RENEW: "SKIP_TOKEN_RENEW",
35
+ USER_STATUS_NOT_ALLOW_LOGIN: "USER_STATUS_NOT_ALLOW_LOGIN"
35
36
  };
36
37
  const _AuthError = class _AuthError extends Error {
37
38
  constructor(options) {
@@ -2,6 +2,7 @@ import { Collection, Model } from '@tachybase/database';
2
2
  import { Auth, AuthConfig } from '../auth';
3
3
  import { JwtService } from './jwt-service';
4
4
  import { ITokenControlService } from './token-control-service';
5
+ import { IUserStatusService } from './user-status-service';
5
6
  /**
6
7
  * BaseAuth
7
8
  * @description A base class with jwt provide some common methods.
@@ -14,6 +15,7 @@ export declare class BaseAuth extends Auth {
14
15
  get userRepository(): import("@tachybase/database").Repository<any, any>;
15
16
  get jwt(): JwtService;
16
17
  get tokenController(): ITokenControlService;
18
+ get userStatusService(): IUserStatusService;
17
19
  set user(user: Model);
18
20
  get user(): Model;
19
21
  getCacheKey(userId: number): string;
@@ -21,6 +23,7 @@ export declare class BaseAuth extends Auth {
21
23
  checkToken(): Promise<{
22
24
  tokenStatus: 'valid' | 'expired' | 'invalid';
23
25
  user: Awaited<ReturnType<Auth['check']>>;
26
+ userStatus: string;
24
27
  jti?: string;
25
28
  temp: any;
26
29
  roleName?: any;
@@ -28,6 +31,11 @@ export declare class BaseAuth extends Auth {
28
31
  }>;
29
32
  check(): ReturnType<Auth['check']>;
30
33
  validate(): Promise<Model>;
34
+ /**
35
+ * 签新 token
36
+ * @param userId 用户 ID
37
+ * @returns 新 token
38
+ */
31
39
  signNewToken(userId: number): Promise<string>;
32
40
  signIn(): Promise<{
33
41
  user: Model<any, any>;
package/lib/base/auth.js CHANGED
@@ -49,6 +49,9 @@ const _BaseAuth = class _BaseAuth extends import_auth.Auth {
49
49
  get tokenController() {
50
50
  return this.ctx.tego.authManager.tokenController;
51
51
  }
52
+ get userStatusService() {
53
+ return this.ctx.tego.authManager.userStatusService;
54
+ }
52
55
  set user(user) {
53
56
  this.ctx.state.currentUser = user;
54
57
  }
@@ -87,7 +90,7 @@ const _BaseAuth = class _BaseAuth extends import_auth.Auth {
87
90
  });
88
91
  }
89
92
  }
90
- const { userId, roleName, iat, temp, jti, exp, signInTime } = payload ?? {};
93
+ const { userId, userStatus = "active", roleName, iat, temp, jti, exp, signInTime } = payload ?? {};
91
94
  const user = userId ? await this.ctx.tego.cache.wrap(
92
95
  this.getCacheKey(userId),
93
96
  () => this.userRepository.findOne({
@@ -103,6 +106,19 @@ const _BaseAuth = class _BaseAuth extends import_auth.Auth {
103
106
  code: import_auth.AuthErrorCode.NOT_EXIST_USER
104
107
  });
105
108
  }
109
+ const statusCheckResult = await this.userStatusService.checkUserStatus(user.id);
110
+ if (!statusCheckResult.allowed) {
111
+ this.ctx.throw(401, {
112
+ message: this.ctx.t(statusCheckResult.errorMessage, { ns: localeNamespace }),
113
+ code: import_auth.AuthErrorCode.USER_STATUS_NOT_ALLOW_LOGIN
114
+ });
115
+ }
116
+ if (statusCheckResult.status !== userStatus) {
117
+ this.ctx.throw(401, {
118
+ message: this.ctx.t("Your account status has changed. Please sign in again.", { ns: localeNamespace }),
119
+ code: import_auth.AuthErrorCode.INVALID_TOKEN
120
+ });
121
+ }
106
122
  if (roleName) {
107
123
  this.ctx.headers["x-role"] = roleName;
108
124
  }
@@ -115,7 +131,7 @@ const _BaseAuth = class _BaseAuth extends import_auth.Auth {
115
131
  }
116
132
  if (!temp) {
117
133
  if (tokenStatus === "valid") {
118
- return { tokenStatus, user, temp };
134
+ return { tokenStatus, user, userStatus, temp };
119
135
  } else {
120
136
  this.ctx.throw(401, {
121
137
  message: this.ctx.t("Your session has expired. Please sign in again.", { ns: localeNamespace }),
@@ -158,13 +174,13 @@ const _BaseAuth = class _BaseAuth extends import_auth.Auth {
158
174
  code: import_auth.AuthErrorCode.INVALID_TOKEN
159
175
  });
160
176
  }
161
- return { tokenStatus, user, jti, signInTime, temp };
177
+ return { tokenStatus, user, userStatus, jti, signInTime, temp };
162
178
  }
163
- return { tokenStatus, user, jti, signInTime, temp };
179
+ return { tokenStatus, user, userStatus, jti, signInTime, temp };
164
180
  }
165
181
  async check() {
166
182
  var _a, _b, _c;
167
- const { tokenStatus, user, jti, temp, signInTime, roleName } = await this.checkToken();
183
+ const { tokenStatus, user, userStatus, jti, temp, signInTime, roleName } = await this.checkToken();
168
184
  if (tokenStatus === "expired") {
169
185
  const tokenPolicy = await this.tokenController.getConfig();
170
186
  try {
@@ -193,7 +209,7 @@ const _BaseAuth = class _BaseAuth extends import_auth.Auth {
193
209
  });
194
210
  const expiresIn = Math.floor(tokenPolicy.tokenExpirationTime / 1e3);
195
211
  const newToken = this.jwt.sign(
196
- { userId: user.id, roleName, temp, signInTime, iat: Math.floor(renewedResult.issuedTime / 1e3) },
212
+ { userId: user.id, userStatus, roleName, temp, signInTime, iat: Math.floor(renewedResult.issuedTime / 1e3) },
197
213
  { jwtid: renewedResult.jti, expiresIn }
198
214
  );
199
215
  this.ctx.res.setHeader("x-new-token", newToken);
@@ -214,12 +230,28 @@ const _BaseAuth = class _BaseAuth extends import_auth.Auth {
214
230
  async validate() {
215
231
  return null;
216
232
  }
233
+ /**
234
+ * 签新 token
235
+ * @param userId 用户 ID
236
+ * @returns 新 token
237
+ */
217
238
  async signNewToken(userId) {
239
+ const user = await this.userRepository.findOne({
240
+ filter: { id: userId },
241
+ fields: ["id", "status"]
242
+ });
243
+ if (!user) {
244
+ this.ctx.throw(401, {
245
+ message: this.ctx.t("User not found. Please sign in again to continue.", { ns: localeNamespace }),
246
+ code: import_auth.AuthErrorCode.NOT_EXIST_USER
247
+ });
248
+ }
218
249
  const tokenInfo = await this.tokenController.add({ userId });
219
250
  const expiresIn = Math.floor((await this.tokenController.getConfig()).tokenExpirationTime / 1e3);
220
251
  const token = this.jwt.sign(
221
252
  {
222
253
  userId,
254
+ userStatus: user.status,
223
255
  temp: true,
224
256
  iat: Math.floor(tokenInfo.issuedTime / 1e3),
225
257
  signInTime: tokenInfo.signInTime
@@ -246,6 +278,13 @@ const _BaseAuth = class _BaseAuth extends import_auth.Auth {
246
278
  code: import_auth.AuthErrorCode.NOT_EXIST_USER
247
279
  });
248
280
  }
281
+ const statusCheckResult = await this.userStatusService.checkUserStatus(user.id);
282
+ if (!statusCheckResult.allowed) {
283
+ this.ctx.throw(401, {
284
+ message: this.ctx.t(statusCheckResult.errorMessage, { ns: localeNamespace }),
285
+ code: import_auth.AuthErrorCode.USER_STATUS_NOT_ALLOW_LOGIN
286
+ });
287
+ }
249
288
  const token = await this.signNewToken(user.id);
250
289
  return {
251
290
  user,
@@ -0,0 +1,77 @@
1
+ /**
2
+ * 用户状态检查结果
3
+ */
4
+ export interface UserStatusCheckResult {
5
+ allowed: boolean;
6
+ status: string;
7
+ statusInfo: {
8
+ title: string;
9
+ color: string;
10
+ allowLogin: boolean;
11
+ };
12
+ errorMessage: string;
13
+ isExpired: boolean;
14
+ }
15
+ /**
16
+ * 用户状态缓存数据
17
+ */
18
+ export interface UserStatusCache {
19
+ userId: number;
20
+ status: string;
21
+ expireAt: Date | null;
22
+ previousStatus: string | null;
23
+ lastChecked: Date;
24
+ }
25
+ /**
26
+ * 用户状态服务接口
27
+ */
28
+ export interface IUserStatusService {
29
+ /**
30
+ * 检查用户状态是否允许登录
31
+ * @param userId 用户ID
32
+ * @returns 检查结果
33
+ */
34
+ checkUserStatus(userId: number): Promise<UserStatusCheckResult>;
35
+ /**
36
+ * 设置用户状态缓存
37
+ * @param userId 用户ID
38
+ * @param data 缓存数据
39
+ */
40
+ setUserStatusCache(userId: number, data: UserStatusCache): Promise<void>;
41
+ /**
42
+ * 从缓存获取用户状态
43
+ * @param userId 用户ID
44
+ * @returns 缓存数据或 null
45
+ */
46
+ getUserStatusFromCache(userId: number): Promise<UserStatusCache | null>;
47
+ /**
48
+ * 获取用户状态缓存键
49
+ * @param userId 用户ID
50
+ * @returns 缓存键
51
+ */
52
+ getUserStatusCacheKey(userId: number): string;
53
+ /**
54
+ * 恢复过期的用户状态
55
+ * @param userId 用户ID
56
+ */
57
+ restoreUserStatus(userId: number): Promise<void>;
58
+ /**
59
+ * 清除用户状态缓存
60
+ * @param userId 用户ID
61
+ */
62
+ clearUserStatusCache(userId: number): Promise<void>;
63
+ /**
64
+ * 记录状态变更历史(如果不存在相同记录)
65
+ * @param params 状态变更参数
66
+ */
67
+ recordStatusHistoryIfNotExists(params: {
68
+ userId: number;
69
+ fromStatus: string;
70
+ toStatus: string;
71
+ reason: string | null;
72
+ expireAt: Date | null;
73
+ operationType: 'manual' | 'auto' | 'system';
74
+ createdBy: number | null;
75
+ transaction?: any;
76
+ }): Promise<void>;
77
+ }
@@ -0,0 +1,15 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __copyProps = (to, from, except, desc) => {
6
+ if (from && typeof from === "object" || typeof from === "function") {
7
+ for (let key of __getOwnPropNames(from))
8
+ if (!__hasOwnProp.call(to, key) && key !== except)
9
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
10
+ }
11
+ return to;
12
+ };
13
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
14
+ var user_status_service_exports = {};
15
+ module.exports = __toCommonJS(user_status_service_exports);
package/lib/index.d.ts CHANGED
@@ -4,3 +4,4 @@ export * from './auth-manager';
4
4
  export * from './base/auth';
5
5
  export * from './base/token-blacklist-service';
6
6
  export * from './base/token-control-service';
7
+ export * from './base/user-status-service';
package/lib/index.js CHANGED
@@ -20,6 +20,7 @@ __reExport(index_exports, require("./auth-manager"), module.exports);
20
20
  __reExport(index_exports, require("./base/auth"), module.exports);
21
21
  __reExport(index_exports, require("./base/token-blacklist-service"), module.exports);
22
22
  __reExport(index_exports, require("./base/token-control-service"), module.exports);
23
+ __reExport(index_exports, require("./base/user-status-service"), module.exports);
23
24
  // Annotate the CommonJS export names for ESM import in node:
24
25
  0 && (module.exports = {
25
26
  ...require("./actions"),
@@ -27,5 +28,6 @@ __reExport(index_exports, require("./base/token-control-service"), module.export
27
28
  ...require("./auth-manager"),
28
29
  ...require("./base/auth"),
29
30
  ...require("./base/token-blacklist-service"),
30
- ...require("./base/token-control-service")
31
+ ...require("./base/token-control-service"),
32
+ ...require("./base/user-status-service")
31
33
  });
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@tachybase/auth",
3
- "version": "1.6.0",
3
+ "version": "1.6.1-alpha.0",
4
4
  "description": "",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./lib/index.js",
7
7
  "types": "./lib/index.d.ts",
8
8
  "dependencies": {
9
9
  "jsonwebtoken": "^8.5.1",
10
- "@tachybase/database": "1.6.0",
11
- "@tachybase/resourcer": "1.6.0",
12
- "@tachybase/utils": "1.6.0",
13
- "@tachybase/actions": "1.6.0"
10
+ "@tachybase/database": "1.6.1-alpha.0",
11
+ "@tachybase/resourcer": "1.6.1-alpha.0",
12
+ "@tachybase/actions": "1.6.1-alpha.0",
13
+ "@tachybase/utils": "1.6.1-alpha.0"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/jsonwebtoken": "^8.5.9",