@nocobase/auth 1.6.0-beta.3 → 1.6.0-beta.5
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 +4 -1
- package/lib/auth-manager.js +14 -14
- package/lib/auth.d.ts +23 -0
- package/lib/auth.js +41 -2
- package/lib/base/auth.d.ts +3 -1
- package/lib/base/auth.js +142 -26
- package/lib/base/jwt-service.d.ts +2 -2
- package/lib/base/jwt-service.js +2 -2
- package/lib/base/token-control-service.d.ts +38 -0
- package/lib/base/token-control-service.js +24 -0
- package/lib/client.d.ts +9 -0
- package/lib/client.js +36 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +3 -1
- package/package.json +7 -7
package/lib/auth-manager.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { Registry } from '@nocobase/utils';
|
|
|
11
11
|
import { Auth, AuthExtend } from './auth';
|
|
12
12
|
import { JwtOptions, JwtService } from './base/jwt-service';
|
|
13
13
|
import { ITokenBlacklistService } from './base/token-blacklist-service';
|
|
14
|
+
import { ITokenControlService } from './base/token-control-service';
|
|
14
15
|
export interface Authenticator {
|
|
15
16
|
authType: string;
|
|
16
17
|
options: Record<string, any>;
|
|
@@ -34,12 +35,14 @@ export declare class AuthManager {
|
|
|
34
35
|
* @internal
|
|
35
36
|
*/
|
|
36
37
|
jwt: JwtService;
|
|
38
|
+
tokenController: ITokenControlService;
|
|
37
39
|
protected options: AuthManagerOptions;
|
|
38
40
|
protected authTypes: Registry<AuthConfig>;
|
|
39
41
|
protected storer: Storer;
|
|
40
42
|
constructor(options: AuthManagerOptions);
|
|
41
43
|
setStorer(storer: Storer): void;
|
|
42
44
|
setTokenBlacklistService(service: ITokenBlacklistService): void;
|
|
45
|
+
setTokenControlService(service: ITokenControlService): void;
|
|
43
46
|
/**
|
|
44
47
|
* registerTypes
|
|
45
48
|
* @description Add a new authenticate type and the corresponding authenticator.
|
|
@@ -63,7 +66,7 @@ export declare class AuthManager {
|
|
|
63
66
|
get(name: string, ctx: Context): Promise<Auth>;
|
|
64
67
|
/**
|
|
65
68
|
* middleware
|
|
66
|
-
* @description Auth middleware, used to check the
|
|
69
|
+
* @description Auth middleware, used to check the user status.
|
|
67
70
|
*/
|
|
68
71
|
middleware(): (ctx: Context & {
|
|
69
72
|
auth: Auth;
|
package/lib/auth-manager.js
CHANGED
|
@@ -37,6 +37,7 @@ const _AuthManager = class _AuthManager {
|
|
|
37
37
|
* @internal
|
|
38
38
|
*/
|
|
39
39
|
jwt;
|
|
40
|
+
tokenController;
|
|
40
41
|
options;
|
|
41
42
|
authTypes = new import_utils.Registry();
|
|
42
43
|
// authenticators collection manager.
|
|
@@ -51,6 +52,9 @@ const _AuthManager = class _AuthManager {
|
|
|
51
52
|
setTokenBlacklistService(service) {
|
|
52
53
|
this.jwt.blacklist = service;
|
|
53
54
|
}
|
|
55
|
+
setTokenControlService(service) {
|
|
56
|
+
this.tokenController = service;
|
|
57
|
+
}
|
|
54
58
|
/**
|
|
55
59
|
* registerTypes
|
|
56
60
|
* @description Add a new authenticate type and the corresponding authenticator.
|
|
@@ -93,19 +97,11 @@ const _AuthManager = class _AuthManager {
|
|
|
93
97
|
}
|
|
94
98
|
/**
|
|
95
99
|
* middleware
|
|
96
|
-
* @description Auth middleware, used to check the
|
|
100
|
+
* @description Auth middleware, used to check the user status.
|
|
97
101
|
*/
|
|
98
102
|
middleware() {
|
|
99
103
|
const self = this;
|
|
100
104
|
return /* @__PURE__ */ __name(async function AuthManagerMiddleware(ctx, next) {
|
|
101
|
-
var _a;
|
|
102
|
-
const token = ctx.getBearerToken();
|
|
103
|
-
if (token && await ((_a = ctx.app.authManager.jwt.blacklist) == null ? void 0 : _a.has(token))) {
|
|
104
|
-
return ctx.throw(401, {
|
|
105
|
-
code: "TOKEN_INVALID",
|
|
106
|
-
message: ctx.t("Token is invalid")
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
105
|
const name = ctx.get(self.options.authKey) || self.options.default;
|
|
110
106
|
let authenticator;
|
|
111
107
|
try {
|
|
@@ -116,11 +112,15 @@ const _AuthManager = class _AuthManager {
|
|
|
116
112
|
ctx.logger.warn(err.message, { method: "check", authenticator: name });
|
|
117
113
|
return next();
|
|
118
114
|
}
|
|
119
|
-
if (authenticator) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
115
|
+
if (!authenticator) {
|
|
116
|
+
return next();
|
|
117
|
+
}
|
|
118
|
+
if (await ctx.auth.skipCheck()) {
|
|
119
|
+
return next();
|
|
120
|
+
}
|
|
121
|
+
const user = await ctx.auth.check();
|
|
122
|
+
if (user) {
|
|
123
|
+
ctx.auth.user = user;
|
|
124
124
|
}
|
|
125
125
|
await next();
|
|
126
126
|
}, "AuthManagerMiddleware");
|
package/lib/auth.d.ts
CHANGED
|
@@ -16,6 +16,24 @@ export type AuthConfig = {
|
|
|
16
16
|
};
|
|
17
17
|
ctx: Context;
|
|
18
18
|
};
|
|
19
|
+
export declare const AuthErrorCode: {
|
|
20
|
+
EMPTY_TOKEN: "EMPTY_TOKEN";
|
|
21
|
+
EXPIRED_TOKEN: "EXPIRED_TOKEN";
|
|
22
|
+
INVALID_TOKEN: "INVALID_TOKEN";
|
|
23
|
+
TOKEN_RENEW_FAILED: "TOKEN_RENEW_FAILED";
|
|
24
|
+
BLOCKED_TOKEN: "BLOCKED_TOKEN";
|
|
25
|
+
EXPIRED_SESSION: "EXPIRED_SESSION";
|
|
26
|
+
NOT_EXIST_USER: "NOT_EXIST_USER";
|
|
27
|
+
SKIP_TOKEN_RENEW: "SKIP_TOKEN_RENEW";
|
|
28
|
+
};
|
|
29
|
+
export type AuthErrorType = keyof typeof AuthErrorCode;
|
|
30
|
+
export declare class AuthError extends Error {
|
|
31
|
+
code: AuthErrorType;
|
|
32
|
+
constructor(options: {
|
|
33
|
+
code: AuthErrorType;
|
|
34
|
+
message: string;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
19
37
|
export type AuthExtend<T extends Auth> = new (config: AuthConfig) => T;
|
|
20
38
|
interface IAuth {
|
|
21
39
|
user: Model;
|
|
@@ -25,6 +43,10 @@ interface IAuth {
|
|
|
25
43
|
signOut(): Promise<any>;
|
|
26
44
|
}
|
|
27
45
|
export declare abstract class Auth implements IAuth {
|
|
46
|
+
/**
|
|
47
|
+
* options keys that are not allowed to use environment variables
|
|
48
|
+
*/
|
|
49
|
+
static optionsKeysNotAllowedInEnv: string[];
|
|
28
50
|
abstract user: Model;
|
|
29
51
|
protected authenticator: Authenticator;
|
|
30
52
|
protected options: {
|
|
@@ -32,6 +54,7 @@ export declare abstract class Auth implements IAuth {
|
|
|
32
54
|
};
|
|
33
55
|
protected ctx: Context;
|
|
34
56
|
constructor(config: AuthConfig);
|
|
57
|
+
skipCheck(): Promise<any>;
|
|
35
58
|
abstract check(): Promise<Model>;
|
|
36
59
|
signIn(): Promise<any>;
|
|
37
60
|
signUp(): Promise<any>;
|
package/lib/auth.js
CHANGED
|
@@ -11,6 +11,7 @@ var __defProp = Object.defineProperty;
|
|
|
11
11
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
12
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
13
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
14
15
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
15
16
|
var __export = (target, all) => {
|
|
16
17
|
for (var name in all)
|
|
@@ -25,11 +26,33 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
25
26
|
return to;
|
|
26
27
|
};
|
|
27
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
28
30
|
var auth_exports = {};
|
|
29
31
|
__export(auth_exports, {
|
|
30
|
-
Auth: () => Auth
|
|
32
|
+
Auth: () => Auth,
|
|
33
|
+
AuthError: () => AuthError,
|
|
34
|
+
AuthErrorCode: () => AuthErrorCode
|
|
31
35
|
});
|
|
32
36
|
module.exports = __toCommonJS(auth_exports);
|
|
37
|
+
const AuthErrorCode = {
|
|
38
|
+
EMPTY_TOKEN: "EMPTY_TOKEN",
|
|
39
|
+
EXPIRED_TOKEN: "EXPIRED_TOKEN",
|
|
40
|
+
INVALID_TOKEN: "INVALID_TOKEN",
|
|
41
|
+
TOKEN_RENEW_FAILED: "TOKEN_RENEW_FAILED",
|
|
42
|
+
BLOCKED_TOKEN: "BLOCKED_TOKEN",
|
|
43
|
+
EXPIRED_SESSION: "EXPIRED_SESSION",
|
|
44
|
+
NOT_EXIST_USER: "NOT_EXIST_USER",
|
|
45
|
+
SKIP_TOKEN_RENEW: "SKIP_TOKEN_RENEW"
|
|
46
|
+
};
|
|
47
|
+
const _AuthError = class _AuthError extends Error {
|
|
48
|
+
code;
|
|
49
|
+
constructor(options) {
|
|
50
|
+
super(options.message);
|
|
51
|
+
this.code = options.code;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
__name(_AuthError, "AuthError");
|
|
55
|
+
let AuthError = _AuthError;
|
|
33
56
|
const _Auth = class _Auth {
|
|
34
57
|
authenticator;
|
|
35
58
|
options;
|
|
@@ -40,6 +63,16 @@ const _Auth = class _Auth {
|
|
|
40
63
|
this.options = options;
|
|
41
64
|
this.ctx = ctx;
|
|
42
65
|
}
|
|
66
|
+
async skipCheck() {
|
|
67
|
+
const token = this.ctx.getBearerToken();
|
|
68
|
+
if (!token && this.ctx.app.options.acl === false) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
const { resourceName, actionName } = this.ctx.action;
|
|
72
|
+
const acl = this.ctx.dataSource.acl;
|
|
73
|
+
const isPublic = await acl.allowManager.isAllowed(resourceName, actionName, this.ctx);
|
|
74
|
+
return isPublic;
|
|
75
|
+
}
|
|
43
76
|
// The following methods are mainly designed for user authentications.
|
|
44
77
|
async signIn() {
|
|
45
78
|
}
|
|
@@ -49,8 +82,14 @@ const _Auth = class _Auth {
|
|
|
49
82
|
}
|
|
50
83
|
};
|
|
51
84
|
__name(_Auth, "Auth");
|
|
85
|
+
/**
|
|
86
|
+
* options keys that are not allowed to use environment variables
|
|
87
|
+
*/
|
|
88
|
+
__publicField(_Auth, "optionsKeysNotAllowedInEnv");
|
|
52
89
|
let Auth = _Auth;
|
|
53
90
|
// Annotate the CommonJS export names for ESM import in node:
|
|
54
91
|
0 && (module.exports = {
|
|
55
|
-
Auth
|
|
92
|
+
Auth,
|
|
93
|
+
AuthError,
|
|
94
|
+
AuthErrorCode
|
|
56
95
|
});
|
package/lib/base/auth.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { Collection, Model } from '@nocobase/database';
|
|
10
10
|
import { Auth, AuthConfig } from '../auth';
|
|
11
11
|
import { JwtService } from './jwt-service';
|
|
12
|
+
import { ITokenControlService } from './token-control-service';
|
|
12
13
|
/**
|
|
13
14
|
* BaseAuth
|
|
14
15
|
* @description A base class with jwt provide some common methods.
|
|
@@ -23,6 +24,7 @@ export declare class BaseAuth extends Auth {
|
|
|
23
24
|
* @internal
|
|
24
25
|
*/
|
|
25
26
|
get jwt(): JwtService;
|
|
27
|
+
get tokenController(): ITokenControlService;
|
|
26
28
|
set user(user: Model);
|
|
27
29
|
get user(): Model;
|
|
28
30
|
/**
|
|
@@ -33,7 +35,7 @@ export declare class BaseAuth extends Auth {
|
|
|
33
35
|
* @internal
|
|
34
36
|
*/
|
|
35
37
|
validateUsername(username: string): boolean;
|
|
36
|
-
check():
|
|
38
|
+
check(): ReturnType<Auth['check']>;
|
|
37
39
|
validate(): Promise<Model>;
|
|
38
40
|
signIn(): Promise<{
|
|
39
41
|
user: Model<any, any>;
|
package/lib/base/auth.js
CHANGED
|
@@ -7,9 +7,11 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
var __create = Object.create;
|
|
10
11
|
var __defProp = Object.defineProperty;
|
|
11
12
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
13
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
13
15
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
16
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
15
17
|
var __export = (target, all) => {
|
|
@@ -24,13 +26,23 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
24
26
|
}
|
|
25
27
|
return to;
|
|
26
28
|
};
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
30
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
31
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
32
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
33
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
34
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
35
|
+
mod
|
|
36
|
+
));
|
|
27
37
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
38
|
var auth_exports = {};
|
|
29
39
|
__export(auth_exports, {
|
|
30
40
|
BaseAuth: () => BaseAuth
|
|
31
41
|
});
|
|
32
42
|
module.exports = __toCommonJS(auth_exports);
|
|
43
|
+
var import_jsonwebtoken = __toESM(require("jsonwebtoken"));
|
|
33
44
|
var import_auth = require("../auth");
|
|
45
|
+
const localeNamespace = "auth";
|
|
34
46
|
const _BaseAuth = class _BaseAuth extends import_auth.Auth {
|
|
35
47
|
userCollection;
|
|
36
48
|
constructor(config) {
|
|
@@ -47,6 +59,9 @@ const _BaseAuth = class _BaseAuth extends import_auth.Auth {
|
|
|
47
59
|
get jwt() {
|
|
48
60
|
return this.ctx.app.authManager.jwt;
|
|
49
61
|
}
|
|
62
|
+
get tokenController() {
|
|
63
|
+
return this.ctx.app.authManager.tokenController;
|
|
64
|
+
}
|
|
50
65
|
set user(user) {
|
|
51
66
|
this.ctx.state.currentUser = user;
|
|
52
67
|
}
|
|
@@ -66,33 +81,119 @@ const _BaseAuth = class _BaseAuth extends import_auth.Auth {
|
|
|
66
81
|
return /^[^@.<>"'/]{1,50}$/.test(username);
|
|
67
82
|
}
|
|
68
83
|
async check() {
|
|
84
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i;
|
|
69
85
|
const token = this.ctx.getBearerToken();
|
|
70
86
|
if (!token) {
|
|
71
|
-
|
|
87
|
+
this.ctx.throw(401, {
|
|
88
|
+
message: this.ctx.t("Unauthenticated. Please sign in to continue.", { ns: localeNamespace }),
|
|
89
|
+
code: import_auth.AuthErrorCode.EMPTY_TOKEN
|
|
90
|
+
});
|
|
72
91
|
}
|
|
92
|
+
let tokenStatus;
|
|
93
|
+
let payload;
|
|
73
94
|
try {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
95
|
+
payload = await this.jwt.decode(token);
|
|
96
|
+
tokenStatus = "valid";
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if (err.name === "TokenExpiredError") {
|
|
99
|
+
tokenStatus = "expired";
|
|
100
|
+
payload = import_jsonwebtoken.default.decode(token);
|
|
101
|
+
} else {
|
|
102
|
+
this.ctx.logger.error(err, { method: "jwt.decode" });
|
|
103
|
+
this.ctx.throw(401, {
|
|
104
|
+
message: this.ctx.t("Your session has expired. Please sign in again.", { ns: localeNamespace }),
|
|
105
|
+
code: import_auth.AuthErrorCode.INVALID_TOKEN
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const { userId, roleName, iat, temp, jti, exp, signInTime } = payload ?? {};
|
|
110
|
+
const tokenPolicy = await this.tokenController.getConfig();
|
|
111
|
+
if (signInTime && Date.now() - signInTime > tokenPolicy.sessionExpirationTime) {
|
|
112
|
+
this.ctx.throw(401, {
|
|
113
|
+
message: this.ctx.t("Your session has expired. Please sign in again.", { ns: localeNamespace }),
|
|
114
|
+
code: import_auth.AuthErrorCode.EXPIRED_SESSION
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
if (tokenStatus === "valid" && Date.now() - iat * 1e3 > tokenPolicy.tokenExpirationTime) {
|
|
118
|
+
tokenStatus = "expired";
|
|
119
|
+
}
|
|
120
|
+
const blocked = await this.jwt.blacklist.has(jti ?? token);
|
|
121
|
+
if (blocked) {
|
|
122
|
+
this.ctx.throw(401, {
|
|
123
|
+
message: this.ctx.t("Your session has expired. Please sign in again.", { ns: localeNamespace }),
|
|
124
|
+
code: import_auth.AuthErrorCode.BLOCKED_TOKEN
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
if (roleName) {
|
|
128
|
+
this.ctx.headers["x-role"] = roleName;
|
|
129
|
+
}
|
|
130
|
+
const cache = this.ctx.cache;
|
|
131
|
+
const user = await cache.wrap(
|
|
132
|
+
this.getCacheKey(userId),
|
|
133
|
+
() => this.userRepository.findOne({
|
|
134
|
+
filter: {
|
|
135
|
+
id: userId
|
|
136
|
+
},
|
|
137
|
+
raw: true
|
|
138
|
+
})
|
|
139
|
+
);
|
|
140
|
+
if (!temp && tokenStatus !== "valid") {
|
|
141
|
+
this.ctx.throw(401, {
|
|
142
|
+
message: this.ctx.t("Your session has expired. Please sign in again.", { ns: localeNamespace }),
|
|
143
|
+
code: import_auth.AuthErrorCode.INVALID_TOKEN
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (tokenStatus === "valid" && user.passwordChangeTz && iat * 1e3 < user.passwordChangeTz) {
|
|
147
|
+
this.ctx.throw(401, {
|
|
148
|
+
message: this.ctx.t("User password changed, please signin again.", { ns: localeNamespace }),
|
|
149
|
+
code: import_auth.AuthErrorCode.INVALID_TOKEN
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
if (tokenStatus === "expired") {
|
|
153
|
+
if (tokenPolicy.expiredTokenRenewLimit > 0 && Date.now() - exp * 1e3 > tokenPolicy.expiredTokenRenewLimit) {
|
|
154
|
+
this.ctx.throw(401, {
|
|
155
|
+
message: this.ctx.t("Your session has expired. Please sign in again.", { ns: localeNamespace }),
|
|
156
|
+
code: import_auth.AuthErrorCode.EXPIRED_SESSION
|
|
157
|
+
});
|
|
77
158
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
159
|
+
try {
|
|
160
|
+
this.ctx.logger.info("token renewing", {
|
|
161
|
+
method: "auth.check",
|
|
162
|
+
url: this.ctx.originalUrl,
|
|
163
|
+
headers: JSON.stringify((_b = (_a = this.ctx) == null ? void 0 : _a.req) == null ? void 0 : _b.headers)
|
|
164
|
+
});
|
|
165
|
+
const isStreamRequest = ((_e = (_d = (_c = this.ctx) == null ? void 0 : _c.req) == null ? void 0 : _d.headers) == null ? void 0 : _e.accept) === "text/event-stream";
|
|
166
|
+
if (isStreamRequest) {
|
|
167
|
+
this.ctx.throw(401, {
|
|
168
|
+
message: "Stream api not allow renew token.",
|
|
169
|
+
code: import_auth.AuthErrorCode.SKIP_TOKEN_RENEW
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
const renewedResult = await this.tokenController.renew(jti);
|
|
173
|
+
this.ctx.logger.info("token renewed", {
|
|
174
|
+
method: "auth.check",
|
|
175
|
+
url: this.ctx.originalUrl,
|
|
176
|
+
headers: JSON.stringify((_g = (_f = this.ctx) == null ? void 0 : _f.req) == null ? void 0 : _g.headers)
|
|
177
|
+
});
|
|
178
|
+
const expiresIn = Math.floor(tokenPolicy.tokenExpirationTime / 1e3);
|
|
179
|
+
const newToken = this.jwt.sign({ userId, roleName, temp, signInTime }, { jwtid: renewedResult.jti, expiresIn });
|
|
180
|
+
this.ctx.res.setHeader("x-new-token", newToken);
|
|
181
|
+
return user;
|
|
182
|
+
} catch (err) {
|
|
183
|
+
this.ctx.logger.info("token renew failed", {
|
|
184
|
+
method: "auth.check",
|
|
185
|
+
url: this.ctx.originalUrl,
|
|
186
|
+
err,
|
|
187
|
+
headers: JSON.stringify((_i = (_h = this.ctx) == null ? void 0 : _h.req) == null ? void 0 : _i.headers)
|
|
188
|
+
});
|
|
189
|
+
const options = err instanceof import_auth.AuthError ? { code: err.code, message: err.message } : { message: err.message, code: err.code ?? import_auth.AuthErrorCode.INVALID_TOKEN };
|
|
190
|
+
this.ctx.throw(401, {
|
|
191
|
+
message: this.ctx.t(options.message, { ns: localeNamespace }),
|
|
192
|
+
code: options.code
|
|
193
|
+
});
|
|
90
194
|
}
|
|
91
|
-
return user;
|
|
92
|
-
} catch (err) {
|
|
93
|
-
this.ctx.logger.error(err, { method: "check" });
|
|
94
|
-
return null;
|
|
95
195
|
}
|
|
196
|
+
return user;
|
|
96
197
|
}
|
|
97
198
|
async validate() {
|
|
98
199
|
return null;
|
|
@@ -102,15 +203,30 @@ const _BaseAuth = class _BaseAuth extends import_auth.Auth {
|
|
|
102
203
|
try {
|
|
103
204
|
user = await this.validate();
|
|
104
205
|
} catch (err) {
|
|
105
|
-
this.ctx.throw(err.status || 401, err.message
|
|
206
|
+
this.ctx.throw(err.status || 401, err.message, {
|
|
207
|
+
...err
|
|
208
|
+
});
|
|
106
209
|
}
|
|
107
210
|
if (!user) {
|
|
108
|
-
this.ctx.throw(401,
|
|
211
|
+
this.ctx.throw(401, {
|
|
212
|
+
message: this.ctx.t("User not found. Please sign in again to continue.", { ns: localeNamespace }),
|
|
213
|
+
code: import_auth.AuthErrorCode.NOT_EXIST_USER
|
|
214
|
+
});
|
|
109
215
|
}
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
216
|
+
const tokenInfo = await this.tokenController.add({ userId: user.id });
|
|
217
|
+
const expiresIn = Math.floor((await this.tokenController.getConfig()).tokenExpirationTime / 1e3);
|
|
218
|
+
const token = this.jwt.sign(
|
|
219
|
+
{
|
|
220
|
+
userId: user.id,
|
|
221
|
+
temp: true,
|
|
222
|
+
iat: Math.floor(tokenInfo.issuedTime / 1e3),
|
|
223
|
+
signInTime: tokenInfo.signInTime
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
jwtid: tokenInfo.jti,
|
|
227
|
+
expiresIn
|
|
228
|
+
}
|
|
229
|
+
);
|
|
114
230
|
return {
|
|
115
231
|
user,
|
|
116
232
|
token
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
|
-
import jwt, { SignOptions } from 'jsonwebtoken';
|
|
9
|
+
import jwt, { JwtPayload, SignOptions } from 'jsonwebtoken';
|
|
10
10
|
import { ITokenBlacklistService } from './token-blacklist-service';
|
|
11
11
|
export interface JwtOptions {
|
|
12
12
|
secret: string;
|
|
@@ -20,7 +20,7 @@ export declare class JwtService {
|
|
|
20
20
|
private expiresIn;
|
|
21
21
|
private secret;
|
|
22
22
|
sign(payload: SignPayload, options?: SignOptions): string;
|
|
23
|
-
decode(token: string): Promise<
|
|
23
|
+
decode(token: string): Promise<JwtPayload>;
|
|
24
24
|
/**
|
|
25
25
|
* @description Block a token so that this token can no longer be used
|
|
26
26
|
*/
|
package/lib/base/jwt-service.js
CHANGED
|
@@ -86,9 +86,9 @@ const _JwtService = class _JwtService {
|
|
|
86
86
|
return null;
|
|
87
87
|
}
|
|
88
88
|
try {
|
|
89
|
-
const { exp } = await this.decode(token);
|
|
89
|
+
const { exp, jti } = await this.decode(token);
|
|
90
90
|
return this.blacklist.add({
|
|
91
|
-
token,
|
|
91
|
+
token: jti ?? token,
|
|
92
92
|
expiration: new Date(exp * 1e3).toString()
|
|
93
93
|
});
|
|
94
94
|
} catch {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
export interface TokenPolicyConfig {
|
|
10
|
+
tokenExpirationTime: string;
|
|
11
|
+
sessionExpirationTime: string;
|
|
12
|
+
expiredTokenRenewLimit: string;
|
|
13
|
+
}
|
|
14
|
+
type millisecond = number;
|
|
15
|
+
export type NumericTokenPolicyConfig = {
|
|
16
|
+
[K in keyof TokenPolicyConfig]: millisecond;
|
|
17
|
+
};
|
|
18
|
+
export type TokenInfo = {
|
|
19
|
+
jti: string;
|
|
20
|
+
userId: number;
|
|
21
|
+
issuedTime: EpochTimeStamp;
|
|
22
|
+
signInTime: EpochTimeStamp;
|
|
23
|
+
renewed: boolean;
|
|
24
|
+
};
|
|
25
|
+
export type JTIStatus = 'valid' | 'inactive' | 'blocked' | 'missing' | 'renewed' | 'expired';
|
|
26
|
+
export interface ITokenControlService {
|
|
27
|
+
getConfig(): Promise<NumericTokenPolicyConfig>;
|
|
28
|
+
setConfig(config: TokenPolicyConfig): Promise<any>;
|
|
29
|
+
renew(jti: string): Promise<{
|
|
30
|
+
jti: string;
|
|
31
|
+
issuedTime: EpochTimeStamp;
|
|
32
|
+
}>;
|
|
33
|
+
add({ userId }: {
|
|
34
|
+
userId: number;
|
|
35
|
+
}): Promise<TokenInfo>;
|
|
36
|
+
removeSessionExpiredTokens(userId: number): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __copyProps = (to, from, except, desc) => {
|
|
15
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
16
|
+
for (let key of __getOwnPropNames(from))
|
|
17
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
18
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
19
|
+
}
|
|
20
|
+
return to;
|
|
21
|
+
};
|
|
22
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
23
|
+
var token_control_service_exports = {};
|
|
24
|
+
module.exports = __toCommonJS(token_control_service_exports);
|
package/lib/client.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
export { AuthErrorCode } from './auth';
|
package/lib/client.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var client_exports = {};
|
|
28
|
+
__export(client_exports, {
|
|
29
|
+
AuthErrorCode: () => import_auth.AuthErrorCode
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(client_exports);
|
|
32
|
+
var import_auth = require("./auth");
|
|
33
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
34
|
+
0 && (module.exports = {
|
|
35
|
+
AuthErrorCode
|
|
36
|
+
});
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -28,11 +28,13 @@ __reExport(src_exports, require("./auth"), module.exports);
|
|
|
28
28
|
__reExport(src_exports, require("./auth-manager"), module.exports);
|
|
29
29
|
__reExport(src_exports, require("./base/auth"), module.exports);
|
|
30
30
|
__reExport(src_exports, require("./base/token-blacklist-service"), module.exports);
|
|
31
|
+
__reExport(src_exports, require("./base/token-control-service"), module.exports);
|
|
31
32
|
// Annotate the CommonJS export names for ESM import in node:
|
|
32
33
|
0 && (module.exports = {
|
|
33
34
|
...require("./actions"),
|
|
34
35
|
...require("./auth"),
|
|
35
36
|
...require("./auth-manager"),
|
|
36
37
|
...require("./base/auth"),
|
|
37
|
-
...require("./base/token-blacklist-service")
|
|
38
|
+
...require("./base/token-blacklist-service"),
|
|
39
|
+
...require("./base/token-control-service")
|
|
38
40
|
});
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/auth",
|
|
3
|
-
"version": "1.6.0-beta.
|
|
3
|
+
"version": "1.6.0-beta.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"main": "./lib/index.js",
|
|
7
7
|
"types": "./lib/index.d.ts",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@nocobase/actions": "1.6.0-beta.
|
|
10
|
-
"@nocobase/cache": "1.6.0-beta.
|
|
11
|
-
"@nocobase/database": "1.6.0-beta.
|
|
12
|
-
"@nocobase/resourcer": "1.6.0-beta.
|
|
13
|
-
"@nocobase/utils": "1.6.0-beta.
|
|
9
|
+
"@nocobase/actions": "1.6.0-beta.5",
|
|
10
|
+
"@nocobase/cache": "1.6.0-beta.5",
|
|
11
|
+
"@nocobase/database": "1.6.0-beta.5",
|
|
12
|
+
"@nocobase/resourcer": "1.6.0-beta.5",
|
|
13
|
+
"@nocobase/utils": "1.6.0-beta.5",
|
|
14
14
|
"@types/jsonwebtoken": "^8.5.8",
|
|
15
15
|
"jsonwebtoken": "^8.5.1"
|
|
16
16
|
},
|
|
@@ -19,5 +19,5 @@
|
|
|
19
19
|
"url": "git+https://github.com/nocobase/nocobase.git",
|
|
20
20
|
"directory": "packages/auth"
|
|
21
21
|
},
|
|
22
|
-
"gitHead": "
|
|
22
|
+
"gitHead": "66e7c7239b29361bc60543886d107ba0e00085d9"
|
|
23
23
|
}
|