@canton-network/wallet-gateway-remote 1.3.0 → 1.5.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/README.md +1 -1
- package/dist/auth/jwt-auth-service.d.ts.map +1 -1
- package/dist/auth/jwt-auth-service.js +6 -5
- package/dist/auth/jwt-auth-service.test.d.ts +2 -0
- package/dist/auth/jwt-auth-service.test.d.ts.map +1 -0
- package/dist/auth/jwt-auth-service.test.js +259 -0
- package/dist/auth/jwt-unsafe-auth-service.test.d.ts +2 -0
- package/dist/auth/jwt-unsafe-auth-service.test.d.ts.map +1 -0
- package/dist/auth/jwt-unsafe-auth-service.test.js +120 -0
- package/dist/config/Config.d.ts +64 -8
- package/dist/config/Config.d.ts.map +1 -1
- package/dist/config/Config.js +1 -0
- package/dist/config/ConfigUtils.d.ts.map +1 -1
- package/dist/config/ConfigUtils.js +3 -0
- package/dist/dapp-api/controller.d.ts +1 -0
- package/dist/dapp-api/controller.d.ts.map +1 -1
- package/dist/dapp-api/controller.js +87 -26
- package/dist/dapp-api/controller.test.d.ts +2 -0
- package/dist/dapp-api/controller.test.d.ts.map +1 -0
- package/dist/dapp-api/controller.test.js +516 -0
- package/dist/dapp-api/rpc-gen/index.d.ts +3 -0
- package/dist/dapp-api/rpc-gen/index.d.ts.map +1 -1
- package/dist/dapp-api/rpc-gen/index.js +1 -0
- package/dist/dapp-api/rpc-gen/typings.d.ts +99 -14
- package/dist/dapp-api/rpc-gen/typings.d.ts.map +1 -1
- package/dist/dapp-api/server.d.ts.map +1 -1
- package/dist/dapp-api/server.js +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +4 -6
- package/dist/ledger/transaction-service.d.ts +14 -11
- package/dist/ledger/transaction-service.d.ts.map +1 -1
- package/dist/ledger/transaction-service.js +95 -8
- package/dist/ledger/transaction-service.test.d.ts +2 -0
- package/dist/ledger/transaction-service.test.d.ts.map +1 -0
- package/dist/ledger/transaction-service.test.js +356 -0
- package/dist/ledger/wallet-allocation/signing-providers/fireblocks-wallet-allocator.d.ts +5 -2
- package/dist/ledger/wallet-allocation/signing-providers/fireblocks-wallet-allocator.d.ts.map +1 -1
- package/dist/ledger/wallet-allocation/signing-providers/fireblocks-wallet-allocator.js +7 -6
- package/dist/ledger/wallet-allocation/wallet-allocation-service.d.ts +11 -5
- package/dist/ledger/wallet-allocation/wallet-allocation-service.d.ts.map +1 -1
- package/dist/ledger/wallet-allocation/wallet-allocation-service.js +28 -14
- package/dist/ledger/wallet-allocation/wallet-allocation-service.test.js +550 -19
- package/dist/ledger/wallet-sync-service.js +3 -3
- package/dist/middleware/apiKeyAuth.d.ts +6 -0
- package/dist/middleware/apiKeyAuth.d.ts.map +1 -0
- package/dist/middleware/apiKeyAuth.js +88 -0
- package/dist/middleware/apiKeyAuth.test.d.ts +2 -0
- package/dist/middleware/apiKeyAuth.test.d.ts.map +1 -0
- package/dist/middleware/apiKeyAuth.test.js +126 -0
- package/dist/middleware/jsonRpcHandler.d.ts +11 -1
- package/dist/middleware/jsonRpcHandler.d.ts.map +1 -1
- package/dist/middleware/jsonRpcHandler.js +14 -15
- package/dist/middleware/jsonRpcHandler.test.d.ts +2 -0
- package/dist/middleware/jsonRpcHandler.test.d.ts.map +1 -0
- package/dist/middleware/jsonRpcHandler.test.js +254 -0
- package/dist/middleware/jwtAuth.d.ts.map +1 -1
- package/dist/middleware/jwtAuth.js +4 -0
- package/dist/middleware/jwtAuth.test.d.ts +2 -0
- package/dist/middleware/jwtAuth.test.d.ts.map +1 -0
- package/dist/middleware/jwtAuth.test.js +93 -0
- package/dist/middleware/rateLimit.js +2 -2
- package/dist/middleware/rateLimit.test.js +26 -0
- package/dist/middleware/sessionHandler.js +1 -1
- package/dist/middleware/sessionHandler.test.d.ts +2 -0
- package/dist/middleware/sessionHandler.test.d.ts.map +1 -0
- package/dist/middleware/sessionHandler.test.js +121 -0
- package/dist/notification/NotificationService.test.d.ts +2 -0
- package/dist/notification/NotificationService.test.d.ts.map +1 -0
- package/dist/notification/NotificationService.test.js +110 -0
- package/dist/signing/signing-drivers.d.ts +7 -0
- package/dist/signing/signing-drivers.d.ts.map +1 -0
- package/dist/signing/signing-drivers.js +3 -0
- package/dist/user-api/controller.d.ts +12 -4
- package/dist/user-api/controller.d.ts.map +1 -1
- package/dist/user-api/controller.js +330 -95
- package/dist/user-api/controller.test.d.ts +2 -0
- package/dist/user-api/controller.test.d.ts.map +1 -0
- package/dist/user-api/controller.test.js +862 -0
- package/dist/user-api/rpc-gen/index.d.ts +30 -0
- package/dist/user-api/rpc-gen/index.d.ts.map +1 -1
- package/dist/user-api/rpc-gen/index.js +10 -0
- package/dist/user-api/rpc-gen/typings.d.ts +170 -25
- package/dist/user-api/rpc-gen/typings.d.ts.map +1 -1
- package/dist/user-api/server.test.js +53 -0
- package/dist/utils.d.ts +19 -5
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +53 -2
- package/dist/utils.test.d.ts +2 -0
- package/dist/utils.test.d.ts.map +1 -0
- package/dist/utils.test.js +24 -0
- package/dist/web/frontend/404/index.html +2 -2
- package/dist/web/frontend/activities/index.html +3 -3
- package/dist/web/frontend/approve/index.html +4 -5
- package/dist/web/frontend/assets/404-MVLWUPwN.js +5 -0
- package/dist/web/frontend/assets/activities-DaWkmGqA.js +61 -0
- package/dist/web/frontend/assets/addIdentityProvider--rBb0ihb.js +28 -0
- package/dist/web/frontend/assets/addNetwork-C6UDsRkU.js +38 -0
- package/dist/web/frontend/assets/addParty-B3fFD1eM.js +45 -0
- package/dist/web/frontend/assets/approve-WbGL4E82.js +21 -0
- package/dist/web/frontend/assets/callback--tihEryB.js +1 -0
- package/dist/web/frontend/assets/identityProviders-Dra4qG6t.js +71 -0
- package/dist/web/frontend/assets/index-Bm76pXA_.js +91 -0
- package/dist/web/frontend/assets/index-D1dC4Cl9.js +4477 -0
- package/dist/web/frontend/assets/login-BC8a-kD_.js +10 -0
- package/dist/web/frontend/assets/networks-B3ztsogL.js +73 -0
- package/dist/web/frontend/assets/reviewIdentityProvider-Co420Dbk.js +43 -0
- package/dist/web/frontend/assets/reviewNetwork-B-Sh8pCH.js +43 -0
- package/dist/web/frontend/assets/{settings-B1ga2TK0.js → settings-DvyUjqLl.js} +1 -1
- package/dist/web/frontend/assets/state-Bq65GwdF.js +1 -0
- package/dist/web/frontend/assets/{utils-CT9Hzi7v.js → utils-B2iziW8V.js} +1 -1
- package/dist/web/frontend/callback/index.html +2 -2
- package/dist/web/frontend/identity-providers/add/index.html +3 -3
- package/dist/web/frontend/identity-providers/index.html +3 -3
- package/dist/web/frontend/identity-providers/review/index.html +3 -3
- package/dist/web/frontend/index.html +1 -1
- package/dist/web/frontend/login/index.html +3 -4
- package/dist/web/frontend/networks/add/index.html +3 -3
- package/dist/web/frontend/networks/index.html +3 -3
- package/dist/web/frontend/networks/review/index.html +3 -3
- package/dist/web/frontend/parties/add/index.html +5 -5
- package/dist/web/frontend/parties/index.html +4 -4
- package/dist/web/frontend/settings/index.html +3 -3
- package/package.json +40 -33
- package/dist/web/frontend/assets/404-B-o9ppJB.js +0 -5
- package/dist/web/frontend/assets/activities-CGjCIjUH.js +0 -61
- package/dist/web/frontend/assets/addIdentityProvider-CR4Wm9Tl.js +0 -28
- package/dist/web/frontend/assets/addNetwork-Dx0-SN4j.js +0 -38
- package/dist/web/frontend/assets/addParty-COhk_rFn.js +0 -41
- package/dist/web/frontend/assets/approve-B2w66l0J.js +0 -21
- package/dist/web/frontend/assets/callback-BTVon_yQ.js +0 -1
- package/dist/web/frontend/assets/identityProviders-CK8zSrd3.js +0 -71
- package/dist/web/frontend/assets/index-CF4BKzgl.js +0 -91
- package/dist/web/frontend/assets/index-DWz_3f3y.js +0 -1
- package/dist/web/frontend/assets/index-_pMHlJoE.js +0 -4042
- package/dist/web/frontend/assets/login-CVoPNVDw.js +0 -10
- package/dist/web/frontend/assets/networks-D1nPvUzM.js +0 -73
- package/dist/web/frontend/assets/reviewIdentityProvider-CNSf2qQv.js +0 -43
- package/dist/web/frontend/assets/reviewNetwork-DGLK-Ume.js +0 -43
- package/dist/web/frontend/assets/state-B2k3ak7d.js +0 -1
|
@@ -4,14 +4,16 @@
|
|
|
4
4
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
5
5
|
import { LedgerClient } from '@canton-network/core-ledger-client';
|
|
6
6
|
import buildController from './rpc-gen/index.js';
|
|
7
|
-
import { assertConnected, authSchema, AuthTokenProvider,
|
|
8
|
-
import {
|
|
7
|
+
import { assertConnected, authSchema, AuthTokenProvider, idpSchema, } from '@canton-network/core-wallet-auth';
|
|
8
|
+
import { isRpcError, SigningProvider } from '@canton-network/core-signing-lib';
|
|
9
9
|
import { PartyAllocationService } from '../ledger/party-allocation-service.js';
|
|
10
10
|
import { WalletAllocationService } from '../ledger/wallet-allocation/wallet-allocation-service.js';
|
|
11
11
|
import { WalletSyncService } from '../ledger/wallet-sync-service.js';
|
|
12
|
-
import { networkStatus } from '../utils.js';
|
|
12
|
+
import { logDynamically, networkStatus } from '../utils.js';
|
|
13
13
|
import { v4 } from 'uuid';
|
|
14
14
|
import { TransactionService } from '../ledger/transaction-service.js';
|
|
15
|
+
import { rpcErrors } from '@canton-network/core-rpc-errors';
|
|
16
|
+
import crypto from 'crypto';
|
|
15
17
|
export const userController = (kernelInfo, userUrl, store, notificationService, authContext, drivers, _logger, adminUserId) => {
|
|
16
18
|
const logger = _logger.child({ component: 'user-controller' });
|
|
17
19
|
const provider = {
|
|
@@ -26,27 +28,6 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
26
28
|
throw new Error('Unauthorized: only the admin user can perform this operation');
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
|
-
async function resolveUserEmail(connectedContext) {
|
|
30
|
-
if (connectedContext.email) {
|
|
31
|
-
return connectedContext.email;
|
|
32
|
-
}
|
|
33
|
-
try {
|
|
34
|
-
const network = await store.getCurrentNetwork();
|
|
35
|
-
if (!network) {
|
|
36
|
-
return undefined;
|
|
37
|
-
}
|
|
38
|
-
const idp = await store.getIdp(network.identityProviderId);
|
|
39
|
-
if (idp.type !== 'oauth') {
|
|
40
|
-
return undefined;
|
|
41
|
-
}
|
|
42
|
-
const userInfo = await fetchOidcUserInfo(idp.configUrl, connectedContext.accessToken);
|
|
43
|
-
return userInfo?.email;
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
logger.warn(error, 'Failed to resolve user email from OIDC userinfo');
|
|
47
|
-
return undefined;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
31
|
return buildController({
|
|
51
32
|
getUser: async () => {
|
|
52
33
|
const userId = assertConnected(authContext).userId;
|
|
@@ -95,12 +76,42 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
95
76
|
listNetworks: async () => {
|
|
96
77
|
const networks = await store.listNetworks();
|
|
97
78
|
return {
|
|
98
|
-
networks: networks.map(
|
|
99
|
-
...n,
|
|
100
|
-
ledgerApi: n.ledgerApi.baseUrl,
|
|
101
|
-
})),
|
|
79
|
+
networks: networks.map(toPublicNetwork),
|
|
102
80
|
};
|
|
103
81
|
},
|
|
82
|
+
getNetwork: async (params) => {
|
|
83
|
+
assertAdmin();
|
|
84
|
+
const network = await store.getNetwork(params.networkId);
|
|
85
|
+
return { network: toNetworkDto(network) };
|
|
86
|
+
},
|
|
87
|
+
selfSignedAccessToken: async (params) => {
|
|
88
|
+
const network = (await store.listNetworks()).find((n) => n.id === params.networkId);
|
|
89
|
+
if (!network) {
|
|
90
|
+
throw new Error(`Network "${params.networkId}" not found`);
|
|
91
|
+
}
|
|
92
|
+
const auth = network.auth;
|
|
93
|
+
if (auth.method !== 'self_signed') {
|
|
94
|
+
throw new Error('Network does not use self_signed authentication');
|
|
95
|
+
}
|
|
96
|
+
const idp = (await store.listIdps()).find((idp) => idp.id === network.identityProviderId);
|
|
97
|
+
if (!idp) {
|
|
98
|
+
throw new Error(`Identity provider "${network.identityProviderId}" not found`);
|
|
99
|
+
}
|
|
100
|
+
if (idp.type !== 'self_signed') {
|
|
101
|
+
throw new Error('Identity provider is not configured for self_signed authentication');
|
|
102
|
+
}
|
|
103
|
+
const accessToken = await new AuthTokenProvider({
|
|
104
|
+
method: 'self_signed',
|
|
105
|
+
issuer: idp.issuer,
|
|
106
|
+
credentials: {
|
|
107
|
+
clientId: params.clientId,
|
|
108
|
+
clientSecret: auth.clientSecret,
|
|
109
|
+
scope: auth.scope,
|
|
110
|
+
audience: auth.audience,
|
|
111
|
+
},
|
|
112
|
+
}, logger).getAccessToken();
|
|
113
|
+
return { accessToken };
|
|
114
|
+
},
|
|
104
115
|
addIdp: async (params) => {
|
|
105
116
|
assertAdmin();
|
|
106
117
|
const validatedIdp = idpSchema.parse(params.idp);
|
|
@@ -122,14 +133,10 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
122
133
|
await store.removeIdp(params.identityProviderId);
|
|
123
134
|
return null;
|
|
124
135
|
},
|
|
125
|
-
listIdps: async () =>
|
|
136
|
+
listIdps: async () => ({ idps: await store.listIdps() }),
|
|
126
137
|
createWallet: async (params) => {
|
|
127
|
-
logger.info(`Creating wallet with params: ${JSON.stringify(params)}`);
|
|
128
138
|
const { signingProviderId, primary, partyHint } = params;
|
|
129
139
|
const connectedContext = assertConnected(authContext);
|
|
130
|
-
const userId = connectedContext.userId;
|
|
131
|
-
const email = await resolveUserEmail(connectedContext);
|
|
132
|
-
const notifier = notificationService.getNotifier(userId);
|
|
133
140
|
const network = await store.getCurrentNetwork();
|
|
134
141
|
if (network === undefined) {
|
|
135
142
|
throw new Error('No network session found');
|
|
@@ -138,6 +145,7 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
138
145
|
if (!network.adminAuth) {
|
|
139
146
|
throw new Error('No admin auth configured');
|
|
140
147
|
}
|
|
148
|
+
const notifier = notificationService.getNotifier(connectedContext.userId);
|
|
141
149
|
const adminTokenProvider = AuthTokenProvider.fromGatewayConfig(idp, network.adminAuth, logger);
|
|
142
150
|
const partyAllocator = new PartyAllocationService({
|
|
143
151
|
synchronizerId: network.synchronizerId,
|
|
@@ -149,7 +157,7 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
149
157
|
if (!drivers[signingProviderId]) {
|
|
150
158
|
throw new Error(`Signing provider ${signingProviderId} not supported`);
|
|
151
159
|
}
|
|
152
|
-
const wallet = await walletAllocationService.createWallet(
|
|
160
|
+
const wallet = await walletAllocationService.createWallet(connectedContext, partyHint, primary ?? false, signingProviderId, params.vaultName);
|
|
153
161
|
// Sync wallets (TODO: separate rights sync from wallet sync as we only need rights sync here)
|
|
154
162
|
const ledgerClient = new LedgerClient({
|
|
155
163
|
baseUrl: new URL(network.ledgerApi.baseUrl),
|
|
@@ -164,24 +172,21 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
164
172
|
return { wallet };
|
|
165
173
|
},
|
|
166
174
|
allocatePartyForWallet: async (params) => {
|
|
167
|
-
logger.info(`Allocating party for wallet: ${JSON.stringify(params)}`);
|
|
168
175
|
const connectedContext = assertConnected(authContext);
|
|
169
176
|
const userId = connectedContext.userId;
|
|
170
|
-
const email = await resolveUserEmail(connectedContext);
|
|
171
|
-
const notifier = notificationService.getNotifier(userId);
|
|
172
177
|
const network = await store.getCurrentNetwork();
|
|
173
178
|
if (!network) {
|
|
174
179
|
throw new Error('No network session found');
|
|
175
180
|
}
|
|
181
|
+
if (!network.adminAuth) {
|
|
182
|
+
throw new Error('No admin auth configured');
|
|
183
|
+
}
|
|
176
184
|
const allWallets = await store.getWallets();
|
|
177
185
|
const existingWallet = allWallets.find((w) => w.partyId === params.partyId && w.networkId === network.id);
|
|
178
186
|
if (!existingWallet) {
|
|
179
187
|
throw new Error(`Wallet not found for party ${params.partyId}`);
|
|
180
188
|
}
|
|
181
189
|
const idp = await store.getIdp(network.identityProviderId);
|
|
182
|
-
if (!network.adminAuth) {
|
|
183
|
-
throw new Error('No admin auth configured');
|
|
184
|
-
}
|
|
185
190
|
const accessTokenProvider = AuthTokenProvider.fromGatewayConfig(idp, network.adminAuth, logger);
|
|
186
191
|
const partyAllocator = new PartyAllocationService({
|
|
187
192
|
synchronizerId: network.synchronizerId,
|
|
@@ -194,7 +199,7 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
194
199
|
if (!drivers[signingProviderId]) {
|
|
195
200
|
throw new Error(`Signing provider ${signingProviderId} not supported`);
|
|
196
201
|
}
|
|
197
|
-
await walletAllocationService.allocateParty(
|
|
202
|
+
await walletAllocationService.allocateParty(connectedContext, existingWallet, signingProviderId);
|
|
198
203
|
// Sync wallets (TODO: separate rights sync from wallet sync as we only need rights sync here)
|
|
199
204
|
const ledgerClient = new LedgerClient({
|
|
200
205
|
baseUrl: new URL(network.ledgerApi.baseUrl),
|
|
@@ -207,6 +212,7 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
207
212
|
const wallets = await store.getWallets();
|
|
208
213
|
const wallet = wallets.find((w) => w.partyId === existingWallet.partyId &&
|
|
209
214
|
w.networkId === network.id);
|
|
215
|
+
const notifier = notificationService.getNotifier(userId);
|
|
210
216
|
notifier?.emit('accountsChanged', wallets);
|
|
211
217
|
return { wallet };
|
|
212
218
|
},
|
|
@@ -219,7 +225,9 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
219
225
|
notifier?.emit('accountsChanged', wallets);
|
|
220
226
|
return null;
|
|
221
227
|
},
|
|
222
|
-
removeWallet: async (params) =>
|
|
228
|
+
removeWallet: async (params) => {
|
|
229
|
+
throw rpcErrors.methodNotSupported();
|
|
230
|
+
},
|
|
223
231
|
listWallets: async (params) => {
|
|
224
232
|
return await store.getWallets(params.filter);
|
|
225
233
|
},
|
|
@@ -235,36 +243,153 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
235
243
|
}
|
|
236
244
|
const connectedContext = assertConnected(authContext);
|
|
237
245
|
const userId = connectedContext.userId;
|
|
238
|
-
const email = await resolveUserEmail(connectedContext);
|
|
239
246
|
const notifier = notificationService.getNotifier(userId);
|
|
240
|
-
const signingProvider = wallet.signingProviderId;
|
|
241
|
-
const driver = drivers[signingProvider]?.controller(userId);
|
|
242
|
-
if (!driver) {
|
|
243
|
-
throw new Error(`No driver found for ${wallet.signingProviderId}`);
|
|
244
|
-
}
|
|
245
247
|
const transactionService = new TransactionService(store, logger, drivers, notifier);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
248
|
+
logDynamically(logger, 'signing transaction with params', {
|
|
249
|
+
info: { transactionId: signParams.transactionId },
|
|
250
|
+
debug: { signParams, wallet, connectedContext },
|
|
251
|
+
});
|
|
252
|
+
const response = await transactionService.sign(connectedContext, wallet, signParams);
|
|
253
|
+
logDynamically(logger, 'transaction signed with response', {
|
|
254
|
+
info: { transactionId: signParams.transactionId },
|
|
255
|
+
debug: { response },
|
|
256
|
+
});
|
|
257
|
+
return response;
|
|
258
|
+
},
|
|
259
|
+
signMessage: async (params) => {
|
|
260
|
+
const pending = await store.getMessageRaw(params.messageId);
|
|
261
|
+
if (!pending) {
|
|
262
|
+
throw new Error(`Message signing request not found with id: ${params.messageId}`);
|
|
263
|
+
}
|
|
264
|
+
if (pending.status !== 'pending') {
|
|
265
|
+
throw new Error(`Cannot sign message with status '${pending.status}'. Only pending messages can be signed.`);
|
|
266
|
+
}
|
|
267
|
+
const userId = assertConnected(authContext).userId;
|
|
268
|
+
if (pending.userId !== userId) {
|
|
269
|
+
throw new Error(`Message signing request ${pending.id} is not owned by user ${userId}`);
|
|
270
|
+
}
|
|
271
|
+
const notifier = notificationService.getNotifier(userId);
|
|
272
|
+
const emitFailedAndPersist = async (details) => {
|
|
273
|
+
// Best-effort: make sure listeners see a terminal state.
|
|
274
|
+
try {
|
|
275
|
+
await store.setMessageRawStatus(pending.id, 'failed');
|
|
261
276
|
}
|
|
262
|
-
|
|
263
|
-
|
|
277
|
+
catch {
|
|
278
|
+
// ignore (e.g. record removed concurrently)
|
|
264
279
|
}
|
|
265
|
-
|
|
266
|
-
|
|
280
|
+
notifier.emit('messageSignature', {
|
|
281
|
+
status: 'failed',
|
|
282
|
+
messageId: pending.id,
|
|
283
|
+
});
|
|
284
|
+
// Preserve the original error message for the caller/UI.
|
|
285
|
+
throw new Error(details);
|
|
286
|
+
};
|
|
287
|
+
const wallet = (await store.getWallets()).find((w) => w.partyId === pending.partyId);
|
|
288
|
+
if (!wallet) {
|
|
289
|
+
return await emitFailedAndPersist(`No wallet found for partyId ${pending.partyId} (from message request ${pending.id})`);
|
|
290
|
+
}
|
|
291
|
+
if (wallet.publicKey !== pending.publicKey) {
|
|
292
|
+
return await emitFailedAndPersist(`Wallet public key changed for partyId ${pending.partyId}; refusing to sign message request ${pending.id}`);
|
|
293
|
+
}
|
|
294
|
+
// TODO: support other signing providers
|
|
295
|
+
if (wallet.signingProviderId !== SigningProvider.WALLET_KERNEL) {
|
|
296
|
+
return await emitFailedAndPersist(`signMessage is only supported for ${SigningProvider.WALLET_KERNEL} wallets, got ${wallet.signingProviderId}`);
|
|
297
|
+
}
|
|
298
|
+
const driver = drivers[SigningProvider.WALLET_KERNEL]?.controller(userId);
|
|
299
|
+
if (!driver) {
|
|
300
|
+
return await emitFailedAndPersist('Wallet Kernel signing driver not available');
|
|
301
|
+
}
|
|
302
|
+
const result = await driver.signMessage({
|
|
303
|
+
message: pending.message,
|
|
304
|
+
keyIdentifier: { publicKey: wallet.publicKey },
|
|
305
|
+
});
|
|
306
|
+
if (isRpcError(result)) {
|
|
307
|
+
await store.setMessageRawStatus(pending.id, 'failed');
|
|
308
|
+
notifier.emit('messageSignature', {
|
|
309
|
+
status: 'failed',
|
|
310
|
+
messageId: pending.id,
|
|
311
|
+
});
|
|
312
|
+
throw new Error(result.error_description);
|
|
313
|
+
}
|
|
314
|
+
if (!result?.signature) {
|
|
315
|
+
await store.setMessageRawStatus(pending.id, 'failed');
|
|
316
|
+
notifier.emit('messageSignature', {
|
|
317
|
+
status: 'failed',
|
|
318
|
+
messageId: pending.id,
|
|
319
|
+
});
|
|
320
|
+
throw new Error(`signMessage failed`);
|
|
321
|
+
}
|
|
322
|
+
await store.setMessageRawStatus(pending.id, 'signed', {
|
|
323
|
+
signedAt: new Date(),
|
|
324
|
+
signature: result.signature,
|
|
325
|
+
});
|
|
326
|
+
notifier.emit('messageSignature', {
|
|
327
|
+
status: 'signed',
|
|
328
|
+
messageId: pending.id,
|
|
329
|
+
signature: result.signature,
|
|
330
|
+
});
|
|
331
|
+
return {
|
|
332
|
+
signature: result.signature,
|
|
333
|
+
publicKey: wallet.publicKey,
|
|
334
|
+
};
|
|
335
|
+
},
|
|
336
|
+
getMessageToSign: async (params) => {
|
|
337
|
+
const message = await store.getMessageRaw(params.messageId);
|
|
338
|
+
if (!message) {
|
|
339
|
+
throw new Error(`Message signing request not found with id: ${params.messageId}`);
|
|
267
340
|
}
|
|
341
|
+
return {
|
|
342
|
+
message: {
|
|
343
|
+
id: message.id,
|
|
344
|
+
status: message.status,
|
|
345
|
+
partyId: message.partyId,
|
|
346
|
+
publicKey: message.publicKey,
|
|
347
|
+
message: message.message,
|
|
348
|
+
...(message.origin !== null && { origin: message.origin }),
|
|
349
|
+
...(message.createdAt && {
|
|
350
|
+
createdAt: message.createdAt.toISOString(),
|
|
351
|
+
}),
|
|
352
|
+
...(message.signedAt && {
|
|
353
|
+
signedAt: message.signedAt.toISOString(),
|
|
354
|
+
}),
|
|
355
|
+
...(message.signature && { signature: message.signature }),
|
|
356
|
+
},
|
|
357
|
+
};
|
|
358
|
+
},
|
|
359
|
+
listMessagesToSign: async () => {
|
|
360
|
+
const messages = await store.listMessageRaws();
|
|
361
|
+
return {
|
|
362
|
+
messages: messages.map((message) => ({
|
|
363
|
+
id: message.id,
|
|
364
|
+
status: message.status,
|
|
365
|
+
partyId: message.partyId,
|
|
366
|
+
publicKey: message.publicKey,
|
|
367
|
+
message: message.message,
|
|
368
|
+
...(message.origin !== null && { origin: message.origin }),
|
|
369
|
+
...(message.createdAt && {
|
|
370
|
+
createdAt: message.createdAt.toISOString(),
|
|
371
|
+
}),
|
|
372
|
+
...(message.signedAt && {
|
|
373
|
+
signedAt: message.signedAt.toISOString(),
|
|
374
|
+
}),
|
|
375
|
+
...(message.signature && { signature: message.signature }),
|
|
376
|
+
})),
|
|
377
|
+
};
|
|
378
|
+
},
|
|
379
|
+
deleteMessageToSign: async (params) => {
|
|
380
|
+
const message = await store.getMessageRaw(params.messageId);
|
|
381
|
+
if (!message) {
|
|
382
|
+
throw new Error(`Message signing request not found with id: ${params.messageId}`);
|
|
383
|
+
}
|
|
384
|
+
if (message.status !== 'pending') {
|
|
385
|
+
throw new Error(`Cannot delete message with status '${message.status}'. Only pending messages can be deleted.`);
|
|
386
|
+
}
|
|
387
|
+
const userId = assertConnected(authContext).userId;
|
|
388
|
+
if (message.userId !== userId) {
|
|
389
|
+
throw new Error(`Message signing request ${message.id} is not owned by user ${userId}`);
|
|
390
|
+
}
|
|
391
|
+
await store.removeMessageRaw(message.id);
|
|
392
|
+
return null;
|
|
268
393
|
},
|
|
269
394
|
execute: async (executeParams) => {
|
|
270
395
|
const wallets = await store.getWallets();
|
|
@@ -272,45 +397,38 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
272
397
|
const transaction = await store.getTransaction(executeParams.transactionId);
|
|
273
398
|
const wallet = wallets.find((w) => w.partyId === executeParams.partyId);
|
|
274
399
|
if (wallet === undefined) {
|
|
275
|
-
throw new Error('
|
|
400
|
+
throw new Error('Requested wallet not found for user');
|
|
276
401
|
}
|
|
277
402
|
if (transaction === undefined) {
|
|
278
403
|
throw new Error('No transaction found');
|
|
279
404
|
}
|
|
280
|
-
const
|
|
405
|
+
const connectedContext = assertConnected(authContext);
|
|
406
|
+
const accessTokenProvider = AuthTokenProvider.fromToken(connectedContext.accessToken, logger);
|
|
281
407
|
if (network === undefined) {
|
|
282
408
|
throw new Error('No network session found');
|
|
283
409
|
}
|
|
284
|
-
const notifier = notificationService.getNotifier(userId);
|
|
285
|
-
// Create AccessTokenProvider for user token
|
|
286
|
-
const userAccessTokenProvider = AuthTokenProvider.fromToken(authContext.accessToken, logger);
|
|
410
|
+
const notifier = notificationService.getNotifier(connectedContext.userId);
|
|
287
411
|
const ledgerClient = new LedgerClient({
|
|
288
412
|
baseUrl: new URL(network.ledgerApi.baseUrl),
|
|
289
413
|
logger,
|
|
290
|
-
accessTokenProvider
|
|
414
|
+
accessTokenProvider,
|
|
291
415
|
});
|
|
292
416
|
const transactionService = new TransactionService(store, logger, drivers, notifier);
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
case SigningProvider.DFNS: {
|
|
309
|
-
return transactionService.executeWithDfns(transaction);
|
|
310
|
-
}
|
|
311
|
-
default:
|
|
312
|
-
throw new Error(`Unsupported signing provider: ${wallet.signingProviderId}`);
|
|
313
|
-
}
|
|
417
|
+
logDynamically(logger, 'executing transaction with params', {
|
|
418
|
+
info: { transactionId: executeParams.transactionId },
|
|
419
|
+
debug: {
|
|
420
|
+
executeParams,
|
|
421
|
+
transaction,
|
|
422
|
+
wallet,
|
|
423
|
+
userId: connectedContext.userId,
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
const response = await transactionService.execute(connectedContext.userId, wallet, transaction, executeParams, ledgerClient, network);
|
|
427
|
+
logDynamically(logger, 'transaction executed with response', {
|
|
428
|
+
info: { transactionId: executeParams.transactionId },
|
|
429
|
+
debug: { response },
|
|
430
|
+
});
|
|
431
|
+
return response;
|
|
314
432
|
},
|
|
315
433
|
addSession: async function (params) {
|
|
316
434
|
try {
|
|
@@ -390,7 +508,7 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
390
508
|
}
|
|
391
509
|
},
|
|
392
510
|
removeSession: async () => {
|
|
393
|
-
logger.info(authContext, 'Removing session');
|
|
511
|
+
logger.info({ authContext }, 'Removing session');
|
|
394
512
|
const userId = assertConnected(authContext).userId;
|
|
395
513
|
const notifier = notificationService.getNotifier(userId);
|
|
396
514
|
await store.removeSession();
|
|
@@ -561,5 +679,122 @@ export const userController = (kernelInfo, userUrl, store, notificationService,
|
|
|
561
679
|
await store.removeTransaction(transaction.id);
|
|
562
680
|
return null;
|
|
563
681
|
},
|
|
682
|
+
generateApiKey: async (params) => {
|
|
683
|
+
const userId = assertConnected(authContext).userId;
|
|
684
|
+
const network = await store.getCurrentNetwork();
|
|
685
|
+
const apiKeyId = v4();
|
|
686
|
+
const generatedApiKey = crypto.randomBytes(32).toString('hex');
|
|
687
|
+
const hashedApiKey = crypto
|
|
688
|
+
.createHash('sha256')
|
|
689
|
+
.update(generatedApiKey)
|
|
690
|
+
.digest('hex');
|
|
691
|
+
const storedApiKey = {
|
|
692
|
+
id: apiKeyId,
|
|
693
|
+
name: params.name,
|
|
694
|
+
digest: hashedApiKey,
|
|
695
|
+
userId,
|
|
696
|
+
networkId: network.id,
|
|
697
|
+
email: authContext?.email || null,
|
|
698
|
+
createdAt: new Date(),
|
|
699
|
+
};
|
|
700
|
+
await store.addApiKey(storedApiKey);
|
|
701
|
+
logDynamically(logger, 'Generated new API key', {
|
|
702
|
+
info: { apiKeyId: storedApiKey.id },
|
|
703
|
+
debug: {
|
|
704
|
+
name: storedApiKey.name,
|
|
705
|
+
userId: storedApiKey.userId,
|
|
706
|
+
networkId: storedApiKey.networkId,
|
|
707
|
+
createdAt: storedApiKey.createdAt,
|
|
708
|
+
},
|
|
709
|
+
});
|
|
710
|
+
return {
|
|
711
|
+
id: storedApiKey.id,
|
|
712
|
+
apiKey: generatedApiKey,
|
|
713
|
+
};
|
|
714
|
+
},
|
|
715
|
+
listApiKeys: async () => {
|
|
716
|
+
const apiKeys = await store.listApiKeys().then((keys) => keys.map((key) => ({
|
|
717
|
+
id: key.id,
|
|
718
|
+
name: key.name,
|
|
719
|
+
createdAt: key.createdAt.toISOString(),
|
|
720
|
+
})));
|
|
721
|
+
return { apiKeys };
|
|
722
|
+
},
|
|
723
|
+
removeApiKey: async (params) => {
|
|
724
|
+
await store.removeApiKey(params.id);
|
|
725
|
+
return null;
|
|
726
|
+
},
|
|
727
|
+
listSigningProviderVaults: async (params) => {
|
|
728
|
+
const network = await store.getCurrentNetwork();
|
|
729
|
+
const idp = await store.getIdp(network.identityProviderId);
|
|
730
|
+
if (!network.adminAuth) {
|
|
731
|
+
throw new Error('No admin auth configured');
|
|
732
|
+
}
|
|
733
|
+
const adminAccessTokenProvider = AuthTokenProvider.fromGatewayConfig(idp, network.adminAuth, logger);
|
|
734
|
+
const partyAllocator = new PartyAllocationService({
|
|
735
|
+
synchronizerId: network.synchronizerId,
|
|
736
|
+
accessTokenProvider: adminAccessTokenProvider,
|
|
737
|
+
httpLedgerUrl: network.ledgerApi.baseUrl,
|
|
738
|
+
logger,
|
|
739
|
+
});
|
|
740
|
+
const walletAllocationService = new WalletAllocationService(store, logger, partyAllocator, drivers);
|
|
741
|
+
if (!drivers[params.signingProviderId]) {
|
|
742
|
+
throw new Error(`Signing provider ${params.signingProviderId} not supported`);
|
|
743
|
+
}
|
|
744
|
+
return walletAllocationService.getVaults(assertConnected(authContext), params.signingProviderId);
|
|
745
|
+
},
|
|
564
746
|
});
|
|
565
747
|
};
|
|
748
|
+
function toAuthDto(auth) {
|
|
749
|
+
const base = {
|
|
750
|
+
method: auth.method,
|
|
751
|
+
audience: auth.audience,
|
|
752
|
+
scope: auth.scope,
|
|
753
|
+
clientId: auth.clientId,
|
|
754
|
+
};
|
|
755
|
+
if (auth.method === 'self_signed') {
|
|
756
|
+
return {
|
|
757
|
+
...base,
|
|
758
|
+
issuer: auth.issuer,
|
|
759
|
+
clientSecret: auth.clientSecret,
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
if (auth.method === 'client_credentials') {
|
|
763
|
+
return {
|
|
764
|
+
...base,
|
|
765
|
+
clientSecret: auth.clientSecret,
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
return base;
|
|
769
|
+
}
|
|
770
|
+
function toNetworkDto(network) {
|
|
771
|
+
return {
|
|
772
|
+
id: network.id,
|
|
773
|
+
name: network.name,
|
|
774
|
+
description: network.description,
|
|
775
|
+
synchronizerId: network.synchronizerId,
|
|
776
|
+
identityProviderId: network.identityProviderId,
|
|
777
|
+
ledgerApi: network.ledgerApi.baseUrl,
|
|
778
|
+
auth: toAuthDto(network.auth),
|
|
779
|
+
...(network.adminAuth
|
|
780
|
+
? { adminAuth: toAuthDto(network.adminAuth) }
|
|
781
|
+
: {}),
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
function toPublicNetwork(network) {
|
|
785
|
+
const auth = network.auth;
|
|
786
|
+
return {
|
|
787
|
+
id: network.id,
|
|
788
|
+
name: network.name,
|
|
789
|
+
description: network.description,
|
|
790
|
+
synchronizerId: network.synchronizerId,
|
|
791
|
+
identityProviderId: network.identityProviderId,
|
|
792
|
+
ledgerApi: network.ledgerApi.baseUrl,
|
|
793
|
+
authMethod: auth.method,
|
|
794
|
+
...(auth.method !== 'client_credentials' && {
|
|
795
|
+
clientId: auth.clientId,
|
|
796
|
+
scope: auth.scope,
|
|
797
|
+
audience: auth.audience,
|
|
798
|
+
}),
|
|
799
|
+
};
|
|
800
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.test.d.ts","sourceRoot":"","sources":["../../src/user-api/controller.test.ts"],"names":[],"mappings":""}
|