@tdxvolt/volt-client-grpc 0.20.7 → 0.20.10
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 +127 -263
- package/package.json +4 -3
- package/src/grpc-utils.js +5 -3
- package/src/volt-client-internal.js +53 -249
- package/src/volt-client.js +34 -5
- package/src/volt-proto-literals.js +35 -7
package/lib/index.cjs
CHANGED
|
@@ -15,6 +15,7 @@ var fs = require('fs');
|
|
|
15
15
|
var path = require('path');
|
|
16
16
|
var jwt = require('jsonwebtoken');
|
|
17
17
|
var crypto = require('crypto');
|
|
18
|
+
var voltUtility = require('@tdxvolt/volt-utility');
|
|
18
19
|
|
|
19
20
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
20
21
|
|
|
@@ -135,12 +136,14 @@ function createClient(
|
|
|
135
136
|
address,
|
|
136
137
|
credentials,
|
|
137
138
|
noSerialise,
|
|
138
|
-
voltId,
|
|
139
|
-
serviceId
|
|
139
|
+
voltId = "",
|
|
140
|
+
serviceId = ""
|
|
140
141
|
) {
|
|
141
142
|
let grpcCredentials;
|
|
142
143
|
if (credentials) {
|
|
143
|
-
const relaying =
|
|
144
|
+
const relaying =
|
|
145
|
+
!!credentials.config?.volt?.relay &&
|
|
146
|
+
!Array.isArray(credentials.config?.volt?.relay);
|
|
144
147
|
const ca = relaying
|
|
145
148
|
? credentials.config?.volt?.relay?.ca_pem
|
|
146
149
|
: credentials.cache.ca;
|
|
@@ -1426,10 +1429,13 @@ message DIDRegistryUpdate {
|
|
|
1426
1429
|
}
|
|
1427
1430
|
|
|
1428
1431
|
message VerifiablePresentation {
|
|
1429
|
-
//
|
|
1432
|
+
// DEPRECATED - use 'credential'
|
|
1430
1433
|
string credential_json = 1;
|
|
1431
1434
|
|
|
1432
|
-
//
|
|
1435
|
+
// Array of credential JSON.
|
|
1436
|
+
repeated string credential = 3;
|
|
1437
|
+
|
|
1438
|
+
// A signature of the credential JSON. The signature should be that of the credential holder over the concatenation of the contents of the \`credential\` array.
|
|
1433
1439
|
string signature = 2;
|
|
1434
1440
|
}
|
|
1435
1441
|
|
|
@@ -1593,7 +1599,7 @@ message ParseCredentialResponse {
|
|
|
1593
1599
|
tdx.volt_api.volt.v1.Status status = 1;
|
|
1594
1600
|
|
|
1595
1601
|
// The parsed credential details.
|
|
1596
|
-
VerifiableCredential verifiable_credential = 2;
|
|
1602
|
+
repeated VerifiableCredential verifiable_credential = 2;
|
|
1597
1603
|
}
|
|
1598
1604
|
|
|
1599
1605
|
message ResolveDIDRequest {
|
|
@@ -1723,6 +1729,7 @@ enum SyncState {
|
|
|
1723
1729
|
SYNC_STATE_SYNCING = 1;
|
|
1724
1730
|
SYNC_STATE_DONE = 2;
|
|
1725
1731
|
SYNC_STATE_UPDATE = 3;
|
|
1732
|
+
SYNC_STATE_AWARENESS = 4;
|
|
1726
1733
|
}
|
|
1727
1734
|
|
|
1728
1735
|
message SyncDocumentStart {
|
|
@@ -1791,7 +1798,8 @@ message GetDocumentJSONResponse {
|
|
|
1791
1798
|
// The current JSON snapshot of the document
|
|
1792
1799
|
string json = 2;
|
|
1793
1800
|
}
|
|
1794
|
-
}
|
|
1801
|
+
}
|
|
1802
|
+
`;
|
|
1795
1803
|
const terminal_api = `syntax = "proto3";
|
|
1796
1804
|
|
|
1797
1805
|
package tdx.volt_api.volt.v1;
|
|
@@ -2507,6 +2515,9 @@ message SessionCredential {
|
|
|
2507
2515
|
|
|
2508
2516
|
// More type-specific data stored with the credential.
|
|
2509
2517
|
string extra_2 = 16;
|
|
2518
|
+
|
|
2519
|
+
// The verification status of the credential.
|
|
2520
|
+
string status = 17;
|
|
2510
2521
|
}
|
|
2511
2522
|
|
|
2512
2523
|
message Session {
|
|
@@ -2530,7 +2541,6 @@ message Session {
|
|
|
2530
2541
|
|
|
2531
2542
|
SessionStatus status = 10;
|
|
2532
2543
|
}
|
|
2533
|
-
|
|
2534
2544
|
`;
|
|
2535
2545
|
const volt_api = `syntax = "proto3";
|
|
2536
2546
|
|
|
@@ -2541,7 +2551,7 @@ import "tdx/volt_api/volt/v1/remote.proto";
|
|
|
2541
2551
|
import "tdx/volt_api/volt/v1/ssi.proto";
|
|
2542
2552
|
import "tdx/volt_api/volt/v1/volt.proto";
|
|
2543
2553
|
|
|
2544
|
-
// The top-level volt management service.
|
|
2554
|
+
// The top-level volt management service.
|
|
2545
2555
|
// With the exception of \`Authenticate\`, all RPCs on this service must submit either a valid client certificate signed by the volt CA, or a signed JWT.
|
|
2546
2556
|
// A client certificate can be obtained from the \`Authenticate\` method.
|
|
2547
2557
|
// All methods include a \`tdx.volt_api.volt.v1.Status\` in the response. If the status contains a non-OK error code the client should assume the remainder of the message is invalid, unless otherwise indicated in the response documentation.
|
|
@@ -2732,7 +2742,7 @@ message AuthenticateResponse {
|
|
|
2732
2742
|
// This should be persisted by the client, and submitted with all subsequent API calls as part of the call metadata.
|
|
2733
2743
|
string session_id = 2;
|
|
2734
2744
|
|
|
2735
|
-
// The identity
|
|
2745
|
+
// The 'primary' session identity assigned to this authentication. This will be the DID in DID-based sessions, otherwise it will be the first email-based credential found, if any, otherwise it will be the public key fingerprint.
|
|
2736
2746
|
string identity_did = 3;
|
|
2737
2747
|
|
|
2738
2748
|
// A certificate issued by the volt CA, binding the request public key to the identity.
|
|
@@ -2788,6 +2798,9 @@ message CanAccessResourceResponse {
|
|
|
2788
2798
|
|
|
2789
2799
|
// The policy decision for this access request.
|
|
2790
2800
|
PolicyDecision decision = 6;
|
|
2801
|
+
|
|
2802
|
+
// The session that the access relates to.
|
|
2803
|
+
string session_id = 7;
|
|
2791
2804
|
}
|
|
2792
2805
|
|
|
2793
2806
|
message CheckCompatibilityRequest {
|
|
@@ -2901,9 +2914,19 @@ message ConnectResource {
|
|
|
2901
2914
|
Resource resource = 2;
|
|
2902
2915
|
}
|
|
2903
2916
|
|
|
2917
|
+
enum ConnectAuthRequestType {
|
|
2918
|
+
CONNECT_AUTH_REQUEST_TYPE_UNKNOWN = 0;
|
|
2919
|
+
CONNECT_AUTH_REQUEST_TYPE_NEW_SESSION = 1;
|
|
2920
|
+
CONNECT_AUTH_REQUEST_TYPE_UPDATE_SESSION = 2;
|
|
2921
|
+
CONNECT_AUTH_REQUEST_TYPE_DELETE_SESSION = 3;
|
|
2922
|
+
CONNECT_AUTH_REQUEST_TYPE_SIGN_REQUEST = 4;
|
|
2923
|
+
}
|
|
2924
|
+
|
|
2904
2925
|
message ConnectAuthRequest {
|
|
2905
2926
|
Session session = 1;
|
|
2906
2927
|
|
|
2928
|
+
ConnectAuthRequestType request_type = 2;
|
|
2929
|
+
|
|
2907
2930
|
string context = 5;
|
|
2908
2931
|
|
|
2909
2932
|
string challenge = 6;
|
|
@@ -3123,6 +3146,9 @@ message GetIdentityResponse {
|
|
|
3123
3146
|
message GetOneTimeTokenRequest {
|
|
3124
3147
|
// Optional token TTL, in seconds. Default is 10 seconds.
|
|
3125
3148
|
int32 ttl = 1;
|
|
3149
|
+
|
|
3150
|
+
// Optional token context.
|
|
3151
|
+
string context = 2;
|
|
3126
3152
|
}
|
|
3127
3153
|
|
|
3128
3154
|
message GetOneTimeTokenResponse {
|
|
@@ -3185,6 +3211,9 @@ message GetResourceResponse {
|
|
|
3185
3211
|
message GetResourcesRequest {
|
|
3186
3212
|
repeated string id = 1;
|
|
3187
3213
|
|
|
3214
|
+
// Wildcards permitted.
|
|
3215
|
+
repeated string alias = 16;
|
|
3216
|
+
|
|
3188
3217
|
// Wildcards permitted.
|
|
3189
3218
|
repeated string name = 2;
|
|
3190
3219
|
|
|
@@ -3313,6 +3342,8 @@ message GetSessionsRequest {
|
|
|
3313
3342
|
string identity_name = 3;
|
|
3314
3343
|
|
|
3315
3344
|
SessionStatus status = 4;
|
|
3345
|
+
|
|
3346
|
+
string identity_email_id = 5;
|
|
3316
3347
|
}
|
|
3317
3348
|
|
|
3318
3349
|
message GetSessionsResponse {
|
|
@@ -4827,8 +4858,6 @@ class VoltConnection extends EventEmitter__default["default"] {
|
|
|
4827
4858
|
|
|
4828
4859
|
const { publicKeyToPem: publicKeyToPem$1, removeCarriageReturn, signBase64 } = js.voltUtils;
|
|
4829
4860
|
|
|
4830
|
-
const { filter, pick } = lodash__default["default"];
|
|
4831
|
-
|
|
4832
4861
|
const log$2 = debug__default["default"]("volt-client-grpc:volt-client-internal");
|
|
4833
4862
|
|
|
4834
4863
|
function issueAuthenticate(authenticateRequest, ttl) {
|
|
@@ -4842,13 +4871,18 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
4842
4871
|
return Promise.reject(new Error(authenticateResponse.status.message));
|
|
4843
4872
|
} else {
|
|
4844
4873
|
switch (authenticateResponse.decision) {
|
|
4845
|
-
case "POLICY_DECISION_PERMIT":
|
|
4874
|
+
case "POLICY_DECISION_PERMIT":
|
|
4875
|
+
case "POLICY_DECISION_PROMPT":
|
|
4876
|
+
case "POLICY_DECISION_PENDING": {
|
|
4846
4877
|
//
|
|
4847
|
-
// The request status is
|
|
4878
|
+
// The request status is not deny => cache the bind response info.
|
|
4848
4879
|
//
|
|
4880
|
+
log$2("decision is %s", authenticateResponse.decision);
|
|
4849
4881
|
|
|
4850
4882
|
// This is the certificate assigned to us by the volt.
|
|
4851
|
-
|
|
4883
|
+
if (authenticateResponse.cert) {
|
|
4884
|
+
this._credential.cache.cert = authenticateResponse.cert;
|
|
4885
|
+
}
|
|
4852
4886
|
|
|
4853
4887
|
// This is the signing CA used by the volt.
|
|
4854
4888
|
this._credential.cache.ca = authenticateResponse.chain;
|
|
@@ -4862,12 +4896,20 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
4862
4896
|
authenticateResponse.identity_did;
|
|
4863
4897
|
}
|
|
4864
4898
|
|
|
4899
|
+
// Maintain a unique list of verifiable credentials.
|
|
4900
|
+
const uniqueVCs = new Map();
|
|
4865
4901
|
this._credential.cache.vc = this._credential.cache.vc || [];
|
|
4902
|
+
for (const existingVC of this._credential.cache.vc) {
|
|
4903
|
+
uniqueVCs.set(existingVC.id, existingVC);
|
|
4904
|
+
}
|
|
4905
|
+
|
|
4866
4906
|
for (const credential of authenticateResponse.credential) {
|
|
4867
4907
|
const vc = JSON.parse(credential);
|
|
4868
|
-
|
|
4908
|
+
uniqueVCs.set(vc.id, vc);
|
|
4869
4909
|
}
|
|
4870
4910
|
|
|
4911
|
+
this._credential.cache.vc = Array.from(uniqueVCs.values());
|
|
4912
|
+
|
|
4871
4913
|
this._credential.saveCache();
|
|
4872
4914
|
break;
|
|
4873
4915
|
}
|
|
@@ -4875,25 +4917,6 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
4875
4917
|
log$2(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
|
|
4876
4918
|
break;
|
|
4877
4919
|
}
|
|
4878
|
-
case "POLICY_DECISION_PROMPT":
|
|
4879
|
-
case "POLICY_DECISION_PENDING": {
|
|
4880
|
-
log$2("access pending approval - waiting...");
|
|
4881
|
-
|
|
4882
|
-
// This is the signing CA used by the volt.
|
|
4883
|
-
this._credential.cache.ca = authenticateResponse.chain;
|
|
4884
|
-
|
|
4885
|
-
// The session id is assigned by the volt.
|
|
4886
|
-
this._credential.cache.session_id = authenticateResponse.session_id;
|
|
4887
|
-
|
|
4888
|
-
if (!this._credential.cache.identity_did) {
|
|
4889
|
-
// Identity DID has been assigned by the volt.
|
|
4890
|
-
this._credential.cache.identity_did =
|
|
4891
|
-
authenticateResponse.identity_did;
|
|
4892
|
-
}
|
|
4893
|
-
|
|
4894
|
-
this._credential.saveCache();
|
|
4895
|
-
break;
|
|
4896
|
-
}
|
|
4897
4920
|
default:
|
|
4898
4921
|
log$2(
|
|
4899
4922
|
"ignoring unknown bind descision %s",
|
|
@@ -4907,16 +4930,12 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
4907
4930
|
});
|
|
4908
4931
|
}
|
|
4909
4932
|
|
|
4910
|
-
function
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
return matches;
|
|
4917
|
-
}
|
|
4918
|
-
|
|
4919
|
-
async function authenticateInternal(noDID = false, ownDID = false, exchangeToken = "") {
|
|
4933
|
+
async function authenticateInternal(
|
|
4934
|
+
noDID = false,
|
|
4935
|
+
ownDID = false,
|
|
4936
|
+
exchangeToken = "",
|
|
4937
|
+
waitForPermit = true
|
|
4938
|
+
) {
|
|
4920
4939
|
try {
|
|
4921
4940
|
if (!this._credential.cache.ca) {
|
|
4922
4941
|
this._credential.cache.ca = this._voltConfig.ca_pem;
|
|
@@ -4947,6 +4966,27 @@ async function authenticateInternal(noDID = false, ownDID = false, exchangeToken
|
|
|
4947
4966
|
host: this._credential.cache.bindIp
|
|
4948
4967
|
};
|
|
4949
4968
|
|
|
4969
|
+
if (this._credential.cache.vc?.length > 0) {
|
|
4970
|
+
const presentation = {
|
|
4971
|
+
credential: []
|
|
4972
|
+
};
|
|
4973
|
+
|
|
4974
|
+
let presentationData = "";
|
|
4975
|
+
for (let credential of this._credential.cache.vc) {
|
|
4976
|
+
const vc = JSON.stringify(credential, null, 2);
|
|
4977
|
+
log$2(vc);
|
|
4978
|
+
presentation.credential.push(vc);
|
|
4979
|
+
presentationData += vc;
|
|
4980
|
+
}
|
|
4981
|
+
|
|
4982
|
+
presentation.signature = signBase64(
|
|
4983
|
+
this._credential.cache.key,
|
|
4984
|
+
presentationData
|
|
4985
|
+
);
|
|
4986
|
+
|
|
4987
|
+
authenticateRequest.verifiable_presentation = [presentation];
|
|
4988
|
+
}
|
|
4989
|
+
|
|
4950
4990
|
if (exchangeToken) {
|
|
4951
4991
|
log$2("setting request exchange token");
|
|
4952
4992
|
authenticateRequest.exchange_token = exchangeToken;
|
|
@@ -4958,9 +4998,7 @@ async function authenticateInternal(noDID = false, ownDID = false, exchangeToken
|
|
|
4958
4998
|
log$2("setting request challenge code");
|
|
4959
4999
|
authenticateRequest.challenge = this._credential.cache.challenge_code;
|
|
4960
5000
|
} else {
|
|
4961
|
-
log$2(
|
|
4962
|
-
"no challenge code in request"
|
|
4963
|
-
);
|
|
5001
|
+
log$2("no challenge code in request");
|
|
4964
5002
|
}
|
|
4965
5003
|
|
|
4966
5004
|
if (noDID) {
|
|
@@ -5038,8 +5076,8 @@ async function authenticateInternal(noDID = false, ownDID = false, exchangeToken
|
|
|
5038
5076
|
);
|
|
5039
5077
|
log$2("authenticate decision: %s", decision);
|
|
5040
5078
|
} while (
|
|
5041
|
-
decision === "
|
|
5042
|
-
decision === "
|
|
5079
|
+
decision === "POLICY_DECISION_PENDING" ||
|
|
5080
|
+
(waitForPermit && decision === "POLICY_DECISION_PROMPT")
|
|
5043
5081
|
);
|
|
5044
5082
|
|
|
5045
5083
|
return decision === "POLICY_DECISION_PERMIT";
|
|
@@ -5154,7 +5192,11 @@ function getVoltAPIClientInternal() {
|
|
|
5154
5192
|
const serviceAddress = this.isRelayed
|
|
5155
5193
|
? this._voltConfig.relay.address
|
|
5156
5194
|
: this._voltConfig.address;
|
|
5157
|
-
log$2(
|
|
5195
|
+
log$2(
|
|
5196
|
+
"creating cached VoltAPI service client on %s for %j",
|
|
5197
|
+
serviceAddress,
|
|
5198
|
+
this._voltConfig
|
|
5199
|
+
);
|
|
5158
5200
|
|
|
5159
5201
|
const serviceDescriptors = getBuiltInServiceDescriptors();
|
|
5160
5202
|
|
|
@@ -5163,9 +5205,7 @@ function getVoltAPIClientInternal() {
|
|
|
5163
5205
|
serviceDescriptors,
|
|
5164
5206
|
serviceAddress,
|
|
5165
5207
|
this._credential,
|
|
5166
|
-
this.isRelayed
|
|
5167
|
-
this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
|
|
5168
|
-
this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
|
|
5208
|
+
this.isRelayed
|
|
5169
5209
|
);
|
|
5170
5210
|
}
|
|
5171
5211
|
|
|
@@ -5250,211 +5290,6 @@ function streamingCallInternal(methodType, method, request, service) {
|
|
|
5250
5290
|
return call;
|
|
5251
5291
|
}
|
|
5252
5292
|
|
|
5253
|
-
async function fetchVoltConfig(discovery_url) {
|
|
5254
|
-
try {
|
|
5255
|
-
const response = await fetch(discovery_url);
|
|
5256
|
-
|
|
5257
|
-
if (!response.ok) {
|
|
5258
|
-
log$2("fetchVoltConfig - error [%s]", response.status);
|
|
5259
|
-
return null;
|
|
5260
|
-
}
|
|
5261
|
-
|
|
5262
|
-
const fullVoltConfig = await response.json();
|
|
5263
|
-
|
|
5264
|
-
if (!fullVoltConfig || typeof fullVoltConfig !== "object") {
|
|
5265
|
-
log$2("fetchVoltConfig - invalid config resolved from: %s", discovery_url);
|
|
5266
|
-
return null;
|
|
5267
|
-
}
|
|
5268
|
-
|
|
5269
|
-
const voltConfig = pick(
|
|
5270
|
-
fullVoltConfig,
|
|
5271
|
-
"id",
|
|
5272
|
-
"display_name",
|
|
5273
|
-
"public_key",
|
|
5274
|
-
"fingerprint",
|
|
5275
|
-
"address",
|
|
5276
|
-
"http_address",
|
|
5277
|
-
"ca_pem",
|
|
5278
|
-
"relay"
|
|
5279
|
-
);
|
|
5280
|
-
|
|
5281
|
-
log$2("fetchVoltConfig from %s", discovery_url);
|
|
5282
|
-
|
|
5283
|
-
return voltConfig;
|
|
5284
|
-
} catch (err) {
|
|
5285
|
-
log$2("fetchVoltConfig - %s error [%s]", discovery_url, err.message);
|
|
5286
|
-
return null;
|
|
5287
|
-
}
|
|
5288
|
-
}
|
|
5289
|
-
|
|
5290
|
-
async function fetchVoltConfigFromDIDInternal(volt_did, registryUrl) {
|
|
5291
|
-
try {
|
|
5292
|
-
const didResolution = getDIDResolutionURL(volt_did, registryUrl);
|
|
5293
|
-
if (!didResolution) {
|
|
5294
|
-
throw new Error(`Invalid Volt DID: ${volt_did}`);
|
|
5295
|
-
}
|
|
5296
|
-
|
|
5297
|
-
const response = await fetch(didResolution);
|
|
5298
|
-
|
|
5299
|
-
if (!response.ok) {
|
|
5300
|
-
throw new Error(`HTTP error, status: ${response.status}`);
|
|
5301
|
-
}
|
|
5302
|
-
|
|
5303
|
-
const didDocument = await response.json();
|
|
5304
|
-
const voltConfigServices = findDIDDocumentService(
|
|
5305
|
-
didDocument,
|
|
5306
|
-
constants.didServiceType.voltConfig
|
|
5307
|
-
);
|
|
5308
|
-
if (voltConfigServices.length !== 1) {
|
|
5309
|
-
log$2(
|
|
5310
|
-
"Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
|
|
5311
|
-
volt_did,
|
|
5312
|
-
voltConfigServices.length
|
|
5313
|
-
);
|
|
5314
|
-
}
|
|
5315
|
-
|
|
5316
|
-
log$2("resolved DID from %s", didResolution);
|
|
5317
|
-
|
|
5318
|
-
const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
|
|
5319
|
-
const voltConfig = await fetchVoltConfig.call(this, serviceEndpoint);
|
|
5320
|
-
if (!voltConfig) {
|
|
5321
|
-
throw new Error(`failed to resolve Volt config from ${serviceEndpoint}`);
|
|
5322
|
-
}
|
|
5323
|
-
|
|
5324
|
-
return voltConfig;
|
|
5325
|
-
} catch (err) {
|
|
5326
|
-
throw new Error(
|
|
5327
|
-
`Failure fetching Volt config from DID document ${volt_did}: ${err.message}`
|
|
5328
|
-
);
|
|
5329
|
-
}
|
|
5330
|
-
}
|
|
5331
|
-
|
|
5332
|
-
async function fetchVoltConfigFromDID(volt_did, registryListIn) {
|
|
5333
|
-
let registryList;
|
|
5334
|
-
if (!registryListIn) {
|
|
5335
|
-
// If no registry list is provided, use the default registry list.
|
|
5336
|
-
registryList = constants.defaultRegistryList;
|
|
5337
|
-
} else {
|
|
5338
|
-
// If a registry list is provided, merge it with the default registry list.
|
|
5339
|
-
registryList = Array.from(
|
|
5340
|
-
new Set(registryListIn.concat(constants.defaultRegistryList))
|
|
5341
|
-
);
|
|
5342
|
-
}
|
|
5343
|
-
|
|
5344
|
-
let voltConfig;
|
|
5345
|
-
for (let registryUrl of registryList) {
|
|
5346
|
-
try {
|
|
5347
|
-
voltConfig = await fetchVoltConfigFromDIDInternal.call(
|
|
5348
|
-
this,
|
|
5349
|
-
volt_did,
|
|
5350
|
-
registryUrl
|
|
5351
|
-
);
|
|
5352
|
-
|
|
5353
|
-
// If we've found a config, break out of the loop.
|
|
5354
|
-
break;
|
|
5355
|
-
} catch (err) {
|
|
5356
|
-
log$2("unable to fetch Volt config from %s: %s", registryUrl, err.message);
|
|
5357
|
-
}
|
|
5358
|
-
}
|
|
5359
|
-
|
|
5360
|
-
if (!voltConfig) {
|
|
5361
|
-
log$2("failed to fetch Volt config for %s from any registry", volt_did);
|
|
5362
|
-
}
|
|
5363
|
-
|
|
5364
|
-
return voltConfig;
|
|
5365
|
-
}
|
|
5366
|
-
|
|
5367
|
-
async function resolveVoltConfig(cfg, didRegistryList) {
|
|
5368
|
-
// Start by resolving the relay if one is specified.
|
|
5369
|
-
let relay = cfg?.relay?.http_address || cfg?.relay;
|
|
5370
|
-
if (relay) {
|
|
5371
|
-
relay = await resolveVoltConfig.call(this, relay, didRegistryList);
|
|
5372
|
-
}
|
|
5373
|
-
|
|
5374
|
-
let voltDID = "";
|
|
5375
|
-
let voltHttpAddress = "";
|
|
5376
|
-
|
|
5377
|
-
if (typeof cfg === "string") {
|
|
5378
|
-
// If the configuration is a string, determine if it is a DID or HTTP address.
|
|
5379
|
-
if (cfg.indexOf("did:") === 0) {
|
|
5380
|
-
voltDID = cfg;
|
|
5381
|
-
} else {
|
|
5382
|
-
voltHttpAddress = cfg;
|
|
5383
|
-
}
|
|
5384
|
-
cfg = {};
|
|
5385
|
-
} else if (typeof cfg === "object") {
|
|
5386
|
-
voltDID = cfg.id;
|
|
5387
|
-
voltHttpAddress = cfg.http_address;
|
|
5388
|
-
} else {
|
|
5389
|
-
throw new Error("configuration is missing the 'volt' object");
|
|
5390
|
-
}
|
|
5391
|
-
|
|
5392
|
-
let resolvedConfig;
|
|
5393
|
-
|
|
5394
|
-
if (relay) {
|
|
5395
|
-
if (!voltDID) {
|
|
5396
|
-
throw new Error("relay specified but no Volt DID provided");
|
|
5397
|
-
}
|
|
5398
|
-
|
|
5399
|
-
// Build the sub-domain URL for the Volt discovery service by inserting the volt DID into the relay URL.
|
|
5400
|
-
const relayURL = relay.http_address;
|
|
5401
|
-
const relayParts = relayURL.split("://");
|
|
5402
|
-
const subdomain = voltDID.substring(constants.voltDIDPrefix.length);
|
|
5403
|
-
voltHttpAddress = `${relayParts[0]}://${subdomain}.${relayParts[1]}`;
|
|
5404
|
-
} else if (voltDID) {
|
|
5405
|
-
resolvedConfig = await fetchVoltConfigFromDID.call(
|
|
5406
|
-
this,
|
|
5407
|
-
voltDID,
|
|
5408
|
-
didRegistryList
|
|
5409
|
-
);
|
|
5410
|
-
}
|
|
5411
|
-
|
|
5412
|
-
if (!resolvedConfig && voltHttpAddress) {
|
|
5413
|
-
try {
|
|
5414
|
-
resolvedConfig = await fetchVoltConfig.call(
|
|
5415
|
-
this,
|
|
5416
|
-
`${voltHttpAddress}/discover`
|
|
5417
|
-
);
|
|
5418
|
-
|
|
5419
|
-
// Update the http address in the resolved config to the one we used to
|
|
5420
|
-
// fetch it, since we may have used the relay rather than local address.
|
|
5421
|
-
resolvedConfig.http_address = voltHttpAddress;
|
|
5422
|
-
} catch (err) {
|
|
5423
|
-
log$2(
|
|
5424
|
-
"failed to resolve Volt configuration %s [%s]",
|
|
5425
|
-
voltHttpAddress,
|
|
5426
|
-
err.message
|
|
5427
|
-
);
|
|
5428
|
-
}
|
|
5429
|
-
}
|
|
5430
|
-
|
|
5431
|
-
if (!resolvedConfig) {
|
|
5432
|
-
if (cfg.address && cfg.ca_pem) {
|
|
5433
|
-
log$2(
|
|
5434
|
-
"unable to resolve %s / %s, using existing configuration",
|
|
5435
|
-
voltDID,
|
|
5436
|
-
voltHttpAddress
|
|
5437
|
-
);
|
|
5438
|
-
} else {
|
|
5439
|
-
throw new Error(
|
|
5440
|
-
`failed to resolve Volt ${voltDID} / ${voltHttpAddress} and no existing configuration`
|
|
5441
|
-
);
|
|
5442
|
-
}
|
|
5443
|
-
} else {
|
|
5444
|
-
log$2("resolved config from: %s", voltDID || voltHttpAddress);
|
|
5445
|
-
log$2(JSON.stringify(resolvedConfig, null, 2));
|
|
5446
|
-
|
|
5447
|
-
cfg = {
|
|
5448
|
-
...cfg,
|
|
5449
|
-
...resolvedConfig
|
|
5450
|
-
};
|
|
5451
|
-
}
|
|
5452
|
-
|
|
5453
|
-
cfg.relay = relay;
|
|
5454
|
-
|
|
5455
|
-
return cfg;
|
|
5456
|
-
}
|
|
5457
|
-
|
|
5458
5293
|
/* eslint-disable no-underscore-dangle */
|
|
5459
5294
|
|
|
5460
5295
|
const {
|
|
@@ -5710,9 +5545,16 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
5710
5545
|
* @param {boolean} [options.noDID] - whether to create a key-based authentication session
|
|
5711
5546
|
* instead of a DID-based authentication session.
|
|
5712
5547
|
* @param {string} [options.exchangeToken] - optional token to exchange in return for an authenticated session.
|
|
5548
|
+
* @param {boolean} [options.waitForAuth] - whether to block and poll on 'prompt' authenticate decisions. Default is true.
|
|
5713
5549
|
*/
|
|
5714
5550
|
async initialise(config, options = {}) {
|
|
5715
|
-
|
|
5551
|
+
log("initialise ", config, options);
|
|
5552
|
+
|
|
5553
|
+
const { ownDID, noDID, didRegistryList, exchangeToken, extraConfig } =
|
|
5554
|
+
options;
|
|
5555
|
+
const waitForAuth =
|
|
5556
|
+
options.waitForAuth === undefined ? true : !!options.waitForAuth;
|
|
5557
|
+
|
|
5716
5558
|
try {
|
|
5717
5559
|
if (!config) {
|
|
5718
5560
|
throw new Error(
|
|
@@ -5759,14 +5601,15 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
5759
5601
|
throw new Error("client_name property required in config");
|
|
5760
5602
|
}
|
|
5761
5603
|
|
|
5762
|
-
this._config.volt = await resolveVoltConfig
|
|
5763
|
-
this,
|
|
5604
|
+
this._config.volt = await voltUtility.resolveVoltConfig(
|
|
5764
5605
|
this._config.volt,
|
|
5765
5606
|
didRegistryList
|
|
5766
5607
|
);
|
|
5767
5608
|
|
|
5768
5609
|
this._voltConfig = this._config.volt;
|
|
5769
5610
|
|
|
5611
|
+
log("config is ", this._voltConfig);
|
|
5612
|
+
|
|
5770
5613
|
if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
|
|
5771
5614
|
throw new Error("'id' property is missing in Volt configuration");
|
|
5772
5615
|
}
|
|
@@ -5798,7 +5641,13 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
5798
5641
|
} else {
|
|
5799
5642
|
// No certificate present in the cache yet => we need to authenticate.
|
|
5800
5643
|
try {
|
|
5801
|
-
isBound = await authenticateInternal.call(
|
|
5644
|
+
isBound = await authenticateInternal.call(
|
|
5645
|
+
this,
|
|
5646
|
+
noDID,
|
|
5647
|
+
ownDID,
|
|
5648
|
+
exchangeToken,
|
|
5649
|
+
waitForAuth
|
|
5650
|
+
);
|
|
5802
5651
|
} catch (bindErr) {
|
|
5803
5652
|
if (bindErr.message === "invalid arguments") {
|
|
5804
5653
|
// This is usually the result of a missing challenge signature or credential.
|
|
@@ -6235,6 +6084,21 @@ class VoltClient extends EventEmitter__default["default"] {
|
|
|
6235
6084
|
return call.start(grpcClient, request);
|
|
6236
6085
|
}
|
|
6237
6086
|
|
|
6087
|
+
/**
|
|
6088
|
+
* TerminalAPI
|
|
6089
|
+
*/
|
|
6090
|
+
CommandStream(request, service) {
|
|
6091
|
+
const grpcClient = this.getVoltAPIClient();
|
|
6092
|
+
|
|
6093
|
+
const commandStreamCall = new GRPCCall(
|
|
6094
|
+
this,
|
|
6095
|
+
"CommandStream",
|
|
6096
|
+
"METHOD_TYPE_BIDI",
|
|
6097
|
+
service
|
|
6098
|
+
);
|
|
6099
|
+
return commandStreamCall.start(grpcClient, request);
|
|
6100
|
+
}
|
|
6101
|
+
|
|
6238
6102
|
/**
|
|
6239
6103
|
* WireAPI
|
|
6240
6104
|
*/
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "0.20.
|
|
6
|
+
"version": "0.20.10",
|
|
7
7
|
"description": "tdx Volt library for nodejs clients",
|
|
8
8
|
"type": "module",
|
|
9
9
|
"exports": {
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
"author": "toby.ealden@gmail.com",
|
|
34
34
|
"license": "ISC",
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@tdxvolt/volt-client-web": "^0.20.
|
|
36
|
+
"@tdxvolt/volt-client-web": "^0.20.10",
|
|
37
|
+
"@tdxvolt/volt-utility": "^0.20.10",
|
|
37
38
|
"debug": "^4.1.1",
|
|
38
39
|
"ip": "^1.1.5",
|
|
39
40
|
"jsonwebtoken": "^8.5.1",
|
|
@@ -42,4 +43,4 @@
|
|
|
42
43
|
"protobufjs": "^7.4.0",
|
|
43
44
|
"uuid": "^8.1.0"
|
|
44
45
|
}
|
|
45
|
-
}
|
|
46
|
+
}
|
package/src/grpc-utils.js
CHANGED
|
@@ -15,12 +15,14 @@ export function createClient(
|
|
|
15
15
|
address,
|
|
16
16
|
credentials,
|
|
17
17
|
noSerialise,
|
|
18
|
-
voltId,
|
|
19
|
-
serviceId
|
|
18
|
+
voltId = "",
|
|
19
|
+
serviceId = ""
|
|
20
20
|
) {
|
|
21
21
|
let grpcCredentials;
|
|
22
22
|
if (credentials) {
|
|
23
|
-
const relaying =
|
|
23
|
+
const relaying =
|
|
24
|
+
!!credentials.config?.volt?.relay &&
|
|
25
|
+
!Array.isArray(credentials.config?.volt?.relay);
|
|
24
26
|
const ca = relaying
|
|
25
27
|
? credentials.config?.volt?.relay?.ca_pem
|
|
26
28
|
: credentials.cache.ca;
|
|
@@ -4,7 +4,6 @@ import debug from "debug";
|
|
|
4
4
|
import ip from "ip";
|
|
5
5
|
import lodash from "lodash";
|
|
6
6
|
import { v4 as generateId } from "uuid";
|
|
7
|
-
import { constants } from "./constants.js";
|
|
8
7
|
import GRPCCall from "./grpc-call.js";
|
|
9
8
|
import { createClient as createGrpcClient } from "./grpc-utils.js";
|
|
10
9
|
import {
|
|
@@ -12,7 +11,6 @@ import {
|
|
|
12
11
|
getServiceDescriptors
|
|
13
12
|
} from "./proto-utils.js";
|
|
14
13
|
import RpcInvocation from "./rpc-invocation.js";
|
|
15
|
-
import * as voltUtils from "./utils.js";
|
|
16
14
|
import VoltConnection from "./volt-connection.js";
|
|
17
15
|
|
|
18
16
|
const { publicKeyToPem, removeCarriageReturn, signBase64 } = voltClientUtils;
|
|
@@ -32,13 +30,18 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
32
30
|
return Promise.reject(new Error(authenticateResponse.status.message));
|
|
33
31
|
} else {
|
|
34
32
|
switch (authenticateResponse.decision) {
|
|
35
|
-
case "POLICY_DECISION_PERMIT":
|
|
33
|
+
case "POLICY_DECISION_PERMIT":
|
|
34
|
+
case "POLICY_DECISION_PROMPT":
|
|
35
|
+
case "POLICY_DECISION_PENDING": {
|
|
36
36
|
//
|
|
37
|
-
// The request status is
|
|
37
|
+
// The request status is not deny => cache the bind response info.
|
|
38
38
|
//
|
|
39
|
+
log("decision is %s", authenticateResponse.decision);
|
|
39
40
|
|
|
40
41
|
// This is the certificate assigned to us by the volt.
|
|
41
|
-
|
|
42
|
+
if (authenticateResponse.cert) {
|
|
43
|
+
this._credential.cache.cert = authenticateResponse.cert;
|
|
44
|
+
}
|
|
42
45
|
|
|
43
46
|
// This is the signing CA used by the volt.
|
|
44
47
|
this._credential.cache.ca = authenticateResponse.chain;
|
|
@@ -52,12 +55,20 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
52
55
|
authenticateResponse.identity_did;
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
// Maintain a unique list of verifiable credentials.
|
|
59
|
+
const uniqueVCs = new Map();
|
|
55
60
|
this._credential.cache.vc = this._credential.cache.vc || [];
|
|
61
|
+
for (const existingVC of this._credential.cache.vc) {
|
|
62
|
+
uniqueVCs.set(existingVC.id, existingVC);
|
|
63
|
+
}
|
|
64
|
+
|
|
56
65
|
for (const credential of authenticateResponse.credential) {
|
|
57
66
|
const vc = JSON.parse(credential);
|
|
58
|
-
|
|
67
|
+
uniqueVCs.set(vc.id, vc);
|
|
59
68
|
}
|
|
60
69
|
|
|
70
|
+
this._credential.cache.vc = Array.from(uniqueVCs.values());
|
|
71
|
+
|
|
61
72
|
this._credential.saveCache();
|
|
62
73
|
break;
|
|
63
74
|
}
|
|
@@ -65,25 +76,6 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
65
76
|
log(">>>>>>>>>>>>>>> access request is DENIED <<<<<<<<<<<<<<<<<<<");
|
|
66
77
|
break;
|
|
67
78
|
}
|
|
68
|
-
case "POLICY_DECISION_PROMPT":
|
|
69
|
-
case "POLICY_DECISION_PENDING": {
|
|
70
|
-
log("access pending approval - waiting...");
|
|
71
|
-
|
|
72
|
-
// This is the signing CA used by the volt.
|
|
73
|
-
this._credential.cache.ca = authenticateResponse.chain;
|
|
74
|
-
|
|
75
|
-
// The session id is assigned by the volt.
|
|
76
|
-
this._credential.cache.session_id = authenticateResponse.session_id;
|
|
77
|
-
|
|
78
|
-
if (!this._credential.cache.identity_did) {
|
|
79
|
-
// Identity DID has been assigned by the volt.
|
|
80
|
-
this._credential.cache.identity_did =
|
|
81
|
-
authenticateResponse.identity_did;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
this._credential.saveCache();
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
79
|
default:
|
|
88
80
|
log(
|
|
89
81
|
"ignoring unknown bind descision %s",
|
|
@@ -97,16 +89,12 @@ function issueAuthenticate(authenticateRequest, ttl) {
|
|
|
97
89
|
});
|
|
98
90
|
}
|
|
99
91
|
|
|
100
|
-
function
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return matches;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export async function authenticateInternal(noDID = false, ownDID = false, exchangeToken = "") {
|
|
92
|
+
export async function authenticateInternal(
|
|
93
|
+
noDID = false,
|
|
94
|
+
ownDID = false,
|
|
95
|
+
exchangeToken = "",
|
|
96
|
+
waitForPermit = true
|
|
97
|
+
) {
|
|
110
98
|
try {
|
|
111
99
|
if (!this._credential.cache.ca) {
|
|
112
100
|
this._credential.cache.ca = this._voltConfig.ca_pem;
|
|
@@ -137,6 +125,27 @@ export async function authenticateInternal(noDID = false, ownDID = false, exchan
|
|
|
137
125
|
host: this._credential.cache.bindIp
|
|
138
126
|
};
|
|
139
127
|
|
|
128
|
+
if (this._credential.cache.vc?.length > 0) {
|
|
129
|
+
const presentation = {
|
|
130
|
+
credential: []
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
let presentationData = "";
|
|
134
|
+
for (let credential of this._credential.cache.vc) {
|
|
135
|
+
const vc = JSON.stringify(credential, null, 2);
|
|
136
|
+
log(vc);
|
|
137
|
+
presentation.credential.push(vc);
|
|
138
|
+
presentationData += vc;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
presentation.signature = signBase64(
|
|
142
|
+
this._credential.cache.key,
|
|
143
|
+
presentationData
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
authenticateRequest.verifiable_presentation = [presentation];
|
|
147
|
+
}
|
|
148
|
+
|
|
140
149
|
if (exchangeToken) {
|
|
141
150
|
log("setting request exchange token");
|
|
142
151
|
authenticateRequest.exchange_token = exchangeToken;
|
|
@@ -148,9 +157,7 @@ export async function authenticateInternal(noDID = false, ownDID = false, exchan
|
|
|
148
157
|
log("setting request challenge code");
|
|
149
158
|
authenticateRequest.challenge = this._credential.cache.challenge_code;
|
|
150
159
|
} else {
|
|
151
|
-
log(
|
|
152
|
-
"no challenge code in request"
|
|
153
|
-
);
|
|
160
|
+
log("no challenge code in request");
|
|
154
161
|
}
|
|
155
162
|
|
|
156
163
|
if (noDID) {
|
|
@@ -228,8 +235,8 @@ export async function authenticateInternal(noDID = false, ownDID = false, exchan
|
|
|
228
235
|
);
|
|
229
236
|
log("authenticate decision: %s", decision);
|
|
230
237
|
} while (
|
|
231
|
-
decision === "
|
|
232
|
-
decision === "
|
|
238
|
+
decision === "POLICY_DECISION_PENDING" ||
|
|
239
|
+
(waitForPermit && decision === "POLICY_DECISION_PROMPT")
|
|
233
240
|
);
|
|
234
241
|
|
|
235
242
|
return decision === "POLICY_DECISION_PERMIT";
|
|
@@ -344,7 +351,11 @@ export function getVoltAPIClientInternal() {
|
|
|
344
351
|
const serviceAddress = this.isRelayed
|
|
345
352
|
? this._voltConfig.relay.address
|
|
346
353
|
: this._voltConfig.address;
|
|
347
|
-
log(
|
|
354
|
+
log(
|
|
355
|
+
"creating cached VoltAPI service client on %s for %j",
|
|
356
|
+
serviceAddress,
|
|
357
|
+
this._voltConfig
|
|
358
|
+
);
|
|
348
359
|
|
|
349
360
|
const serviceDescriptors = getBuiltInServiceDescriptors();
|
|
350
361
|
|
|
@@ -353,9 +364,7 @@ export function getVoltAPIClientInternal() {
|
|
|
353
364
|
serviceDescriptors,
|
|
354
365
|
serviceAddress,
|
|
355
366
|
this._credential,
|
|
356
|
-
this.isRelayed
|
|
357
|
-
this._voltConfig?.relay?.cloud ? this._voltConfig.id : "",
|
|
358
|
-
this._voltConfig?.relay?.cloud ? this._voltConfig.id : ""
|
|
367
|
+
this.isRelayed
|
|
359
368
|
);
|
|
360
369
|
}
|
|
361
370
|
|
|
@@ -439,208 +448,3 @@ export function streamingCallInternal(methodType, method, request, service) {
|
|
|
439
448
|
|
|
440
449
|
return call;
|
|
441
450
|
}
|
|
442
|
-
|
|
443
|
-
export async function fetchVoltConfig(discovery_url) {
|
|
444
|
-
try {
|
|
445
|
-
const response = await fetch(discovery_url);
|
|
446
|
-
|
|
447
|
-
if (!response.ok) {
|
|
448
|
-
log("fetchVoltConfig - error [%s]", response.status);
|
|
449
|
-
return null;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
const fullVoltConfig = await response.json();
|
|
453
|
-
|
|
454
|
-
if (!fullVoltConfig || typeof fullVoltConfig !== "object") {
|
|
455
|
-
log("fetchVoltConfig - invalid config resolved from: %s", discovery_url);
|
|
456
|
-
return null;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
const voltConfig = pick(
|
|
460
|
-
fullVoltConfig,
|
|
461
|
-
"id",
|
|
462
|
-
"display_name",
|
|
463
|
-
"public_key",
|
|
464
|
-
"fingerprint",
|
|
465
|
-
"address",
|
|
466
|
-
"http_address",
|
|
467
|
-
"ca_pem",
|
|
468
|
-
"relay"
|
|
469
|
-
);
|
|
470
|
-
|
|
471
|
-
log("fetchVoltConfig from %s", discovery_url);
|
|
472
|
-
|
|
473
|
-
return voltConfig;
|
|
474
|
-
} catch (err) {
|
|
475
|
-
log("fetchVoltConfig - %s error [%s]", discovery_url, err.message);
|
|
476
|
-
return null;
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
async function fetchVoltConfigFromDIDInternal(volt_did, registryUrl) {
|
|
481
|
-
try {
|
|
482
|
-
const didResolution = voltUtils.getDIDResolutionURL(volt_did, registryUrl);
|
|
483
|
-
if (!didResolution) {
|
|
484
|
-
throw new Error(`Invalid Volt DID: ${volt_did}`);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
const response = await fetch(didResolution);
|
|
488
|
-
|
|
489
|
-
if (!response.ok) {
|
|
490
|
-
throw new Error(`HTTP error, status: ${response.status}`);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
const didDocument = await response.json();
|
|
494
|
-
const voltConfigServices = findDIDDocumentService(
|
|
495
|
-
didDocument,
|
|
496
|
-
constants.didServiceType.voltConfig
|
|
497
|
-
);
|
|
498
|
-
if (voltConfigServices.length !== 1) {
|
|
499
|
-
log(
|
|
500
|
-
"Unexpected number of Volt configuration endpoints found in DID document for %s, services found: %d",
|
|
501
|
-
volt_did,
|
|
502
|
-
voltConfigServices.length
|
|
503
|
-
);
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
log("resolved DID from %s", didResolution);
|
|
507
|
-
|
|
508
|
-
const serviceEndpoint = voltConfigServices[0].serviceEndpoint;
|
|
509
|
-
const voltConfig = await fetchVoltConfig.call(this, serviceEndpoint);
|
|
510
|
-
if (!voltConfig) {
|
|
511
|
-
throw new Error(`failed to resolve Volt config from ${serviceEndpoint}`);
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
return voltConfig;
|
|
515
|
-
} catch (err) {
|
|
516
|
-
throw new Error(
|
|
517
|
-
`Failure fetching Volt config from DID document ${volt_did}: ${err.message}`
|
|
518
|
-
);
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
export async function fetchVoltConfigFromDID(volt_did, registryListIn) {
|
|
523
|
-
let registryList;
|
|
524
|
-
if (!registryListIn) {
|
|
525
|
-
// If no registry list is provided, use the default registry list.
|
|
526
|
-
registryList = constants.defaultRegistryList;
|
|
527
|
-
} else {
|
|
528
|
-
// If a registry list is provided, merge it with the default registry list.
|
|
529
|
-
registryList = Array.from(
|
|
530
|
-
new Set(registryListIn.concat(constants.defaultRegistryList))
|
|
531
|
-
);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
let voltConfig;
|
|
535
|
-
for (let registryUrl of registryList) {
|
|
536
|
-
try {
|
|
537
|
-
voltConfig = await fetchVoltConfigFromDIDInternal.call(
|
|
538
|
-
this,
|
|
539
|
-
volt_did,
|
|
540
|
-
registryUrl
|
|
541
|
-
);
|
|
542
|
-
|
|
543
|
-
// If we've found a config, break out of the loop.
|
|
544
|
-
break;
|
|
545
|
-
} catch (err) {
|
|
546
|
-
log("unable to fetch Volt config from %s: %s", registryUrl, err.message);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
if (!voltConfig) {
|
|
551
|
-
log("failed to fetch Volt config for %s from any registry", volt_did);
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
return voltConfig;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
export async function resolveVoltConfig(cfg, didRegistryList) {
|
|
558
|
-
// Start by resolving the relay if one is specified.
|
|
559
|
-
let relay = cfg?.relay?.http_address || cfg?.relay;
|
|
560
|
-
if (relay) {
|
|
561
|
-
relay = await resolveVoltConfig.call(this, relay, didRegistryList);
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
let voltDID = "";
|
|
565
|
-
let voltHttpAddress = "";
|
|
566
|
-
|
|
567
|
-
if (typeof cfg === "string") {
|
|
568
|
-
// If the configuration is a string, determine if it is a DID or HTTP address.
|
|
569
|
-
if (cfg.indexOf("did:") === 0) {
|
|
570
|
-
voltDID = cfg;
|
|
571
|
-
} else {
|
|
572
|
-
voltHttpAddress = cfg;
|
|
573
|
-
}
|
|
574
|
-
cfg = {};
|
|
575
|
-
} else if (typeof cfg === "object") {
|
|
576
|
-
voltDID = cfg.id;
|
|
577
|
-
voltHttpAddress = cfg.http_address;
|
|
578
|
-
} else {
|
|
579
|
-
throw new Error("configuration is missing the 'volt' object");
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
let resolvedConfig;
|
|
583
|
-
|
|
584
|
-
if (relay) {
|
|
585
|
-
if (!voltDID) {
|
|
586
|
-
throw new Error("relay specified but no Volt DID provided");
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
// Build the sub-domain URL for the Volt discovery service by inserting the volt DID into the relay URL.
|
|
590
|
-
const relayURL = relay.http_address;
|
|
591
|
-
const relayParts = relayURL.split("://");
|
|
592
|
-
const subdomain = voltDID.substring(constants.voltDIDPrefix.length);
|
|
593
|
-
voltHttpAddress = `${relayParts[0]}://${subdomain}.${relayParts[1]}`;
|
|
594
|
-
} else if (voltDID) {
|
|
595
|
-
resolvedConfig = await fetchVoltConfigFromDID.call(
|
|
596
|
-
this,
|
|
597
|
-
voltDID,
|
|
598
|
-
didRegistryList
|
|
599
|
-
);
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
if (!resolvedConfig && voltHttpAddress) {
|
|
603
|
-
try {
|
|
604
|
-
resolvedConfig = await fetchVoltConfig.call(
|
|
605
|
-
this,
|
|
606
|
-
`${voltHttpAddress}/discover`
|
|
607
|
-
);
|
|
608
|
-
|
|
609
|
-
// Update the http address in the resolved config to the one we used to
|
|
610
|
-
// fetch it, since we may have used the relay rather than local address.
|
|
611
|
-
resolvedConfig.http_address = voltHttpAddress;
|
|
612
|
-
} catch (err) {
|
|
613
|
-
log(
|
|
614
|
-
"failed to resolve Volt configuration %s [%s]",
|
|
615
|
-
voltHttpAddress,
|
|
616
|
-
err.message
|
|
617
|
-
);
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
if (!resolvedConfig) {
|
|
622
|
-
if (cfg.address && cfg.ca_pem) {
|
|
623
|
-
log(
|
|
624
|
-
"unable to resolve %s / %s, using existing configuration",
|
|
625
|
-
voltDID,
|
|
626
|
-
voltHttpAddress
|
|
627
|
-
);
|
|
628
|
-
} else {
|
|
629
|
-
throw new Error(
|
|
630
|
-
`failed to resolve Volt ${voltDID} / ${voltHttpAddress} and no existing configuration`
|
|
631
|
-
);
|
|
632
|
-
}
|
|
633
|
-
} else {
|
|
634
|
-
log("resolved config from: %s", voltDID || voltHttpAddress);
|
|
635
|
-
log(JSON.stringify(resolvedConfig, null, 2));
|
|
636
|
-
|
|
637
|
-
cfg = {
|
|
638
|
-
...cfg,
|
|
639
|
-
...resolvedConfig
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
cfg.relay = relay;
|
|
644
|
-
|
|
645
|
-
return cfg;
|
|
646
|
-
}
|
package/src/volt-client.js
CHANGED
|
@@ -7,11 +7,11 @@ import {
|
|
|
7
7
|
authenticateInternal,
|
|
8
8
|
connectInternal,
|
|
9
9
|
getVoltAPIClientInternal,
|
|
10
|
-
resolveVoltConfig,
|
|
11
10
|
streamingCallInternal,
|
|
12
11
|
unaryCallInternal
|
|
13
12
|
} from "./volt-client-internal.js";
|
|
14
13
|
import { VoltCredential } from "./volt-credential.js";
|
|
14
|
+
import { resolveVoltConfig } from "@tdxvolt/volt-utility";
|
|
15
15
|
|
|
16
16
|
const { readFile } = fs.promises;
|
|
17
17
|
const log = debug("volt-client-grpc:volt-client");
|
|
@@ -85,9 +85,16 @@ export class VoltClient extends EventEmitter {
|
|
|
85
85
|
* @param {boolean} [options.noDID] - whether to create a key-based authentication session
|
|
86
86
|
* instead of a DID-based authentication session.
|
|
87
87
|
* @param {string} [options.exchangeToken] - optional token to exchange in return for an authenticated session.
|
|
88
|
+
* @param {boolean} [options.waitForAuth] - whether to block and poll on 'prompt' authenticate decisions. Default is true.
|
|
88
89
|
*/
|
|
89
90
|
async initialise(config, options = {}) {
|
|
90
|
-
|
|
91
|
+
log("initialise ", config, options);
|
|
92
|
+
|
|
93
|
+
const { ownDID, noDID, didRegistryList, exchangeToken, extraConfig } =
|
|
94
|
+
options;
|
|
95
|
+
const waitForAuth =
|
|
96
|
+
options.waitForAuth === undefined ? true : !!options.waitForAuth;
|
|
97
|
+
|
|
91
98
|
try {
|
|
92
99
|
if (!config) {
|
|
93
100
|
throw new Error(
|
|
@@ -134,14 +141,15 @@ export class VoltClient extends EventEmitter {
|
|
|
134
141
|
throw new Error("client_name property required in config");
|
|
135
142
|
}
|
|
136
143
|
|
|
137
|
-
this._config.volt = await resolveVoltConfig
|
|
138
|
-
this,
|
|
144
|
+
this._config.volt = await resolveVoltConfig(
|
|
139
145
|
this._config.volt,
|
|
140
146
|
didRegistryList
|
|
141
147
|
);
|
|
142
148
|
|
|
143
149
|
this._voltConfig = this._config.volt;
|
|
144
150
|
|
|
151
|
+
log("config is ", this._voltConfig);
|
|
152
|
+
|
|
145
153
|
if (!this._voltConfig.id || typeof this._voltConfig.id !== "string") {
|
|
146
154
|
throw new Error("'id' property is missing in Volt configuration");
|
|
147
155
|
}
|
|
@@ -173,7 +181,13 @@ export class VoltClient extends EventEmitter {
|
|
|
173
181
|
} else {
|
|
174
182
|
// No certificate present in the cache yet => we need to authenticate.
|
|
175
183
|
try {
|
|
176
|
-
isBound = await authenticateInternal.call(
|
|
184
|
+
isBound = await authenticateInternal.call(
|
|
185
|
+
this,
|
|
186
|
+
noDID,
|
|
187
|
+
ownDID,
|
|
188
|
+
exchangeToken,
|
|
189
|
+
waitForAuth
|
|
190
|
+
);
|
|
177
191
|
} catch (bindErr) {
|
|
178
192
|
if (bindErr.message === "invalid arguments") {
|
|
179
193
|
// This is usually the result of a missing challenge signature or credential.
|
|
@@ -612,6 +626,21 @@ export class VoltClient extends EventEmitter {
|
|
|
612
626
|
return call.start(grpcClient, request);
|
|
613
627
|
}
|
|
614
628
|
|
|
629
|
+
/**
|
|
630
|
+
* TerminalAPI
|
|
631
|
+
*/
|
|
632
|
+
CommandStream(request, service) {
|
|
633
|
+
const grpcClient = this.getVoltAPIClient();
|
|
634
|
+
|
|
635
|
+
const commandStreamCall = new GRPCCall(
|
|
636
|
+
this,
|
|
637
|
+
"CommandStream",
|
|
638
|
+
"METHOD_TYPE_BIDI",
|
|
639
|
+
service
|
|
640
|
+
);
|
|
641
|
+
return commandStreamCall.start(grpcClient, request);
|
|
642
|
+
}
|
|
643
|
+
|
|
615
644
|
/**
|
|
616
645
|
* WireAPI
|
|
617
646
|
*/
|
|
@@ -881,10 +881,13 @@ message DIDRegistryUpdate {
|
|
|
881
881
|
}
|
|
882
882
|
|
|
883
883
|
message VerifiablePresentation {
|
|
884
|
-
//
|
|
884
|
+
// DEPRECATED - use 'credential'
|
|
885
885
|
string credential_json = 1;
|
|
886
886
|
|
|
887
|
-
//
|
|
887
|
+
// Array of credential JSON.
|
|
888
|
+
repeated string credential = 3;
|
|
889
|
+
|
|
890
|
+
// A signature of the credential JSON. The signature should be that of the credential holder over the concatenation of the contents of the \`credential\` array.
|
|
888
891
|
string signature = 2;
|
|
889
892
|
}
|
|
890
893
|
|
|
@@ -1048,7 +1051,7 @@ message ParseCredentialResponse {
|
|
|
1048
1051
|
tdx.volt_api.volt.v1.Status status = 1;
|
|
1049
1052
|
|
|
1050
1053
|
// The parsed credential details.
|
|
1051
|
-
VerifiableCredential verifiable_credential = 2;
|
|
1054
|
+
repeated VerifiableCredential verifiable_credential = 2;
|
|
1052
1055
|
}
|
|
1053
1056
|
|
|
1054
1057
|
message ResolveDIDRequest {
|
|
@@ -1178,6 +1181,7 @@ enum SyncState {
|
|
|
1178
1181
|
SYNC_STATE_SYNCING = 1;
|
|
1179
1182
|
SYNC_STATE_DONE = 2;
|
|
1180
1183
|
SYNC_STATE_UPDATE = 3;
|
|
1184
|
+
SYNC_STATE_AWARENESS = 4;
|
|
1181
1185
|
}
|
|
1182
1186
|
|
|
1183
1187
|
message SyncDocumentStart {
|
|
@@ -1246,7 +1250,8 @@ message GetDocumentJSONResponse {
|
|
|
1246
1250
|
// The current JSON snapshot of the document
|
|
1247
1251
|
string json = 2;
|
|
1248
1252
|
}
|
|
1249
|
-
}
|
|
1253
|
+
}
|
|
1254
|
+
`;
|
|
1250
1255
|
export const terminal_api = `syntax = "proto3";
|
|
1251
1256
|
|
|
1252
1257
|
package tdx.volt_api.volt.v1;
|
|
@@ -1962,6 +1967,9 @@ message SessionCredential {
|
|
|
1962
1967
|
|
|
1963
1968
|
// More type-specific data stored with the credential.
|
|
1964
1969
|
string extra_2 = 16;
|
|
1970
|
+
|
|
1971
|
+
// The verification status of the credential.
|
|
1972
|
+
string status = 17;
|
|
1965
1973
|
}
|
|
1966
1974
|
|
|
1967
1975
|
message Session {
|
|
@@ -1985,7 +1993,6 @@ message Session {
|
|
|
1985
1993
|
|
|
1986
1994
|
SessionStatus status = 10;
|
|
1987
1995
|
}
|
|
1988
|
-
|
|
1989
1996
|
`;
|
|
1990
1997
|
export const volt_api = `syntax = "proto3";
|
|
1991
1998
|
|
|
@@ -1996,7 +2003,7 @@ import "tdx/volt_api/volt/v1/remote.proto";
|
|
|
1996
2003
|
import "tdx/volt_api/volt/v1/ssi.proto";
|
|
1997
2004
|
import "tdx/volt_api/volt/v1/volt.proto";
|
|
1998
2005
|
|
|
1999
|
-
// The top-level volt management service.
|
|
2006
|
+
// The top-level volt management service.
|
|
2000
2007
|
// With the exception of \`Authenticate\`, all RPCs on this service must submit either a valid client certificate signed by the volt CA, or a signed JWT.
|
|
2001
2008
|
// A client certificate can be obtained from the \`Authenticate\` method.
|
|
2002
2009
|
// All methods include a \`tdx.volt_api.volt.v1.Status\` in the response. If the status contains a non-OK error code the client should assume the remainder of the message is invalid, unless otherwise indicated in the response documentation.
|
|
@@ -2187,7 +2194,7 @@ message AuthenticateResponse {
|
|
|
2187
2194
|
// This should be persisted by the client, and submitted with all subsequent API calls as part of the call metadata.
|
|
2188
2195
|
string session_id = 2;
|
|
2189
2196
|
|
|
2190
|
-
// The identity
|
|
2197
|
+
// The 'primary' session identity assigned to this authentication. This will be the DID in DID-based sessions, otherwise it will be the first email-based credential found, if any, otherwise it will be the public key fingerprint.
|
|
2191
2198
|
string identity_did = 3;
|
|
2192
2199
|
|
|
2193
2200
|
// A certificate issued by the volt CA, binding the request public key to the identity.
|
|
@@ -2243,6 +2250,9 @@ message CanAccessResourceResponse {
|
|
|
2243
2250
|
|
|
2244
2251
|
// The policy decision for this access request.
|
|
2245
2252
|
PolicyDecision decision = 6;
|
|
2253
|
+
|
|
2254
|
+
// The session that the access relates to.
|
|
2255
|
+
string session_id = 7;
|
|
2246
2256
|
}
|
|
2247
2257
|
|
|
2248
2258
|
message CheckCompatibilityRequest {
|
|
@@ -2356,9 +2366,19 @@ message ConnectResource {
|
|
|
2356
2366
|
Resource resource = 2;
|
|
2357
2367
|
}
|
|
2358
2368
|
|
|
2369
|
+
enum ConnectAuthRequestType {
|
|
2370
|
+
CONNECT_AUTH_REQUEST_TYPE_UNKNOWN = 0;
|
|
2371
|
+
CONNECT_AUTH_REQUEST_TYPE_NEW_SESSION = 1;
|
|
2372
|
+
CONNECT_AUTH_REQUEST_TYPE_UPDATE_SESSION = 2;
|
|
2373
|
+
CONNECT_AUTH_REQUEST_TYPE_DELETE_SESSION = 3;
|
|
2374
|
+
CONNECT_AUTH_REQUEST_TYPE_SIGN_REQUEST = 4;
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2359
2377
|
message ConnectAuthRequest {
|
|
2360
2378
|
Session session = 1;
|
|
2361
2379
|
|
|
2380
|
+
ConnectAuthRequestType request_type = 2;
|
|
2381
|
+
|
|
2362
2382
|
string context = 5;
|
|
2363
2383
|
|
|
2364
2384
|
string challenge = 6;
|
|
@@ -2578,6 +2598,9 @@ message GetIdentityResponse {
|
|
|
2578
2598
|
message GetOneTimeTokenRequest {
|
|
2579
2599
|
// Optional token TTL, in seconds. Default is 10 seconds.
|
|
2580
2600
|
int32 ttl = 1;
|
|
2601
|
+
|
|
2602
|
+
// Optional token context.
|
|
2603
|
+
string context = 2;
|
|
2581
2604
|
}
|
|
2582
2605
|
|
|
2583
2606
|
message GetOneTimeTokenResponse {
|
|
@@ -2640,6 +2663,9 @@ message GetResourceResponse {
|
|
|
2640
2663
|
message GetResourcesRequest {
|
|
2641
2664
|
repeated string id = 1;
|
|
2642
2665
|
|
|
2666
|
+
// Wildcards permitted.
|
|
2667
|
+
repeated string alias = 16;
|
|
2668
|
+
|
|
2643
2669
|
// Wildcards permitted.
|
|
2644
2670
|
repeated string name = 2;
|
|
2645
2671
|
|
|
@@ -2768,6 +2794,8 @@ message GetSessionsRequest {
|
|
|
2768
2794
|
string identity_name = 3;
|
|
2769
2795
|
|
|
2770
2796
|
SessionStatus status = 4;
|
|
2797
|
+
|
|
2798
|
+
string identity_email_id = 5;
|
|
2771
2799
|
}
|
|
2772
2800
|
|
|
2773
2801
|
message GetSessionsResponse {
|