@novasamatech/host-papp 0.5.0-1 → 0.5.0-11
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/.papi/descriptors/dist/common-types.d.ts +8667 -0
- package/.papi/descriptors/dist/common.d.ts +1 -0
- package/.papi/descriptors/dist/descriptors-UUEW32EL.mjs +27 -0
- package/.papi/descriptors/dist/descriptors.d.ts +1 -0
- package/.papi/descriptors/dist/index.d.ts +10 -0
- package/.papi/descriptors/dist/index.js +237 -0
- package/.papi/descriptors/dist/index.mjs +148 -0
- package/.papi/descriptors/dist/metadataTypes-E4AQJDJR.mjs +6 -0
- package/.papi/descriptors/dist/metadataTypes.d.ts +2 -0
- package/.papi/descriptors/dist/people_lite.d.ts +7757 -0
- package/.papi/descriptors/dist/people_lite_metadata-EIVHV27X.mjs +6 -0
- package/.papi/descriptors/dist/people_lite_metadata.d.ts +2 -0
- package/.papi/descriptors/package.json +24 -0
- package/.papi/metadata/people_lite.scale +0 -0
- package/.papi/polkadot-api.json +15 -0
- package/dist/adapters/identity/rpc.d.ts +6 -4
- package/dist/adapters/identity/rpc.js +96 -26
- package/dist/adapters/identity/types.d.ts +3 -1
- package/dist/adapters/lazyClient/papi.js +5 -0
- package/dist/adapters/lazyClient/types.d.ts +1 -0
- package/dist/adapters/statement/rpc.js +58 -10
- package/dist/adapters/statement/types.d.ts +6 -3
- package/dist/adapters/storage/localStorage.js +26 -4
- package/dist/adapters/storage/memory.js +14 -4
- package/dist/adapters/storage/types.d.ts +5 -2
- package/dist/adapters/storage/types.js +1 -1
- package/dist/components/auth/codec.d.ts +9 -0
- package/dist/components/auth/codec.js +10 -0
- package/dist/components/auth/codecs.d.ts +9 -0
- package/dist/components/auth/codecs.js +10 -0
- package/dist/components/auth/index.d.ts +36 -0
- package/dist/components/auth/index.js +150 -0
- package/dist/components/auth/types.d.ts +15 -0
- package/dist/components/auth/types.js +1 -0
- package/dist/components/session.d.ts +34 -0
- package/dist/components/session.js +54 -0
- package/dist/components/sso/index.d.ts +36 -0
- package/dist/components/sso/index.js +150 -0
- package/dist/components/sso/scale/handshake.d.ts +9 -0
- package/dist/components/sso/scale/handshake.js +10 -0
- package/dist/components/sso/types.d.ts +15 -0
- package/dist/components/sso/types.js +1 -0
- package/dist/components/transport.d.ts +27 -0
- package/dist/components/transport.js +57 -0
- package/dist/components/user/codec.d.ts +16 -0
- package/dist/components/user/codec.js +13 -0
- package/dist/components/user/index.d.ts +22 -0
- package/dist/components/user/index.js +58 -0
- package/dist/components/user/ssoMessageStream.d.ts +10 -0
- package/dist/components/user/ssoMessageStream.js +8 -0
- package/dist/components/user/ssoSession.d.ts +5 -0
- package/dist/components/user/ssoSession.js +5 -0
- package/dist/components/user/storage.d.ts +27 -0
- package/dist/components/user/storage.js +143 -0
- package/dist/components/user/types.d.ts +6 -0
- package/dist/components/user/types.js +1 -0
- package/dist/components/user/userSessionStorage.d.ts +20 -0
- package/dist/components/user/userSessionStorage.js +24 -0
- package/dist/components/user.d.ts +74 -0
- package/dist/components/user.js +188 -0
- package/dist/constants.d.ts +2 -1
- package/dist/constants.js +5 -1
- package/dist/crypto.d.ts +29 -0
- package/dist/crypto.js +86 -0
- package/dist/helpers/abortError.d.ts +4 -0
- package/dist/helpers/abortError.js +8 -0
- package/dist/helpers/callbackRaceResolver.d.ts +1 -0
- package/dist/helpers/callbackRaceResolver.js +17 -0
- package/dist/helpers/result.d.ts +12 -0
- package/dist/helpers/result.js +15 -0
- package/dist/helpers/result.spec.d.ts +1 -0
- package/dist/helpers/result.spec.js +23 -0
- package/dist/helpers/state.d.ts +16 -0
- package/dist/helpers/state.js +51 -0
- package/dist/helpers/utils.d.ts +2 -1
- package/dist/helpers/utils.js +11 -2
- package/dist/helpers/zipWith.d.ts +4 -0
- package/dist/helpers/zipWith.js +11 -0
- package/dist/identity/impl.d.ts +6 -0
- package/dist/identity/impl.js +68 -0
- package/dist/identity/rpcAdapter.d.ts +3 -0
- package/dist/identity/rpcAdapter.js +46 -0
- package/dist/identity/types.d.ts +21 -0
- package/dist/identity/types.js +1 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +2 -7
- package/dist/modules/crypto.d.ts +8 -9
- package/dist/modules/crypto.js +20 -42
- package/dist/modules/secretStorage.d.ts +13 -12
- package/dist/modules/secretStorage.js +34 -43
- package/dist/modules/session/helpers.d.ts +5 -0
- package/dist/modules/session/helpers.js +29 -0
- package/dist/modules/session/session.d.ts +12 -0
- package/dist/modules/session/session.js +50 -0
- package/dist/modules/session/types.d.ts +12 -0
- package/dist/modules/session/types.js +1 -0
- package/dist/modules/signIn.d.ts +32 -11
- package/dist/modules/signIn.js +97 -101
- package/dist/modules/state.d.ts +16 -0
- package/dist/modules/state.js +50 -0
- package/dist/modules/statementStore.d.ts +10 -11
- package/dist/modules/statementStore.js +16 -14
- package/dist/modules/statementTopic.d.ts +34 -0
- package/dist/modules/statementTopic.js +46 -0
- package/dist/modules/storageView.d.ts +25 -0
- package/dist/modules/storageView.js +51 -0
- package/dist/modules/syncStorage.d.ts +25 -0
- package/dist/modules/syncStorage.js +76 -0
- package/dist/modules/transport/codec.d.ts +24 -0
- package/dist/modules/transport/codec.js +36 -0
- package/dist/modules/transport/crypto.d.ts +2 -0
- package/dist/modules/transport/crypto.js +20 -0
- package/dist/modules/transport/transport.d.ts +42 -0
- package/dist/modules/transport/transport.js +66 -0
- package/dist/modules/user.d.ts +67 -0
- package/dist/modules/user.js +188 -0
- package/dist/modules/userManager.d.ts +15 -0
- package/dist/modules/userManager.js +105 -0
- package/dist/modules/userStorage.d.ts +19 -0
- package/dist/modules/userStorage.js +108 -0
- package/dist/modules/userStore.d.ts +15 -0
- package/dist/modules/userStore.js +105 -0
- package/dist/papp.d.ts +25 -13
- package/dist/papp.js +19 -50
- package/dist/sso/auth/attestationService.d.ts +18 -0
- package/dist/sso/auth/attestationService.js +171 -0
- package/dist/sso/auth/impl.d.ts +53 -0
- package/dist/sso/auth/impl.js +161 -0
- package/dist/sso/auth/scale/handshake.d.ts +9 -0
- package/dist/sso/auth/scale/handshake.js +10 -0
- package/dist/sso/auth/types.d.ts +17 -0
- package/dist/sso/auth/types.js +1 -0
- package/dist/sso/session/impl.d.ts +23 -0
- package/dist/sso/session/impl.js +57 -0
- package/dist/sso/session/scale/remoteMessage.d.ts +10 -0
- package/dist/sso/session/scale/remoteMessage.js +13 -0
- package/dist/sso/session/sessionManager.d.ts +23 -0
- package/dist/sso/session/sessionManager.js +58 -0
- package/dist/sso/session/ssoSession.d.ts +8 -0
- package/dist/sso/session/ssoSession.js +5 -0
- package/dist/sso/session/ssoSessionStorage.d.ts +21 -0
- package/dist/sso/session/ssoSessionStorage.js +20 -0
- package/dist/sso/session/types.d.ts +6 -0
- package/dist/sso/session/types.js +1 -0
- package/dist/sso/session/userSessionStorage.d.ts +21 -0
- package/dist/sso/session/userSessionStorage.js +20 -0
- package/dist/sso/sessionManager/attestationService.d.ts +5 -0
- package/dist/sso/sessionManager/attestationService.js +15 -0
- package/dist/sso/sessionManager/impl.d.ts +22 -0
- package/dist/sso/sessionManager/impl.js +71 -0
- package/dist/sso/sessionManager/repository/ssoSessionRepository.d.ts +22 -0
- package/dist/sso/sessionManager/repository/ssoSessionRepository.js +27 -0
- package/dist/sso/sessionManager/scale/hex.d.ts +1 -0
- package/dist/sso/sessionManager/scale/hex.js +3 -0
- package/dist/sso/sessionManager/scale/remoteMessage.d.ts +43 -0
- package/dist/sso/sessionManager/scale/remoteMessage.js +13 -0
- package/dist/sso/sessionManager/scale/signPayloadRequest.d.ts +19 -0
- package/dist/sso/sessionManager/scale/signPayloadRequest.js +19 -0
- package/dist/sso/sessionManager/scale/signPayloadResponse.d.ts +12 -0
- package/dist/sso/sessionManager/scale/signPayloadResponse.js +9 -0
- package/dist/sso/sessionManager/scale/signRequest.d.ts +19 -0
- package/dist/sso/sessionManager/scale/signRequest.js +19 -0
- package/dist/sso/sessionManager/scale/signResponse.d.ts +6 -0
- package/dist/sso/sessionManager/scale/signResponse.js +5 -0
- package/dist/sso/sessionManager/ssoSession.d.ts +23 -0
- package/dist/sso/sessionManager/ssoSession.js +69 -0
- package/dist/sso/sessionManager/ssoSessionProver.d.ts +4 -0
- package/dist/sso/sessionManager/ssoSessionProver.js +35 -0
- package/dist/sso/sessionManager/types.d.ts +6 -0
- package/dist/sso/sessionManager/types.js +1 -0
- package/dist/sso/sessionManager/userSession.d.ts +22 -0
- package/dist/sso/sessionManager/userSession.js +111 -0
- package/dist/sso/ssoSessionProver.d.ts +4 -0
- package/dist/sso/ssoSessionProver.js +35 -0
- package/dist/sso/ssoSessionRepository.d.ts +18 -0
- package/dist/sso/ssoSessionRepository.js +27 -0
- package/dist/sso/userSecretRepository.d.ts +17 -0
- package/dist/sso/userSecretRepository.js +45 -0
- package/dist/sso/userSessionRepository.d.ts +18 -0
- package/dist/sso/userSessionRepository.js +26 -0
- package/dist/structs.d.ts +10 -10
- package/dist/structs.js +17 -13
- package/dist/types.d.ts +1 -1
- package/package.json +14 -7
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CodecType } from 'scale-ts';
|
|
2
|
+
import type { StorageAdapter } from '../adapters/storage/types.js';
|
|
3
|
+
import type { Account } from '../modules/statementStore.js';
|
|
4
|
+
import type { Transport } from '../modules/transport/transport.js';
|
|
5
|
+
import type { Callback } from '../types.js';
|
|
6
|
+
export type Session<T> = {
|
|
7
|
+
subscribe(callback: Callback<T>): VoidFunction;
|
|
8
|
+
dispose(): void;
|
|
9
|
+
};
|
|
10
|
+
export declare const HostRemoteMessageCodec: import("scale-ts").Codec<{
|
|
11
|
+
messageId: string;
|
|
12
|
+
data: {
|
|
13
|
+
tag: "v1";
|
|
14
|
+
value: {
|
|
15
|
+
tag: "Disconnected";
|
|
16
|
+
value: undefined;
|
|
17
|
+
} | {
|
|
18
|
+
tag: "SigningRequest";
|
|
19
|
+
value: Uint8Array<ArrayBufferLike>;
|
|
20
|
+
} | {
|
|
21
|
+
tag: "SigningResponse";
|
|
22
|
+
value: Uint8Array<ArrayBufferLike>;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}>;
|
|
26
|
+
export type SSOSession = Session<CodecType<typeof HostRemoteMessageCodec>>;
|
|
27
|
+
type SsoSessionParams = {
|
|
28
|
+
ownAccount: Account;
|
|
29
|
+
peerAccount: Account;
|
|
30
|
+
transport: Transport;
|
|
31
|
+
storage: StorageAdapter;
|
|
32
|
+
};
|
|
33
|
+
export declare function createSSOSession({ ownAccount, peerAccount, transport, storage }: SsoSessionParams): SSOSession;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { fromThrowable, okAsync } from 'neverthrow';
|
|
2
|
+
import { toHex } from 'polkadot-api/utils';
|
|
3
|
+
import { Bytes, Enum, Struct, _void, str } from 'scale-ts';
|
|
4
|
+
import { toError } from '../helpers/utils.js';
|
|
5
|
+
import { createSessionId } from '../modules/statementStore.js';
|
|
6
|
+
import { storageListView } from '../modules/storageView.js';
|
|
7
|
+
// SSO
|
|
8
|
+
export const HostRemoteMessageCodec = Struct({
|
|
9
|
+
messageId: str,
|
|
10
|
+
data: Enum({
|
|
11
|
+
v1: Enum({
|
|
12
|
+
Disconnected: _void,
|
|
13
|
+
// TODO implement
|
|
14
|
+
SigningRequest: Bytes(),
|
|
15
|
+
// TODO implement
|
|
16
|
+
SigningResponse: Bytes(),
|
|
17
|
+
}),
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
export function createSSOSession({ ownAccount, peerAccount, transport, storage }) {
|
|
21
|
+
const peerSession = createSessionId(peerAccount, ownAccount);
|
|
22
|
+
const processedStorage = storageListView({
|
|
23
|
+
key: `Session_Processed_${toHex(peerSession)}`,
|
|
24
|
+
storage,
|
|
25
|
+
initial: [],
|
|
26
|
+
from: JSON.parse,
|
|
27
|
+
to: JSON.stringify,
|
|
28
|
+
});
|
|
29
|
+
let subscriptions = [];
|
|
30
|
+
return {
|
|
31
|
+
subscribe(callback) {
|
|
32
|
+
const fn = fromThrowable(callback, toError);
|
|
33
|
+
const unsub = transport.handleRequest(ownAccount, peerAccount, HostRemoteMessageCodec, async (message) => {
|
|
34
|
+
processedStorage.read().andThen(processed => {
|
|
35
|
+
if (processed.includes(message.requestId)) {
|
|
36
|
+
return okAsync();
|
|
37
|
+
}
|
|
38
|
+
return fn(message.data).asyncAndThen(() => processedStorage.mutate(p => p.concat(message.requestId)));
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
subscriptions.push(unsub);
|
|
42
|
+
return () => {
|
|
43
|
+
unsub();
|
|
44
|
+
subscriptions = subscriptions.filter(x => x !== unsub);
|
|
45
|
+
};
|
|
46
|
+
},
|
|
47
|
+
dispose() {
|
|
48
|
+
for (const unsub of subscriptions) {
|
|
49
|
+
unsub();
|
|
50
|
+
}
|
|
51
|
+
subscriptions = [];
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Transport } from '@novasamatech/statement-store';
|
|
2
|
+
import type { ResultAsync } from 'neverthrow';
|
|
3
|
+
import type { UserSession, UserSessionStorage } from '../user/userSessionStorage.js';
|
|
4
|
+
import type { AuthentificationStatus } from './types.js';
|
|
5
|
+
export type AuthComponent = ReturnType<typeof createAuthComponent>;
|
|
6
|
+
type Params = {
|
|
7
|
+
/**
|
|
8
|
+
* Host app Id.
|
|
9
|
+
* CAUTION! This value should be stable.
|
|
10
|
+
*/
|
|
11
|
+
appId: string;
|
|
12
|
+
/**
|
|
13
|
+
* URL for additional metadata that will be displayed during pairing process.
|
|
14
|
+
* Content of provided json shound be
|
|
15
|
+
* ```ts
|
|
16
|
+
* interface Metadata {
|
|
17
|
+
* name: string;
|
|
18
|
+
* icon: string; // url for icon. Icon should be a rasterized image with min size 256x256 px.
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
metadata: string;
|
|
23
|
+
transport: Transport;
|
|
24
|
+
userSessionStorage: UserSessionStorage;
|
|
25
|
+
};
|
|
26
|
+
export declare function createAuthComponent({ appId, metadata, transport, userSessionStorage }: Params): {
|
|
27
|
+
status: {
|
|
28
|
+
read: () => AuthentificationStatus;
|
|
29
|
+
subscribe: (fn: (value: AuthentificationStatus) => void) => () => void;
|
|
30
|
+
onFirstSubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
|
|
31
|
+
onLastUnsubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
|
|
32
|
+
};
|
|
33
|
+
authenticate(): ResultAsync<UserSession | null, Error>;
|
|
34
|
+
abortAuthentication(): void;
|
|
35
|
+
};
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { createAccountId, createLocalSessionAccount } from '@novasamatech/statement-store';
|
|
2
|
+
import { toHex } from '@polkadot-api/utils';
|
|
3
|
+
import { err, errAsync, fromPromise, fromThrowable, ok } from 'neverthrow';
|
|
4
|
+
import { AbortError } from '../../helpers/abortError.js';
|
|
5
|
+
import { toError } from '../../helpers/utils.js';
|
|
6
|
+
import { ENCR_SECRET_SEED_SIZE, SS_SECRET_SEED_SIZE, createEncrSecret, createRandomSeed, createSharedSecret, createSsHardDerivation, createSsSecret, decrypt, getEncrPub, getSsPub, khash, mergeBytes, stringToBytes, } from '../../modules/crypto.js';
|
|
7
|
+
import { createState, readonly } from '../../modules/state.js';
|
|
8
|
+
import { createUserSession } from '../../modules/statementStore.js';
|
|
9
|
+
import { HandshakeData, HandshakeResponsePayload, HandshakeResponseSensitiveData } from './scale/handshake.js';
|
|
10
|
+
export function createAuthComponent({ appId, metadata, transport, userSessionStorage }) {
|
|
11
|
+
const authStatus = createState({ step: 'none' });
|
|
12
|
+
let authResults = null;
|
|
13
|
+
let abort = null;
|
|
14
|
+
function handshake(signal) {
|
|
15
|
+
try {
|
|
16
|
+
authStatus.write({ step: 'initial' });
|
|
17
|
+
const { encrSecret, encrPublicKey, ssPublicKey } = getSecretKeys(appId);
|
|
18
|
+
const localAccount = createLocalSessionAccount(createAccountId(ssPublicKey));
|
|
19
|
+
const handshakePayload = createHandshakePayloadV1({ ssPublicKey, encrPublicKey, metadata }).andTee(payload => authStatus.write({ step: 'pairing', payload: createDeeplink(payload) }));
|
|
20
|
+
const handshakeTopic = createHandshakeTopic(localAccount, encrPublicKey);
|
|
21
|
+
const pappResponse = handshakePayload
|
|
22
|
+
.andThen(() => handshakeTopic)
|
|
23
|
+
.asyncAndThen(topic => waitForStatements(callback => transport.subscribeSession(topic, callback), signal, (statements, resolve) => {
|
|
24
|
+
for (const statement of [...statements].reverse()) {
|
|
25
|
+
if (!statement.data)
|
|
26
|
+
continue;
|
|
27
|
+
const session = retrieveSession({
|
|
28
|
+
hostAccount: localAccount,
|
|
29
|
+
encrSecret,
|
|
30
|
+
payload: statement.data.asBytes(),
|
|
31
|
+
});
|
|
32
|
+
resolve(session);
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}));
|
|
36
|
+
const userCreated = pappResponse.andThen(userSessionStorage.add);
|
|
37
|
+
return userCreated
|
|
38
|
+
.orElse(e => (AbortError.isAbortError(e) ? ok(null) : err(toError(e))))
|
|
39
|
+
.andTee(session => {
|
|
40
|
+
if (session) {
|
|
41
|
+
authStatus.write({ step: 'finished', session });
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
authStatus.write({ step: 'none' });
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
.orTee(e => authStatus.write({ step: 'error', message: e.message }));
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
return errAsync(toError(e));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const authModule = {
|
|
54
|
+
status: readonly(authStatus),
|
|
55
|
+
authenticate() {
|
|
56
|
+
if (authResults) {
|
|
57
|
+
return authResults;
|
|
58
|
+
}
|
|
59
|
+
abort = new AbortController();
|
|
60
|
+
authResults = handshake(abort.signal);
|
|
61
|
+
return authResults;
|
|
62
|
+
},
|
|
63
|
+
abortAuthentication() {
|
|
64
|
+
if (abort) {
|
|
65
|
+
authResults = null;
|
|
66
|
+
authStatus.reset();
|
|
67
|
+
abort.abort(new AbortError('Aborted by user.'));
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
return authModule;
|
|
72
|
+
}
|
|
73
|
+
const createHandshakeTopic = fromThrowable((account, encrPublicKey) => khash(account.accountId, mergeBytes(encrPublicKey, stringToBytes('topic'))), toError);
|
|
74
|
+
const createHandshakePayloadV1 = fromThrowable(({ encrPublicKey, ssPublicKey, metadata, }) => HandshakeData.enc({
|
|
75
|
+
tag: 'V1',
|
|
76
|
+
value: [ssPublicKey, encrPublicKey, metadata],
|
|
77
|
+
}), toError);
|
|
78
|
+
function parseHandshakePayload(payload) {
|
|
79
|
+
const decoded = HandshakeResponsePayload.dec(payload);
|
|
80
|
+
switch (decoded.tag) {
|
|
81
|
+
case 'V1':
|
|
82
|
+
return {
|
|
83
|
+
encrypted: decoded.value[0],
|
|
84
|
+
tmpKey: decoded.value[1],
|
|
85
|
+
};
|
|
86
|
+
default:
|
|
87
|
+
throw new Error('Unsupported handshake payload version');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function retrieveSession({ payload, encrSecret, hostAccount, }) {
|
|
91
|
+
const { encrypted, tmpKey } = parseHandshakePayload(payload);
|
|
92
|
+
const symmetricKey = createSharedSecret(encrSecret, tmpKey);
|
|
93
|
+
const decrypted = decrypt(symmetricKey, encrypted);
|
|
94
|
+
const [pappEncrPublicKey, pappAccountId] = HandshakeResponseSensitiveData.dec(decrypted);
|
|
95
|
+
const sharedSecret = createSharedSecret(encrSecret, pappEncrPublicKey);
|
|
96
|
+
const peerAccount = createAccount(pappAccountId, sharedSecret);
|
|
97
|
+
return createUserSession(hostAccount, peerAccount);
|
|
98
|
+
}
|
|
99
|
+
function getSsKeys(appId) {
|
|
100
|
+
const seed = createRandomSeed(appId, SS_SECRET_SEED_SIZE);
|
|
101
|
+
const ssSecret = createSsHardDerivation(createSsSecret(seed), '//wallet');
|
|
102
|
+
return {
|
|
103
|
+
ssSecret: ssSecret,
|
|
104
|
+
ssPublicKey: getSsPub(ssSecret),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function getEncrKeys(appId) {
|
|
108
|
+
const seed = createRandomSeed(appId, ENCR_SECRET_SEED_SIZE);
|
|
109
|
+
const encrSecret = createEncrSecret(seed);
|
|
110
|
+
return {
|
|
111
|
+
encrSecret,
|
|
112
|
+
encrPublicKey: getEncrPub(encrSecret),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function getSecretKeys(appId) {
|
|
116
|
+
const ss = getSsKeys(appId);
|
|
117
|
+
const encr = getEncrKeys(appId);
|
|
118
|
+
return {
|
|
119
|
+
...ss,
|
|
120
|
+
...encr,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function createDeeplink(payload) {
|
|
124
|
+
return `polkadotapp://pair?handshake=${toHex(payload)}`;
|
|
125
|
+
}
|
|
126
|
+
function waitForStatements(subscribe, abortSignal, callback) {
|
|
127
|
+
return fromPromise(new Promise((resolve, reject) => {
|
|
128
|
+
const unsubscribe = subscribe(statements => {
|
|
129
|
+
if (abortSignal?.aborted) {
|
|
130
|
+
unsubscribe();
|
|
131
|
+
try {
|
|
132
|
+
abortSignal.throwIfAborted();
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
reject(e);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
callback(statements, value => {
|
|
140
|
+
unsubscribe();
|
|
141
|
+
resolve(value);
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
unsubscribe();
|
|
146
|
+
reject(e);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}), toError);
|
|
150
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const HandshakeData: import("scale-ts").Codec<{
|
|
2
|
+
tag: "V1";
|
|
3
|
+
value: [import("../../../modules/crypto.js").SsPublicKey, import("../../../modules/crypto.js").EncrPublicKey, string];
|
|
4
|
+
}>;
|
|
5
|
+
export declare const HandshakeResponsePayload: import("scale-ts").Codec<{
|
|
6
|
+
tag: "V1";
|
|
7
|
+
value: [Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>];
|
|
8
|
+
}>;
|
|
9
|
+
export declare const HandshakeResponseSensitiveData: import("scale-ts").Codec<[Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>]>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Bytes, Enum, Tuple, str } from 'scale-ts';
|
|
2
|
+
import { EncrPubKey, SsPubKey } from '../../../modules/crypto.js';
|
|
3
|
+
export const HandshakeData = Enum({
|
|
4
|
+
V1: Tuple(SsPubKey, EncrPubKey, str),
|
|
5
|
+
});
|
|
6
|
+
export const HandshakeResponsePayload = Enum({
|
|
7
|
+
// [encrypted, tmp_key]
|
|
8
|
+
V1: Tuple(Bytes(), Bytes(65)),
|
|
9
|
+
});
|
|
10
|
+
export const HandshakeResponseSensitiveData = Tuple(Bytes(65), Bytes(32));
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { UserSession } from '../user/userSessionStorage.js';
|
|
2
|
+
export type AuthentificationStatus = {
|
|
3
|
+
step: 'none';
|
|
4
|
+
} | {
|
|
5
|
+
step: 'initial';
|
|
6
|
+
} | {
|
|
7
|
+
step: 'pairing';
|
|
8
|
+
payload: string;
|
|
9
|
+
} | {
|
|
10
|
+
step: 'error';
|
|
11
|
+
message: string;
|
|
12
|
+
} | {
|
|
13
|
+
step: 'finished';
|
|
14
|
+
session: UserSession;
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Statement } from '@polkadot-api/sdk-statement';
|
|
2
|
+
import type { Codec } from 'scale-ts';
|
|
3
|
+
import type { StatementAdapter } from '../adapters/statement/types.js';
|
|
4
|
+
import type { Account } from '../modules/statementStore.js';
|
|
5
|
+
import type { TransportError } from '../structs.js';
|
|
6
|
+
import type { Callback } from '../types.js';
|
|
7
|
+
export type Transport = ReturnType<typeof createTransport>;
|
|
8
|
+
type RequestMessage<T> = {
|
|
9
|
+
type: 'request';
|
|
10
|
+
requestId: string;
|
|
11
|
+
data: T;
|
|
12
|
+
};
|
|
13
|
+
type ResponseMessage = {
|
|
14
|
+
type: 'response';
|
|
15
|
+
requestId: string;
|
|
16
|
+
code: TransportError;
|
|
17
|
+
};
|
|
18
|
+
type Message<T> = RequestMessage<T> | ResponseMessage;
|
|
19
|
+
type Params = {
|
|
20
|
+
adapter: StatementAdapter;
|
|
21
|
+
};
|
|
22
|
+
export declare function createTransport({ adapter }: Params): {
|
|
23
|
+
subscribe<T>(ownAccount: Account, peerAccount: Account, codec: Codec<T>, callback: Callback<Message<T>[]>): VoidFunction;
|
|
24
|
+
subscribeSession(sessionId: Uint8Array, callback: Callback<Statement[]>): VoidFunction;
|
|
25
|
+
handleRequest<T>(ownAccount: Account, peerAccount: Account, codec: Codec<T>, callback: Callback<RequestMessage<T>>): VoidFunction;
|
|
26
|
+
};
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { fromThrowable, ok } from 'neverthrow';
|
|
2
|
+
import { seq } from '../helpers/result.js';
|
|
3
|
+
import { nonNullable, toError } from '../helpers/utils.js';
|
|
4
|
+
import { decrypt } from '../modules/crypto.js';
|
|
5
|
+
import { createSessionId } from '../modules/statementStore.js';
|
|
6
|
+
import { StatementData } from '../structs.js';
|
|
7
|
+
const decryptResults = fromThrowable(decrypt, toError);
|
|
8
|
+
export function createTransport({ adapter }) {
|
|
9
|
+
function mapMessage(statementData) {
|
|
10
|
+
switch (statementData.tag) {
|
|
11
|
+
case 'request':
|
|
12
|
+
return statementData.value.data.map((data, index) => ({
|
|
13
|
+
type: 'request',
|
|
14
|
+
requestId: `${statementData.value.requestId}-${index.toString()}`,
|
|
15
|
+
data,
|
|
16
|
+
}));
|
|
17
|
+
case 'response':
|
|
18
|
+
return [
|
|
19
|
+
{
|
|
20
|
+
type: 'response',
|
|
21
|
+
requestId: statementData.value.requestId,
|
|
22
|
+
code: statementData.value.responseCode,
|
|
23
|
+
},
|
|
24
|
+
];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const transport = {
|
|
28
|
+
subscribe(ownAccount, peerAccount, codec, callback) {
|
|
29
|
+
const sessionId = createSessionId(peerAccount, ownAccount);
|
|
30
|
+
const statementDataCodec = StatementData(codec);
|
|
31
|
+
return adapter.subscribeStatements([sessionId], statements => {
|
|
32
|
+
seq(...statements.map(statement => {
|
|
33
|
+
if (!statement.data)
|
|
34
|
+
return ok(null);
|
|
35
|
+
return decryptResults(peerAccount.publicKey, statement.data.asBytes())
|
|
36
|
+
.map(statementDataCodec.dec)
|
|
37
|
+
.orElse(() => ok(null));
|
|
38
|
+
}))
|
|
39
|
+
.map(messages => messages.filter(nonNullable).flatMap(mapMessage))
|
|
40
|
+
.andTee(messages => {
|
|
41
|
+
if (messages.length > 0) {
|
|
42
|
+
callback(messages);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
subscribeSession(sessionId, callback) {
|
|
48
|
+
return adapter.subscribeStatements([sessionId], callback);
|
|
49
|
+
},
|
|
50
|
+
handleRequest(ownAccount, peerAccount, codec, callback) {
|
|
51
|
+
return transport.subscribe(ownAccount, peerAccount, codec, messages => {
|
|
52
|
+
messages.filter(m => m.type === 'request').forEach(callback);
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
return transport;
|
|
57
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const HostRemoteMessageCodec: import("scale-ts").Codec<{
|
|
2
|
+
messageId: string;
|
|
3
|
+
data: {
|
|
4
|
+
tag: "v1";
|
|
5
|
+
value: {
|
|
6
|
+
tag: "Disconnected";
|
|
7
|
+
value: undefined;
|
|
8
|
+
} | {
|
|
9
|
+
tag: "SigningRequest";
|
|
10
|
+
value: Uint8Array<ArrayBufferLike>;
|
|
11
|
+
} | {
|
|
12
|
+
tag: "SigningResponse";
|
|
13
|
+
value: Uint8Array<ArrayBufferLike>;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Bytes, Enum, Struct, _void, str } from 'scale-ts';
|
|
2
|
+
export const HostRemoteMessageCodec = Struct({
|
|
3
|
+
messageId: str,
|
|
4
|
+
data: Enum({
|
|
5
|
+
v1: Enum({
|
|
6
|
+
Disconnected: _void,
|
|
7
|
+
// TODO implement
|
|
8
|
+
SigningRequest: Bytes(),
|
|
9
|
+
// TODO implement
|
|
10
|
+
SigningResponse: Bytes(),
|
|
11
|
+
}),
|
|
12
|
+
}),
|
|
13
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { StorageAdapter } from '../../adapters/storage/types.js';
|
|
2
|
+
import type { Transport } from '../../modules/transport/transport.js';
|
|
3
|
+
import type { UserSession, UserSessionStorage } from './userSessionStorage.js';
|
|
4
|
+
export type UserSessionsComponent = ReturnType<typeof createUserSessionsComponent>;
|
|
5
|
+
type Params = {
|
|
6
|
+
transport: Transport;
|
|
7
|
+
storage: StorageAdapter;
|
|
8
|
+
userSessionStorage: UserSessionStorage;
|
|
9
|
+
};
|
|
10
|
+
export declare function createUserSessionsComponent({ userSessionStorage, storage, transport }: Params): {
|
|
11
|
+
sessions: {
|
|
12
|
+
add(value: UserSession): import("neverthrow").ResultAsync<UserSession, Error>;
|
|
13
|
+
mutate(fn: (value: UserSession[]) => UserSession[]): import("neverthrow").ResultAsync<UserSession[], Error>;
|
|
14
|
+
read(): any;
|
|
15
|
+
write(value: UserSession[]): any;
|
|
16
|
+
clear(): any;
|
|
17
|
+
subscribe(fn: (value: UserSession[]) => void): () => void;
|
|
18
|
+
};
|
|
19
|
+
disconnect: (session: UserSession) => import("neverthrow").ResultAsync<undefined, Error>;
|
|
20
|
+
destroy(): void;
|
|
21
|
+
};
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { okAsync } from 'neverthrow';
|
|
2
|
+
import { createSSOSession } from './ssoSession.js';
|
|
3
|
+
export function createUserSessionsComponent({ userSessionStorage, storage, transport }) {
|
|
4
|
+
let unsubStatements = null;
|
|
5
|
+
const disconnect = (session) => {
|
|
6
|
+
return userSessionStorage.mutate(sessions => sessions.filter(s => s.id !== session.id)).map(() => undefined);
|
|
7
|
+
};
|
|
8
|
+
const unsubSessions = userSessionStorage.subscribe(userSessions => {
|
|
9
|
+
if (unsubStatements) {
|
|
10
|
+
unsubStatements();
|
|
11
|
+
unsubStatements = null;
|
|
12
|
+
}
|
|
13
|
+
const ssoSessions = [];
|
|
14
|
+
for (const userSession of userSessions) {
|
|
15
|
+
const session = createSSOSession({
|
|
16
|
+
ownAccount: userSession.host,
|
|
17
|
+
peerAccount: userSession.peer,
|
|
18
|
+
storage,
|
|
19
|
+
transport,
|
|
20
|
+
});
|
|
21
|
+
session.subscribe(message => {
|
|
22
|
+
switch (message.data.tag) {
|
|
23
|
+
case 'v1': {
|
|
24
|
+
switch (message.data.value.tag) {
|
|
25
|
+
case 'Disconnected':
|
|
26
|
+
return disconnect(userSession).map(() => true);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return okAsync(false);
|
|
31
|
+
});
|
|
32
|
+
ssoSessions.push(session);
|
|
33
|
+
}
|
|
34
|
+
unsubStatements = () => {
|
|
35
|
+
for (const session of ssoSessions) {
|
|
36
|
+
session.dispose();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
return {
|
|
41
|
+
sessions: userSessionStorage,
|
|
42
|
+
disconnect,
|
|
43
|
+
destroy() {
|
|
44
|
+
unsubSessions();
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// function createDisconnectMessage(ssSecret: SsSecret, topic: Uint8Array) {
|
|
49
|
+
// const statement = createStatement(ssSecret, {
|
|
50
|
+
// priority: 0,
|
|
51
|
+
// channel: createRequestChannel(topic),
|
|
52
|
+
// topics: [topic],
|
|
53
|
+
// data: SSOMessage.enc({
|
|
54
|
+
// tag: 'Disconnected',
|
|
55
|
+
// value: undefined,
|
|
56
|
+
// }),
|
|
57
|
+
// });
|
|
58
|
+
// }
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SessionParams } from '../../modules/session/session.js';
|
|
2
|
+
import type { Session } from '../../modules/session/types.js';
|
|
3
|
+
import { HostRemoteMessageCodec } from './codec.js';
|
|
4
|
+
export type SsoSession = Session<typeof HostRemoteMessageCodec>;
|
|
5
|
+
export declare function createSSOSession({ ownAccount, peerAccount, transport, storage, }: Omit<SessionParams<any>, 'codec'>): SsoSession;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createSession } from '../../modules/session/session.js';
|
|
2
|
+
import { HostRemoteMessageCodec } from './codec.js';
|
|
3
|
+
export function createSSOSession({ ownAccount, peerAccount, transport, storage, }) {
|
|
4
|
+
return createSession({ ownAccount, peerAccount, transport, storage, codec: HostRemoteMessageCodec });
|
|
5
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ResultAsync } from 'neverthrow';
|
|
2
|
+
import type { StorageAdapter } from '../../adapters/storage/types.js';
|
|
3
|
+
import type { Callback } from '../../types.js';
|
|
4
|
+
import type { UserSecrets, UserSession } from './types.js';
|
|
5
|
+
export type UserStorage = ReturnType<typeof createUserStorage>;
|
|
6
|
+
type Params = {
|
|
7
|
+
appId: string;
|
|
8
|
+
storage: StorageAdapter;
|
|
9
|
+
};
|
|
10
|
+
export declare const createUserStorage: ({ appId, storage }: Params) => {
|
|
11
|
+
sessions: {
|
|
12
|
+
read(accountId: string): ResultAsync<UserSession | null, Error>;
|
|
13
|
+
readSelectedUser(): ResultAsync<UserSession | null, Error>;
|
|
14
|
+
subscribeSessions(callback: Callback<UserSession[]>): () => void;
|
|
15
|
+
create(user: UserSession, secrets: UserSecrets): ResultAsync<UserSession, Error>;
|
|
16
|
+
remove(accountId: string): ResultAsync<void, Error>;
|
|
17
|
+
readSecrets(accountId: string): ResultAsync<UserSecrets | null, Error>;
|
|
18
|
+
};
|
|
19
|
+
accounts: {
|
|
20
|
+
read(): ResultAsync<string[], Error>;
|
|
21
|
+
subscribe(callback: (accounts: string[]) => void): () => void;
|
|
22
|
+
readSelectedUser(): ResultAsync<string | null, Error>;
|
|
23
|
+
subscribeSelectedAccount(callback: (accountId: string | null) => void): VoidFunction;
|
|
24
|
+
select(accountId: string): ResultAsync<undefined, Error>;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export {};
|