@dxos/client-services 0.1.35 → 0.1.36
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/lib/browser/{chunk-H5IXSSI7.mjs → chunk-2AYYVPWN.mjs} +835 -950
- package/dist/lib/browser/chunk-2AYYVPWN.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +9 -11
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +212 -74
- package/dist/lib/browser/packlets/testing/index.mjs.map +3 -3
- package/dist/lib/node/index.cjs +942 -1058
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +1089 -1067
- package/dist/lib/node/packlets/testing/index.cjs.map +4 -4
- package/dist/types/src/packlets/identity/identity-manager.d.ts +1 -0
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +3 -3
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts +18 -0
- package/dist/types/src/packlets/invitations/device-invitation-protocol.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/device-invitation-protocol.test.d.ts +2 -0
- package/dist/types/src/packlets/invitations/device-invitation-protocol.test.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/index.d.ts +4 -4
- package/dist/types/src/packlets/invitations/index.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts +12 -0
- package/dist/types/src/packlets/invitations/invitation-protocol.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +38 -0
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/invitations-service.d.ts +13 -15
- package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts +22 -0
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -0
- package/dist/types/src/packlets/invitations/space-invitation-protocol.test.d.ts +2 -0
- package/dist/types/src/packlets/invitations/space-invitation-protocol.test.d.ts.map +1 -0
- package/dist/types/src/packlets/services/service-context.d.ts +6 -4
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +2 -0
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts +31 -2
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +3 -3
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/tests/invitations.test.d.ts +2 -0
- package/dist/types/src/packlets/tests/invitations.test.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/worker-runtime.d.ts +2 -0
- package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
- package/package.json +30 -30
- package/src/packlets/identity/identity-manager.ts +2 -1
- package/src/packlets/identity/identity.ts +3 -3
- package/src/packlets/invitations/{device-invitations-handler.test.ts → device-invitation-protocol.test.ts} +3 -3
- package/src/packlets/invitations/device-invitation-protocol.ts +83 -0
- package/src/packlets/invitations/index.ts +4 -4
- package/src/packlets/invitations/invitation-protocol.ts +21 -0
- package/src/packlets/invitations/invitations-handler.ts +462 -0
- package/src/packlets/invitations/invitations-service.ts +127 -104
- package/src/packlets/invitations/space-invitation-protocol.test.ts +221 -0
- package/src/packlets/invitations/space-invitation-protocol.ts +117 -0
- package/src/packlets/services/service-context.test.ts +11 -4
- package/src/packlets/services/service-context.ts +40 -19
- package/src/packlets/services/service-host.ts +4 -12
- package/src/packlets/services/service-registry.test.ts +23 -25
- package/src/packlets/spaces/data-space-manager.ts +5 -0
- package/src/packlets/spaces/spaces-service.ts +7 -2
- package/src/packlets/storage/storage.ts +2 -0
- package/src/packlets/testing/invitation-utils.ts +219 -31
- package/src/packlets/testing/test-builder.ts +33 -22
- package/src/packlets/tests/client-services.test.ts +33 -76
- package/src/packlets/tests/halo-profile.test.ts +2 -28
- package/src/packlets/tests/invitations.test.ts +389 -0
- package/src/packlets/tests/spaces-invitations.test.ts +12 -30
- package/src/packlets/tests/spaces.test.ts +30 -28
- package/src/packlets/vault/worker-runtime.ts +4 -0
- package/dist/lib/browser/chunk-H5IXSSI7.mjs.map +0 -7
- package/dist/types/src/packlets/invitations/device-invitations-handler.d.ts +0 -30
- package/dist/types/src/packlets/invitations/device-invitations-handler.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/device-invitations-handler.test.d.ts +0 -2
- package/dist/types/src/packlets/invitations/device-invitations-handler.test.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/device-invitations-service.d.ts +0 -13
- package/dist/types/src/packlets/invitations/device-invitations-service.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/device-invitations-service.test.d.ts +0 -2
- package/dist/types/src/packlets/invitations/device-invitations-service.test.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts +0 -26
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.test.d.ts +0 -2
- package/dist/types/src/packlets/invitations/space-invitations-handler.test.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/space-invitations-service.d.ts +0 -16
- package/dist/types/src/packlets/invitations/space-invitations-service.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/space-invitations-service.test.d.ts +0 -2
- package/dist/types/src/packlets/invitations/space-invitations-service.test.d.ts.map +0 -1
- package/src/packlets/invitations/device-invitations-handler.ts +0 -370
- package/src/packlets/invitations/device-invitations-service.test.ts +0 -96
- package/src/packlets/invitations/device-invitations-service.ts +0 -24
- package/src/packlets/invitations/space-invitations-handler.test.ts +0 -246
- package/src/packlets/invitations/space-invitations-handler.ts +0 -463
- package/src/packlets/invitations/space-invitations-service.test.ts +0 -129
- package/src/packlets/invitations/space-invitations-service.ts +0 -35
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { AbstractInvitationsHandler, AuthenticatingInvitationProvider, CancellableInvitationObservable, InvitationsOptions } from '@dxos/client';
|
|
2
|
-
import { Keyring } from '@dxos/keyring';
|
|
3
|
-
import { NetworkManager } from '@dxos/network-manager';
|
|
4
|
-
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
5
|
-
import { Identity, JoinIdentityParams } from '../identity';
|
|
6
|
-
declare type DeviceInvitationsHandlerParams = {
|
|
7
|
-
networkManager: NetworkManager;
|
|
8
|
-
keyring: Keyring;
|
|
9
|
-
getIdentity: () => Identity;
|
|
10
|
-
acceptIdentity: (identity: JoinIdentityParams) => Promise<Identity>;
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Handles the life-cycle of device invitations between peers.
|
|
14
|
-
*/
|
|
15
|
-
export declare class DeviceInvitationsHandler extends AbstractInvitationsHandler {
|
|
16
|
-
private readonly _params;
|
|
17
|
-
constructor(_params: DeviceInvitationsHandlerParams);
|
|
18
|
-
/**
|
|
19
|
-
* Creates an invitation and listens for a join request from the invited (guest) peer.
|
|
20
|
-
*/
|
|
21
|
-
createInvitation(context: void, options?: InvitationsOptions): CancellableInvitationObservable;
|
|
22
|
-
/**
|
|
23
|
-
* Waits for the host peer (inviter) to accept our join request.
|
|
24
|
-
* The local guest peer (invitee) then sends the local halo invitation to the host,
|
|
25
|
-
* which then writes the guest's credentials to the halo.
|
|
26
|
-
*/
|
|
27
|
-
acceptInvitation(invitation: Invitation, options?: InvitationsOptions): AuthenticatingInvitationProvider;
|
|
28
|
-
}
|
|
29
|
-
export {};
|
|
30
|
-
//# sourceMappingURL=device-invitations-handler.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"device-invitations-handler.d.ts","sourceRoot":"","sources":["../../../../../src/packlets/invitations/device-invitations-handler.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,0BAA0B,EAC1B,gCAAgC,EAEhC,+BAA+B,EAE/B,kBAAkB,EAGnB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxC,OAAO,EAAiC,cAAc,EAAgB,MAAM,uBAAuB,CAAC;AAEpG,OAAO,EAAE,UAAU,EAAE,MAAM,4CAA4C,CAAC;AAUxE,OAAO,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE3D,aAAK,8BAA8B,GAAG;IACpC,cAAc,EAAE,cAAc,CAAC;IAC/B,OAAO,EAAE,OAAO,CAAC;IAEjB,WAAW,EAAE,MAAM,QAAQ,CAAC;IAC5B,cAAc,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACrE,CAAC;AAEF;;GAEG;AAEH,qBAAa,wBAAyB,SAAQ,0BAA0B;IAGpE,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,8BAA8B;IAK1D;;OAEG;IACH,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,+BAA+B;IAoH9F;;;;OAIG;IACH,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,gCAAgC;CAmHzG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"device-invitations-handler.test.d.ts","sourceRoot":"","sources":["../../../../../src/packlets/invitations/device-invitations-handler.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { InvitationsHandler } from '@dxos/client';
|
|
2
|
-
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
3
|
-
import { IdentityManager } from '../identity';
|
|
4
|
-
import { AbstractInvitationsService } from './invitations-service';
|
|
5
|
-
/**
|
|
6
|
-
* Adapts invitation service observable to client/service stream.
|
|
7
|
-
*/
|
|
8
|
-
export declare class DeviceInvitationsServiceImpl extends AbstractInvitationsService {
|
|
9
|
-
private readonly invitationsHandler;
|
|
10
|
-
constructor(identityManager: IdentityManager, invitationsHandler: InvitationsHandler);
|
|
11
|
-
getContext(invitation: Invitation): void;
|
|
12
|
-
}
|
|
13
|
-
//# sourceMappingURL=device-invitations-service.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"device-invitations-service.d.ts","sourceRoot":"","sources":["../../../../../src/packlets/invitations/device-invitations-service.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,4CAA4C,CAAC;AAExE,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAEnE;;GAEG;AACH,qBAAa,4BAA6B,SAAQ,0BAA0B;IAIxE,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBADnC,eAAe,EAAE,eAAe,EACf,kBAAkB,EAAE,kBAAkB;IAKhD,UAAU,CAAC,UAAU,EAAE,UAAU;CAC3C"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"device-invitations-service.test.d.ts","sourceRoot":"","sources":["../../../../../src/packlets/invitations/device-invitations-service.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { AuthenticatingInvitationProvider, CancellableInvitationObservable, AbstractInvitationsHandler, InvitationsOptions } from '@dxos/client';
|
|
2
|
-
import { SigningContext } from '@dxos/echo-pipeline';
|
|
3
|
-
import { Keyring } from '@dxos/keyring';
|
|
4
|
-
import { NetworkManager } from '@dxos/network-manager';
|
|
5
|
-
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
6
|
-
import { DataSpace, DataSpaceManager } from '../spaces';
|
|
7
|
-
/**
|
|
8
|
-
* Handles the life-cycle of Space invitations between peers.
|
|
9
|
-
*/
|
|
10
|
-
export declare class SpaceInvitationsHandler extends AbstractInvitationsHandler<DataSpace> {
|
|
11
|
-
private readonly _spaceManager;
|
|
12
|
-
private readonly _signingContext;
|
|
13
|
-
private readonly _keyring;
|
|
14
|
-
constructor(networkManager: NetworkManager, _spaceManager: DataSpaceManager, _signingContext: SigningContext, _keyring: Keyring);
|
|
15
|
-
/**
|
|
16
|
-
* Creates an invitation and listens for a join request from the invited (guest) peer.
|
|
17
|
-
*/
|
|
18
|
-
createInvitation(space: DataSpace, options?: InvitationsOptions): CancellableInvitationObservable;
|
|
19
|
-
/**
|
|
20
|
-
* Waits for the host peer (inviter) to accept our join request.
|
|
21
|
-
* The local guest peer (invitee) then sends the local space invitation to the host,
|
|
22
|
-
* which then writes the guest's credentials to the space.
|
|
23
|
-
*/
|
|
24
|
-
acceptInvitation(invitation: Invitation, options?: InvitationsOptions): AuthenticatingInvitationProvider;
|
|
25
|
-
}
|
|
26
|
-
//# sourceMappingURL=space-invitations-handler.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"space-invitations-handler.d.ts","sourceRoot":"","sources":["../../../../../src/packlets/invitations/space-invitations-handler.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,gCAAgC,EAEhC,+BAA+B,EAI/B,0BAA0B,EAC1B,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAGxC,OAAO,EAAiC,cAAc,EAAiC,MAAM,uBAAuB,CAAC;AAErH,OAAO,EAAE,UAAU,EAAE,MAAM,4CAA4C,CAAC;AAexE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAIxD;;GAEG;AAEH,qBAAa,uBAAwB,SAAQ,0BAA0B,CAAC,SAAS,CAAC;IAG9E,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAHzB,cAAc,EAAE,cAAc,EACb,aAAa,EAAE,gBAAgB,EAC/B,eAAe,EAAE,cAAc,EAC/B,QAAQ,EAAE,OAAO;IAKpC;;OAEG;IACH,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,+BAA+B;IA4KjG;;;;OAIG;IACH,gBAAgB,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,gCAAgC;CAgJzG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"space-invitations-handler.test.d.ts","sourceRoot":"","sources":["../../../../../src/packlets/invitations/space-invitations-handler.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { InvitationsHandler } from '@dxos/client';
|
|
2
|
-
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
3
|
-
import { Provider } from '@dxos/util';
|
|
4
|
-
import { IdentityManager } from '../identity';
|
|
5
|
-
import { DataSpace, DataSpaceManager } from '../spaces';
|
|
6
|
-
import { AbstractInvitationsService } from './invitations-service';
|
|
7
|
-
/**
|
|
8
|
-
* Adapts invitation service observable to client/service stream.
|
|
9
|
-
*/
|
|
10
|
-
export declare class SpaceInvitationsServiceImpl extends AbstractInvitationsService<DataSpace> {
|
|
11
|
-
private readonly invitationsHandler;
|
|
12
|
-
private readonly _getSpaceManager;
|
|
13
|
-
constructor(identityManager: IdentityManager, invitationsHandler: Provider<InvitationsHandler<DataSpace>>, _getSpaceManager: Provider<DataSpaceManager>);
|
|
14
|
-
getContext(invitation: Invitation): DataSpace;
|
|
15
|
-
}
|
|
16
|
-
//# sourceMappingURL=space-invitations-service.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"space-invitations-service.d.ts","sourceRoot":"","sources":["../../../../../src/packlets/invitations/space-invitations-service.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,4CAA4C,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AAEnE;;GAEG;AACH,qBAAa,2BAA4B,SAAQ,0BAA0B,CAAC,SAAS,CAAC;IAIlF,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAFjC,eAAe,EAAE,eAAe,EACf,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,EAC3D,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,CAAC;IAKtD,UAAU,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS;CAOvD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"space-invitations-service.test.d.ts","sourceRoot":"","sources":["../../../../../src/packlets/invitations/space-invitations-service.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2022 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import assert from 'node:assert';
|
|
6
|
-
|
|
7
|
-
import { scheduleTask, sleep, Trigger } from '@dxos/async';
|
|
8
|
-
import {
|
|
9
|
-
AbstractInvitationsHandler,
|
|
10
|
-
AuthenticatingInvitationProvider,
|
|
11
|
-
AUTHENTICATION_CODE_LENGTH,
|
|
12
|
-
CancellableInvitationObservable,
|
|
13
|
-
InvitationObservableProvider,
|
|
14
|
-
InvitationsOptions,
|
|
15
|
-
INVITATION_TIMEOUT,
|
|
16
|
-
ON_CLOSE_DELAY
|
|
17
|
-
} from '@dxos/client';
|
|
18
|
-
import { Context } from '@dxos/context';
|
|
19
|
-
import { generatePasscode } from '@dxos/credentials';
|
|
20
|
-
import { Keyring } from '@dxos/keyring';
|
|
21
|
-
import { PublicKey } from '@dxos/keys';
|
|
22
|
-
import { log } from '@dxos/log';
|
|
23
|
-
import { createTeleportProtocolFactory, NetworkManager, StarTopology } from '@dxos/network-manager';
|
|
24
|
-
import { schema } from '@dxos/protocols';
|
|
25
|
-
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
26
|
-
import {
|
|
27
|
-
AuthenticationRequest,
|
|
28
|
-
AuthenticationResponse,
|
|
29
|
-
HaloAdmissionCredentials,
|
|
30
|
-
HaloAdmissionOffer,
|
|
31
|
-
HaloHostService
|
|
32
|
-
} from '@dxos/protocols/proto/dxos/halo/invitations';
|
|
33
|
-
import { ExtensionContext, RpcExtension } from '@dxos/teleport';
|
|
34
|
-
|
|
35
|
-
import { Identity, JoinIdentityParams } from '../identity';
|
|
36
|
-
|
|
37
|
-
type DeviceInvitationsHandlerParams = {
|
|
38
|
-
networkManager: NetworkManager;
|
|
39
|
-
keyring: Keyring;
|
|
40
|
-
|
|
41
|
-
getIdentity: () => Identity;
|
|
42
|
-
acceptIdentity: (identity: JoinIdentityParams) => Promise<Identity>;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Handles the life-cycle of device invitations between peers.
|
|
47
|
-
*/
|
|
48
|
-
// TODO(dmaretskyi): Split into Host and Guest parts.
|
|
49
|
-
export class DeviceInvitationsHandler extends AbstractInvitationsHandler {
|
|
50
|
-
// prettier-ignore
|
|
51
|
-
constructor(
|
|
52
|
-
private readonly _params: DeviceInvitationsHandlerParams
|
|
53
|
-
) {
|
|
54
|
-
super(_params.networkManager);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Creates an invitation and listens for a join request from the invited (guest) peer.
|
|
59
|
-
*/
|
|
60
|
-
createInvitation(context: void, options?: InvitationsOptions): CancellableInvitationObservable {
|
|
61
|
-
const { type, timeout = INVITATION_TIMEOUT, swarmKey } = options ?? {};
|
|
62
|
-
assert(type !== Invitation.Type.OFFLINE);
|
|
63
|
-
const identity = this._params.getIdentity();
|
|
64
|
-
|
|
65
|
-
// TODO(dmaretskyi): Add invitation kind: halo/space.
|
|
66
|
-
const invitation: Invitation = {
|
|
67
|
-
type,
|
|
68
|
-
invitationId: PublicKey.random().toHex(),
|
|
69
|
-
swarmKey: swarmKey ?? PublicKey.random(),
|
|
70
|
-
authenticationCode: generatePasscode(AUTHENTICATION_CODE_LENGTH)
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const ctx = new Context({
|
|
74
|
-
onError: (err) => {
|
|
75
|
-
void ctx.dispose();
|
|
76
|
-
observable.callback.onError(err);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// TODO(burdon): Stop anything pending.
|
|
81
|
-
const observable = new InvitationObservableProvider(async () => {
|
|
82
|
-
await ctx.dispose();
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
let authenticationCode: string;
|
|
86
|
-
const complete = new Trigger<PublicKey>();
|
|
87
|
-
|
|
88
|
-
// Called for every connecting peer.
|
|
89
|
-
const createExtension = () =>
|
|
90
|
-
new HostHaloInvitationExtension({
|
|
91
|
-
requestAdmission: async () => {
|
|
92
|
-
log('responding with admission offer', {
|
|
93
|
-
host: identity.deviceKey
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// TODO(burdon): Is this the right place to set this state?
|
|
97
|
-
observable.callback.onAuthenticating?.(invitation);
|
|
98
|
-
return {
|
|
99
|
-
identityKey: identity.identityKey,
|
|
100
|
-
haloSpaceKey: identity.haloSpaceKey,
|
|
101
|
-
genesisFeedKey: identity.haloGenesisFeedKey,
|
|
102
|
-
controlTimeframe: identity.controlPipeline.state.timeframe
|
|
103
|
-
};
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
authenticate: async ({ authenticationCode: code }) => {
|
|
107
|
-
log('received authentication request', { authenticationCode: code });
|
|
108
|
-
authenticationCode = code;
|
|
109
|
-
return { status: AuthenticationResponse.Status.OK };
|
|
110
|
-
},
|
|
111
|
-
|
|
112
|
-
// TODO(burdon): Not used: controlFeedKey, dataFeedKey.
|
|
113
|
-
presentAdmissionCredentials: async (credentials) => {
|
|
114
|
-
try {
|
|
115
|
-
// Check authenticated.
|
|
116
|
-
if (invitation.type !== Invitation.Type.INTERACTIVE_TESTING) {
|
|
117
|
-
if (invitation.authenticationCode === undefined || invitation.authenticationCode !== authenticationCode) {
|
|
118
|
-
throw new Error(`invalid authentication code: ${authenticationCode}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
log('writing guest credentials', { host: identity.deviceKey, guest: credentials.deviceKey });
|
|
123
|
-
// TODO(burdon): Check if already admitted.
|
|
124
|
-
await identity.admitDevice(credentials);
|
|
125
|
-
|
|
126
|
-
// Updating credentials complete.
|
|
127
|
-
complete.wake(credentials.deviceKey);
|
|
128
|
-
} catch (err) {
|
|
129
|
-
// TODO(burdon): Generic RPC callback to report error to client.
|
|
130
|
-
observable.callback.onError(err);
|
|
131
|
-
throw err; // Propagate error to guest.
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
|
|
135
|
-
onOpen: () => {
|
|
136
|
-
scheduleTask(ctx, async () => {
|
|
137
|
-
try {
|
|
138
|
-
log('connected', { host: identity.deviceKey });
|
|
139
|
-
observable.callback.onConnected?.(invitation);
|
|
140
|
-
const deviceKey = await complete.wait({ timeout });
|
|
141
|
-
log('admitted guest', { host: identity.deviceKey, guest: deviceKey });
|
|
142
|
-
observable.callback.onSuccess(invitation);
|
|
143
|
-
} catch (err) {
|
|
144
|
-
if (!observable.cancelled) {
|
|
145
|
-
log.error('failed', err);
|
|
146
|
-
observable.callback.onError(err);
|
|
147
|
-
}
|
|
148
|
-
} finally {
|
|
149
|
-
// NOTE: If we close immediately after `complete` trigger finishes, Guest won't receive the reply to the last RPC.
|
|
150
|
-
// TODO(dmaretskyi): Implement a soft-close which waits for the last connection to terminate before closing.
|
|
151
|
-
await sleep(ON_CLOSE_DELAY);
|
|
152
|
-
await ctx.dispose();
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
scheduleTask(ctx, async () => {
|
|
159
|
-
const topic = invitation.swarmKey!;
|
|
160
|
-
const swarmConnection = await this._networkManager.joinSwarm({
|
|
161
|
-
topic,
|
|
162
|
-
peerId: topic,
|
|
163
|
-
protocolProvider: createTeleportProtocolFactory(async (teleport) => {
|
|
164
|
-
teleport.addExtension('dxos.halo.invitations', createExtension());
|
|
165
|
-
}),
|
|
166
|
-
topology: new StarTopology(topic)
|
|
167
|
-
});
|
|
168
|
-
ctx.onDispose(() => swarmConnection.close());
|
|
169
|
-
|
|
170
|
-
observable.callback.onConnecting?.(invitation);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
return observable;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Waits for the host peer (inviter) to accept our join request.
|
|
178
|
-
* The local guest peer (invitee) then sends the local halo invitation to the host,
|
|
179
|
-
* which then writes the guest's credentials to the halo.
|
|
180
|
-
*/
|
|
181
|
-
acceptInvitation(invitation: Invitation, options?: InvitationsOptions): AuthenticatingInvitationProvider {
|
|
182
|
-
const { timeout = INVITATION_TIMEOUT } = options ?? {};
|
|
183
|
-
|
|
184
|
-
const ctx = new Context({
|
|
185
|
-
onError: (err) => {
|
|
186
|
-
void ctx.dispose();
|
|
187
|
-
observable.callback.onError(err);
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
const authenticated = new Trigger<string>();
|
|
192
|
-
const observable = new AuthenticatingInvitationProvider({
|
|
193
|
-
onCancel: async () => {
|
|
194
|
-
await ctx.dispose();
|
|
195
|
-
},
|
|
196
|
-
|
|
197
|
-
// TODO(burdon): Consider retry.
|
|
198
|
-
onAuthenticate: async (code: string) => {
|
|
199
|
-
authenticated.wake(code);
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
let connectionCount = 0;
|
|
204
|
-
const complete = new Trigger<PublicKey>();
|
|
205
|
-
|
|
206
|
-
const createExtension = (): GuestHaloInvitationExtension => {
|
|
207
|
-
const extension = new GuestHaloInvitationExtension({
|
|
208
|
-
onOpen: () => {
|
|
209
|
-
scheduleTask(ctx, async () => {
|
|
210
|
-
try {
|
|
211
|
-
// TODO(burdon): Bug where guest may create multiple connections.
|
|
212
|
-
if (++connectionCount > 1) {
|
|
213
|
-
throw new Error(`multiple connections detected: ${connectionCount}`);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
log('connected');
|
|
217
|
-
observable.callback.onConnected?.(invitation);
|
|
218
|
-
|
|
219
|
-
// 1. Send request.
|
|
220
|
-
log('sending admission request');
|
|
221
|
-
const { identityKey, haloSpaceKey, genesisFeedKey, controlTimeframe } =
|
|
222
|
-
await extension.rpc.HaloHostService.requestAdmission();
|
|
223
|
-
|
|
224
|
-
// 2. Get authentication code.
|
|
225
|
-
// TODO(burdon): Test timeout (options for timeouts at different steps).
|
|
226
|
-
if (invitation.type === undefined || invitation.type === Invitation.Type.INTERACTIVE) {
|
|
227
|
-
log('guest waiting for authentication code...');
|
|
228
|
-
observable.callback.onAuthenticating?.(invitation);
|
|
229
|
-
const authenticationCode = await authenticated.wait({ timeout });
|
|
230
|
-
log('sending authentication request');
|
|
231
|
-
await extension.rpc.HaloHostService.authenticate({ authenticationCode });
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const deviceKey = await this._params.keyring.createKey();
|
|
235
|
-
const controlFeedKey = await this._params.keyring.createKey();
|
|
236
|
-
const dataFeedKey = await this._params.keyring.createKey();
|
|
237
|
-
|
|
238
|
-
// 3. Send admission credentials to host (with local identity keys).
|
|
239
|
-
log('presenting admission credentials', { identityKey, deviceKey, controlFeedKey, dataFeedKey });
|
|
240
|
-
await extension.rpc.HaloHostService.presentAdmissionCredentials({
|
|
241
|
-
deviceKey,
|
|
242
|
-
controlFeedKey,
|
|
243
|
-
dataFeedKey
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// 4. Create local identity.
|
|
247
|
-
// TODO(burdon): Abandon if does not complete (otherwise retry will fail).
|
|
248
|
-
const identity = await this._params.acceptIdentity({
|
|
249
|
-
identityKey,
|
|
250
|
-
deviceKey,
|
|
251
|
-
haloSpaceKey,
|
|
252
|
-
haloGenesisFeedKey: genesisFeedKey,
|
|
253
|
-
controlFeedKey,
|
|
254
|
-
dataFeedKey,
|
|
255
|
-
controlTimeframe
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
// 5. Success.
|
|
259
|
-
log('admitted by host', { guest: identity.deviceKey, identityKey });
|
|
260
|
-
complete.wake(identityKey);
|
|
261
|
-
} catch (err) {
|
|
262
|
-
if (!observable.cancelled) {
|
|
263
|
-
log.warn('auth failed', err);
|
|
264
|
-
observable.callback.onError(err);
|
|
265
|
-
}
|
|
266
|
-
} finally {
|
|
267
|
-
await ctx.dispose();
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
return extension;
|
|
273
|
-
};
|
|
274
|
-
|
|
275
|
-
scheduleTask(ctx, async () => {
|
|
276
|
-
assert(invitation.swarmKey);
|
|
277
|
-
const topic = invitation.swarmKey;
|
|
278
|
-
const swarmConnection = await this._networkManager.joinSwarm({
|
|
279
|
-
topic,
|
|
280
|
-
peerId: PublicKey.random(),
|
|
281
|
-
protocolProvider: createTeleportProtocolFactory(async (teleport) => {
|
|
282
|
-
teleport.addExtension('dxos.halo.invitations', createExtension());
|
|
283
|
-
}),
|
|
284
|
-
topology: new StarTopology(topic)
|
|
285
|
-
});
|
|
286
|
-
ctx.onDispose(() => swarmConnection.close());
|
|
287
|
-
|
|
288
|
-
observable.callback.onConnecting?.(invitation);
|
|
289
|
-
invitation.identityKey = await complete.wait();
|
|
290
|
-
observable.callback.onSuccess(invitation);
|
|
291
|
-
await ctx.dispose();
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
return observable;
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
type HostHaloInvitationExtensionCallbacks = {
|
|
299
|
-
requestAdmission: () => Promise<HaloAdmissionOffer>;
|
|
300
|
-
authenticate: (request: AuthenticationRequest) => Promise<AuthenticationResponse>;
|
|
301
|
-
presentAdmissionCredentials: (request: HaloAdmissionCredentials) => Promise<void>;
|
|
302
|
-
// Deliberately not async to not block the extensions opening.
|
|
303
|
-
onOpen: () => void;
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* Host's side for a connection to a concrete peer in p2p network during invitation.
|
|
308
|
-
*/
|
|
309
|
-
class HostHaloInvitationExtension extends RpcExtension<{}, { HaloHostService: HaloHostService }> {
|
|
310
|
-
constructor(private readonly _callbacks: HostHaloInvitationExtensionCallbacks) {
|
|
311
|
-
super({
|
|
312
|
-
exposed: {
|
|
313
|
-
HaloHostService: schema.getService('dxos.halo.invitations.HaloHostService')
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
protected override async getHandlers(): Promise<{ HaloHostService: HaloHostService }> {
|
|
319
|
-
return {
|
|
320
|
-
// TODO(dmaretskyi): For now this is just forwarding the data to callbacks since we don't have session-specific logic.
|
|
321
|
-
// Perhaps in the future we will have more complex logic here.
|
|
322
|
-
HaloHostService: {
|
|
323
|
-
requestAdmission: async () => {
|
|
324
|
-
return this._callbacks.requestAdmission();
|
|
325
|
-
},
|
|
326
|
-
|
|
327
|
-
authenticate: async (credentials) => {
|
|
328
|
-
return this._callbacks.authenticate(credentials);
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
// TODO(burdon): Not used: controlFeedKey, dataFeedKey.
|
|
332
|
-
presentAdmissionCredentials: async (credentials) => {
|
|
333
|
-
return this._callbacks.presentAdmissionCredentials(credentials);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
override async onOpen(context: ExtensionContext) {
|
|
340
|
-
await super.onOpen(context);
|
|
341
|
-
this._callbacks.onOpen();
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
type GuestHaloInvitationExtensionCallbacks = {
|
|
346
|
-
// Deliberately not async to not block the extensions opening.
|
|
347
|
-
onOpen: () => void;
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
/**
|
|
351
|
-
* Guest's side for a connection to a concrete peer in p2p network during invitation.
|
|
352
|
-
*/
|
|
353
|
-
class GuestHaloInvitationExtension extends RpcExtension<{ HaloHostService: HaloHostService }, {}> {
|
|
354
|
-
constructor(private readonly _callbacks: GuestHaloInvitationExtensionCallbacks) {
|
|
355
|
-
super({
|
|
356
|
-
requested: {
|
|
357
|
-
HaloHostService: schema.getService('dxos.halo.invitations.HaloHostService')
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
protected override async getHandlers() {
|
|
363
|
-
return {};
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
override async onOpen(context: ExtensionContext) {
|
|
367
|
-
await super.onOpen(context);
|
|
368
|
-
this._callbacks.onOpen();
|
|
369
|
-
}
|
|
370
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2022 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { expect } from 'chai';
|
|
6
|
-
import assert from 'node:assert';
|
|
7
|
-
import waitForExpect from 'wait-for-expect';
|
|
8
|
-
|
|
9
|
-
import { asyncChain, Trigger } from '@dxos/async';
|
|
10
|
-
import { DeviceInvitationsProxy } from '@dxos/client';
|
|
11
|
-
import { raise } from '@dxos/debug';
|
|
12
|
-
import { Invitation, DeviceInvitationsService } from '@dxos/protocols/proto/dxos/client/services';
|
|
13
|
-
import { describe, test, afterTest } from '@dxos/test';
|
|
14
|
-
|
|
15
|
-
import { ServiceContext } from '../services';
|
|
16
|
-
import { createPeers } from '../testing';
|
|
17
|
-
import { DeviceInvitationsServiceImpl } from './device-invitations-service';
|
|
18
|
-
|
|
19
|
-
const closeAfterTest = async (peer: ServiceContext) => {
|
|
20
|
-
afterTest(() => peer.close());
|
|
21
|
-
return peer;
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
describe('services/device-invitation-service', () => {
|
|
25
|
-
test('creates identity and invites peer', async () => {
|
|
26
|
-
const [host, guest] = await asyncChain<ServiceContext>([closeAfterTest])(createPeers(2));
|
|
27
|
-
|
|
28
|
-
await host.identityManager.createIdentity();
|
|
29
|
-
expect(host.identityManager.identity!.authorizedDeviceKeys.size).to.eq(1);
|
|
30
|
-
|
|
31
|
-
// prettier-ignore
|
|
32
|
-
const service1: DeviceInvitationsService = new DeviceInvitationsServiceImpl(
|
|
33
|
-
host.identityManager,
|
|
34
|
-
host.deviceInvitations
|
|
35
|
-
);
|
|
36
|
-
// prettier-ignore
|
|
37
|
-
const service2: DeviceInvitationsService = new DeviceInvitationsServiceImpl(
|
|
38
|
-
guest.identityManager,
|
|
39
|
-
guest.deviceInvitations
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
const success1 = new Trigger<Invitation>();
|
|
43
|
-
const success2 = new Trigger<Invitation>();
|
|
44
|
-
|
|
45
|
-
const authenticationCode = new Trigger<string>();
|
|
46
|
-
|
|
47
|
-
{
|
|
48
|
-
const proxy1 = new DeviceInvitationsProxy(service1);
|
|
49
|
-
const observable1 = proxy1.createInvitation();
|
|
50
|
-
observable1.subscribe({
|
|
51
|
-
onConnecting: (invitation: Invitation) => {
|
|
52
|
-
const proxy2 = new DeviceInvitationsProxy(service2);
|
|
53
|
-
const observable2 = proxy2.acceptInvitation(invitation);
|
|
54
|
-
observable2.subscribe({
|
|
55
|
-
onAuthenticating: async () => {
|
|
56
|
-
await observable2.authenticate(await authenticationCode.wait());
|
|
57
|
-
},
|
|
58
|
-
onSuccess: (invitation: Invitation) => {
|
|
59
|
-
// TODO(burdon): No device.
|
|
60
|
-
// expect(guest.identityManager.identity!.authorizedDeviceKeys.size).to.eq(1);
|
|
61
|
-
success2.wake(invitation);
|
|
62
|
-
},
|
|
63
|
-
onError: (err: Error) => raise(new Error(err.message))
|
|
64
|
-
});
|
|
65
|
-
},
|
|
66
|
-
onConnected: (invitation: Invitation) => {
|
|
67
|
-
assert(invitation.authenticationCode);
|
|
68
|
-
authenticationCode.wake(invitation.authenticationCode);
|
|
69
|
-
},
|
|
70
|
-
onSuccess: (invitation: Invitation) => {
|
|
71
|
-
success1.wake(invitation);
|
|
72
|
-
},
|
|
73
|
-
onCancelled: () => raise(new Error()),
|
|
74
|
-
onTimeout: (err: Error) => raise(new Error(err.message)),
|
|
75
|
-
onError: (err: Error) => raise(new Error(err.message))
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Check same identity.
|
|
80
|
-
const [invitation1, invitation2] = await Promise.all([success1.wait(), success2.wait()]);
|
|
81
|
-
expect(invitation1.identityKey).not.to.exist;
|
|
82
|
-
expect(invitation2.identityKey).to.deep.eq(host.identityManager.identity!.identityKey);
|
|
83
|
-
expect(invitation2.identityKey).to.deep.eq(guest.identityManager.identity!.identityKey);
|
|
84
|
-
expect(invitation1.state).to.eq(Invitation.State.SUCCESS);
|
|
85
|
-
expect(invitation2.state).to.eq(Invitation.State.SUCCESS);
|
|
86
|
-
|
|
87
|
-
// Check devices.
|
|
88
|
-
// TODO(burdon): Incorrect number of devices.
|
|
89
|
-
await waitForExpect(() => {
|
|
90
|
-
expect(host.identityManager.identity!.authorizedDeviceKeys.size).to.eq(2);
|
|
91
|
-
expect(guest.identityManager.identity!.authorizedDeviceKeys.size).to.eq(2);
|
|
92
|
-
});
|
|
93
|
-
// console.log(host.identityManager.identity!.authorizedDeviceKeys.size);
|
|
94
|
-
// console.log(guest.identityManager.identity!.authorizedDeviceKeys.size);
|
|
95
|
-
});
|
|
96
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2022 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { InvitationsHandler } from '@dxos/client';
|
|
6
|
-
import { Invitation } from '@dxos/protocols/proto/dxos/client/services';
|
|
7
|
-
|
|
8
|
-
import { IdentityManager } from '../identity';
|
|
9
|
-
import { AbstractInvitationsService } from './invitations-service';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Adapts invitation service observable to client/service stream.
|
|
13
|
-
*/
|
|
14
|
-
export class DeviceInvitationsServiceImpl extends AbstractInvitationsService {
|
|
15
|
-
// prettier-ignore
|
|
16
|
-
constructor (
|
|
17
|
-
identityManager: IdentityManager,
|
|
18
|
-
private readonly invitationsHandler: InvitationsHandler
|
|
19
|
-
) {
|
|
20
|
-
super(identityManager, () => invitationsHandler);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
override getContext(invitation: Invitation) {}
|
|
24
|
-
}
|