@mdbase-dev/connect 0.1.0-beta.26 → 0.1.0-beta.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +82 -44
- package/dist/application-identity.d.ts +32 -3
- package/dist/application-identity.js +104 -12
- package/dist/application-session.d.ts +120 -0
- package/dist/application-session.js +281 -0
- package/dist/browser/integrity.json +1 -1
- package/dist/browser/mdbase-connect.min.js +3 -3
- package/dist/browser/mdbase-connect.min.js.sha384 +1 -1
- package/dist/capabilities.d.ts +23 -0
- package/dist/capabilities.js +112 -0
- package/dist/collection-client.d.ts +3 -2
- package/dist/collection-client.js +5 -2
- package/dist/connect-options.d.ts +3 -0
- package/dist/connect.d.ts +8 -0
- package/dist/connect.js +73 -102
- package/dist/connection-types.d.ts +7 -0
- package/dist/connection.d.ts +3 -14
- package/dist/connection.js +8 -2
- package/dist/index.d.ts +5 -4
- package/dist/index.js +3 -3
- package/dist/internal-types.d.ts +0 -1
- package/dist/mdbase-connect.d.ts +6 -2
- package/dist/mdbase-connect.js +10 -3
- package/dist/operation-helpers.js +1 -1
- package/dist/outcomes.d.ts +3 -3
- package/dist/outcomes.js +4 -2
- package/dist/runtime-utils.d.ts +2 -0
- package/dist/runtime-utils.js +23 -0
- package/dist/selection.d.ts +3 -3
- package/dist/session.d.ts +2 -2
- package/package.json +2 -2
- package/dist/first-contact-presentation.d.ts +0 -5
- package/dist/first-contact-presentation.js +0 -51
- package/dist/first-contact.d.ts +0 -14
- package/dist/first-contact.js +0 -145
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
export { decryptRelayResponse, encryptRelayRequest, IndexedDbGrantKeyStore, MemoryGrantKeyStore, RelayCryptoError, type RelayBinding, type GrantKeyRecord, type GrantKeyStore } from "./crypto.js";
|
|
2
2
|
export { FileTransferCryptoError, GrantFileTransferCipher, type FileTransferBinding } from "./file-crypto.js";
|
|
3
|
-
export {
|
|
4
|
-
export { applicationInstallationId, authorizationSigningMessage, signApplicationAuthorization } from "./application-identity.js";
|
|
3
|
+
export { ApplicationIdentityStoreError, IndexedDbApplicationIdentityStore, MemoryApplicationIdentityStore, applicationInstallationId, authorizationSigningMessage, signApplicationAuthorization, type ApplicationIdentity, type ApplicationIdentityStore } from "./application-identity.js";
|
|
5
4
|
export * from "./authorization-types.js";
|
|
5
|
+
export * from "./application-session.js";
|
|
6
6
|
export * from "./collection-client.js";
|
|
7
7
|
export * from "./connect-options.js";
|
|
8
8
|
export * from "./mdbase-connect.js";
|
|
9
9
|
export * from "./connection.js";
|
|
10
10
|
export * from "./connection-types.js";
|
|
11
|
+
export * from "./capabilities.js";
|
|
11
12
|
export * from "./errors.js";
|
|
12
13
|
export * from "./files.js";
|
|
13
14
|
export * from "./notifications.js";
|
|
14
15
|
export * from "./outcomes.js";
|
|
15
16
|
export * from "./operation-types.js";
|
|
16
17
|
export * from "./selection.js";
|
|
17
|
-
export
|
|
18
|
+
export type { MdbaseUnavailableReason } from "./session.js";
|
|
18
19
|
export { createPkce } from "./runtime-utils.js";
|
|
19
|
-
export type { ApplicationProvisions, ApplicationRequirements, ApplicationNotifications, ApplicationAuthorizationBinding, ApplicationAuthorizationProof, CollectionChange, CollectionChangesPage, CollectionContractDescriptor, CollectionDescription, CollectionFileMetadata, CollectionOperation as MdbaseOperation, CollectionTypeDescriptor, CollectionTypeDocument, ConnectOperationOutcome, ConnectProblem, ConnectProblemCategory, ConnectProblemCode, ConnectProblemDetailsByCode, ConnectRecoveryAction, ContractRequirement, DataContractViewIdentity, GrantScope,
|
|
20
|
+
export type { ApplicationProvisions, ApplicationCapabilityId, ApplicationCapabilityRequirements, ApplicationRequirements, ApplicationNotifications, ApplicationAuthorizationBinding, ApplicationAuthorizationProof, CollectionChange, CollectionChangesPage, CollectionContractDescriptor, CollectionDescription, CollectionFileMetadata, FileAction, CollectionOperation as MdbaseOperation, CollectionTypeDescriptor, CollectionTypeDocument, ConnectOperationOutcome, ConnectProblem, ConnectProblemCategory, ConnectProblemCode, ConnectProblemDetailsByCode, ConnectRecoveryAction, ContractRequirement, DataContractViewIdentity, GrantScope, JsonObject, MdbaseAppManifest, NotificationCriterion, MdbaseDiagnostic, MdbaseOperationEnvelope, KnownConnectProblem, RecordDocument, QueryRecord, SavedNamedView, SavedViewDocument, SavedViewExecution, SavedViewList, SavedViewPresentation, SavedViewProperty, SavedViewSource, SavedViewSourceDocument, ReadViewSourceInput, CreateViewSourceInput, UpdateViewSourceInput, DeleteViewSourceInput, DeleteViewSourceResult, ExecuteViewInput, TypePackManifest, TypePackManifestResource, TypePackApplyResult, TypePackAssessment, TypePackReceipt, AssessTypePackInput, ApplyTypePackInput, TypePackProvision, TypePackResourceDiff, TypePackSourceResource, UnknownConnectProblem } from "@mdbase-dev/connect-protocol";
|
package/dist/index.js
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
export { decryptRelayResponse, encryptRelayRequest, IndexedDbGrantKeyStore, MemoryGrantKeyStore, RelayCryptoError } from "./crypto.js";
|
|
2
2
|
export { FileTransferCryptoError, GrantFileTransferCipher } from "./file-crypto.js";
|
|
3
|
-
export {
|
|
4
|
-
export { applicationInstallationId, authorizationSigningMessage, signApplicationAuthorization } from "./application-identity.js";
|
|
3
|
+
export { ApplicationIdentityStoreError, IndexedDbApplicationIdentityStore, MemoryApplicationIdentityStore, applicationInstallationId, authorizationSigningMessage, signApplicationAuthorization } from "./application-identity.js";
|
|
5
4
|
export * from "./authorization-types.js";
|
|
5
|
+
export * from "./application-session.js";
|
|
6
6
|
export * from "./collection-client.js";
|
|
7
7
|
export * from "./connect-options.js";
|
|
8
8
|
export * from "./mdbase-connect.js";
|
|
9
9
|
export * from "./connection.js";
|
|
10
10
|
export * from "./connection-types.js";
|
|
11
|
+
export * from "./capabilities.js";
|
|
11
12
|
export * from "./errors.js";
|
|
12
13
|
export * from "./files.js";
|
|
13
14
|
export * from "./notifications.js";
|
|
14
15
|
export * from "./outcomes.js";
|
|
15
16
|
export * from "./operation-types.js";
|
|
16
17
|
export * from "./selection.js";
|
|
17
|
-
export * from "./session.js";
|
|
18
18
|
export { createPkce } from "./runtime-utils.js";
|
package/dist/internal-types.d.ts
CHANGED
package/dist/mdbase-connect.d.ts
CHANGED
|
@@ -3,14 +3,16 @@ import type { MdbaseConnectOptions } from "./connect-options.js";
|
|
|
3
3
|
import { MdbaseConnection, type MdbaseAuthorizationOutcome, type MdbaseAuthorizationResult, type MdbaseAuthorizeOptions, type MdbaseConnectEnvironment } from "./connection.js";
|
|
4
4
|
import type { MdbaseConnectionInfo } from "./connection-types.js";
|
|
5
5
|
import { type AuthorizationProblemCode, type ConnectOutcome, type RegistrationProblemCode } from "./outcomes.js";
|
|
6
|
-
import {
|
|
6
|
+
import { MdbaseApplicationSession, type MdbaseApplicationSessionOptions } from "./application-session.js";
|
|
7
|
+
import type { MdbaseUnavailableReason } from "./session.js";
|
|
7
8
|
import type { Application } from "./internal-types.js";
|
|
8
9
|
export declare class MdbaseConnect<Frontmatter extends JsonObject = JsonObject> {
|
|
9
10
|
private readonly internals;
|
|
10
11
|
constructor(options: MdbaseConnectOptions);
|
|
11
12
|
register(): Promise<ConnectOutcome<Application, RegistrationProblemCode>>;
|
|
12
13
|
authorize(options?: MdbaseAuthorizeOptions): Promise<ConnectOutcome<MdbaseAuthorizationOutcome<Frontmatter>, AuthorizationProblemCode>>;
|
|
13
|
-
|
|
14
|
+
createApplicationSession(options: MdbaseApplicationSessionOptions): MdbaseApplicationSession<Frontmatter>;
|
|
15
|
+
manifest(): Promise<ConnectOutcome<import("@mdbase-dev/connect-protocol").MdbaseAppManifest, RegistrationProblemCode>>;
|
|
14
16
|
environment(): MdbaseConnectEnvironment;
|
|
15
17
|
completeAuthorization(callbackUrl?: string): Promise<ConnectOutcome<MdbaseAuthorizationResult<Frontmatter>, AuthorizationProblemCode>>;
|
|
16
18
|
connections(): MdbaseConnectionInfo[];
|
|
@@ -18,4 +20,6 @@ export declare class MdbaseConnect<Frontmatter extends JsonObject = JsonObject>
|
|
|
18
20
|
unavailableReason(collectionId: string): MdbaseUnavailableReason | null;
|
|
19
21
|
onConnectionsChange(listener: (connections: MdbaseConnectionInfo[]) => void): () => void;
|
|
20
22
|
forgetAll(): void;
|
|
23
|
+
/** Forget a public TOFU pin after independently verifying a replaced connector. */
|
|
24
|
+
forgetConnectorIdentity(connectorId: string): void;
|
|
21
25
|
}
|
package/dist/mdbase-connect.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { MdbaseConnectInternals } from "./connect.js";
|
|
2
2
|
import { defaultCallbackUrl } from "./runtime-utils.js";
|
|
3
3
|
import { AUTHORIZATION_PROBLEM_CODES, REGISTRATION_PROBLEM_CODES, captureConnectOutcome } from "./outcomes.js";
|
|
4
|
-
import {
|
|
4
|
+
import { MdbaseApplicationSession } from "./application-session.js";
|
|
5
5
|
export class MdbaseConnect {
|
|
6
6
|
internals;
|
|
7
7
|
constructor(options) {
|
|
@@ -13,8 +13,11 @@ export class MdbaseConnect {
|
|
|
13
13
|
authorize(options = {}) {
|
|
14
14
|
return captureConnectOutcome(() => this.internals.authorize(options), AUTHORIZATION_PROBLEM_CODES);
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
return new
|
|
16
|
+
createApplicationSession(options) {
|
|
17
|
+
return new MdbaseApplicationSession(this, options);
|
|
18
|
+
}
|
|
19
|
+
manifest() {
|
|
20
|
+
return captureConnectOutcome(() => this.internals.manifestDeclaration(), REGISTRATION_PROBLEM_CODES);
|
|
18
21
|
}
|
|
19
22
|
environment() {
|
|
20
23
|
return this.internals.environment();
|
|
@@ -39,4 +42,8 @@ export class MdbaseConnect {
|
|
|
39
42
|
this.connection(connection.collectionId)?.forget();
|
|
40
43
|
}
|
|
41
44
|
}
|
|
45
|
+
/** Forget a public TOFU pin after independently verifying a replaced connector. */
|
|
46
|
+
forgetConnectorIdentity(connectorId) {
|
|
47
|
+
this.internals.forgetConnectorIdentity(connectorId);
|
|
48
|
+
}
|
|
42
49
|
}
|
|
@@ -34,7 +34,7 @@ export function isMutation(operation, input) {
|
|
|
34
34
|
|| operation === "rename"
|
|
35
35
|
|| operation === "create_type"
|
|
36
36
|
|| operation === "update_type"
|
|
37
|
-
|| operation === "
|
|
37
|
+
|| operation === "apply_type_pack"
|
|
38
38
|
|| operation === "put_timer"
|
|
39
39
|
|| operation === "cancel_timer"
|
|
40
40
|
|| operation === "reconcile_timers";
|
package/dist/outcomes.d.ts
CHANGED
|
@@ -18,14 +18,14 @@ export declare const COMMON_OPERATION_PROBLEM_CODES: readonly ["access_denied",
|
|
|
18
18
|
export declare const ALL_CONNECT_PROBLEM_CODES: ConnectProblemCode[];
|
|
19
19
|
export type RegistrationProblemCode = "discovery_failed" | "invalid_application_manifest" | "invalid_request" | "manifest_load_failed" | "rate_limited" | "temporarily_unavailable" | "timeout";
|
|
20
20
|
export declare const REGISTRATION_PROBLEM_CODES: readonly ["discovery_failed", "invalid_application_manifest", "invalid_request", "manifest_load_failed", "rate_limited", "temporarily_unavailable", "timeout"];
|
|
21
|
-
export type AuthorizationProblemCode = RegistrationProblemCode | "access_denied" | "approval_window_blocked" | "authorization_cancelled" | "browser_required" | "collection_access_denied" | "collection_configuration_invalid" | "collection_contracts_missing" | "collection_incompatible" | "collection_invalid" | "collection_kind_unsupported" | "collection_mismatch" | "collection_not_found" | "collection_type_registry_invalid" | "collection_version_unsupported" | "connector_identity_changed" | "connector_offline" | "connector_upgrade_required" | "device_authorization_failed" | "encryption_required" | "expired_token" | "
|
|
22
|
-
export declare const AUTHORIZATION_PROBLEM_CODES: readonly ["discovery_failed", "invalid_application_manifest", "invalid_request", "manifest_load_failed", "rate_limited", "temporarily_unavailable", "timeout", "access_denied", "approval_window_blocked", "authorization_cancelled", "browser_required", "collection_access_denied", "collection_configuration_invalid", "collection_contracts_missing", "collection_incompatible", "collection_invalid", "collection_kind_unsupported", "collection_mismatch", "collection_not_found", "collection_type_registry_invalid", "collection_version_unsupported", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "device_authorization_failed", "encryption_required", "expired_token", "
|
|
21
|
+
export type AuthorizationProblemCode = RegistrationProblemCode | "access_denied" | "application_identity_unavailable" | "approval_window_blocked" | "authorization_cancelled" | "authorization_replayed" | "browser_required" | "collection_access_denied" | "collection_configuration_invalid" | "collection_contracts_missing" | "collection_incompatible" | "collection_invalid" | "collection_kind_unsupported" | "collection_mismatch" | "collection_not_found" | "collection_type_registry_invalid" | "collection_version_unsupported" | "connector_identity_changed" | "connector_offline" | "connector_upgrade_required" | "device_authorization_failed" | "encryption_required" | "expired_token" | "invalid_application_authorization" | "invalid_callback" | "invalid_device_authorization_response" | "invalid_token_response" | "scope_denied" | "reconnect_required" | "token_exchange_failed";
|
|
22
|
+
export declare const AUTHORIZATION_PROBLEM_CODES: readonly ["discovery_failed", "invalid_application_manifest", "invalid_request", "manifest_load_failed", "rate_limited", "temporarily_unavailable", "timeout", "access_denied", "application_identity_unavailable", "approval_window_blocked", "authorization_cancelled", "authorization_replayed", "browser_required", "collection_access_denied", "collection_configuration_invalid", "collection_contracts_missing", "collection_incompatible", "collection_invalid", "collection_kind_unsupported", "collection_mismatch", "collection_not_found", "collection_type_registry_invalid", "collection_version_unsupported", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "device_authorization_failed", "encryption_required", "expired_token", "invalid_application_authorization", "invalid_callback", "invalid_device_authorization_response", "invalid_token_response", "scope_denied", "reconnect_required", "token_exchange_failed"];
|
|
23
23
|
export type NotificationProblemCode = RegistrationProblemCode | "authorization_expired" | "invalid_push_subscription" | "managed_fcm_not_declared" | "not_authorized" | "notification_criterion_not_declared" | "notification_reauthorization_required" | "notification_registration_failed" | "notification_unregistration_failed" | "notifications_not_declared" | "notifications_unavailable";
|
|
24
24
|
export declare const NOTIFICATION_PROBLEM_CODES: readonly ["discovery_failed", "invalid_application_manifest", "invalid_request", "manifest_load_failed", "rate_limited", "temporarily_unavailable", "timeout", "authorization_expired", "invalid_push_subscription", "managed_fcm_not_declared", "not_authorized", "notification_criterion_not_declared", "notification_reauthorization_required", "notification_registration_failed", "notification_unregistration_failed", "notifications_not_declared", "notifications_unavailable"];
|
|
25
25
|
export type DirectAccessProblemCode = "connector_offline" | "connector_upgrade_required" | "invalid_operation_response" | "not_authorized" | "operation_failed" | "temporarily_unavailable" | "timeout";
|
|
26
26
|
export declare const DIRECT_ACCESS_PROBLEM_CODES: readonly ["connector_offline", "connector_upgrade_required", "invalid_operation_response", "not_authorized", "operation_failed", "temporarily_unavailable", "timeout"];
|
|
27
27
|
export type SessionProblemCode = AuthorizationProblemCode | "collection_not_ready" | "collection_not_selected" | "unknown_collection";
|
|
28
|
-
export declare const SESSION_PROBLEM_CODES: readonly ["discovery_failed", "invalid_application_manifest", "invalid_request", "manifest_load_failed", "rate_limited", "temporarily_unavailable", "timeout", "access_denied", "approval_window_blocked", "authorization_cancelled", "browser_required", "collection_access_denied", "collection_configuration_invalid", "collection_contracts_missing", "collection_incompatible", "collection_invalid", "collection_kind_unsupported", "collection_mismatch", "collection_not_found", "collection_type_registry_invalid", "collection_version_unsupported", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "device_authorization_failed", "encryption_required", "expired_token", "
|
|
28
|
+
export declare const SESSION_PROBLEM_CODES: readonly ["discovery_failed", "invalid_application_manifest", "invalid_request", "manifest_load_failed", "rate_limited", "temporarily_unavailable", "timeout", "access_denied", "application_identity_unavailable", "approval_window_blocked", "authorization_cancelled", "authorization_replayed", "browser_required", "collection_access_denied", "collection_configuration_invalid", "collection_contracts_missing", "collection_incompatible", "collection_invalid", "collection_kind_unsupported", "collection_mismatch", "collection_not_found", "collection_type_registry_invalid", "collection_version_unsupported", "connector_identity_changed", "connector_offline", "connector_upgrade_required", "device_authorization_failed", "encryption_required", "expired_token", "invalid_application_authorization", "invalid_callback", "invalid_device_authorization_response", "invalid_token_response", "scope_denied", "reconnect_required", "token_exchange_failed", "collection_not_ready", "collection_not_selected", "unknown_collection"];
|
|
29
29
|
export type CollectionSetupProblemCode = "collection_configuration_invalid" | "collection_invalid" | "collection_type_registry_invalid" | "collection_version_unsupported";
|
|
30
30
|
export type CollectionDescriptionProblemCode = CommonOperationProblemCode | CollectionSetupProblemCode;
|
|
31
31
|
export type CollectionReadProblemCode = CollectionDescriptionProblemCode | "invalid_path" | "operation_invalid";
|
package/dist/outcomes.js
CHANGED
|
@@ -41,8 +41,10 @@ export const REGISTRATION_PROBLEM_CODES = [
|
|
|
41
41
|
export const AUTHORIZATION_PROBLEM_CODES = [
|
|
42
42
|
...REGISTRATION_PROBLEM_CODES,
|
|
43
43
|
"access_denied",
|
|
44
|
+
"application_identity_unavailable",
|
|
44
45
|
"approval_window_blocked",
|
|
45
46
|
"authorization_cancelled",
|
|
47
|
+
"authorization_replayed",
|
|
46
48
|
"browser_required",
|
|
47
49
|
"collection_access_denied",
|
|
48
50
|
"collection_configuration_invalid",
|
|
@@ -60,12 +62,12 @@ export const AUTHORIZATION_PROBLEM_CODES = [
|
|
|
60
62
|
"device_authorization_failed",
|
|
61
63
|
"encryption_required",
|
|
62
64
|
"expired_token",
|
|
63
|
-
"
|
|
65
|
+
"invalid_application_authorization",
|
|
64
66
|
"invalid_callback",
|
|
65
67
|
"invalid_device_authorization_response",
|
|
66
|
-
"invalid_first_contact",
|
|
67
68
|
"invalid_token_response",
|
|
68
69
|
"scope_denied",
|
|
70
|
+
"reconnect_required",
|
|
69
71
|
"token_exchange_failed"
|
|
70
72
|
];
|
|
71
73
|
export const NOTIFICATION_PROBLEM_CODES = [
|
package/dist/runtime-utils.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export declare function parseGrantScope(value: unknown): GrantScope | null;
|
|
|
22
22
|
export declare function stripTrailingSlash(value: string): string;
|
|
23
23
|
export declare function defaultManifestSource(): string;
|
|
24
24
|
export declare function defaultRedirectUri(): string;
|
|
25
|
+
export declare function applicationStorageOrigin(manifest: MdbaseAppManifest | string, manifestSource: string, redirectUri: string, registeredPortable: boolean): string;
|
|
26
|
+
export declare function collectionIdFromTokenKey(key: string): string;
|
|
25
27
|
export declare function defaultCallbackUrl(): string;
|
|
26
28
|
export declare function defaultStorage(memoryOnly: boolean): Storage;
|
|
27
29
|
export declare class MemoryStorage implements Storage {
|
package/dist/runtime-utils.js
CHANGED
|
@@ -240,6 +240,29 @@ export function defaultRedirectUri() {
|
|
|
240
240
|
}
|
|
241
241
|
return location.href.split(/[?#]/)[0];
|
|
242
242
|
}
|
|
243
|
+
export function applicationStorageOrigin(manifest, manifestSource, redirectUri, registeredPortable) {
|
|
244
|
+
if ((typeof manifest !== "string" && manifest.distribution === "portable")
|
|
245
|
+
|| registeredPortable)
|
|
246
|
+
return "null";
|
|
247
|
+
const redirect = new URL(redirectUri);
|
|
248
|
+
if (["http:", "https:"].includes(redirect.protocol))
|
|
249
|
+
return redirect.origin;
|
|
250
|
+
if (typeof location !== "undefined")
|
|
251
|
+
return location.origin;
|
|
252
|
+
if (typeof manifest !== "string")
|
|
253
|
+
return new URL(manifest.homepage).origin;
|
|
254
|
+
try {
|
|
255
|
+
return new URL(manifestSource).origin;
|
|
256
|
+
}
|
|
257
|
+
catch {
|
|
258
|
+
return "";
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
export function collectionIdFromTokenKey(key) {
|
|
262
|
+
const marker = ":token:";
|
|
263
|
+
const index = key.lastIndexOf(marker);
|
|
264
|
+
return index < 0 ? "" : key.slice(index + marker.length);
|
|
265
|
+
}
|
|
243
266
|
export function defaultCallbackUrl() {
|
|
244
267
|
if (typeof location === "undefined") {
|
|
245
268
|
throw connectError("callback_url_required", "callbackUrl is required outside a browser environment.");
|
package/dist/selection.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export type MdbaseSelectionHistory = "push" | "replace";
|
|
2
|
-
export interface
|
|
2
|
+
export interface MdbaseApplicationSelection {
|
|
3
3
|
selectedCollectionId(): string | null;
|
|
4
4
|
select(collectionId: string | null, options?: {
|
|
5
5
|
history?: MdbaseSelectionHistory;
|
|
@@ -15,7 +15,7 @@ export interface MdbaseBrowserSelectionOptions {
|
|
|
15
15
|
fallbackPath?: string;
|
|
16
16
|
}
|
|
17
17
|
/** URL-backed collection selection for browser and browser-shell applications. */
|
|
18
|
-
export declare class MdbaseBrowserSelection implements
|
|
18
|
+
export declare class MdbaseBrowserSelection implements MdbaseApplicationSelection {
|
|
19
19
|
private readonly collectionParameter;
|
|
20
20
|
private readonly fallbackPath;
|
|
21
21
|
private readonly listeners;
|
|
@@ -35,7 +35,7 @@ export declare class MdbaseBrowserSelection implements MdbaseSessionSelection {
|
|
|
35
35
|
private currentUrl;
|
|
36
36
|
private browserHistory;
|
|
37
37
|
}
|
|
38
|
-
export declare class MdbaseMemorySelection implements
|
|
38
|
+
export declare class MdbaseMemorySelection implements MdbaseApplicationSelection {
|
|
39
39
|
private collectionId;
|
|
40
40
|
private readonly listeners;
|
|
41
41
|
selectedCollectionId(): string | null;
|
package/dist/session.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { CollectionOperation, JsonObject } from "@mdbase-dev/connect-protoc
|
|
|
2
2
|
import { MdbaseConnection, type MdbaseAuthorizationOutcome, type MdbaseAuthorizationResult, type MdbaseAuthorizeOptions } from "./connection.js";
|
|
3
3
|
import type { MdbaseAuthorizationCapabilities, MdbaseConnectionInfo } from "./connection-types.js";
|
|
4
4
|
import { type AuthorizationProblemCode, type ConnectOutcome, type SessionProblemCode } from "./outcomes.js";
|
|
5
|
-
import type { MdbaseSelectionHistory,
|
|
5
|
+
import type { MdbaseSelectionHistory, MdbaseApplicationSelection } from "./selection.js";
|
|
6
6
|
export interface MdbaseSessionConnect<Frontmatter extends JsonObject> {
|
|
7
7
|
authorize(options?: MdbaseAuthorizeOptions): Promise<ConnectOutcome<MdbaseAuthorizationOutcome<Frontmatter>, AuthorizationProblemCode>>;
|
|
8
8
|
completeAuthorization(callbackUrl: string): Promise<ConnectOutcome<MdbaseAuthorizationResult<Frontmatter>, AuthorizationProblemCode>>;
|
|
@@ -12,7 +12,7 @@ export interface MdbaseSessionConnect<Frontmatter extends JsonObject> {
|
|
|
12
12
|
onConnectionsChange(listener: (connections: MdbaseConnectionInfo[]) => void): () => void;
|
|
13
13
|
}
|
|
14
14
|
export interface MdbaseSessionOptions {
|
|
15
|
-
selection:
|
|
15
|
+
selection: MdbaseApplicationSelection;
|
|
16
16
|
operations?: CollectionOperation[];
|
|
17
17
|
autoSelect?: "only" | "never";
|
|
18
18
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mdbase-dev/connect",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.28",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"README.md"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@mdbase-dev/connect-protocol": "0.1.0-beta.
|
|
28
|
+
"@mdbase-dev/connect-protocol": "0.1.0-beta.28"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"esbuild": "^0.28.1",
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { MdbaseAuthorizeOptions } from "./connection.js";
|
|
2
|
-
import type { GrantKeyRecord, GrantKeyStore } from "./crypto.js";
|
|
3
|
-
import type { Application } from "./internal-types.js";
|
|
4
|
-
export declare function presentFirstContact(value: unknown, application: Application, installation: GrantKeyRecord, options: MdbaseAuthorizeOptions, previousFingerprint: string): Promise<string>;
|
|
5
|
-
export declare function applicationIdentity(keyStore: GrantKeyStore, serverUrl: string, application: Application): Promise<GrantKeyRecord>;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { applicationInstallationId } from "./application-identity.js";
|
|
2
|
-
import { deriveFirstContactSas } from "./first-contact.js";
|
|
3
|
-
import { connectError } from "./errors.js";
|
|
4
|
-
export async function presentFirstContact(value, application, installation, options, previousFingerprint) {
|
|
5
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
6
|
-
throw connectError("invalid_first_contact", "The authorization service returned an invalid first-contact challenge.");
|
|
7
|
-
}
|
|
8
|
-
const binding = value;
|
|
9
|
-
const installationId = await applicationInstallationId(installation);
|
|
10
|
-
if (binding.application_id !== application.id
|
|
11
|
-
|| binding.application_installation_id !== installationId
|
|
12
|
-
|| binding.application_agreement_public_key !== installation.agreementPublicKey
|
|
13
|
-
|| binding.application_signing_public_key !== installation.signingPublicKey) {
|
|
14
|
-
throw connectError("invalid_first_contact", "The first-contact challenge does not match this application installation.");
|
|
15
|
-
}
|
|
16
|
-
const fingerprint = JSON.stringify(binding);
|
|
17
|
-
if (fingerprint === previousFingerprint)
|
|
18
|
-
return previousFingerprint;
|
|
19
|
-
if (!options.onFirstContact) {
|
|
20
|
-
throw connectError("first_contact_handler_required", "Local access requires the application to display a first-contact comparison code.");
|
|
21
|
-
}
|
|
22
|
-
let authenticationString;
|
|
23
|
-
try {
|
|
24
|
-
authenticationString = await deriveFirstContactSas(binding, "application", installation);
|
|
25
|
-
}
|
|
26
|
-
catch (cause) {
|
|
27
|
-
throw connectError("invalid_first_contact", "The authorization service returned an invalid first-contact challenge.", { cause });
|
|
28
|
-
}
|
|
29
|
-
await options.onFirstContact({
|
|
30
|
-
binding,
|
|
31
|
-
authenticationString,
|
|
32
|
-
applicationId: binding.application_id,
|
|
33
|
-
connectorId: binding.connector_id
|
|
34
|
-
});
|
|
35
|
-
return fingerprint;
|
|
36
|
-
}
|
|
37
|
-
export async function applicationIdentity(keyStore, serverUrl, application) {
|
|
38
|
-
const handle = `application-installation:${serverUrl}:${application.id}`;
|
|
39
|
-
const existing = await keyStore.get(handle);
|
|
40
|
-
if (existing)
|
|
41
|
-
return existing;
|
|
42
|
-
try {
|
|
43
|
-
return await keyStore.create(handle);
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
const raced = await keyStore.get(handle);
|
|
47
|
-
if (raced)
|
|
48
|
-
return raced;
|
|
49
|
-
throw error;
|
|
50
|
-
}
|
|
51
|
-
}
|
package/dist/first-contact.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { type FirstContactBinding } from "@mdbase-dev/connect-protocol";
|
|
2
|
-
export type FirstContactRole = "application" | "connector";
|
|
3
|
-
export declare class FirstContactCryptoError extends Error {
|
|
4
|
-
readonly code: string;
|
|
5
|
-
constructor(code: string, message: string);
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Derive the independently displayed first-contact authentication string.
|
|
9
|
-
* The private key must belong to the endpoint role named in the transcript.
|
|
10
|
-
*/
|
|
11
|
-
export declare function deriveFirstContactSas(binding: FirstContactBinding, role: FirstContactRole, identity: {
|
|
12
|
-
agreementPrivateKey: CryptoKey;
|
|
13
|
-
agreementPublicKey: string;
|
|
14
|
-
}): Promise<string>;
|
package/dist/first-contact.js
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { FIRST_CONTACT_PROTOCOL_VERSION } from "@mdbase-dev/connect-protocol";
|
|
2
|
-
const TRANSCRIPT_DOMAIN = new TextEncoder().encode("mdbase-connect first-contact transcript\0");
|
|
3
|
-
const SAS_INFO = new TextEncoder().encode("mdbase-connect first-contact sas v1");
|
|
4
|
-
const CROCKFORD_BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
5
|
-
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6
|
-
export class FirstContactCryptoError extends Error {
|
|
7
|
-
code;
|
|
8
|
-
constructor(code, message) {
|
|
9
|
-
super(message);
|
|
10
|
-
this.code = code;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Derive the independently displayed first-contact authentication string.
|
|
15
|
-
* The private key must belong to the endpoint role named in the transcript.
|
|
16
|
-
*/
|
|
17
|
-
export async function deriveFirstContactSas(binding, role, identity) {
|
|
18
|
-
const keys = await validatedKeys(binding);
|
|
19
|
-
const own = role === "application"
|
|
20
|
-
? binding.application_agreement_public_key
|
|
21
|
-
: binding.connector_agreement_public_key;
|
|
22
|
-
const peer = role === "application"
|
|
23
|
-
? keys.connector
|
|
24
|
-
: keys.applicationAgreement;
|
|
25
|
-
if (identity.agreementPublicKey !== own) {
|
|
26
|
-
throw new FirstContactCryptoError("identity_mismatch", "The first-contact identity does not match the transcript.");
|
|
27
|
-
}
|
|
28
|
-
let peerKey;
|
|
29
|
-
try {
|
|
30
|
-
peerKey = await crypto.subtle.importKey("raw", peer, { name: "ECDH", namedCurve: "P-256" }, false, []);
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
throw new FirstContactCryptoError("invalid_public_key", "The first-contact public key is invalid.");
|
|
34
|
-
}
|
|
35
|
-
const shared = await crypto.subtle.deriveBits({ name: "ECDH", public: peerKey }, identity.agreementPrivateKey, 256);
|
|
36
|
-
const transcript = firstContactTranscript(binding, keys);
|
|
37
|
-
const salt = await crypto.subtle.digest("SHA-256", transcript);
|
|
38
|
-
const hkdfKey = await crypto.subtle.importKey("raw", shared, "HKDF", false, ["deriveBits"]);
|
|
39
|
-
const sas = new Uint8Array(await crypto.subtle.deriveBits({ name: "HKDF", hash: "SHA-256", salt, info: SAS_INFO }, hkdfKey, 40));
|
|
40
|
-
return formatSas(sas);
|
|
41
|
-
}
|
|
42
|
-
async function validatedKeys(binding) {
|
|
43
|
-
if (binding.protocol_version !== FIRST_CONTACT_PROTOCOL_VERSION) {
|
|
44
|
-
throw new FirstContactCryptoError("unsupported_version", "The first-contact protocol version is not supported.");
|
|
45
|
-
}
|
|
46
|
-
const applicationAgreement = canonicalPublicKey(binding.application_agreement_public_key);
|
|
47
|
-
const applicationSigning = canonicalPublicKey(binding.application_signing_public_key);
|
|
48
|
-
const connector = canonicalPublicKey(binding.connector_agreement_public_key);
|
|
49
|
-
if (equalBytes(applicationAgreement, applicationSigning)
|
|
50
|
-
|| equalBytes(applicationAgreement, connector)
|
|
51
|
-
|| equalBytes(applicationSigning, connector)) {
|
|
52
|
-
throw new FirstContactCryptoError("invalid_public_key", "First contact requires distinct endpoint agreement and signing keys.");
|
|
53
|
-
}
|
|
54
|
-
try {
|
|
55
|
-
await Promise.all([
|
|
56
|
-
applicationAgreement,
|
|
57
|
-
applicationSigning,
|
|
58
|
-
connector
|
|
59
|
-
].map((key) => crypto.subtle.importKey("raw", key, { name: "ECDH", namedCurve: "P-256" }, false, [])));
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
throw new FirstContactCryptoError("invalid_public_key", "The first-contact public key is invalid.");
|
|
63
|
-
}
|
|
64
|
-
return { applicationAgreement, applicationSigning, connector };
|
|
65
|
-
}
|
|
66
|
-
function firstContactTranscript(binding, keys) {
|
|
67
|
-
const chunks = [
|
|
68
|
-
TRANSCRIPT_DOMAIN,
|
|
69
|
-
u32(binding.protocol_version),
|
|
70
|
-
field(uuidBytes(binding.application_id)),
|
|
71
|
-
field(uuidBytes(binding.application_installation_id)),
|
|
72
|
-
field(keys.applicationAgreement),
|
|
73
|
-
field(keys.applicationSigning),
|
|
74
|
-
field(uuidBytes(binding.connector_id)),
|
|
75
|
-
field(keys.connector)
|
|
76
|
-
];
|
|
77
|
-
const length = chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
|
|
78
|
-
const output = new Uint8Array(length);
|
|
79
|
-
let offset = 0;
|
|
80
|
-
for (const chunk of chunks) {
|
|
81
|
-
output.set(chunk, offset);
|
|
82
|
-
offset += chunk.byteLength;
|
|
83
|
-
}
|
|
84
|
-
return output;
|
|
85
|
-
}
|
|
86
|
-
function field(value) {
|
|
87
|
-
const output = new Uint8Array(4 + value.byteLength);
|
|
88
|
-
output.set(u32(value.byteLength));
|
|
89
|
-
output.set(value, 4);
|
|
90
|
-
return output;
|
|
91
|
-
}
|
|
92
|
-
function u32(value) {
|
|
93
|
-
const output = new Uint8Array(4);
|
|
94
|
-
new DataView(output.buffer).setUint32(0, value, false);
|
|
95
|
-
return output;
|
|
96
|
-
}
|
|
97
|
-
function uuidBytes(value) {
|
|
98
|
-
if (!UUID_PATTERN.test(value)) {
|
|
99
|
-
throw new FirstContactCryptoError("invalid_binding", "The first-contact transcript contains an invalid identifier.");
|
|
100
|
-
}
|
|
101
|
-
const compact = value.replaceAll("-", "");
|
|
102
|
-
return Uint8Array.from({ length: 16 }, (_, index) => Number.parseInt(compact.slice(index * 2, index * 2 + 2), 16));
|
|
103
|
-
}
|
|
104
|
-
function canonicalPublicKey(value) {
|
|
105
|
-
if (!/^[A-Za-z0-9_-]+$/.test(value)) {
|
|
106
|
-
throw new FirstContactCryptoError("invalid_public_key", "The first-contact public key is invalid.");
|
|
107
|
-
}
|
|
108
|
-
const normalized = value.replaceAll("-", "+").replaceAll("_", "/");
|
|
109
|
-
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
|
|
110
|
-
let bytes;
|
|
111
|
-
try {
|
|
112
|
-
bytes = Uint8Array.from(atob(padded), (character) => character.charCodeAt(0));
|
|
113
|
-
}
|
|
114
|
-
catch {
|
|
115
|
-
throw new FirstContactCryptoError("invalid_public_key", "The first-contact public key is invalid.");
|
|
116
|
-
}
|
|
117
|
-
if (bytes.byteLength !== 65
|
|
118
|
-
|| bytes[0] !== 4
|
|
119
|
-
|| base64Url(bytes) !== value) {
|
|
120
|
-
throw new FirstContactCryptoError("invalid_public_key", "The first-contact public key is invalid.");
|
|
121
|
-
}
|
|
122
|
-
return bytes;
|
|
123
|
-
}
|
|
124
|
-
function base64Url(value) {
|
|
125
|
-
return btoa(String.fromCharCode(...value))
|
|
126
|
-
.replaceAll("+", "-")
|
|
127
|
-
.replaceAll("/", "_")
|
|
128
|
-
.replace(/=+$/u, "");
|
|
129
|
-
}
|
|
130
|
-
function equalBytes(left, right) {
|
|
131
|
-
return left.byteLength === right.byteLength
|
|
132
|
-
&& left.every((value, index) => value === right[index]);
|
|
133
|
-
}
|
|
134
|
-
function formatSas(value) {
|
|
135
|
-
let bits = 0n;
|
|
136
|
-
for (const byte of value)
|
|
137
|
-
bits = (bits << 8n) | BigInt(byte);
|
|
138
|
-
let output = "";
|
|
139
|
-
for (let index = 7; index >= 0; index -= 1) {
|
|
140
|
-
output += CROCKFORD_BASE32[Number((bits >> BigInt(index * 5)) & 31n)];
|
|
141
|
-
if (index === 4)
|
|
142
|
-
output += "-";
|
|
143
|
-
}
|
|
144
|
-
return output;
|
|
145
|
-
}
|