@tdxvolt/volt-client-grpc 0.15.1 → 0.16.0-beta.2

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/index.cjs CHANGED
@@ -38,37 +38,37 @@ const constants = {
38
38
  collectionOperation: {
39
39
  add: "ADD",
40
40
  remove: "REMOVE",
41
- update: "UPDATE",
41
+ update: "UPDATE"
42
42
  },
43
43
  crypto: {
44
44
  subjectDefaults: [
45
45
  {
46
46
  name: "countryName",
47
- value: "GB",
47
+ value: "GB"
48
48
  },
49
49
  {
50
50
  name: "localityName",
51
- value: "Southampton",
51
+ value: "Southampton"
52
52
  },
53
53
  {
54
54
  name: "organizationName",
55
- value: "nquiringminds Ltd",
56
- },
55
+ value: "nquiringminds Ltd"
56
+ }
57
57
  ],
58
- userIdOID: "0.9.2342.19200300.100.1.1",
58
+ userIdOID: "0.9.2342.19200300.100.1.1"
59
59
  },
60
60
  defaultDIDHostName: "cloud.tdxvolt.com",
61
61
  didServiceType: {
62
- voltConfig: "tdx-volt-config",
62
+ voltConfig: "tdx-volt-config"
63
63
  },
64
64
  identityType: {
65
65
  did: "did",
66
- volt: "volt",
66
+ volt: "volt"
67
67
  },
68
68
  onlineStatus: {
69
69
  offline: "offline",
70
70
  online: "online",
71
- unknown: "unknown",
71
+ unknown: "unknown"
72
72
  },
73
73
  publicKeyPrefix: "-----BEGIN PUBLIC KEY-----",
74
74
  privateKeyPrefix: "-----BEGIN RSA PRIVATE KEY-----",
@@ -79,14 +79,14 @@ const constants = {
79
79
  proxy: "proxy",
80
80
  proxyView: "proxy-view",
81
81
  vc: "vc",
82
- vcView: "vc-view",
82
+ vcView: "vc-view"
83
83
  },
84
84
  voltStatus: {
85
85
  pending: "",
86
86
  online: "online",
87
87
  offline: "offline",
88
88
  deleted: "deleted",
89
- revoked: "revoked",
89
+ revoked: "revoked"
90
90
  },
91
91
  /**
92
92
  * These top-level service types should map to a protobuf definition
@@ -96,16 +96,16 @@ const constants = {
96
96
  fileAPI: "tdx.volt_api.volt.v1.FileAPI",
97
97
  sqliteServerAPI: "tdx.volt_api.data.v1.SqliteServerAPI",
98
98
  sqliteDatabaseAPI: "tdx.volt_api.data.v1.SqliteDatabaseAPI",
99
- ssiAPI: "tdx.volt_api.ssi.v1.SsiAPI",
99
+ ssiAPI: "tdx.volt_api.volt.v1.SsiAPI",
100
100
  relayAPI: "tdx.volt_api.relay.v1.RelayAPI",
101
101
  voltAPI: "tdx.volt_api.volt.v1.VoltAPI",
102
- wireAPI: "tdx.volt_api.volt.v1.WireAPI",
102
+ wireAPI: "tdx.volt_api.volt.v1.WireAPI"
103
103
  },
104
104
  tunnelInvokeMethod: "/tdx.volt_api.volt.v1.VoltAPI/Invoke",
105
105
  tunnelPingInterval: 10000,
106
106
  tunnelPingTimeout: 2500,
107
107
  tunnelReconnectAfter: 30000,
108
- tunnelTimeoutInterval: 10000 * 2, // This must always be greater than `tunnelPingInterval`.
108
+ tunnelTimeoutInterval: 10000 * 2 // This must always be greater than `tunnelPingInterval`.
109
109
  };
110
110
 
111
111
  const { pki: pki$2 } = forge__default["default"];
@@ -167,11 +167,16 @@ const createPublicKeyFingerprint = (key) => {
167
167
  return createBase58Hash(removeCarriageReturn(key));
168
168
  };
169
169
 
170
+ const base64ToBase64Url = (base64) => {
171
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
172
+ };
173
+
170
174
  const signBase64 = (key, msg) => {
171
175
  const md = forge__default["default"].md.sha256.create();
172
176
  md.update(msg);
173
177
  const signature = key.sign(md);
174
- return forge__default["default"].util.encode64(signature);
178
+ const base64Standard = forge__default["default"].util.encode64(signature);
179
+ return base64ToBase64Url(base64Standard);
175
180
  };
176
181
 
177
182
  const verifyBase64 = (key, msg, signature) => {
@@ -240,7 +245,8 @@ const createRSAKey = (passphrase = "") => {
240
245
  const sha256Base64 = (msg) => {
241
246
  const md = forge__default["default"].md.sha256.create();
242
247
  md.update(msg);
243
- return forge__default["default"].util.encode64(md.digest().bytes());
248
+ const base64Standard = forge__default["default"].util.encode64(md.digest().bytes());
249
+ return base64ToBase64Url(base64Standard);
244
250
  };
245
251
 
246
252
  const decryptPrivateKey = (pem, passphrase) => {
@@ -248,6 +254,20 @@ const decryptPrivateKey = (pem, passphrase) => {
248
254
  return forgePrivateKeyToPem(decryptedKey);
249
255
  };
250
256
 
257
+ const jwsSignature = (signature) => {
258
+ //
259
+ // Use an unattached payload JWS signature for the DID document proof.
260
+ // This is a JWS with an empty payload and a detached payload digest.
261
+ //
262
+ // (https://tools.ietf.org/html/rfc7797#appendix-F) as used in the VC spec
263
+ // (https://www.w3.org/TR/vc-data-model/).
264
+ //
265
+ const jwsHeader = base64ToBase64Url(
266
+ forge__default["default"].util.encode64('{"alg":"RS256","b64": false}')
267
+ );
268
+ return jwsHeader + ".." + signature;
269
+ };
270
+
251
271
  var utils = /*#__PURE__*/Object.freeze({
252
272
  __proto__: null,
253
273
  createSecureContextOptions: createSecureContextOptions,
@@ -258,12 +278,14 @@ var utils = /*#__PURE__*/Object.freeze({
258
278
  removeCarriageReturn: removeCarriageReturn,
259
279
  createBase58Hash: createBase58Hash,
260
280
  createPublicKeyFingerprint: createPublicKeyFingerprint,
281
+ base64ToBase64Url: base64ToBase64Url,
261
282
  signBase64: signBase64,
262
283
  verifyBase64: verifyBase64,
263
284
  getDIDResolutionURL: getDIDResolutionURL,
264
285
  createRSAKey: createRSAKey,
265
286
  sha256Base64: sha256Base64,
266
- decryptPrivateKey: decryptPrivateKey
287
+ decryptPrivateKey: decryptPrivateKey,
288
+ jwsSignature: jwsSignature
267
289
  });
268
290
 
269
291
  /* eslint-disable no-underscore-dangle */
@@ -567,8 +589,12 @@ class VoltCredential {
567
589
  return this._cryptoCache;
568
590
  }
569
591
 
570
- get clientId() {
571
- return this._cryptoCache.client_id;
592
+ get sessionId() {
593
+ return this._cryptoCache.session_id;
594
+ }
595
+
596
+ get identityId() {
597
+ return this._cryptoCache.identity_id;
572
598
  }
573
599
 
574
600
  get voltPublicKey() {
@@ -656,7 +682,7 @@ class VoltCredential {
656
682
  aud: audience,
657
683
  iat: Math.floor(Date.now() / 1000) - ttl,
658
684
  exp: Math.floor(Date.now() / 1000) + ttl,
659
- sub: this._cryptoCache.client_id
685
+ sub: this._cryptoCache.session_id
660
686
  };
661
687
 
662
688
  let sharedKey;
@@ -738,12 +764,13 @@ class VoltCredential {
738
764
  }
739
765
 
740
766
  static rsaEncrypt(key, buffer) {
741
- return crypto__default["default"].publicEncrypt(key, buffer).toString("base64");
767
+ const cipher = crypto__default["default"].publicEncrypt(key, buffer);
768
+ return cipher.toString("base64url");
742
769
  }
743
770
 
744
771
  static rsaDecrypt(key, buffer) {
745
772
  const keyObj = crypto__default["default"].createPrivateKey(key);
746
- const cipher = Buffer.from(buffer, "base64");
773
+ const cipher = Buffer.from(buffer, "base64url");
747
774
  return Buffer.from(crypto__default["default"].privateDecrypt(keyObj, cipher));
748
775
  }
749
776
  }
@@ -881,7 +908,7 @@ const log$4 = debug__default["default"]("volt-client-grpc:grpc-call");
881
908
  function _prepareInvokeRequest(
882
909
  request,
883
910
  method,
884
- methodType = "METHOD_TYPE_UNARY",
911
+ methodType = "METHOD_TYPE_UNARY"
885
912
  ) {
886
913
  const isServiceRelayed =
887
914
  this._service?.service_description.host_type ===
@@ -894,7 +921,7 @@ function _prepareInvokeRequest(
894
921
  this._voltClient.voltConfig.id,
895
922
  this._service?.service_description.host_public_key ||
896
923
  this._voltClient.credential.voltPublicKey,
897
- true,
924
+ true
898
925
  );
899
926
 
900
927
  let invokeRequest;
@@ -925,42 +952,40 @@ function _prepareInvokeRequest(
925
952
  method_name: this._grpcClient[method].path,
926
953
  method_type: methodType,
927
954
  token: methodToken.token,
928
- request: requestPayload,
929
- },
955
+ request: requestPayload
956
+ }
930
957
  };
931
958
 
932
- let targetFingerprint = [];
959
+ let targetDID = [];
933
960
  let invokePayload;
934
961
  if (this._voltClient.isVoltRelay || isServiceRelayed) {
935
962
  // When sending via a relay we need to encrypt the payload.
936
963
  invokePayload = VoltCredential.aesEncrypt(
937
964
  tunnelMethod.responseSerialize(tunnelResp),
938
965
  methodToken.sharedKey.key,
939
- methodToken.sharedKey.iv,
966
+ methodToken.sharedKey.iv
940
967
  );
941
968
  } else {
942
969
  invokePayload = tunnelMethod.responseSerialize(tunnelResp);
943
970
  }
944
971
 
945
972
  if (this._voltClient.isVoltRelay) {
946
- targetFingerprint.push(this._voltClient.credential.voltFingerprint);
973
+ targetDID.push(this._voltClient.voltConfig.id);
947
974
  }
948
975
 
949
976
  if (isServiceRelayed) {
950
977
  // Add another relay hop if the target service is relayed.
951
978
  log$4("target service is relayed");
952
- targetFingerprint.push(
953
- this._service.service_description.host_fingerprint,
954
- );
979
+ targetDID.push(this._service.service_description.host_client_id);
955
980
  }
956
981
 
957
- log$4("target is %j", targetFingerprint);
982
+ log$4("target is %j", targetDID);
958
983
 
959
984
  invokeRequest = {
960
- target_fingerprint: targetFingerprint,
985
+ target_did: targetDID,
961
986
  token: methodToken.token,
962
987
  payload: invokePayload,
963
- target_service_id: isServiceRelayed ? this._service.id : undefined,
988
+ target_service_id: isServiceRelayed ? this._service.id : undefined
964
989
  };
965
990
 
966
991
  // Replace the target method with a call to Invoke on the relay Volt.
@@ -973,7 +998,7 @@ function _prepareInvokeRequest(
973
998
  meta: methodToken,
974
999
  method: callMethod,
975
1000
  methodName: method,
976
- request: invokeRequest,
1001
+ request: invokeRequest
977
1002
  };
978
1003
  }
979
1004
 
@@ -993,18 +1018,18 @@ function _preparePayloadRequest(request, invokeInfo) {
993
1018
 
994
1019
  const tunnelResp = {
995
1020
  method_payload: {
996
- payload: requestPayload,
997
- },
1021
+ payload: requestPayload
1022
+ }
998
1023
  };
999
1024
 
1000
1025
  const invokePayload = VoltCredential.aesEncrypt(
1001
1026
  tunnelMethod.responseSerialize(tunnelResp),
1002
1027
  invokeInfo.meta.sharedKey.key,
1003
- invokeInfo.meta.sharedKey.iv,
1028
+ invokeInfo.meta.sharedKey.iv
1004
1029
  );
1005
1030
 
1006
1031
  payloadRequest = {
1007
- payload: invokePayload,
1032
+ payload: invokePayload
1008
1033
  };
1009
1034
  } else {
1010
1035
  payloadRequest = request;
@@ -1028,7 +1053,7 @@ function _parseResponse(method, meta, response) {
1028
1053
  decryptedPayload = VoltCredential.aesDecrypt(
1029
1054
  response.payload,
1030
1055
  meta.sharedKey.key,
1031
- meta.sharedKey.iv,
1056
+ meta.sharedKey.iv
1032
1057
  );
1033
1058
  } else {
1034
1059
  decryptedPayload = response.payload;
@@ -1039,12 +1064,12 @@ function _parseResponse(method, meta, response) {
1039
1064
  if (responsePayload.payload === "method_payload") {
1040
1065
  try {
1041
1066
  invokeResponse.payload = this._grpcClient[method].responseDeserialize(
1042
- responsePayload.method_payload.payload,
1067
+ responsePayload.method_payload.payload
1043
1068
  );
1044
1069
  } catch (err) {
1045
1070
  log$4("failure deserialising payload for %s", this._methodName);
1046
1071
  throw new Error(
1047
- "failure deserialising payload - check protobuf definition matches with data being sent",
1072
+ "failure deserialising payload - check protobuf definition matches with data being sent"
1048
1073
  );
1049
1074
  }
1050
1075
  } else if (responsePayload.payload === "method_end") {
@@ -1058,11 +1083,11 @@ function _parseResponse(method, meta, response) {
1058
1083
  "Tunnel status received: %s, code %d, description: %s",
1059
1084
  response.status.message,
1060
1085
  response.status.code,
1061
- response.status.description || "n/a",
1086
+ response.status.description || "n/a"
1062
1087
  );
1063
1088
  invokeResponse = {
1064
1089
  ended: true,
1065
- error: `Error in tunnel: ${response.status.message}`,
1090
+ error: `Error in tunnel: ${response.status.message}`
1066
1091
  };
1067
1092
  } else {
1068
1093
  invokeResponse.methodId = response.invoke_id;
@@ -1072,11 +1097,11 @@ function _parseResponse(method, meta, response) {
1072
1097
  const decryptedPayload = VoltCredential.aesDecrypt(
1073
1098
  response,
1074
1099
  meta.sharedKey.key,
1075
- meta.sharedKey.iv,
1100
+ meta.sharedKey.iv
1076
1101
  );
1077
1102
  invokeResponse = {
1078
1103
  payload:
1079
- this._grpcClient[method].responseDeserializeOriginal(decryptedPayload),
1104
+ this._grpcClient[method].responseDeserializeOriginal(decryptedPayload)
1080
1105
  };
1081
1106
  } else {
1082
1107
  invokeResponse = { payload: response };
@@ -1106,7 +1131,7 @@ class GRPCCall extends EventEmitter__default["default"] {
1106
1131
  this,
1107
1132
  request,
1108
1133
  this._methodName,
1109
- this._methodType,
1134
+ this._methodType
1110
1135
  );
1111
1136
 
1112
1137
  const isServiceRelayed =
@@ -1121,7 +1146,7 @@ class GRPCCall extends EventEmitter__default["default"] {
1121
1146
  }
1122
1147
 
1123
1148
  this._call = callClient[this._initialRequest.method](
1124
- this._initialRequest.meta.metadata,
1149
+ this._initialRequest.meta.metadata
1125
1150
  );
1126
1151
 
1127
1152
  this._call.on("data", (streamResponse) => {
@@ -1130,14 +1155,14 @@ class GRPCCall extends EventEmitter__default["default"] {
1130
1155
  this,
1131
1156
  this._methodName,
1132
1157
  this._initialRequest.meta,
1133
- streamResponse,
1158
+ streamResponse
1134
1159
  );
1135
1160
  if (parsedResponse.payload) {
1136
1161
  // Interpret a non-empty status message as an error.
1137
1162
  if (parsedResponse.payload?.status?.message) {
1138
1163
  this.emit(
1139
1164
  "error",
1140
- new Error(parsedResponse.payload.status.message),
1165
+ new Error(parsedResponse.payload.status.message)
1141
1166
  );
1142
1167
  } else {
1143
1168
  this.emit("data", parsedResponse.payload);
@@ -1156,7 +1181,7 @@ class GRPCCall extends EventEmitter__default["default"] {
1156
1181
  log$4(
1157
1182
  "failure processing %s call response: %s",
1158
1183
  this._methodName,
1159
- err.message,
1184
+ err.message
1160
1185
  );
1161
1186
  this.emit("error", err);
1162
1187
  }
@@ -1178,7 +1203,7 @@ class GRPCCall extends EventEmitter__default["default"] {
1178
1203
  log$4(
1179
1204
  "ended call %s, method id: %s",
1180
1205
  this._methodName,
1181
- this._initialRequest.methodId || "n/a - not tunnelling",
1206
+ this._initialRequest.methodId || "n/a - not tunnelling"
1182
1207
  );
1183
1208
  this.emit("end");
1184
1209
  });
@@ -1211,7 +1236,7 @@ class GRPCCall extends EventEmitter__default["default"] {
1211
1236
  const payloadRequest = _preparePayloadRequest.call(
1212
1237
  this,
1213
1238
  request,
1214
- this._initialRequest,
1239
+ this._initialRequest
1215
1240
  );
1216
1241
  return this._call.write(payloadRequest);
1217
1242
  } else {
@@ -1219,7 +1244,7 @@ class GRPCCall extends EventEmitter__default["default"] {
1219
1244
  this,
1220
1245
  request,
1221
1246
  this._methodName,
1222
- this._methodType,
1247
+ this._methodType
1223
1248
  );
1224
1249
 
1225
1250
  return this._call.write(this._initialRequest.request);
@@ -1691,30 +1716,31 @@ const voltServices = [
1691
1716
  constants.serviceType.wireAPI
1692
1717
  ];
1693
1718
 
1694
- function issueBind(bindRequest, ttl) {
1719
+ function issueAuthenticate(authenticateRequest, ttl) {
1695
1720
  // eslint-disable-next-line no-use-before-define
1696
1721
  return unaryCallInternal
1697
- .call(this, "Bind", bindRequest)
1698
- .then((bindResponse) => {
1699
- log$1("got binding request response %j", bindResponse);
1700
- if (bindResponse.status?.code) {
1701
- log$1("error in bind response: %s", bindResponse.status.message);
1702
- return Promise.reject(new Error(bindResponse.status.message));
1722
+ .call(this, "Authenticate", authenticateRequest)
1723
+ .then((authenticateResponse) => {
1724
+ log$1("got authenticate response %j", authenticateResponse);
1725
+ if (authenticateResponse.status?.code) {
1726
+ log$1("error in bind response: %s", authenticateResponse.status.message);
1727
+ return Promise.reject(new Error(authenticateResponse.status.message));
1703
1728
  } else {
1704
- switch (bindResponse.decision) {
1729
+ switch (authenticateResponse.decision) {
1705
1730
  case "POLICY_DECISION_PERMIT": {
1706
1731
  //
1707
1732
  // The request status is permit => cache the bind response info.
1708
1733
  //
1709
1734
 
1710
1735
  // This is the certificate assigned to us by the volt.
1711
- this._credential.cache.cert = bindResponse.cert;
1736
+ this._credential.cache.cert = authenticateResponse.cert;
1712
1737
 
1713
1738
  // This is the signing CA used by the volt.
1714
- this._credential.cache.ca = bindResponse.chain;
1739
+ this._credential.cache.ca = authenticateResponse.chain;
1715
1740
 
1716
1741
  // Identity resource id is assigned by the volt.
1717
- this._credential.cache.client_id = bindResponse.identity_id;
1742
+ this._credential.cache.session_id = authenticateResponse.session_id;
1743
+
1718
1744
  this._credential.saveCache();
1719
1745
  break;
1720
1746
  }
@@ -1728,12 +1754,15 @@ function issueBind(bindRequest, ttl) {
1728
1754
  break;
1729
1755
  }
1730
1756
  default:
1731
- log$1("ignoring unknown bind descision %s", bindResponse.status);
1757
+ log$1(
1758
+ "ignoring unknown bind descision %s",
1759
+ authenticateResponse.status
1760
+ );
1732
1761
  break;
1733
1762
  }
1734
1763
  }
1735
1764
 
1736
- return bindResponse.decision;
1765
+ return authenticateResponse.decision;
1737
1766
  });
1738
1767
  }
1739
1768
 
@@ -1746,7 +1775,7 @@ function findDIDDocumentService(document, serviceType) {
1746
1775
  return matches;
1747
1776
  }
1748
1777
 
1749
- async function bindInternal() {
1778
+ async function authenticateInternal(createDID = true) {
1750
1779
  try {
1751
1780
  // Check if we need to resolve a Relay volt.
1752
1781
  if (
@@ -1760,6 +1789,7 @@ async function bindInternal() {
1760
1789
  this,
1761
1790
  `${relayURL}/discovery`
1762
1791
  );
1792
+
1763
1793
  if (this._voltConfig.relay.ca_pem) {
1764
1794
  log$1(
1765
1795
  "Auto-fetched Relay CA %s, remote address %s",
@@ -1793,21 +1823,13 @@ async function bindInternal() {
1793
1823
  this._credential.saveCache();
1794
1824
 
1795
1825
  // Form the request message.
1796
- const bindRequest = {
1826
+ const authenticateRequest = {
1797
1827
  binding_name: this._config.client_name,
1798
- public_key: publicKeyPem,
1799
- host: this._credential.cache.bindIp,
1800
- x509_credential: []
1828
+ host: this._credential.cache.bindIp
1801
1829
  };
1802
1830
 
1803
- if (this._credential.cache.cert) {
1804
- bindRequest.x509_credential = [
1805
- this._credential.cache.cert + this._credential.cache.ca
1806
- ];
1807
- }
1808
-
1809
1831
  if (this._voltConfig.challenge_code) {
1810
- bindRequest.challenge = signBase64(
1832
+ authenticateRequest.challenge = signBase64(
1811
1833
  keyPair.privateKey,
1812
1834
  this._voltConfig.challenge_code
1813
1835
  );
@@ -1817,15 +1839,71 @@ async function bindInternal() {
1817
1839
  );
1818
1840
  }
1819
1841
 
1820
- // For remote connections, add our cloud-issued certificate as an additional credential.
1821
- if (this._voltConfig?.relay?.ca_pem && this._credential.cache.cloud_cert) {
1822
- bindRequest.x509_credential = bindRequest.x509_credential.concat(
1823
- this._credential.cache.cloud_cert + this._voltConfig.relay.ca_pem
1842
+ if (!createDID) {
1843
+ authenticateRequest.public_key = publicKeyPem;
1844
+ } else {
1845
+ if (!this._credential.cache.identity_id) {
1846
+ // If we don't have an identity id, then we need to create a DID.
1847
+ this._credential.cache.identity_id = `did:volt:${uuid.v4()}`;
1848
+ }
1849
+
1850
+ const did = this._credential.cache.identity_id;
1851
+
1852
+ // Order of properties in the DID document is important,
1853
+ // they must be in alphabetical order throughout the document.
1854
+ // This is because the DID document is signed and the signature
1855
+ // is over the entire document.
1856
+ const didDocument = {
1857
+ "@context": "https://www.w3.org/ns/did/v1",
1858
+ authentication: [`${did}#key-1`],
1859
+ description: this._config.client_name,
1860
+ id: did,
1861
+ verificationMethod: [
1862
+ {
1863
+ controller: did,
1864
+ id: `${did}#key-1`,
1865
+ publicKeyPem: publicKeyPem,
1866
+ type: "RsaSignature2018"
1867
+ }
1868
+ ]
1869
+ };
1870
+
1871
+ const signDocument = JSON.stringify(didDocument, null, 2);
1872
+ log$1("signing did document");
1873
+ log$1(signDocument);
1874
+
1875
+ // Sign the DID document.
1876
+ const documentDigest = signBase64(
1877
+ keyPair.privateKey,
1878
+ signDocument
1879
+ );
1880
+
1881
+ const proofSignature = jwsSignature(documentDigest);
1882
+
1883
+ // Create the DID document proof.
1884
+ didDocument.proof = {
1885
+ created: new Date().toISOString(),
1886
+ jws: proofSignature,
1887
+ proofPurpose: "assertionMethod",
1888
+ type: "RsaSignature2018",
1889
+ verificationMethod: `${did}#key-1`
1890
+ };
1891
+
1892
+ // Set the DID document with proof in the authenticate request.
1893
+ authenticateRequest.did_document = JSON.stringify(didDocument, null, 2);
1894
+
1895
+ // Now sign the entire DID document, including the proof and set it in the
1896
+ // bind request. This is required by the Volt to verify that we have the private key for the DID.
1897
+ authenticateRequest.did_document_signature = signBase64(
1898
+ keyPair.privateKey,
1899
+ authenticateRequest.did_document
1824
1900
  );
1901
+
1902
+ log$1("did document is %s", authenticateRequest.did_document);
1903
+ log$1("did signature is %s", authenticateRequest.did_document_signature);
1825
1904
  }
1826
1905
 
1827
- log$1("sending %d x509 credentials", bindRequest.x509_credential.length);
1828
- log$1("bind TTL is ", this._config.bindRequestTTL);
1906
+ log$1("authenticate TTL is ", this._config.bindRequestTTL);
1829
1907
 
1830
1908
  // This will block while the request is pending.
1831
1909
  const pollInterval = 10000;
@@ -1837,12 +1915,12 @@ async function bindInternal() {
1837
1915
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
1838
1916
  }
1839
1917
  // eslint-disable-next-line no-await-in-loop
1840
- decision = await issueBind.call(
1918
+ decision = await issueAuthenticate.call(
1841
1919
  this,
1842
- bindRequest,
1920
+ authenticateRequest,
1843
1921
  this._voltConfig.bindRequestTTL
1844
1922
  );
1845
- log$1("binding decision: %s", decision);
1923
+ log$1("authenticate decision: %s", decision);
1846
1924
  } while (
1847
1925
  decision === "POLICY_DECISION_PROMPT" ||
1848
1926
  decision === "POLICY_DECISION_PENDING"
@@ -1964,8 +2042,6 @@ function getVoltAPIClientInternal() {
1964
2042
 
1965
2043
  function getAPIClientInternal(service) {
1966
2044
  if (!this._cachedService[service.id]) {
1967
- service.service_description.host_type === "SERVICE_HOST_TYPE_RELAYED";
1968
-
1969
2045
  const serviceAddress = this.isRemote
1970
2046
  ? this._voltConfig.relay.address
1971
2047
  : service.service_description.host_address;
@@ -2013,9 +2089,8 @@ function getServiceClient(service) {
2013
2089
  }
2014
2090
 
2015
2091
  function unaryCallInternal(method, request, service) {
2016
- const grpcClient = getServiceClient.call(this, service);
2017
-
2018
2092
  return new Promise((resolve, reject) => {
2093
+ const grpcClient = getServiceClient.call(this, service);
2019
2094
  const call = new GRPCCall(this, method, "METHOD_TYPE_UNARY", service);
2020
2095
 
2021
2096
  let response = null;
@@ -2264,14 +2339,14 @@ class VoltClient extends EventEmitter__default["default"] {
2264
2339
  if (this._credential._cryptoCache.cert) {
2265
2340
  isBound = true;
2266
2341
  } else {
2267
- // No certificate present in the cache yet => we need to bind.
2342
+ // No certificate present in the cache yet => we need to authenticate.
2268
2343
  try {
2269
- isBound = await bindInternal.call(this);
2344
+ isBound = await authenticateInternal.call(this);
2270
2345
  } catch (bindErr) {
2271
2346
  if (bindErr.message === "invalid arguments") {
2272
2347
  // This is usually the result of a missing challenge signature or credential.
2273
2348
  log(
2274
- "failure binding to Volt - did you supply a challenge code or verified credential?"
2349
+ "failure authenticating on Volt - did you supply a challenge code or verified credential?"
2275
2350
  );
2276
2351
  throw bindErr;
2277
2352
  }
@@ -2280,7 +2355,7 @@ class VoltClient extends EventEmitter__default["default"] {
2280
2355
 
2281
2356
  if (!isBound) {
2282
2357
  // This is usually because of request being denied.
2283
- throw new Error("Volt binding failed");
2358
+ throw new Error("Volt authentication failed");
2284
2359
  }
2285
2360
 
2286
2361
  return Promise.resolve();
@@ -2506,6 +2581,10 @@ class VoltClient extends EventEmitter__default["default"] {
2506
2581
  * VoltAPI - volt management
2507
2582
  */
2508
2583
 
2584
+ Authenticate(request) {
2585
+ return unaryCallInternal.call(this, "Authenticate", request);
2586
+ }
2587
+
2509
2588
  Bind(request) {
2510
2589
  return unaryCallInternal.call(this, "Bind", request);
2511
2590
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.15.1",
6
+ "version": "0.16.0-beta.2",
7
7
  "description": "tdx Volt library for nodejs clients",
8
8
  "type": "module",
9
9
  "exports": {
@@ -45,7 +45,7 @@
45
45
  "jsonwebtoken": "^8.5.1",
46
46
  "lodash": "^4.17.15",
47
47
  "minimist": "^1.2.0",
48
- "node-forge": "^0.9.0",
48
+ "node-forge": "^1.3.1",
49
49
  "uuid": "^8.1.0"
50
50
  }
51
- }
51
+ }
@@ -50,9 +50,9 @@ message SqlExecuteRequest {
50
50
  // This can be used to limit the number of rows returned by `SELECT` statements to avoid overloading a client. It is similar to using 'LIMIT/OFFSET' clauses on the 'SELECT' statement, but this method is easier to manage and the entire result set will be prepared on the Volt, and the client can then page through it by sending successive messages.
51
51
  uint32 page_size = 3;
52
52
 
53
- // The list of values to set for each parameter defined in a database query.
53
+ // The values to set for each parameter defined in a database query.
54
54
  // This field is only relevant to 'query' databases, i.e. those with kind `tdx:sqlite-query`.
55
- // The values in the list should be in the same order as the parameters defined by the query attributes.
55
+ // The values in the map should be keyed by parameter name.
56
56
  // A query may have no parameters defined, in which case leave this field empty.
57
57
  map<string, tdx.volt_api.volt.v1.AttributeValue> parameter = 4;
58
58
  }