@nocobase/plugin-auth 0.10.0-alpha.5 → 0.11.0-alpha.1
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/client.d.ts +2 -3
- package/lib/client/AuthProvider.d.ts +2 -0
- package/lib/client/AuthProvider.js +56 -0
- package/lib/client/index.d.ts +5 -3
- package/lib/client/index.js +13 -42
- package/lib/server/basic-auth.js +6 -6
- package/lib/server/collections/authenticators.d.ts +1 -1
- package/lib/server/collections/token-blacklist.d.ts +3 -0
- package/lib/server/collections/token-blacklist.js +21 -0
- package/lib/server/collections/users-authenticators.d.ts +1 -1
- package/lib/server/plugin.d.ts +0 -2
- package/lib/server/plugin.js +6 -7
- package/lib/server/token-blacklist.d.ts +15 -0
- package/lib/server/token-blacklist.js +82 -0
- package/package.json +30 -8
- package/server.d.ts +2 -3
- package/src/client/AuthProvider.tsx +41 -0
- package/src/client/index.tsx +9 -40
- package/src/server/__tests__/token-blacklist.test.ts +73 -0
- package/src/server/basic-auth.ts +6 -6
- package/src/server/collections/token-blacklist.ts +19 -0
- package/src/server/plugin.ts +10 -10
- package/src/server/token-blacklist.ts +66 -0
package/client.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export { default } from './lib/client';
|
|
1
|
+
export * from './src/client';
|
|
2
|
+
export { default } from './src/client';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.AuthProvider = void 0;
|
|
7
|
+
function _client() {
|
|
8
|
+
const data = require("@nocobase/client");
|
|
9
|
+
_client = function _client() {
|
|
10
|
+
return data;
|
|
11
|
+
};
|
|
12
|
+
return data;
|
|
13
|
+
}
|
|
14
|
+
function _react() {
|
|
15
|
+
const data = _interopRequireDefault(require("react"));
|
|
16
|
+
_react = function _react() {
|
|
17
|
+
return data;
|
|
18
|
+
};
|
|
19
|
+
return data;
|
|
20
|
+
}
|
|
21
|
+
var _Authenticator = require("./settings/Authenticator");
|
|
22
|
+
var _SigninPage = _interopRequireDefault(require("./basic/SigninPage"));
|
|
23
|
+
var _preset = require("../preset");
|
|
24
|
+
var _SignupPage = _interopRequireDefault(require("./basic/SignupPage"));
|
|
25
|
+
var _locale = require("./locale");
|
|
26
|
+
var _Options = require("./basic/Options");
|
|
27
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
28
|
+
const AuthProvider = props => {
|
|
29
|
+
const _useAuthTranslation = (0, _locale.useAuthTranslation)(),
|
|
30
|
+
t = _useAuthTranslation.t;
|
|
31
|
+
return _react().default.createElement(_client().SettingsCenterProvider, {
|
|
32
|
+
settings: {
|
|
33
|
+
auth: {
|
|
34
|
+
title: t('Authentication'),
|
|
35
|
+
icon: 'LoginOutlined',
|
|
36
|
+
tabs: {
|
|
37
|
+
authenticators: {
|
|
38
|
+
title: t('Authenticators'),
|
|
39
|
+
component: () => _react().default.createElement(_Authenticator.Authenticator, null)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}, _react().default.createElement(_client().OptionsComponentProvider, {
|
|
45
|
+
authType: _preset.presetAuthType,
|
|
46
|
+
component: _Options.Options
|
|
47
|
+
}, _react().default.createElement(_client().SigninPageProvider, {
|
|
48
|
+
authType: _preset.presetAuthType,
|
|
49
|
+
tabTitle: t('Sign in via email'),
|
|
50
|
+
component: _SigninPage.default
|
|
51
|
+
}, _react().default.createElement(_client().SignupPageProvider, {
|
|
52
|
+
authType: _preset.presetAuthType,
|
|
53
|
+
component: _SignupPage.default
|
|
54
|
+
}, props.children))));
|
|
55
|
+
};
|
|
56
|
+
exports.AuthProvider = AuthProvider;
|
package/lib/client/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
declare
|
|
3
|
-
|
|
1
|
+
import { Plugin } from '@nocobase/client';
|
|
2
|
+
export declare class AuthPlugin extends Plugin {
|
|
3
|
+
load(): Promise<void>;
|
|
4
|
+
}
|
|
5
|
+
export default AuthPlugin;
|
package/lib/client/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default = void 0;
|
|
6
|
+
exports.default = exports.AuthPlugin = void 0;
|
|
7
7
|
function _client() {
|
|
8
8
|
const data = require("@nocobase/client");
|
|
9
9
|
_client = function _client() {
|
|
@@ -11,46 +11,17 @@ function _client() {
|
|
|
11
11
|
};
|
|
12
12
|
return data;
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
var _AuthProvider = require("./AuthProvider");
|
|
15
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
16
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
17
|
+
class AuthPlugin extends _client().Plugin {
|
|
18
|
+
load() {
|
|
19
|
+
var _this = this;
|
|
20
|
+
return _asyncToGenerator(function* () {
|
|
21
|
+
_this.app.use(_AuthProvider.AuthProvider);
|
|
22
|
+
})();
|
|
23
|
+
}
|
|
20
24
|
}
|
|
21
|
-
|
|
22
|
-
var
|
|
23
|
-
var _preset = require("../preset");
|
|
24
|
-
var _SignupPage = _interopRequireDefault(require("./basic/SignupPage"));
|
|
25
|
-
var _locale = require("./locale");
|
|
26
|
-
var _Options = require("./basic/Options");
|
|
27
|
-
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
28
|
-
var _default = props => {
|
|
29
|
-
const _useAuthTranslation = (0, _locale.useAuthTranslation)(),
|
|
30
|
-
t = _useAuthTranslation.t;
|
|
31
|
-
return _react().default.createElement(_client().SettingsCenterProvider, {
|
|
32
|
-
settings: {
|
|
33
|
-
auth: {
|
|
34
|
-
title: t('Authentication'),
|
|
35
|
-
icon: 'LoginOutlined',
|
|
36
|
-
tabs: {
|
|
37
|
-
authenticators: {
|
|
38
|
-
title: t('Authenticators'),
|
|
39
|
-
component: () => _react().default.createElement(_Authenticator.Authenticator, null)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}, _react().default.createElement(_client().OptionsComponentProvider, {
|
|
45
|
-
authType: _preset.presetAuthType,
|
|
46
|
-
component: _Options.Options
|
|
47
|
-
}, _react().default.createElement(_client().SigninPageProvider, {
|
|
48
|
-
authType: _preset.presetAuthType,
|
|
49
|
-
tabTitle: t('Sign in via email'),
|
|
50
|
-
component: _SigninPage.default
|
|
51
|
-
}, _react().default.createElement(_client().SignupPageProvider, {
|
|
52
|
-
authType: _preset.presetAuthType,
|
|
53
|
-
component: _SignupPage.default
|
|
54
|
-
}, props.children))));
|
|
55
|
-
};
|
|
25
|
+
exports.AuthPlugin = AuthPlugin;
|
|
26
|
+
var _default = AuthPlugin;
|
|
56
27
|
exports.default = _default;
|
package/lib/server/basic-auth.js
CHANGED
|
@@ -11,7 +11,6 @@ function _auth() {
|
|
|
11
11
|
};
|
|
12
12
|
return data;
|
|
13
13
|
}
|
|
14
|
-
var _preset = require("../preset");
|
|
15
14
|
function _crypto() {
|
|
16
15
|
const data = _interopRequireDefault(require("crypto"));
|
|
17
16
|
_crypto = function _crypto() {
|
|
@@ -19,6 +18,7 @@ function _crypto() {
|
|
|
19
18
|
};
|
|
20
19
|
return data;
|
|
21
20
|
}
|
|
21
|
+
var _preset = require("../preset");
|
|
22
22
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
23
23
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
24
24
|
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
@@ -47,7 +47,7 @@ class BasicAuth extends _auth().BaseAuth {
|
|
|
47
47
|
ns: _preset.namespace
|
|
48
48
|
}));
|
|
49
49
|
}
|
|
50
|
-
const user = yield _this.
|
|
50
|
+
const user = yield _this.userRepository.findOne({
|
|
51
51
|
where: {
|
|
52
52
|
[uniqueField]: values[uniqueField]
|
|
53
53
|
}
|
|
@@ -96,7 +96,7 @@ class BasicAuth extends _auth().BaseAuth {
|
|
|
96
96
|
ns: _preset.namespace
|
|
97
97
|
}));
|
|
98
98
|
}
|
|
99
|
-
const user = yield _this3.
|
|
99
|
+
const user = yield _this3.userRepository.findOne({
|
|
100
100
|
where: {
|
|
101
101
|
email
|
|
102
102
|
}
|
|
@@ -119,7 +119,7 @@ class BasicAuth extends _auth().BaseAuth {
|
|
|
119
119
|
email = _ctx$action$params$va.email,
|
|
120
120
|
password = _ctx$action$params$va.password,
|
|
121
121
|
resetToken = _ctx$action$params$va.resetToken;
|
|
122
|
-
const user = yield _this4.
|
|
122
|
+
const user = yield _this4.userRepository.findOne({
|
|
123
123
|
where: {
|
|
124
124
|
email,
|
|
125
125
|
resetToken
|
|
@@ -140,7 +140,7 @@ class BasicAuth extends _auth().BaseAuth {
|
|
|
140
140
|
return _asyncToGenerator(function* () {
|
|
141
141
|
const ctx = _this5.ctx;
|
|
142
142
|
const token = ctx.action.params.token;
|
|
143
|
-
const user = yield _this5.
|
|
143
|
+
const user = yield _this5.userRepository.findOne({
|
|
144
144
|
where: {
|
|
145
145
|
resetToken: token
|
|
146
146
|
}
|
|
@@ -162,7 +162,7 @@ class BasicAuth extends _auth().BaseAuth {
|
|
|
162
162
|
if (!currentUser) {
|
|
163
163
|
ctx.throw(401);
|
|
164
164
|
}
|
|
165
|
-
const user = yield _this6.
|
|
165
|
+
const user = yield _this6.userRepository.findOne({
|
|
166
166
|
where: {
|
|
167
167
|
email: currentUser.email
|
|
168
168
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _default = {
|
|
8
|
+
namespace: 'auth.token-black',
|
|
9
|
+
duplicator: 'optional',
|
|
10
|
+
name: 'tokenBlacklist',
|
|
11
|
+
model: 'TokenBlacklistModel',
|
|
12
|
+
fields: [{
|
|
13
|
+
type: 'string',
|
|
14
|
+
name: 'token',
|
|
15
|
+
index: true
|
|
16
|
+
}, {
|
|
17
|
+
type: 'date',
|
|
18
|
+
name: 'expiration'
|
|
19
|
+
}]
|
|
20
|
+
};
|
|
21
|
+
exports.default = _default;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CollectionOptions } from '@nocobase/database';
|
|
2
|
-
declare const _default: CollectionOptions;
|
|
3
2
|
/**
|
|
4
3
|
* Collection for user information of extended authentication methods,
|
|
5
4
|
* such as saml, oicd, oauth, sms, etc.
|
|
6
5
|
*/
|
|
6
|
+
declare const _default: CollectionOptions;
|
|
7
7
|
export default _default;
|
package/lib/server/plugin.d.ts
CHANGED
|
@@ -4,8 +4,6 @@ export declare class AuthPlugin extends Plugin {
|
|
|
4
4
|
beforeLoad(): Promise<void>;
|
|
5
5
|
load(): Promise<void>;
|
|
6
6
|
install(options?: InstallOptions): Promise<void>;
|
|
7
|
-
afterEnable(): Promise<void>;
|
|
8
|
-
afterDisable(): Promise<void>;
|
|
9
7
|
remove(): Promise<void>;
|
|
10
8
|
}
|
|
11
9
|
export default AuthPlugin;
|
package/lib/server/plugin.js
CHANGED
|
@@ -18,12 +18,13 @@ function _path() {
|
|
|
18
18
|
};
|
|
19
19
|
return data;
|
|
20
20
|
}
|
|
21
|
-
var _basicAuth = require("./basic-auth");
|
|
22
21
|
var _preset = require("../preset");
|
|
23
22
|
var _auth = _interopRequireDefault(require("./actions/auth"));
|
|
24
23
|
var _authenticators = _interopRequireDefault(require("./actions/authenticators"));
|
|
24
|
+
var _basicAuth = require("./basic-auth");
|
|
25
25
|
var _locale = require("./locale");
|
|
26
26
|
var _authenticator = require("./model/authenticator");
|
|
27
|
+
var _tokenBlacklist = require("./token-blacklist");
|
|
27
28
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
28
29
|
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
29
30
|
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
@@ -72,6 +73,10 @@ class AuthPlugin extends _server().Plugin {
|
|
|
72
73
|
return get;
|
|
73
74
|
}()
|
|
74
75
|
});
|
|
76
|
+
if (!_this2.app.authManager.jwt.blacklist) {
|
|
77
|
+
// If blacklist service is not set, should configure default blacklist service
|
|
78
|
+
_this2.app.authManager.setTokenBlacklistService(new _tokenBlacklist.TokenBlacklistService(_this2));
|
|
79
|
+
}
|
|
75
80
|
_this2.app.authManager.registerTypes(_preset.presetAuthType, {
|
|
76
81
|
auth: _basicAuth.BasicAuth
|
|
77
82
|
});
|
|
@@ -115,12 +120,6 @@ class AuthPlugin extends _server().Plugin {
|
|
|
115
120
|
});
|
|
116
121
|
})();
|
|
117
122
|
}
|
|
118
|
-
afterEnable() {
|
|
119
|
-
return _asyncToGenerator(function* () {})();
|
|
120
|
-
}
|
|
121
|
-
afterDisable() {
|
|
122
|
-
return _asyncToGenerator(function* () {})();
|
|
123
|
-
}
|
|
124
123
|
remove() {
|
|
125
124
|
return _asyncToGenerator(function* () {})();
|
|
126
125
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ITokenBlacklistService } from '@nocobase/auth';
|
|
2
|
+
import { Repository } from '@nocobase/database';
|
|
3
|
+
import { CronJob } from 'cron';
|
|
4
|
+
import AuthPlugin from './plugin';
|
|
5
|
+
export declare class TokenBlacklistService implements ITokenBlacklistService {
|
|
6
|
+
protected plugin: AuthPlugin;
|
|
7
|
+
repo: Repository;
|
|
8
|
+
cronJob: CronJob;
|
|
9
|
+
constructor(plugin: AuthPlugin);
|
|
10
|
+
get app(): import("@nocobase/server").default<import("@nocobase/server").DefaultState, import("@nocobase/server").DefaultContext>;
|
|
11
|
+
createCronJob(): CronJob;
|
|
12
|
+
has(token: string): Promise<boolean>;
|
|
13
|
+
add(values: any): Promise<[import("@nocobase/database").Model<any, any>, boolean]>;
|
|
14
|
+
deleteByExpiration(): Promise<any>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.TokenBlacklistService = void 0;
|
|
7
|
+
function _cron() {
|
|
8
|
+
const data = require("cron");
|
|
9
|
+
_cron = function _cron() {
|
|
10
|
+
return data;
|
|
11
|
+
};
|
|
12
|
+
return data;
|
|
13
|
+
}
|
|
14
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
|
|
15
|
+
function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
|
|
16
|
+
class TokenBlacklistService {
|
|
17
|
+
constructor(plugin) {
|
|
18
|
+
this.plugin = void 0;
|
|
19
|
+
this.repo = void 0;
|
|
20
|
+
this.cronJob = void 0;
|
|
21
|
+
this.plugin = plugin;
|
|
22
|
+
this.repo = plugin.db.getRepository('tokenBlacklist');
|
|
23
|
+
this.cronJob = this.createCronJob();
|
|
24
|
+
}
|
|
25
|
+
get app() {
|
|
26
|
+
return this.plugin.app;
|
|
27
|
+
}
|
|
28
|
+
createCronJob() {
|
|
29
|
+
var _this = this;
|
|
30
|
+
const cronJob = new (_cron().CronJob)(
|
|
31
|
+
// every day at 03:00
|
|
32
|
+
'0 3 * * *',
|
|
33
|
+
/*#__PURE__*/
|
|
34
|
+
//
|
|
35
|
+
_asyncToGenerator(function* () {
|
|
36
|
+
_this.app.logger.info(`${_this.plugin.name}: Start delete expired blacklist token`);
|
|
37
|
+
yield _this.deleteByExpiration();
|
|
38
|
+
_this.app.logger.info(`${_this.plugin.name}: End delete expired blacklist token`);
|
|
39
|
+
}), null);
|
|
40
|
+
this.app.once('beforeStart', () => {
|
|
41
|
+
cronJob.start();
|
|
42
|
+
});
|
|
43
|
+
this.app.once('beforeStop', () => {
|
|
44
|
+
cronJob.stop();
|
|
45
|
+
});
|
|
46
|
+
return cronJob;
|
|
47
|
+
}
|
|
48
|
+
has(token) {
|
|
49
|
+
var _this2 = this;
|
|
50
|
+
return _asyncToGenerator(function* () {
|
|
51
|
+
return !!(yield _this2.repo.findOne({
|
|
52
|
+
where: {
|
|
53
|
+
token
|
|
54
|
+
}
|
|
55
|
+
}));
|
|
56
|
+
})();
|
|
57
|
+
}
|
|
58
|
+
add(values) {
|
|
59
|
+
var _this3 = this;
|
|
60
|
+
return _asyncToGenerator(function* () {
|
|
61
|
+
return _this3.repo.model.findOrCreate({
|
|
62
|
+
defaults: values,
|
|
63
|
+
where: {
|
|
64
|
+
token: values.token
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
})();
|
|
68
|
+
}
|
|
69
|
+
deleteByExpiration() {
|
|
70
|
+
var _this4 = this;
|
|
71
|
+
return _asyncToGenerator(function* () {
|
|
72
|
+
return _this4.repo.destroy({
|
|
73
|
+
filter: {
|
|
74
|
+
expiration: {
|
|
75
|
+
$dateNotAfter: new Date()
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
})();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.TokenBlacklistService = TokenBlacklistService;
|
package/package.json
CHANGED
|
@@ -1,17 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/plugin-auth",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"main": "lib/server/index.js",
|
|
3
|
+
"version": "0.11.0-alpha.1",
|
|
4
|
+
"main": "./lib/server/index.js",
|
|
5
|
+
"files": [
|
|
6
|
+
"lib",
|
|
7
|
+
"src",
|
|
8
|
+
"README.md",
|
|
9
|
+
"README.zh-CN.md",
|
|
10
|
+
"CHANGELOG.md",
|
|
11
|
+
"server.js",
|
|
12
|
+
"server.d.ts",
|
|
13
|
+
"client.js",
|
|
14
|
+
"client.d.ts"
|
|
15
|
+
],
|
|
5
16
|
"devDependencies": {
|
|
6
|
-
"@
|
|
7
|
-
"@
|
|
8
|
-
"@
|
|
9
|
-
"@nocobase/
|
|
10
|
-
"@nocobase/test": "0.
|
|
17
|
+
"@ant-design/icons": "^5.1.4",
|
|
18
|
+
"@formily/react": "2.2.26",
|
|
19
|
+
"@formily/shared": "2.2.26",
|
|
20
|
+
"@nocobase/auth": "0.11.0-alpha.1",
|
|
21
|
+
"@nocobase/test": "0.11.0-alpha.1",
|
|
22
|
+
"@types/cron": "^2.0.1",
|
|
23
|
+
"antd": "^5.6.4",
|
|
24
|
+
"react": "^18.2.0",
|
|
25
|
+
"react-i18next": "^11.15.1"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@nocobase/actions": "0.11.0-alpha.1",
|
|
29
|
+
"@nocobase/client": "0.11.0-alpha.1",
|
|
30
|
+
"@nocobase/database": "0.11.0-alpha.1",
|
|
31
|
+
"@nocobase/server": "0.11.0-alpha.1",
|
|
32
|
+
"cron": "^2.3.1"
|
|
11
33
|
},
|
|
12
34
|
"displayName": "Authentication",
|
|
13
35
|
"displayName.zh-CN": "用户认证",
|
|
14
36
|
"description": "Basic authentication and authenticator management.",
|
|
15
37
|
"description.zh-CN": "提供基础认证功能和扩展认证器管理功能。",
|
|
16
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "7581b6d3a3a54f09f06a9effb7e3e65328281b2b"
|
|
17
39
|
}
|
package/server.d.ts
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
3
|
-
export { default } from './lib/server';
|
|
1
|
+
export * from './src/server';
|
|
2
|
+
export { default } from './src/server';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
OptionsComponentProvider,
|
|
3
|
+
SettingsCenterProvider,
|
|
4
|
+
SigninPageProvider,
|
|
5
|
+
SignupPageProvider,
|
|
6
|
+
} from '@nocobase/client';
|
|
7
|
+
import React, { FC } from 'react';
|
|
8
|
+
import { Authenticator } from './settings/Authenticator';
|
|
9
|
+
import SigninPage from './basic/SigninPage';
|
|
10
|
+
import { presetAuthType } from '../preset';
|
|
11
|
+
import SignupPage from './basic/SignupPage';
|
|
12
|
+
import { useAuthTranslation } from './locale';
|
|
13
|
+
import { Options } from './basic/Options';
|
|
14
|
+
|
|
15
|
+
export const AuthProvider: FC = (props) => {
|
|
16
|
+
const { t } = useAuthTranslation();
|
|
17
|
+
return (
|
|
18
|
+
<SettingsCenterProvider
|
|
19
|
+
settings={{
|
|
20
|
+
auth: {
|
|
21
|
+
title: t('Authentication'),
|
|
22
|
+
icon: 'LoginOutlined',
|
|
23
|
+
tabs: {
|
|
24
|
+
authenticators: {
|
|
25
|
+
title: t('Authenticators'),
|
|
26
|
+
component: () => <Authenticator />,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
}}
|
|
31
|
+
>
|
|
32
|
+
<OptionsComponentProvider authType={presetAuthType} component={Options}>
|
|
33
|
+
<SigninPageProvider authType={presetAuthType} tabTitle={t('Sign in via email')} component={SigninPage}>
|
|
34
|
+
<SignupPageProvider authType={presetAuthType} component={SignupPage}>
|
|
35
|
+
{props.children}
|
|
36
|
+
</SignupPageProvider>
|
|
37
|
+
</SigninPageProvider>
|
|
38
|
+
</OptionsComponentProvider>
|
|
39
|
+
</SettingsCenterProvider>
|
|
40
|
+
);
|
|
41
|
+
};
|
package/src/client/index.tsx
CHANGED
|
@@ -1,41 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
SettingsCenterProvider,
|
|
4
|
-
SigninPageProvider,
|
|
5
|
-
SignupPageProvider,
|
|
6
|
-
} from '@nocobase/client';
|
|
7
|
-
import React from 'react';
|
|
8
|
-
import { Authenticator } from './settings/Authenticator';
|
|
9
|
-
import SigninPage from './basic/SigninPage';
|
|
10
|
-
import { presetAuthType } from '../preset';
|
|
11
|
-
import SignupPage from './basic/SignupPage';
|
|
12
|
-
import { useAuthTranslation } from './locale';
|
|
13
|
-
import { Options } from './basic/Options';
|
|
1
|
+
import { Plugin } from '@nocobase/client';
|
|
2
|
+
import { AuthProvider } from './AuthProvider';
|
|
14
3
|
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
icon: 'LoginOutlined',
|
|
23
|
-
tabs: {
|
|
24
|
-
authenticators: {
|
|
25
|
-
title: t('Authenticators'),
|
|
26
|
-
component: () => <Authenticator />,
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
}}
|
|
31
|
-
>
|
|
32
|
-
<OptionsComponentProvider authType={presetAuthType} component={Options}>
|
|
33
|
-
<SigninPageProvider authType={presetAuthType} tabTitle={t('Sign in via email')} component={SigninPage}>
|
|
34
|
-
<SignupPageProvider authType={presetAuthType} component={SignupPage}>
|
|
35
|
-
{props.children}
|
|
36
|
-
</SignupPageProvider>
|
|
37
|
-
</SigninPageProvider>
|
|
38
|
-
</OptionsComponentProvider>
|
|
39
|
-
</SettingsCenterProvider>
|
|
40
|
-
);
|
|
41
|
-
};
|
|
4
|
+
export class AuthPlugin extends Plugin {
|
|
5
|
+
async load() {
|
|
6
|
+
this.app.use(AuthProvider);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default AuthPlugin;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import Database, { Repository } from '@nocobase/database';
|
|
2
|
+
import { MockServer, mockServer } from '@nocobase/test';
|
|
3
|
+
import { TokenBlacklistService } from '../token-blacklist';
|
|
4
|
+
|
|
5
|
+
describe('token-blacklist', () => {
|
|
6
|
+
let app: MockServer;
|
|
7
|
+
let db: Database;
|
|
8
|
+
let repo: Repository;
|
|
9
|
+
let tokenBlacklist: TokenBlacklistService;
|
|
10
|
+
|
|
11
|
+
beforeAll(async () => {
|
|
12
|
+
app = mockServer({
|
|
13
|
+
plugins: ['auth'],
|
|
14
|
+
});
|
|
15
|
+
await app.loadAndInstall({ clean: true });
|
|
16
|
+
db = app.db;
|
|
17
|
+
repo = db.getRepository('tokenBlacklist');
|
|
18
|
+
tokenBlacklist = new TokenBlacklistService(app.getPlugin('auth'));
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterAll(async () => {
|
|
22
|
+
await db.close();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(async () => {
|
|
26
|
+
await repo.destroy({
|
|
27
|
+
truncate: true,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('add and has correctly', async () => {
|
|
32
|
+
await tokenBlacklist.add({
|
|
33
|
+
token: 'test',
|
|
34
|
+
expiration: new Date(),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await tokenBlacklist.add({
|
|
38
|
+
token: 'test1',
|
|
39
|
+
expiration: new Date(),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(tokenBlacklist.has('test')).toBeTruthy();
|
|
43
|
+
expect(tokenBlacklist.has('test1')).toBeTruthy();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('add same token correctly', async () => {
|
|
47
|
+
await tokenBlacklist.add({
|
|
48
|
+
token: 'test',
|
|
49
|
+
expiration: new Date(),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
await tokenBlacklist.add({
|
|
53
|
+
token: 'test',
|
|
54
|
+
expiration: new Date(),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
expect(tokenBlacklist.has('test')).toBeTruthy();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('delete expired token correctly', async () => {
|
|
61
|
+
await tokenBlacklist.add({
|
|
62
|
+
token: 'should be deleted',
|
|
63
|
+
expiration: new Date('2020-01-01'),
|
|
64
|
+
});
|
|
65
|
+
await tokenBlacklist.add({
|
|
66
|
+
token: 'should not be deleted',
|
|
67
|
+
expiration: new Date('2100-01-01'),
|
|
68
|
+
});
|
|
69
|
+
await tokenBlacklist.deleteByExpiration();
|
|
70
|
+
expect(await tokenBlacklist.has('should be deleted')).not.toBeTruthy();
|
|
71
|
+
expect(await tokenBlacklist.has('should not be deleted')).toBeTruthy();
|
|
72
|
+
});
|
|
73
|
+
});
|
package/src/server/basic-auth.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AuthConfig, BaseAuth } from '@nocobase/auth';
|
|
2
|
-
import { namespace } from '../preset';
|
|
3
2
|
import { PasswordField } from '@nocobase/database';
|
|
4
3
|
import crypto from 'crypto';
|
|
4
|
+
import { namespace } from '../preset';
|
|
5
5
|
|
|
6
6
|
export class BasicAuth extends BaseAuth {
|
|
7
7
|
constructor(config: AuthConfig) {
|
|
@@ -16,7 +16,7 @@ export class BasicAuth extends BaseAuth {
|
|
|
16
16
|
if (!values[uniqueField]) {
|
|
17
17
|
ctx.throw(400, ctx.t('Please fill in your email address', { ns: namespace }));
|
|
18
18
|
}
|
|
19
|
-
const user = await this.
|
|
19
|
+
const user = await this.userRepository.findOne({
|
|
20
20
|
where: {
|
|
21
21
|
[uniqueField]: values[uniqueField],
|
|
22
22
|
},
|
|
@@ -54,7 +54,7 @@ export class BasicAuth extends BaseAuth {
|
|
|
54
54
|
if (!email) {
|
|
55
55
|
ctx.throw(400, ctx.t('Please fill in your email address', { ns: namespace }));
|
|
56
56
|
}
|
|
57
|
-
const user = await this.
|
|
57
|
+
const user = await this.userRepository.findOne({
|
|
58
58
|
where: {
|
|
59
59
|
email,
|
|
60
60
|
},
|
|
@@ -72,7 +72,7 @@ export class BasicAuth extends BaseAuth {
|
|
|
72
72
|
const {
|
|
73
73
|
values: { email, password, resetToken },
|
|
74
74
|
} = ctx.action.params;
|
|
75
|
-
const user = await this.
|
|
75
|
+
const user = await this.userRepository.findOne({
|
|
76
76
|
where: {
|
|
77
77
|
email,
|
|
78
78
|
resetToken,
|
|
@@ -91,7 +91,7 @@ export class BasicAuth extends BaseAuth {
|
|
|
91
91
|
async getUserByResetToken() {
|
|
92
92
|
const ctx = this.ctx;
|
|
93
93
|
const { token } = ctx.action.params;
|
|
94
|
-
const user = await this.
|
|
94
|
+
const user = await this.userRepository.findOne({
|
|
95
95
|
where: {
|
|
96
96
|
resetToken: token,
|
|
97
97
|
},
|
|
@@ -111,7 +111,7 @@ export class BasicAuth extends BaseAuth {
|
|
|
111
111
|
if (!currentUser) {
|
|
112
112
|
ctx.throw(401);
|
|
113
113
|
}
|
|
114
|
-
const user = await this.
|
|
114
|
+
const user = await this.userRepository.findOne({
|
|
115
115
|
where: {
|
|
116
116
|
email: currentUser.email,
|
|
117
117
|
},
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { CollectionOptions } from '@nocobase/client';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
namespace: 'auth.token-black',
|
|
5
|
+
duplicator: 'optional',
|
|
6
|
+
name: 'tokenBlacklist',
|
|
7
|
+
model: 'TokenBlacklistModel',
|
|
8
|
+
fields: [
|
|
9
|
+
{
|
|
10
|
+
type: 'string',
|
|
11
|
+
name: 'token',
|
|
12
|
+
index: true,
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
type: 'date',
|
|
16
|
+
name: 'expiration',
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
} as CollectionOptions;
|
package/src/server/plugin.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
+
import { Model } from '@nocobase/database';
|
|
1
2
|
import { InstallOptions, Plugin } from '@nocobase/server';
|
|
2
3
|
import { resolve } from 'path';
|
|
3
|
-
import {
|
|
4
|
-
import { presetAuthType, presetAuthenticator } from '../preset';
|
|
4
|
+
import { namespace, presetAuthenticator, presetAuthType } from '../preset';
|
|
5
5
|
import authActions from './actions/auth';
|
|
6
6
|
import authenticatorsActions from './actions/authenticators';
|
|
7
|
+
import { BasicAuth } from './basic-auth';
|
|
7
8
|
import { enUS, zhCN } from './locale';
|
|
8
|
-
import { namespace } from '../preset';
|
|
9
9
|
import { AuthModel } from './model/authenticator';
|
|
10
|
-
import {
|
|
10
|
+
import { TokenBlacklistService } from './token-blacklist';
|
|
11
11
|
|
|
12
12
|
export class AuthPlugin extends Plugin {
|
|
13
13
|
afterAdd() {}
|
|
14
|
-
|
|
15
14
|
async beforeLoad() {
|
|
16
15
|
this.app.i18n.addResources('zh-CN', namespace, zhCN);
|
|
17
16
|
this.app.i18n.addResources('en-US', namespace, enUS);
|
|
@@ -40,6 +39,12 @@ export class AuthPlugin extends Plugin {
|
|
|
40
39
|
return authenticator || authenticators[0];
|
|
41
40
|
},
|
|
42
41
|
});
|
|
42
|
+
|
|
43
|
+
if (!this.app.authManager.jwt.blacklist) {
|
|
44
|
+
// If blacklist service is not set, should configure default blacklist service
|
|
45
|
+
this.app.authManager.setTokenBlacklistService(new TokenBlacklistService(this));
|
|
46
|
+
}
|
|
47
|
+
|
|
43
48
|
this.app.authManager.registerTypes(presetAuthType, {
|
|
44
49
|
auth: BasicAuth,
|
|
45
50
|
});
|
|
@@ -81,11 +86,6 @@ export class AuthPlugin extends Plugin {
|
|
|
81
86
|
},
|
|
82
87
|
});
|
|
83
88
|
}
|
|
84
|
-
|
|
85
|
-
async afterEnable() {}
|
|
86
|
-
|
|
87
|
-
async afterDisable() {}
|
|
88
|
-
|
|
89
89
|
async remove() {}
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ITokenBlacklistService } from '@nocobase/auth';
|
|
2
|
+
import { Repository } from '@nocobase/database';
|
|
3
|
+
import { CronJob } from 'cron';
|
|
4
|
+
import AuthPlugin from './plugin';
|
|
5
|
+
|
|
6
|
+
export class TokenBlacklistService implements ITokenBlacklistService {
|
|
7
|
+
repo: Repository;
|
|
8
|
+
cronJob: CronJob;
|
|
9
|
+
|
|
10
|
+
constructor(protected plugin: AuthPlugin) {
|
|
11
|
+
this.repo = plugin.db.getRepository('tokenBlacklist');
|
|
12
|
+
this.cronJob = this.createCronJob();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
get app() {
|
|
16
|
+
return this.plugin.app;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
createCronJob() {
|
|
20
|
+
const cronJob = new CronJob(
|
|
21
|
+
// every day at 03:00
|
|
22
|
+
'0 3 * * *', //
|
|
23
|
+
async () => {
|
|
24
|
+
this.app.logger.info(`${this.plugin.name}: Start delete expired blacklist token`);
|
|
25
|
+
await this.deleteByExpiration();
|
|
26
|
+
this.app.logger.info(`${this.plugin.name}: End delete expired blacklist token`);
|
|
27
|
+
},
|
|
28
|
+
null,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
this.app.once('beforeStart', () => {
|
|
32
|
+
cronJob.start();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
this.app.once('beforeStop', () => {
|
|
36
|
+
cronJob.stop();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return cronJob;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async has(token: string) {
|
|
43
|
+
return !!(await this.repo.findOne({
|
|
44
|
+
where: {
|
|
45
|
+
token,
|
|
46
|
+
},
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
async add(values) {
|
|
50
|
+
return this.repo.model.findOrCreate({
|
|
51
|
+
defaults: values,
|
|
52
|
+
where: {
|
|
53
|
+
token: values.token,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async deleteByExpiration() {
|
|
58
|
+
return this.repo.destroy({
|
|
59
|
+
filter: {
|
|
60
|
+
expiration: {
|
|
61
|
+
$dateNotAfter: new Date(),
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|