@tachybase/auth 1.6.0-alpha.9 → 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.
- package/lib/auth-manager.d.ts +3 -0
- package/lib/auth-manager.js +3 -0
- package/lib/auth.d.ts +1 -0
- package/lib/auth.js +2 -1
- package/lib/base/auth.d.ts +8 -0
- package/lib/base/auth.js +45 -6
- package/lib/base/user-status-service.d.ts +77 -0
- package/lib/base/user-status-service.js +15 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +3 -1
- package/package.json +5 -5
package/lib/auth-manager.d.ts
CHANGED
|
@@ -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.
|
package/lib/auth-manager.js
CHANGED
|
@@ -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) {
|
package/lib/base/auth.d.ts
CHANGED
|
@@ -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
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.
|
|
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/
|
|
11
|
-
"@tachybase/
|
|
12
|
-
"@tachybase/
|
|
13
|
-
"@tachybase/utils": "1.6.
|
|
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",
|