@novasamatech/host-papp 0.5.0-6 → 0.5.0-8
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/adapters/identity/rpc.d.ts +4 -2
- 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/adapters/transport/rpc.d.ts +3 -0
- package/dist/adapters/transport/rpc.js +51 -0
- package/dist/adapters/transport/types.d.ts +6 -0
- package/dist/adapters/transport/types.js +1 -0
- 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/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/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/utils.d.ts +2 -1
- package/dist/helpers/utils.js +11 -2
- package/dist/helpers.d.ts +1 -0
- package/dist/helpers.js +3 -0
- package/dist/index.d.ts +2 -1
- package/dist/modules/accounts.d.ts +1 -0
- package/dist/modules/accounts.js +2 -0
- package/dist/modules/crypto.d.ts +5 -2
- package/dist/modules/crypto.js +16 -5
- package/dist/modules/secretStorage.d.ts +7 -9
- package/dist/modules/secretStorage.js +20 -33
- 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 +46 -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 +95 -95
- 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 +14 -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 +18 -0
- package/dist/modules/transport/transport.d.ts +27 -0
- package/dist/modules/transport/transport.js +56 -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 +8 -7
- package/dist/papp.js +19 -31
- package/dist/structs.d.ts +10 -10
- package/dist/structs.js +17 -13
- package/dist/types.d.ts +1 -1
- package/package.json +8 -6
|
@@ -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,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,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(): import("neverthrow").ResultAsync<UserSession[], Error>;
|
|
15
|
+
write(value: UserSession[]): import("neverthrow").ResultAsync<null, Error> | import("neverthrow").ResultAsync<UserSession[], Error>;
|
|
16
|
+
clear(): import("neverthrow").ResultAsync<void, Error>;
|
|
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 {};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { okAsync } from 'neverthrow';
|
|
2
|
+
import { callbackRaceResolver } from '../../helpers/callbackRaceResolver.js';
|
|
3
|
+
import { seq, seqAsync } from '../../helpers/result.js';
|
|
4
|
+
import { nonNullable } from '../../helpers/utils.js';
|
|
5
|
+
import { createSecretStorage } from '../../modules/secretStorage.js';
|
|
6
|
+
import { storageListView, storageView } from '../../modules/storageView.js';
|
|
7
|
+
export const createUserStorage = ({ appId, storage }) => {
|
|
8
|
+
const secretStorage = createSecretStorage(appId, storage);
|
|
9
|
+
const usersStorage = createSessionsStorage(storage);
|
|
10
|
+
const selectedUserStorage = createSelectedUserStorage(storage);
|
|
11
|
+
const store = {
|
|
12
|
+
sessions: {
|
|
13
|
+
read(accountId) {
|
|
14
|
+
const sessions = seqAsync(secretStorage.readSessionId(accountId, 'A'), secretStorage.readSessionId(accountId, 'B'));
|
|
15
|
+
return sessions.map(([sessionIdA, sessionIdB]) => {
|
|
16
|
+
if (nonNullable(sessionIdA) && nonNullable(sessionIdB)) {
|
|
17
|
+
return { accountId, sessionIdA, sessionIdB };
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
readSelectedUser() {
|
|
23
|
+
return store.accounts.readSelectedUser().andThen(selectedUser => {
|
|
24
|
+
if (selectedUser === null) {
|
|
25
|
+
return okAsync(null);
|
|
26
|
+
}
|
|
27
|
+
return store.sessions.read(selectedUser).andThen(user => {
|
|
28
|
+
if (user === null) {
|
|
29
|
+
return selectedUserStorage.clear().map(() => user);
|
|
30
|
+
}
|
|
31
|
+
return okAsync(user);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
},
|
|
35
|
+
subscribeSessions(callback) {
|
|
36
|
+
const resolver = callbackRaceResolver(callback, async (accounts) => {
|
|
37
|
+
if (accounts.length === 0) {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
return seq(...(await Promise.all(accounts.map(store.sessions.read))))
|
|
41
|
+
.map(sessions => sessions.filter(nonNullable))
|
|
42
|
+
.unwrapOr([]);
|
|
43
|
+
});
|
|
44
|
+
return store.accounts.subscribe(resolver);
|
|
45
|
+
},
|
|
46
|
+
create(user, secrets) {
|
|
47
|
+
return usersStorage
|
|
48
|
+
.add(user.accountId)
|
|
49
|
+
.andThen(() => seqAsync(secretStorage.writeSsSecret(user.accountId, secrets.ss), secretStorage.writeEncrSecret(user.accountId, secrets.encr), secretStorage.writeSharedSecret(user.accountId, secrets.sharedSecret), secretStorage.writeSessionId(user.accountId, 'A', user.sessionIdA), secretStorage.writeSessionId(user.accountId, 'B', user.sessionIdB), selectedUserStorage.write(user.accountId)).map(() => user));
|
|
50
|
+
},
|
|
51
|
+
remove(accountId) {
|
|
52
|
+
const op = seqAsync(secretStorage.clearSsSecret(accountId), secretStorage.clearEncrSecret(accountId), secretStorage.clearSharedSecret(accountId), secretStorage.clearSessionId(accountId, 'A'), secretStorage.clearSessionId(accountId, 'B'), usersStorage.remove(accountId), selectedUserStorage.read());
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
54
|
+
return op.andThen(([_1, _2, _3, _4, _5, users, selectedUser]) => {
|
|
55
|
+
if (selectedUser === accountId) {
|
|
56
|
+
const nextSelectedUser = users.at(0);
|
|
57
|
+
if (nextSelectedUser) {
|
|
58
|
+
return selectedUserStorage.write(nextSelectedUser).map(() => undefined);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return selectedUserStorage.clear();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return okAsync(undefined);
|
|
65
|
+
});
|
|
66
|
+
},
|
|
67
|
+
readSecrets(accountId) {
|
|
68
|
+
const op = seqAsync(secretStorage.readSsSecret(accountId), secretStorage.readEncrSecret(accountId), secretStorage.readSharedSecret(accountId));
|
|
69
|
+
return op.map(([ss, encr, sharedSecret]) => {
|
|
70
|
+
if (nonNullable(ss) && nonNullable(encr) && nonNullable(sharedSecret)) {
|
|
71
|
+
return { ss, encr, sharedSecret };
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
accounts: {
|
|
78
|
+
read() {
|
|
79
|
+
return usersStorage.read();
|
|
80
|
+
},
|
|
81
|
+
subscribe(callback) {
|
|
82
|
+
return usersStorage.subscribe(callback);
|
|
83
|
+
},
|
|
84
|
+
readSelectedUser() {
|
|
85
|
+
return selectedUserStorage.read();
|
|
86
|
+
},
|
|
87
|
+
subscribeSelectedAccount(callback) {
|
|
88
|
+
return selectedUserStorage.subscribe(callback);
|
|
89
|
+
},
|
|
90
|
+
select(accountId) {
|
|
91
|
+
return selectedUserStorage.write(accountId).map(() => undefined);
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
return store;
|
|
96
|
+
};
|
|
97
|
+
const createSessionsStorage = (storage) => {
|
|
98
|
+
const view = storageListView({
|
|
99
|
+
storage,
|
|
100
|
+
key: 'Users',
|
|
101
|
+
autosync: true,
|
|
102
|
+
initial: [],
|
|
103
|
+
from: x => JSON.parse(x),
|
|
104
|
+
to: x => JSON.stringify(x),
|
|
105
|
+
});
|
|
106
|
+
return {
|
|
107
|
+
read() {
|
|
108
|
+
return view.read();
|
|
109
|
+
},
|
|
110
|
+
add(user) {
|
|
111
|
+
return view.mutate(users => {
|
|
112
|
+
if (users.some(x => x === user)) {
|
|
113
|
+
throw new Error(`User ${user} already exists.`);
|
|
114
|
+
}
|
|
115
|
+
return users.concat(user);
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
remove(user) {
|
|
119
|
+
return view.mutate(users => {
|
|
120
|
+
const newUsers = users.filter(x => x !== user);
|
|
121
|
+
if (newUsers.length !== users.length) {
|
|
122
|
+
return newUsers;
|
|
123
|
+
}
|
|
124
|
+
return users;
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
subscribe(callback) {
|
|
128
|
+
return view.subscribe(v => {
|
|
129
|
+
callback(v ?? []);
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
const createSelectedUserStorage = (storage) => {
|
|
135
|
+
return storageView({
|
|
136
|
+
storage,
|
|
137
|
+
key: 'SelectedUser',
|
|
138
|
+
autosync: true,
|
|
139
|
+
initial: null,
|
|
140
|
+
from: x => x,
|
|
141
|
+
to: x => x,
|
|
142
|
+
});
|
|
143
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { StorageAdapter } from '../../adapters/storage/types.js';
|
|
2
|
+
import type { Account } from '../../modules/session/types.js';
|
|
3
|
+
export type UserSessionStorage = ReturnType<typeof createUserSessionStorage>;
|
|
4
|
+
export type UserSession = {
|
|
5
|
+
id: string;
|
|
6
|
+
host: Account;
|
|
7
|
+
peer: Account;
|
|
8
|
+
};
|
|
9
|
+
type Params = {
|
|
10
|
+
storage: StorageAdapter;
|
|
11
|
+
};
|
|
12
|
+
export declare const createUserSessionStorage: ({ storage }: Params) => {
|
|
13
|
+
add(value: UserSession): import("neverthrow").ResultAsync<UserSession, Error>;
|
|
14
|
+
mutate(fn: (value: UserSession[]) => UserSession[]): import("neverthrow").ResultAsync<UserSession[], Error>;
|
|
15
|
+
read(): import("neverthrow").ResultAsync<UserSession[], Error>;
|
|
16
|
+
write(value: UserSession[]): import("neverthrow").ResultAsync<null, Error> | import("neverthrow").ResultAsync<UserSession[], Error>;
|
|
17
|
+
clear(): import("neverthrow").ResultAsync<void, Error>;
|
|
18
|
+
subscribe(fn: (value: UserSession[]) => void): () => void;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { fromHex, toHex } from 'polkadot-api/utils';
|
|
2
|
+
import { Bytes, Option, Struct, Vector, str } from 'scale-ts';
|
|
3
|
+
import { storageListView } from '../../modules/storageView.js';
|
|
4
|
+
const accountCodec = Struct({
|
|
5
|
+
accountId: Bytes(),
|
|
6
|
+
publicKey: Bytes(),
|
|
7
|
+
pin: Option(str),
|
|
8
|
+
});
|
|
9
|
+
const userSessionCodec = Struct({
|
|
10
|
+
id: str,
|
|
11
|
+
host: accountCodec,
|
|
12
|
+
peer: accountCodec,
|
|
13
|
+
});
|
|
14
|
+
const userSessionsCodec = Vector(userSessionCodec);
|
|
15
|
+
export const createUserSessionStorage = ({ storage }) => {
|
|
16
|
+
return storageListView({
|
|
17
|
+
storage,
|
|
18
|
+
key: 'Sessions',
|
|
19
|
+
autosync: true,
|
|
20
|
+
initial: [],
|
|
21
|
+
from: x => userSessionsCodec.dec(fromHex(x)),
|
|
22
|
+
to: x => toHex(userSessionsCodec.enc(x)),
|
|
23
|
+
});
|
|
24
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { StatementAdapter } from '../adapters/statement/types.js';
|
|
2
|
+
import type { StorageAdapter } from '../adapters/storage/types.js';
|
|
3
|
+
import type { Result } from '../helpers/result.js';
|
|
4
|
+
import type { EncrPublicKey, SsPublicKey } from '../modules/crypto.js';
|
|
5
|
+
import type { UserSession } from '../modules/userStorage.js';
|
|
6
|
+
export declare const HandshakeData: import("scale-ts").Codec<{
|
|
7
|
+
tag: "V1";
|
|
8
|
+
value: [SsPublicKey, EncrPublicKey, string];
|
|
9
|
+
}>;
|
|
10
|
+
export declare const HandshakeResponsePayload: import("scale-ts").Codec<{
|
|
11
|
+
tag: "V1";
|
|
12
|
+
value: [Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>];
|
|
13
|
+
}>;
|
|
14
|
+
export declare const HandshakeResponseSensitiveData: import("scale-ts").Codec<[Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>]>;
|
|
15
|
+
export type AuthentificationStatus = {
|
|
16
|
+
step: 'none';
|
|
17
|
+
} | {
|
|
18
|
+
step: 'initial';
|
|
19
|
+
} | {
|
|
20
|
+
step: 'pairing';
|
|
21
|
+
payload: string;
|
|
22
|
+
} | {
|
|
23
|
+
step: 'error';
|
|
24
|
+
message: string;
|
|
25
|
+
} | {
|
|
26
|
+
step: 'finished';
|
|
27
|
+
user: UserSession;
|
|
28
|
+
};
|
|
29
|
+
type Params = {
|
|
30
|
+
/**
|
|
31
|
+
* Host app Id.
|
|
32
|
+
* CAUTION! This value should be stable.
|
|
33
|
+
*/
|
|
34
|
+
appId: string;
|
|
35
|
+
/**
|
|
36
|
+
* URL for additional metadata that will be displayed during pairing process.
|
|
37
|
+
* Content of provided json shound be
|
|
38
|
+
* ```ts
|
|
39
|
+
* interface Metadata {
|
|
40
|
+
* name: string;
|
|
41
|
+
* icon: string; // url for icon. Icon should be a rasterized image with min size 256x256 px.
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
metadata: string;
|
|
46
|
+
statements: StatementAdapter;
|
|
47
|
+
storage: StorageAdapter;
|
|
48
|
+
};
|
|
49
|
+
export declare function createUserComponent({ appId, metadata, statements, storage }: Params): {
|
|
50
|
+
authStatus: {
|
|
51
|
+
touched(): boolean;
|
|
52
|
+
read(): AuthentificationStatus;
|
|
53
|
+
write(value: AuthentificationStatus): AuthentificationStatus;
|
|
54
|
+
reset(): void;
|
|
55
|
+
subscribe(fn: (value: AuthentificationStatus) => void): () => void;
|
|
56
|
+
onFirstSubscribe(callback: VoidFunction): import("nanoevents").Unsubscribe;
|
|
57
|
+
onLastUnsubscribe(callback: VoidFunction): import("nanoevents").Unsubscribe;
|
|
58
|
+
};
|
|
59
|
+
storage: {
|
|
60
|
+
sessions: {
|
|
61
|
+
read(accountId: string): Promise<Result<UserSession | null>>;
|
|
62
|
+
readSelectedUser(): Promise<Result<UserSession | null>>;
|
|
63
|
+
create(user: UserSession): Promise<Result<UserSession>>;
|
|
64
|
+
remove(accountId: string): Promise<Result<void, Error>>;
|
|
65
|
+
};
|
|
66
|
+
accounts: {
|
|
67
|
+
read(): Promise<Result<string[], Error>>;
|
|
68
|
+
select(accountId: string): Promise<Result<string | null, Error> | Result<null, Error>>;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
authenticate(): Promise<Result<UserSession | null>>;
|
|
72
|
+
abortAuthentication(): void;
|
|
73
|
+
};
|
|
74
|
+
export {};
|