@mdbase-dev/connect-protocol 0.1.0-beta.22 → 0.1.0-beta.24
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/dist/application-authorization.d.ts +30 -0
- package/dist/application-authorization.js +170 -0
- package/dist/connect-problems.generated.d.ts +22 -0
- package/dist/connect-problems.generated.js +2 -0
- package/dist/index.d.ts +20 -3
- package/dist/index.js +4 -0
- package/dist/operations.d.ts +1 -0
- package/dist/operations.js +1 -0
- package/package.json +1 -1
- package/schemas/connect-problem.v1.schema.json +80 -0
- package/schemas/connect-problems.v1.catalog.json +2 -0
- package/schemas/connect-protocol.v1.schema.json +63 -2
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ApplicationFileRequirement } from "./files.js";
|
|
2
|
+
import type { CollectionOperation } from "./operations.js";
|
|
3
|
+
export type ApplicationAuthorizationFlow = "authorization_code" | "device_code";
|
|
4
|
+
export interface ApplicationAuthorizationBinding {
|
|
5
|
+
protocol_version: 1;
|
|
6
|
+
authorization_id: string;
|
|
7
|
+
application_id: string;
|
|
8
|
+
application_manifest_digest: string;
|
|
9
|
+
application_installation_id: string;
|
|
10
|
+
installation_agreement_public_key: string;
|
|
11
|
+
installation_signing_public_key: string;
|
|
12
|
+
grant_agreement_public_key: string;
|
|
13
|
+
grant_signing_public_key: string;
|
|
14
|
+
flow: ApplicationAuthorizationFlow;
|
|
15
|
+
authorization_nonce: string;
|
|
16
|
+
issued_at: string;
|
|
17
|
+
expires_at: string;
|
|
18
|
+
redirect_uri?: string;
|
|
19
|
+
state?: string;
|
|
20
|
+
code_challenge: string;
|
|
21
|
+
requested_operations: CollectionOperation[];
|
|
22
|
+
requested_files?: ApplicationFileRequirement;
|
|
23
|
+
collection_id?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface ApplicationAuthorizationProof {
|
|
26
|
+
binding: ApplicationAuthorizationBinding;
|
|
27
|
+
signature: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function applicationInstallationIdFromPublicKeys(agreementPublicKey: string, signingPublicKey: string): Promise<string>;
|
|
30
|
+
export declare function authorizationSigningMessage(binding: ApplicationAuthorizationBinding): Uint8Array;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const INSTALLATION_ID_DOMAIN = new TextEncoder().encode("mdbase-connect application installation id v1\0");
|
|
2
|
+
const AUTHORIZATION_PROOF_DOMAIN = new TextEncoder().encode("mdbase-connect application authorization proof\0");
|
|
3
|
+
export async function applicationInstallationIdFromPublicKeys(agreementPublicKey, signingPublicKey) {
|
|
4
|
+
const agreement = publicKey(agreementPublicKey);
|
|
5
|
+
const signing = publicKey(signingPublicKey);
|
|
6
|
+
if (equalBytes(agreement, signing)) {
|
|
7
|
+
throw new Error("Application installation agreement and signing keys must be distinct.");
|
|
8
|
+
}
|
|
9
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", concat([INSTALLATION_ID_DOMAIN, field(agreement), field(signing)])));
|
|
10
|
+
const id = digest.slice(0, 16);
|
|
11
|
+
id[6] = (id[6] & 0x0f) | 0x80;
|
|
12
|
+
id[8] = (id[8] & 0x3f) | 0x80;
|
|
13
|
+
return [
|
|
14
|
+
hex(id.slice(0, 4)),
|
|
15
|
+
hex(id.slice(4, 6)),
|
|
16
|
+
hex(id.slice(6, 8)),
|
|
17
|
+
hex(id.slice(8, 10)),
|
|
18
|
+
hex(id.slice(10, 16))
|
|
19
|
+
].join("-");
|
|
20
|
+
}
|
|
21
|
+
export function authorizationSigningMessage(binding) {
|
|
22
|
+
const keys = [
|
|
23
|
+
publicKey(binding.installation_agreement_public_key),
|
|
24
|
+
publicKey(binding.installation_signing_public_key),
|
|
25
|
+
publicKey(binding.grant_agreement_public_key),
|
|
26
|
+
publicKey(binding.grant_signing_public_key)
|
|
27
|
+
];
|
|
28
|
+
if (keys.some((key, index) => keys.slice(index + 1).some((other) => equalBytes(key, other)))) {
|
|
29
|
+
throw new Error("Application authorization keys must be distinct.");
|
|
30
|
+
}
|
|
31
|
+
const nonce = canonicalBase64(binding.authorization_nonce);
|
|
32
|
+
const challenge = canonicalBase64(binding.code_challenge);
|
|
33
|
+
if (binding.protocol_version !== 1
|
|
34
|
+
|| nonce.byteLength !== 32
|
|
35
|
+
|| !/^[0-9a-f]{64}$/u.test(binding.application_manifest_digest)
|
|
36
|
+
|| !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/u.test(binding.issued_at)
|
|
37
|
+
|| !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?Z$/u.test(binding.expires_at)
|
|
38
|
+
|| challenge.byteLength !== 32
|
|
39
|
+
|| (binding.requested_operations.length === 0 && binding.requested_files === undefined)
|
|
40
|
+
|| new Set(binding.requested_operations).size !== binding.requested_operations.length
|
|
41
|
+
|| binding.requested_operations.some((operation) => !operation || operation.includes("\0"))
|
|
42
|
+
|| (binding.flow === "authorization_code"
|
|
43
|
+
? binding.redirect_uri === undefined || binding.state === undefined
|
|
44
|
+
: binding.flow === "device_code"
|
|
45
|
+
? binding.redirect_uri !== undefined || binding.state !== undefined
|
|
46
|
+
: true)) {
|
|
47
|
+
throw new Error("Application authorization binding is invalid.");
|
|
48
|
+
}
|
|
49
|
+
validateRequestedFiles(binding.requested_files);
|
|
50
|
+
const operationFields = binding.requested_operations.map((operation) => field(new TextEncoder().encode(operation)));
|
|
51
|
+
return concat([
|
|
52
|
+
AUTHORIZATION_PROOF_DOMAIN,
|
|
53
|
+
u32(binding.protocol_version),
|
|
54
|
+
field(uuidBytes(binding.application_id)),
|
|
55
|
+
field(uuidBytes(binding.authorization_id)),
|
|
56
|
+
field(hexBytes(binding.application_manifest_digest)),
|
|
57
|
+
field(uuidBytes(binding.application_installation_id)),
|
|
58
|
+
...keys.map(field),
|
|
59
|
+
field(new TextEncoder().encode(binding.flow)),
|
|
60
|
+
field(nonce),
|
|
61
|
+
field(new TextEncoder().encode(binding.issued_at)),
|
|
62
|
+
field(new TextEncoder().encode(binding.expires_at)),
|
|
63
|
+
optionalString(binding.redirect_uri),
|
|
64
|
+
optionalString(binding.state),
|
|
65
|
+
field(new TextEncoder().encode(binding.code_challenge)),
|
|
66
|
+
u32(operationFields.length),
|
|
67
|
+
...operationFields,
|
|
68
|
+
requestedFiles(binding.requested_files),
|
|
69
|
+
optionalUuid(binding.collection_id)
|
|
70
|
+
]);
|
|
71
|
+
}
|
|
72
|
+
function validateRequestedFiles(files) {
|
|
73
|
+
if (files === undefined)
|
|
74
|
+
return;
|
|
75
|
+
if (files.actions.length === 0
|
|
76
|
+
|| new Set(files.actions).size !== files.actions.length
|
|
77
|
+
|| (files.scope.kind === "selected_folders"
|
|
78
|
+
&& (files.scope.folders.length === 0
|
|
79
|
+
|| new Set(files.scope.folders).size !== files.scope.folders.length
|
|
80
|
+
|| files.scope.folders.some((folder) => !folder || folder.includes("\0"))))) {
|
|
81
|
+
throw new Error("Application authorization file request is invalid.");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function requestedFiles(files) {
|
|
85
|
+
if (files === undefined)
|
|
86
|
+
return new Uint8Array([0]);
|
|
87
|
+
const actions = files.actions.map((action) => field(new TextEncoder().encode(action)));
|
|
88
|
+
return concat([
|
|
89
|
+
new Uint8Array([1]),
|
|
90
|
+
u32(actions.length),
|
|
91
|
+
...actions,
|
|
92
|
+
field(new TextEncoder().encode(files.scope.kind)),
|
|
93
|
+
...(files.scope.kind === "selected_folders"
|
|
94
|
+
? [
|
|
95
|
+
u32(files.scope.folders.length),
|
|
96
|
+
...files.scope.folders.map((folder) => field(new TextEncoder().encode(folder)))
|
|
97
|
+
]
|
|
98
|
+
: [])
|
|
99
|
+
]);
|
|
100
|
+
}
|
|
101
|
+
function optionalString(value) {
|
|
102
|
+
return value === undefined
|
|
103
|
+
? new Uint8Array([0])
|
|
104
|
+
: concat([new Uint8Array([1]), field(new TextEncoder().encode(value))]);
|
|
105
|
+
}
|
|
106
|
+
function optionalUuid(value) {
|
|
107
|
+
return value === undefined
|
|
108
|
+
? new Uint8Array([0])
|
|
109
|
+
: concat([new Uint8Array([1]), field(uuidBytes(value))]);
|
|
110
|
+
}
|
|
111
|
+
function publicKey(value) {
|
|
112
|
+
const key = canonicalBase64(value);
|
|
113
|
+
if (key.byteLength !== 65 || key[0] !== 4) {
|
|
114
|
+
throw new Error("Application authorization public key is invalid.");
|
|
115
|
+
}
|
|
116
|
+
return key;
|
|
117
|
+
}
|
|
118
|
+
function canonicalBase64(value) {
|
|
119
|
+
if (!/^[A-Za-z0-9_-]+$/u.test(value))
|
|
120
|
+
throw new Error("Invalid base64url value.");
|
|
121
|
+
const normalized = value.replaceAll("-", "+").replaceAll("_", "/");
|
|
122
|
+
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
|
|
123
|
+
const decoded = Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
|
|
124
|
+
if (base64Url(decoded) !== value)
|
|
125
|
+
throw new Error("Non-canonical base64url value.");
|
|
126
|
+
return decoded;
|
|
127
|
+
}
|
|
128
|
+
function uuidBytes(value) {
|
|
129
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu.test(value)) {
|
|
130
|
+
throw new Error("Invalid UUID value.");
|
|
131
|
+
}
|
|
132
|
+
const compact = value.replaceAll("-", "");
|
|
133
|
+
return hexBytes(compact);
|
|
134
|
+
}
|
|
135
|
+
function hexBytes(value) {
|
|
136
|
+
if (!/^[0-9a-f]+$/u.test(value) || value.length % 2 !== 0) {
|
|
137
|
+
throw new Error("Invalid hexadecimal value.");
|
|
138
|
+
}
|
|
139
|
+
return Uint8Array.from({ length: value.length / 2 }, (_, index) => Number.parseInt(value.slice(index * 2, index * 2 + 2), 16));
|
|
140
|
+
}
|
|
141
|
+
function field(value) {
|
|
142
|
+
return concat([u32(value.byteLength), value]);
|
|
143
|
+
}
|
|
144
|
+
function u32(value) {
|
|
145
|
+
const output = new Uint8Array(4);
|
|
146
|
+
new DataView(output.buffer).setUint32(0, value, false);
|
|
147
|
+
return output;
|
|
148
|
+
}
|
|
149
|
+
function concat(values) {
|
|
150
|
+
const output = new Uint8Array(values.reduce((length, value) => length + value.byteLength, 0));
|
|
151
|
+
let offset = 0;
|
|
152
|
+
for (const value of values) {
|
|
153
|
+
output.set(value, offset);
|
|
154
|
+
offset += value.byteLength;
|
|
155
|
+
}
|
|
156
|
+
return output;
|
|
157
|
+
}
|
|
158
|
+
function base64Url(value) {
|
|
159
|
+
return btoa(String.fromCharCode(...value))
|
|
160
|
+
.replaceAll("+", "-")
|
|
161
|
+
.replaceAll("/", "_")
|
|
162
|
+
.replace(/=+$/u, "");
|
|
163
|
+
}
|
|
164
|
+
function equalBytes(left, right) {
|
|
165
|
+
return left.byteLength === right.byteLength
|
|
166
|
+
&& left.every((value, index) => value === right[index]);
|
|
167
|
+
}
|
|
168
|
+
function hex(value) {
|
|
169
|
+
return [...value].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
170
|
+
}
|
|
@@ -152,6 +152,10 @@ export declare const CONNECT_PROBLEM_CATALOG: {
|
|
|
152
152
|
readonly category: "conflict";
|
|
153
153
|
readonly recovery: "retry";
|
|
154
154
|
};
|
|
155
|
+
readonly first_contact_handler_required: {
|
|
156
|
+
readonly category: "compatibility";
|
|
157
|
+
readonly recovery: "upgrade_application";
|
|
158
|
+
};
|
|
155
159
|
readonly hosted_provider_unavailable: {
|
|
156
160
|
readonly category: "availability";
|
|
157
161
|
readonly recovery: "retry";
|
|
@@ -176,6 +180,10 @@ export declare const CONNECT_PROBLEM_CATALOG: {
|
|
|
176
180
|
readonly category: "integrity";
|
|
177
181
|
readonly recovery: "contact_support";
|
|
178
182
|
};
|
|
183
|
+
readonly invalid_first_contact: {
|
|
184
|
+
readonly category: "integrity";
|
|
185
|
+
readonly recovery: "contact_support";
|
|
186
|
+
};
|
|
179
187
|
readonly invalid_loopback_url: {
|
|
180
188
|
readonly category: "validation";
|
|
181
189
|
readonly recovery: "fix_request";
|
|
@@ -438,6 +446,7 @@ export interface ConnectProblemDetailsByCode {
|
|
|
438
446
|
"file_not_found": undefined;
|
|
439
447
|
"file_source_mismatch": undefined;
|
|
440
448
|
"file_upload_incomplete": undefined;
|
|
449
|
+
"first_contact_handler_required": undefined;
|
|
441
450
|
"hosted_provider_unavailable": undefined;
|
|
442
451
|
"insufficient_access": {
|
|
443
452
|
"required_operations": Array<string>;
|
|
@@ -448,6 +457,7 @@ export interface ConnectProblemDetailsByCode {
|
|
|
448
457
|
"invalid_callback": undefined;
|
|
449
458
|
"invalid_device_authorization_response": undefined;
|
|
450
459
|
"invalid_encrypted_response": undefined;
|
|
460
|
+
"invalid_first_contact": undefined;
|
|
451
461
|
"invalid_loopback_url": undefined;
|
|
452
462
|
"invalid_mutation_id": undefined;
|
|
453
463
|
"invalid_operation_response": undefined;
|
|
@@ -730,6 +740,12 @@ export interface ConnectProblemByCode {
|
|
|
730
740
|
recovery: "retry";
|
|
731
741
|
details?: never;
|
|
732
742
|
};
|
|
743
|
+
"first_contact_handler_required": ConnectProblemBase & {
|
|
744
|
+
code: "first_contact_handler_required";
|
|
745
|
+
category: "compatibility";
|
|
746
|
+
recovery: "upgrade_application";
|
|
747
|
+
details?: never;
|
|
748
|
+
};
|
|
733
749
|
"hosted_provider_unavailable": ConnectProblemBase & {
|
|
734
750
|
code: "hosted_provider_unavailable";
|
|
735
751
|
category: "availability";
|
|
@@ -766,6 +782,12 @@ export interface ConnectProblemByCode {
|
|
|
766
782
|
recovery: "contact_support";
|
|
767
783
|
details?: never;
|
|
768
784
|
};
|
|
785
|
+
"invalid_first_contact": ConnectProblemBase & {
|
|
786
|
+
code: "invalid_first_contact";
|
|
787
|
+
category: "integrity";
|
|
788
|
+
recovery: "contact_support";
|
|
789
|
+
details?: never;
|
|
790
|
+
};
|
|
769
791
|
"invalid_loopback_url": ConnectProblemBase & {
|
|
770
792
|
code: "invalid_loopback_url";
|
|
771
793
|
category: "validation";
|
|
@@ -39,12 +39,14 @@ export const CONNECT_PROBLEM_CATALOG = {
|
|
|
39
39
|
"file_not_found": { category: "conflict", recovery: "refresh" },
|
|
40
40
|
"file_source_mismatch": { category: "conflict", recovery: "refresh" },
|
|
41
41
|
"file_upload_incomplete": { category: "conflict", recovery: "retry" },
|
|
42
|
+
"first_contact_handler_required": { category: "compatibility", recovery: "upgrade_application" },
|
|
42
43
|
"hosted_provider_unavailable": { category: "availability", recovery: "retry" },
|
|
43
44
|
"insufficient_access": { category: "authorization", recovery: "reauthorize" },
|
|
44
45
|
"invalid_application_manifest": { category: "validation", recovery: "fix_request" },
|
|
45
46
|
"invalid_callback": { category: "validation", recovery: "fix_request" },
|
|
46
47
|
"invalid_device_authorization_response": { category: "integrity", recovery: "contact_support" },
|
|
47
48
|
"invalid_encrypted_response": { category: "integrity", recovery: "contact_support" },
|
|
49
|
+
"invalid_first_contact": { category: "integrity", recovery: "contact_support" },
|
|
48
50
|
"invalid_loopback_url": { category: "validation", recovery: "fix_request" },
|
|
49
51
|
"invalid_mutation_id": { category: "validation", recovery: "fix_request" },
|
|
50
52
|
"invalid_operation_response": { category: "integrity", recovery: "contact_support" },
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import type { ConnectProblem } from "./connect-problems.generated.js";
|
|
2
2
|
import type { ApplicationFileRequirement, CollectionFileDescriptor, FileCapability } from "./files.js";
|
|
3
|
+
import type { CollectionOperation } from "./operations.js";
|
|
4
|
+
import type { ApplicationAuthorizationProof } from "./application-authorization.js";
|
|
3
5
|
export * from "./connect-problems.generated.js";
|
|
4
6
|
export * from "./files.js";
|
|
7
|
+
export * from "./operations.js";
|
|
8
|
+
export * from "./application-authorization.js";
|
|
5
9
|
export declare const CONTROL_PROTOCOL_VERSION: 1;
|
|
6
10
|
export declare const ENCRYPTED_RELAY_PROTOCOL_VERSION: 1;
|
|
11
|
+
export declare const FIRST_CONTACT_PROTOCOL_VERSION: 1;
|
|
7
12
|
export declare const LOOPBACK_PROTOCOL_VERSION: 1;
|
|
8
13
|
export declare const DEFAULT_LOOPBACK_PORT: 28485;
|
|
9
14
|
export declare const RELAY_ENCRYPTION_SUITE: "P256-HKDF-SHA256-AES256GCM";
|
|
10
15
|
export declare const SYNC_PROTOCOL_VERSION: 1;
|
|
11
16
|
export declare const CONTRACT_SETUP_CAPABILITY: "contract-setup-v1";
|
|
12
17
|
export declare const FILE_RELAY_CAPABILITY: "file-relay-v1";
|
|
13
|
-
export declare const RELAY_REQUIRED_CAPABILITIES: readonly ["authorization-activation", "encrypted-relay", "policy-ack"];
|
|
14
|
-
export declare const RELAY_CAPABILITIES: readonly ["authorization-activation", "encrypted-relay", "policy-ack", "contract-setup-v1", "file-relay-v1"];
|
|
18
|
+
export declare const RELAY_REQUIRED_CAPABILITIES: readonly ["application-trust-v1", "authorization-activation", "encrypted-relay", "policy-ack"];
|
|
19
|
+
export declare const RELAY_CAPABILITIES: readonly ["application-trust-v1", "authorization-activation", "encrypted-relay", "policy-ack", "contract-setup-v1", "file-relay-v1"];
|
|
15
20
|
export declare const AUTHORITY_PROOF_VERSION: 1;
|
|
16
21
|
export declare const AUTHORITY_PROOF_ALGORITHM: "P256-SHA256";
|
|
17
22
|
export declare const AUTHORITY_PROOF_DOMAIN: "mdbase-authority-request-proof-v1";
|
|
@@ -37,7 +42,6 @@ export declare const CONNECT_SCHEMA_IDS: {
|
|
|
37
42
|
readonly files: "https://mdbase.dev/connect/schemas/files.v1.json";
|
|
38
43
|
readonly sync: "https://mdbase.dev/connect/schemas/sync.v1.json";
|
|
39
44
|
};
|
|
40
|
-
export type CollectionOperation = "describe" | "changes" | "read" | "query" | "list_views" | "execute_view" | "read_view_source" | "create_view_source" | "update_view_source" | "delete_view_source" | "validate" | "create" | "update" | "delete" | "rename" | "read_type" | "create_type" | "update_type" | "install_type_pack" | "list_timers" | "put_timer" | "cancel_timer" | "reconcile_timers" | "sync";
|
|
41
45
|
export interface NotificationCriterion {
|
|
42
46
|
/** Stable, manifest-owned identifier selected by an installation. */
|
|
43
47
|
id: string;
|
|
@@ -275,7 +279,11 @@ export interface GrantPolicy {
|
|
|
275
279
|
created_at: string;
|
|
276
280
|
encryption?: GrantEncryption;
|
|
277
281
|
file_capability?: FileCapability;
|
|
282
|
+
first_contact: FirstContactBinding;
|
|
283
|
+
application_authorization: ApplicationAuthorizationProof;
|
|
278
284
|
}
|
|
285
|
+
/** Presentation-only grant shape used outside a local authorization boundary. */
|
|
286
|
+
export type GrantSummary = Omit<GrantPolicy, "first_contact" | "application_authorization">;
|
|
279
287
|
export interface GrantEncryption {
|
|
280
288
|
protocol_version: 1;
|
|
281
289
|
suite: typeof RELAY_ENCRYPTION_SUITE;
|
|
@@ -286,6 +294,15 @@ export interface GrantEncryption {
|
|
|
286
294
|
application_agreement_public_key: string;
|
|
287
295
|
connector_agreement_public_key: string;
|
|
288
296
|
}
|
|
297
|
+
export interface FirstContactBinding {
|
|
298
|
+
protocol_version: typeof FIRST_CONTACT_PROTOCOL_VERSION;
|
|
299
|
+
application_id: string;
|
|
300
|
+
application_installation_id: string;
|
|
301
|
+
application_agreement_public_key: string;
|
|
302
|
+
application_signing_public_key: string;
|
|
303
|
+
connector_id: string;
|
|
304
|
+
connector_agreement_public_key: string;
|
|
305
|
+
}
|
|
289
306
|
export interface EncryptedRelayEnvelope {
|
|
290
307
|
protocol_version: 1;
|
|
291
308
|
suite: typeof RELAY_ENCRYPTION_SUITE;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export * from "./connect-problems.generated.js";
|
|
2
2
|
export * from "./files.js";
|
|
3
|
+
export * from "./operations.js";
|
|
4
|
+
export * from "./application-authorization.js";
|
|
3
5
|
export const CONTROL_PROTOCOL_VERSION = 1;
|
|
4
6
|
export const ENCRYPTED_RELAY_PROTOCOL_VERSION = 1;
|
|
7
|
+
export const FIRST_CONTACT_PROTOCOL_VERSION = 1;
|
|
5
8
|
export const LOOPBACK_PROTOCOL_VERSION = 1;
|
|
6
9
|
export const DEFAULT_LOOPBACK_PORT = 28_485;
|
|
7
10
|
export const RELAY_ENCRYPTION_SUITE = "P256-HKDF-SHA256-AES256GCM";
|
|
@@ -9,6 +12,7 @@ export const SYNC_PROTOCOL_VERSION = 1;
|
|
|
9
12
|
export const CONTRACT_SETUP_CAPABILITY = "contract-setup-v1";
|
|
10
13
|
export const FILE_RELAY_CAPABILITY = "file-relay-v1";
|
|
11
14
|
export const RELAY_REQUIRED_CAPABILITIES = [
|
|
15
|
+
"application-trust-v1",
|
|
12
16
|
"authorization-activation",
|
|
13
17
|
"encrypted-relay",
|
|
14
18
|
"policy-ack"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type CollectionOperation = "describe" | "changes" | "read" | "query" | "list_views" | "execute_view" | "read_view_source" | "create_view_source" | "update_view_source" | "delete_view_source" | "validate" | "create" | "update" | "delete" | "rename" | "read_type" | "create_type" | "update_type" | "install_type_pack" | "list_timers" | "put_timer" | "cancel_timer" | "reconcile_timers" | "sync";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1666,6 +1666,46 @@
|
|
|
1666
1666
|
}
|
|
1667
1667
|
}
|
|
1668
1668
|
},
|
|
1669
|
+
{
|
|
1670
|
+
"type": "object",
|
|
1671
|
+
"additionalProperties": false,
|
|
1672
|
+
"required": [
|
|
1673
|
+
"problem_version",
|
|
1674
|
+
"code",
|
|
1675
|
+
"category",
|
|
1676
|
+
"recovery",
|
|
1677
|
+
"message"
|
|
1678
|
+
],
|
|
1679
|
+
"properties": {
|
|
1680
|
+
"problem_version": {
|
|
1681
|
+
"const": 1
|
|
1682
|
+
},
|
|
1683
|
+
"code": {
|
|
1684
|
+
"const": "first_contact_handler_required"
|
|
1685
|
+
},
|
|
1686
|
+
"category": {
|
|
1687
|
+
"const": "compatibility"
|
|
1688
|
+
},
|
|
1689
|
+
"recovery": {
|
|
1690
|
+
"const": "upgrade_application"
|
|
1691
|
+
},
|
|
1692
|
+
"message": {
|
|
1693
|
+
"type": "string",
|
|
1694
|
+
"minLength": 1
|
|
1695
|
+
},
|
|
1696
|
+
"operation_outcome": {
|
|
1697
|
+
"enum": [
|
|
1698
|
+
"not_sent",
|
|
1699
|
+
"rejected",
|
|
1700
|
+
"unknown"
|
|
1701
|
+
]
|
|
1702
|
+
},
|
|
1703
|
+
"trace_id": {
|
|
1704
|
+
"type": "string",
|
|
1705
|
+
"minLength": 1
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
},
|
|
1669
1709
|
{
|
|
1670
1710
|
"type": "object",
|
|
1671
1711
|
"additionalProperties": false,
|
|
@@ -1936,6 +1976,46 @@
|
|
|
1936
1976
|
}
|
|
1937
1977
|
}
|
|
1938
1978
|
},
|
|
1979
|
+
{
|
|
1980
|
+
"type": "object",
|
|
1981
|
+
"additionalProperties": false,
|
|
1982
|
+
"required": [
|
|
1983
|
+
"problem_version",
|
|
1984
|
+
"code",
|
|
1985
|
+
"category",
|
|
1986
|
+
"recovery",
|
|
1987
|
+
"message"
|
|
1988
|
+
],
|
|
1989
|
+
"properties": {
|
|
1990
|
+
"problem_version": {
|
|
1991
|
+
"const": 1
|
|
1992
|
+
},
|
|
1993
|
+
"code": {
|
|
1994
|
+
"const": "invalid_first_contact"
|
|
1995
|
+
},
|
|
1996
|
+
"category": {
|
|
1997
|
+
"const": "integrity"
|
|
1998
|
+
},
|
|
1999
|
+
"recovery": {
|
|
2000
|
+
"const": "contact_support"
|
|
2001
|
+
},
|
|
2002
|
+
"message": {
|
|
2003
|
+
"type": "string",
|
|
2004
|
+
"minLength": 1
|
|
2005
|
+
},
|
|
2006
|
+
"operation_outcome": {
|
|
2007
|
+
"enum": [
|
|
2008
|
+
"not_sent",
|
|
2009
|
+
"rejected",
|
|
2010
|
+
"unknown"
|
|
2011
|
+
]
|
|
2012
|
+
},
|
|
2013
|
+
"trace_id": {
|
|
2014
|
+
"type": "string",
|
|
2015
|
+
"minLength": 1
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
},
|
|
1939
2019
|
{
|
|
1940
2020
|
"type": "object",
|
|
1941
2021
|
"additionalProperties": false,
|
|
@@ -151,6 +151,7 @@
|
|
|
151
151
|
"file_not_found": { "category": "conflict", "recovery": "refresh" },
|
|
152
152
|
"file_source_mismatch": { "category": "conflict", "recovery": "refresh" },
|
|
153
153
|
"file_upload_incomplete": { "category": "conflict", "recovery": "retry" },
|
|
154
|
+
"first_contact_handler_required": { "category": "compatibility", "recovery": "upgrade_application" },
|
|
154
155
|
"hosted_provider_unavailable": { "category": "availability", "recovery": "retry" },
|
|
155
156
|
"insufficient_access": {
|
|
156
157
|
"category": "authorization",
|
|
@@ -168,6 +169,7 @@
|
|
|
168
169
|
"invalid_callback": { "category": "validation", "recovery": "fix_request" },
|
|
169
170
|
"invalid_device_authorization_response": { "category": "integrity", "recovery": "contact_support" },
|
|
170
171
|
"invalid_encrypted_response": { "category": "integrity", "recovery": "contact_support" },
|
|
172
|
+
"invalid_first_contact": { "category": "integrity", "recovery": "contact_support" },
|
|
171
173
|
"invalid_loopback_url": { "category": "validation", "recovery": "fix_request" },
|
|
172
174
|
"invalid_mutation_id": { "category": "validation", "recovery": "fix_request" },
|
|
173
175
|
"invalid_operation_response": { "category": "integrity", "recovery": "contact_support" },
|
|
@@ -202,7 +202,7 @@
|
|
|
202
202
|
"grantPolicy": {
|
|
203
203
|
"type": "object",
|
|
204
204
|
"additionalProperties": false,
|
|
205
|
-
"required": ["id", "application_id", "collection_id", "operations", "scope", "application_name", "application_homepage", "collection_name", "created_at"],
|
|
205
|
+
"required": ["id", "application_id", "collection_id", "operations", "scope", "application_name", "application_distribution", "application_homepage", "application_origin", "collection_name", "created_at", "encryption", "first_contact", "application_authorization"],
|
|
206
206
|
"properties": {
|
|
207
207
|
"id": { "$ref": "#/$defs/uuid" },
|
|
208
208
|
"application_id": { "$ref": "#/$defs/uuid" },
|
|
@@ -230,9 +230,70 @@
|
|
|
230
230
|
"encryption": { "$ref": "#/$defs/grantEncryption" },
|
|
231
231
|
"file_capability": {
|
|
232
232
|
"$ref": "https://mdbase.dev/connect/schemas/files.v1.json#/$defs/fileCapability"
|
|
233
|
-
}
|
|
233
|
+
},
|
|
234
|
+
"first_contact": { "$ref": "#/$defs/firstContactBinding" },
|
|
235
|
+
"application_authorization": { "$ref": "#/$defs/applicationAuthorizationProof" }
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
"firstContactBinding": {
|
|
239
|
+
"type": "object",
|
|
240
|
+
"additionalProperties": false,
|
|
241
|
+
"required": ["protocol_version", "application_id", "application_installation_id", "application_agreement_public_key", "application_signing_public_key", "connector_id", "connector_agreement_public_key"],
|
|
242
|
+
"properties": {
|
|
243
|
+
"protocol_version": { "const": 1 },
|
|
244
|
+
"application_id": { "$ref": "#/$defs/uuid" },
|
|
245
|
+
"application_installation_id": { "$ref": "#/$defs/uuid" },
|
|
246
|
+
"application_agreement_public_key": { "$ref": "#/$defs/p256PublicKey" },
|
|
247
|
+
"application_signing_public_key": { "$ref": "#/$defs/p256PublicKey" },
|
|
248
|
+
"connector_id": { "$ref": "#/$defs/uuid" },
|
|
249
|
+
"connector_agreement_public_key": { "$ref": "#/$defs/p256PublicKey" }
|
|
250
|
+
}
|
|
251
|
+
},
|
|
252
|
+
"applicationAuthorizationProof": {
|
|
253
|
+
"type": "object",
|
|
254
|
+
"additionalProperties": false,
|
|
255
|
+
"required": ["binding", "signature"],
|
|
256
|
+
"properties": {
|
|
257
|
+
"binding": { "$ref": "#/$defs/applicationAuthorizationBinding" },
|
|
258
|
+
"signature": { "type": "string", "pattern": "^[A-Za-z0-9_-]{86}$" }
|
|
234
259
|
}
|
|
235
260
|
},
|
|
261
|
+
"applicationAuthorizationBinding": {
|
|
262
|
+
"type": "object",
|
|
263
|
+
"additionalProperties": false,
|
|
264
|
+
"required": ["protocol_version", "authorization_id", "application_id", "application_manifest_digest", "application_installation_id", "installation_agreement_public_key", "installation_signing_public_key", "grant_agreement_public_key", "grant_signing_public_key", "flow", "authorization_nonce", "issued_at", "expires_at", "code_challenge", "requested_operations"],
|
|
265
|
+
"properties": {
|
|
266
|
+
"protocol_version": { "const": 1 },
|
|
267
|
+
"authorization_id": { "$ref": "#/$defs/uuid" },
|
|
268
|
+
"application_id": { "$ref": "#/$defs/uuid" },
|
|
269
|
+
"application_manifest_digest": { "type": "string", "pattern": "^[0-9a-f]{64}$" },
|
|
270
|
+
"application_installation_id": { "$ref": "#/$defs/uuid" },
|
|
271
|
+
"installation_agreement_public_key": { "$ref": "#/$defs/p256PublicKey" },
|
|
272
|
+
"installation_signing_public_key": { "$ref": "#/$defs/p256PublicKey" },
|
|
273
|
+
"grant_agreement_public_key": { "$ref": "#/$defs/p256PublicKey" },
|
|
274
|
+
"grant_signing_public_key": { "$ref": "#/$defs/p256PublicKey" },
|
|
275
|
+
"flow": { "enum": ["authorization_code", "device_code"] },
|
|
276
|
+
"authorization_nonce": { "type": "string", "pattern": "^[A-Za-z0-9_-]{43}$" },
|
|
277
|
+
"issued_at": { "type": "string", "format": "date-time" },
|
|
278
|
+
"expires_at": { "type": "string", "format": "date-time" },
|
|
279
|
+
"redirect_uri": { "type": "string", "format": "uri" },
|
|
280
|
+
"state": { "type": "string", "maxLength": 500 },
|
|
281
|
+
"code_challenge": { "type": "string", "pattern": "^[A-Za-z0-9_-]{43}$" },
|
|
282
|
+
"requested_operations": {
|
|
283
|
+
"type": "array",
|
|
284
|
+
"uniqueItems": true,
|
|
285
|
+
"items": { "$ref": "#/$defs/operation" }
|
|
286
|
+
},
|
|
287
|
+
"requested_files": {
|
|
288
|
+
"$ref": "https://mdbase.dev/connect/schemas/files.v1.json#/$defs/applicationFileRequirement"
|
|
289
|
+
},
|
|
290
|
+
"collection_id": { "$ref": "#/$defs/uuid" }
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
"p256PublicKey": {
|
|
294
|
+
"type": "string",
|
|
295
|
+
"pattern": "^[A-Za-z0-9_-]{87}$"
|
|
296
|
+
},
|
|
236
297
|
"authorizationCollectionOffer": {
|
|
237
298
|
"type": "object",
|
|
238
299
|
"additionalProperties": false,
|