@arcblock/event-hub 1.25.6 → 1.26.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/lib/client.js CHANGED
@@ -6,10 +6,9 @@ const queue = require('axon/lib/plugins/queue');
6
6
  const { fromSecretKey, WalletType } = require('@ocap/wallet');
7
7
  const { DidType, isEthereumType } = require('@arcblock/did');
8
8
 
9
- const JWT = require('@arcblock/jwt');
10
9
  const { toTypeInfo } = require('@arcblock/did');
11
10
 
12
- const { RESERVED_EVENT_PREFIX, EVENT_AUTH, EVENT_AUTH_FAIL } = require('./constant');
11
+ const { RESERVED_EVENT_PREFIX, EVENT_AUTH, EVENT_AUTH_SUCCESS, EVENT_AUTH_FAIL } = require('./constant');
13
12
 
14
13
  const checkEvent = (event) => {
15
14
  if (event.startsWith(RESERVED_EVENT_PREFIX)) {
@@ -33,8 +32,8 @@ const defaultOpts = {
33
32
  port: undefined,
34
33
  hostname: '0.0.0.0',
35
34
  autoConnect: false,
35
+ wallet: undefined,
36
36
  did: undefined,
37
- pk: undefined,
38
37
  sk: undefined,
39
38
  };
40
39
 
@@ -42,25 +41,59 @@ class Socket extends axon.SubEmitterSocket {
42
41
  constructor(opts) {
43
42
  super();
44
43
 
45
- const wallet = getWallet(opts.sk, toTypeInfo(opts.did));
44
+ const wallet = opts.wallet || getWallet(opts.sk, toTypeInfo(opts.did));
45
+ this._authenticated = false;
46
+ this._pendingMessages = [];
47
+ this._wallet = wallet;
46
48
 
47
49
  this.sock.use((sock) => {
48
- sock.on('connect', () => {
50
+ sock.on('connect', async () => {
51
+ this._authenticated = false;
52
+
53
+ // Use wallet.signJWT() so business can override it for remote signing
54
+ const token = await wallet.signJWT();
49
55
  sock.send(EVENT_AUTH, {
50
56
  pk: wallet.publicKey,
51
- token: JWT.sign(wallet.address, wallet.secretKey),
57
+ token,
52
58
  });
59
+ });
53
60
 
54
- sock.on(EVENT_AUTH_FAIL, ({ msg } = {}) => {
55
- this.emit('error', { msg });
56
- sock.close();
57
- });
61
+ sock.on('close', () => {
62
+ this._authenticated = false;
63
+ this._pendingMessages = [];
58
64
  });
59
65
  });
60
66
 
67
+ // Listen for auth events on the SubEmitterSocket instance (this), not on raw sock
68
+ this.on(EVENT_AUTH_SUCCESS, () => {
69
+ this._authenticated = true;
70
+
71
+ // Send all pending messages after authentication
72
+ const pending = this._pendingMessages.slice();
73
+ this._pendingMessages = [];
74
+ pending.forEach(([event, data]) => this.sock.send(event, data));
75
+ });
76
+
77
+ this.on(EVENT_AUTH_FAIL, ({ msg } = {}) => {
78
+ this.emit('error', { msg });
79
+ this._authenticated = false;
80
+ this._pendingMessages = [];
81
+ this.sock.socks.forEach((s) => s.close());
82
+ });
83
+
61
84
  this.sock.use(queue());
62
85
  this.sock.use(roundrobin({ fallback: this.sock.enqueue }));
63
- this.send = this.sock.send.bind(this.sock);
86
+
87
+ const originalSend = this.sock.send.bind(this.sock);
88
+ this.send = (event, data) => {
89
+ // Check both authentication AND connection status
90
+ const hasConnection = this.sock.socks && this.sock.socks.length > 0;
91
+ if (this._authenticated && hasConnection) {
92
+ originalSend(event, data);
93
+ } else {
94
+ this._pendingMessages.push([event, data]);
95
+ }
96
+ };
64
97
  }
65
98
  }
66
99
 
@@ -72,18 +105,27 @@ class Client extends EventEmitter {
72
105
  throw new Error('port should not be empty');
73
106
  }
74
107
 
75
- if (!opts.did || !opts.sk) {
76
- throw new Error('did, sk should not be empty');
108
+ if (!opts.wallet) {
109
+ if (!opts.did) {
110
+ throw new Error('did should not be empty when wallet is not provided');
111
+ }
112
+
113
+ // sk should be provided when wallet is not provided
114
+ if (!opts.sk) {
115
+ throw new Error('Either wallet or sk should be provided');
116
+ }
77
117
  }
78
118
 
79
- const wallet = getWallet(opts.sk, toTypeInfo(opts.did));
119
+ const wallet = opts.wallet || getWallet(opts.sk, toTypeInfo(opts.did));
120
+ const did = opts.wallet ? opts.wallet.address : opts.did;
80
121
 
81
- if (wallet.address !== opts.did) {
82
- throw new Error('did, sk does not match');
122
+ if (wallet.address !== did) {
123
+ throw new Error('did does not match the provided wallet/sk');
83
124
  }
84
125
 
85
- this.opts = Object.assign({}, defaultOpts, opts, { pk: wallet.publicKey });
126
+ this.opts = Object.assign({}, defaultOpts, opts, { wallet, did });
86
127
  this._client = new Socket(this.opts);
128
+ this._wallet = wallet;
87
129
 
88
130
  this._bindEvent();
89
131
 
@@ -98,6 +140,8 @@ class Client extends EventEmitter {
98
140
  }
99
141
 
100
142
  close() {
143
+ this._client._authenticated = false;
144
+ this._client._pendingMessages = [];
101
145
  this._client.close();
102
146
  }
103
147
 
@@ -123,6 +167,15 @@ class Client extends EventEmitter {
123
167
  }
124
168
  }
125
169
 
170
+ /**
171
+ * Get the wallet instance
172
+ * Business can override wallet.signJWT() for remote signing
173
+ * @returns {WalletObject}
174
+ */
175
+ getWallet() {
176
+ return this._wallet;
177
+ }
178
+
126
179
  _bindEvent() {
127
180
  this._client.on('error', this.emit.bind(this, 'error'));
128
181
  }
package/lib/constant.js CHANGED
@@ -1,5 +1,6 @@
1
1
  module.exports = {
2
2
  RESERVED_EVENT_PREFIX: 'EVENT_HUB:',
3
3
  EVENT_AUTH: 'EVENT_HUB:AUTH',
4
+ EVENT_AUTH_SUCCESS: 'EVENT_HUB:AUTH_SUCCESS',
4
5
  EVENT_AUTH_FAIL: 'EVENT_HUB:AUTH_FAIL',
5
6
  };
package/lib/index.js CHANGED
@@ -21,3 +21,6 @@ if (port && did && sk) {
21
21
  // export fallback Client because missing any of port / did / sk
22
22
  module.exports = new ClientFallback();
23
23
  }
24
+
25
+ module.exports.Client = Client;
26
+ module.exports.ClientFallback = ClientFallback;
package/lib/server.js CHANGED
@@ -2,7 +2,7 @@
2
2
  const axon = require('axon');
3
3
  const Message = require('amp-message');
4
4
  const JWT = require('@arcblock/jwt');
5
- const { EVENT_AUTH, EVENT_AUTH_FAIL } = require('./constant');
5
+ const { EVENT_AUTH, EVENT_AUTH_SUCCESS, EVENT_AUTH_FAIL } = require('./constant');
6
6
 
7
7
  const getDid = (jwt) => jwt.iss.replace(/^did:abt:/, '');
8
8
 
@@ -101,18 +101,26 @@ class Server extends axon.Socket {
101
101
  const msg = new Message(buf);
102
102
  const [event, data] = msg.args;
103
103
  if (event === EVENT_AUTH) {
104
- try {
105
- const { pk, token } = data;
106
- if (!this.jwt.verify(token, pk)) {
107
- throw new Error('token verify failed');
108
- }
109
- const did = getDid(this.jwt.decode(token));
110
- sock.channel = did;
111
- } catch (err) {
112
- console.error(err);
113
- const resBuf = this.pack([EVENT_AUTH_FAIL, { msg: err.message }]);
114
- sock.write(resBuf);
115
- }
104
+ // JWT.verify returns a Promise but is synchronously resolved (no await inside)
105
+ // We use .then() to handle it and send confirmation to client
106
+ const { pk, token } = data;
107
+ this.jwt
108
+ .verify(token, pk)
109
+ .then((valid) => {
110
+ if (!valid) {
111
+ throw new Error('token verify failed');
112
+ }
113
+ const did = getDid(this.jwt.decode(token));
114
+ sock.channel = did;
115
+ // Send authentication success confirmation
116
+ const successBuf = this.pack([EVENT_AUTH_SUCCESS, {}]);
117
+ sock.write(successBuf);
118
+ })
119
+ .catch((err) => {
120
+ console.error(err);
121
+ const resBuf = this.pack([EVENT_AUTH_FAIL, { msg: err.message }]);
122
+ sock.write(resBuf);
123
+ });
116
124
  return;
117
125
  }
118
126
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.25.6",
6
+ "version": "1.26.1",
7
7
  "description": "A nodejs module for local and remote Inter Process Event",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,9 +19,9 @@
19
19
  "dependencies": {
20
20
  "amp-message": "~0.1.2",
21
21
  "axon": "^2.0.3",
22
- "@ocap/wallet": "1.25.6",
23
- "@arcblock/jwt": "1.25.6",
24
- "@arcblock/did": "1.25.6"
22
+ "@arcblock/did": "1.26.1",
23
+ "@arcblock/jwt": "1.26.1",
24
+ "@ocap/wallet": "1.26.1"
25
25
  },
26
26
  "devDependencies": {
27
27
  "jest": "^29.7.0"