@novasamatech/host-papp 0.5.0-7 → 0.5.0-9

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.
Files changed (82) hide show
  1. package/dist/adapters/identity/rpc.d.ts +3 -3
  2. package/dist/components/sso/index.d.ts +36 -0
  3. package/dist/components/sso/index.js +150 -0
  4. package/dist/components/sso/scale/handshake.d.ts +9 -0
  5. package/dist/components/sso/scale/handshake.js +10 -0
  6. package/dist/components/sso/types.d.ts +15 -0
  7. package/dist/components/sso/types.js +1 -0
  8. package/dist/components/user/index.d.ts +3 -3
  9. package/dist/components/user/userSessionStorage.d.ts +3 -3
  10. package/dist/components/user/userSessionStorage.js +1 -1
  11. package/dist/crypto.d.ts +23 -0
  12. package/dist/crypto.js +51 -0
  13. package/dist/helpers/state.d.ts +16 -0
  14. package/dist/helpers/state.js +51 -0
  15. package/dist/helpers/zipWith.d.ts +4 -0
  16. package/dist/helpers/zipWith.js +11 -0
  17. package/dist/identity/impl.d.ts +6 -0
  18. package/dist/identity/impl.js +68 -0
  19. package/dist/identity/rpcAdapter.d.ts +3 -0
  20. package/dist/identity/rpcAdapter.js +38 -0
  21. package/dist/identity/types.d.ts +22 -0
  22. package/dist/identity/types.js +1 -0
  23. package/dist/index.d.ts +4 -4
  24. package/dist/index.js +1 -7
  25. package/dist/modules/crypto.d.ts +4 -8
  26. package/dist/modules/crypto.js +10 -43
  27. package/dist/modules/secretStorage.d.ts +12 -9
  28. package/dist/modules/secretStorage.js +36 -32
  29. package/dist/modules/session/helpers.d.ts +1 -1
  30. package/dist/modules/session/helpers.js +2 -2
  31. package/dist/modules/session/session.js +18 -14
  32. package/dist/modules/statementStore.d.ts +2 -2
  33. package/dist/modules/statementStore.js +4 -2
  34. package/dist/modules/storageView.d.ts +6 -6
  35. package/dist/modules/transport/crypto.js +5 -3
  36. package/dist/modules/transport/transport.d.ts +17 -2
  37. package/dist/modules/transport/transport.js +15 -5
  38. package/dist/papp.d.ts +24 -13
  39. package/dist/papp.js +18 -37
  40. package/dist/sso/auth/impl.d.ts +48 -0
  41. package/dist/sso/auth/impl.js +148 -0
  42. package/dist/sso/auth/scale/handshake.d.ts +9 -0
  43. package/dist/sso/auth/scale/handshake.js +10 -0
  44. package/dist/sso/auth/types.d.ts +15 -0
  45. package/dist/sso/auth/types.js +1 -0
  46. package/dist/sso/session/impl.d.ts +23 -0
  47. package/dist/sso/session/impl.js +57 -0
  48. package/dist/sso/session/scale/remoteMessage.d.ts +10 -0
  49. package/dist/sso/session/scale/remoteMessage.js +13 -0
  50. package/dist/sso/session/sessionManager.d.ts +23 -0
  51. package/dist/sso/session/sessionManager.js +58 -0
  52. package/dist/sso/session/ssoSession.d.ts +8 -0
  53. package/dist/sso/session/ssoSession.js +5 -0
  54. package/dist/sso/session/ssoSessionStorage.d.ts +21 -0
  55. package/dist/sso/session/ssoSessionStorage.js +20 -0
  56. package/dist/sso/session/types.d.ts +6 -0
  57. package/dist/sso/session/types.js +1 -0
  58. package/dist/sso/session/userSessionStorage.d.ts +21 -0
  59. package/dist/sso/session/userSessionStorage.js +20 -0
  60. package/dist/sso/sessionManager/impl.d.ts +22 -0
  61. package/dist/sso/sessionManager/impl.js +71 -0
  62. package/dist/sso/sessionManager/repository/ssoSessionRepository.d.ts +22 -0
  63. package/dist/sso/sessionManager/repository/ssoSessionRepository.js +27 -0
  64. package/dist/sso/sessionManager/scale/remoteMessage.d.ts +23 -0
  65. package/dist/sso/sessionManager/scale/remoteMessage.js +14 -0
  66. package/dist/sso/sessionManager/ssoSession.d.ts +23 -0
  67. package/dist/sso/sessionManager/ssoSession.js +69 -0
  68. package/dist/sso/sessionManager/ssoSessionProver.d.ts +4 -0
  69. package/dist/sso/sessionManager/ssoSessionProver.js +35 -0
  70. package/dist/sso/sessionManager/types.d.ts +6 -0
  71. package/dist/sso/sessionManager/types.js +1 -0
  72. package/dist/sso/sessionManager/userSession.d.ts +24 -0
  73. package/dist/sso/sessionManager/userSession.js +75 -0
  74. package/dist/sso/ssoSessionProver.d.ts +4 -0
  75. package/dist/sso/ssoSessionProver.js +35 -0
  76. package/dist/sso/ssoSessionRepository.d.ts +18 -0
  77. package/dist/sso/ssoSessionRepository.js +27 -0
  78. package/dist/sso/userSecretRepository.d.ts +16 -0
  79. package/dist/sso/userSecretRepository.js +44 -0
  80. package/dist/sso/userSessionRepository.d.ts +18 -0
  81. package/dist/sso/userSessionRepository.js +27 -0
  82. package/package.json +5 -3
@@ -1,6 +1,6 @@
1
+ import type { LazyClient } from '@novasamatech/statement-store';
2
+ import type { StorageAdapter } from '@novasamatech/storage-adapter';
1
3
  import { ResultAsync } from 'neverthrow';
2
- import type { LazyClientAdapter } from '../lazyClient/types.js';
3
- import type { StorageAdapter } from '../storage/types.js';
4
4
  import type { Identity, IdentityAdapter } from './types.js';
5
5
  export declare function createCachedIdentityRequester(storage: StorageAdapter, getKey: (accountId: string) => string, request: (accounts: string[]) => ResultAsync<(Identity | null)[], Error>): (accounts: string[]) => ResultAsync<Record<string, Identity | null>, Error>;
6
- export declare function createIdentityRpcAdapter(lazyClient: LazyClientAdapter, storage: StorageAdapter): IdentityAdapter;
6
+ export declare function createIdentityRpcAdapter(lazyClient: LazyClient, storage: StorageAdapter): IdentityAdapter;
@@ -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 {};
@@ -11,9 +11,9 @@ export declare function createUserSessionsComponent({ userSessionStorage, storag
11
11
  sessions: {
12
12
  add(value: UserSession): import("neverthrow").ResultAsync<UserSession, Error>;
13
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>;
14
+ read(): any;
15
+ write(value: UserSession[]): any;
16
+ clear(): any;
17
17
  subscribe(fn: (value: UserSession[]) => void): () => void;
18
18
  };
19
19
  disconnect: (session: UserSession) => import("neverthrow").ResultAsync<undefined, Error>;
@@ -12,9 +12,9 @@ type Params = {
12
12
  export declare const createUserSessionStorage: ({ storage }: Params) => {
13
13
  add(value: UserSession): import("neverthrow").ResultAsync<UserSession, Error>;
14
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>;
15
+ read(): any;
16
+ write(value: UserSession[]): any;
17
+ clear(): any;
18
18
  subscribe(fn: (value: UserSession[]) => void): () => void;
19
19
  };
20
20
  export {};
@@ -1,4 +1,4 @@
1
- import { fromHex, toHex } from 'polkadot-api/utils';
1
+ import { fromHex, toHex } from '@polkadot-api/utils';
2
2
  import { Bytes, Option, Struct, Vector, str } from 'scale-ts';
3
3
  import { storageListView } from '../../modules/storageView.js';
4
4
  const accountCodec = Struct({
@@ -0,0 +1,23 @@
1
+ import type { Codec } from 'scale-ts';
2
+ import type { Branded } from './types.js';
3
+ export type SsPublicKey = Branded<Uint8Array, 'SsPublicKey'>;
4
+ export type SsSecret = Branded<Uint8Array, 'SsSecret'>;
5
+ export type EncrPublicKey = Branded<Uint8Array, 'EncrPublicKey'>;
6
+ export type EncrSecret = Branded<Uint8Array, 'EncrSecret'>;
7
+ export type SharedSecret = Branded<Uint8Array, 'SharedSecret'>;
8
+ export type SharedSession = Branded<Uint8Array, 'SharedSession'>;
9
+ export declare function BrandedBytesCodec<T extends Uint8Array>(length?: number): Codec<T>;
10
+ export declare const SsPubKey: Codec<SsPublicKey>;
11
+ export declare const EncrPubKey: Codec<EncrPublicKey>;
12
+ export declare function stringToBytes(str: string): Uint8Array<ArrayBuffer>;
13
+ export declare function bytesToString(bytes: Uint8Array): string;
14
+ export declare const SS_SECRET_SEED_SIZE = 32;
15
+ export declare function createSsSecret(seed?: Uint8Array): SsSecret;
16
+ export declare function createSsHardDerivation(secret: SsSecret, derivation: string | number): SsSecret;
17
+ export declare function getSsPub(secret: SsSecret): SsPublicKey;
18
+ export declare function signWithSsSecret(secret: SsSecret, message: Uint8Array): Uint8Array<ArrayBufferLike>;
19
+ export declare function verifyWithSsSecret(message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean;
20
+ export declare const ENCR_SECRET_SEED_SIZE = 48;
21
+ export declare function createEncrSecret(seed?: Uint8Array): EncrSecret;
22
+ export declare function getEncrPub(secret: EncrSecret): EncrPublicKey;
23
+ export declare function createSharedSecret(secret: EncrSecret, publicKey: Uint8Array): SharedSecret;
package/dist/crypto.js ADDED
@@ -0,0 +1,51 @@
1
+ import { p256 } from '@noble/curves/nist.js';
2
+ import { randomBytes } from '@noble/hashes/utils.js';
3
+ import { HDKD as sr25519HDKD, getPublicKey as sr25519GetPublicKey, secretFromSeed as sr25519SecretFromSeed, sign as sr25519Sign, verify as sr25519Verify, } from '@scure/sr25519';
4
+ import { Bytes, str, u32 } from 'scale-ts';
5
+ // schemas
6
+ export function BrandedBytesCodec(length) {
7
+ return Bytes(length);
8
+ }
9
+ export const SsPubKey = BrandedBytesCodec(32);
10
+ export const EncrPubKey = BrandedBytesCodec(65);
11
+ // helpers
12
+ const textEncoder = new TextEncoder();
13
+ const textDecoder = new TextDecoder();
14
+ export function stringToBytes(str) {
15
+ return textEncoder.encode(str);
16
+ }
17
+ export function bytesToString(bytes) {
18
+ return textDecoder.decode(bytes);
19
+ }
20
+ // statement store key pair
21
+ export const SS_SECRET_SEED_SIZE = 32;
22
+ export function createSsSecret(seed = randomBytes(SS_SECRET_SEED_SIZE)) {
23
+ return sr25519SecretFromSeed(seed);
24
+ }
25
+ export function createSsHardDerivation(secret, derivation) {
26
+ const chainCode = new Uint8Array(32);
27
+ chainCode.set(typeof derivation === 'string' ? str.enc(derivation) : u32.enc(derivation));
28
+ return sr25519HDKD.secretHard(secret, chainCode);
29
+ }
30
+ export function getSsPub(secret) {
31
+ return sr25519GetPublicKey(secret);
32
+ }
33
+ export function signWithSsSecret(secret, message) {
34
+ return sr25519Sign(secret, message);
35
+ }
36
+ export function verifyWithSsSecret(message, signature, publicKey) {
37
+ return sr25519Verify(message, signature, publicKey);
38
+ }
39
+ // encryption key pair
40
+ export const ENCR_SECRET_SEED_SIZE = 48;
41
+ export function createEncrSecret(seed = randomBytes(ENCR_SECRET_SEED_SIZE)) {
42
+ const { secretKey } = p256.keygen(seed);
43
+ return secretKey;
44
+ }
45
+ export function getEncrPub(secret) {
46
+ return p256.getPublicKey(secret, false);
47
+ }
48
+ export function createSharedSecret(secret, publicKey) {
49
+ // slicing first byte: @noble/curves adds y offset at the start
50
+ return p256.getSharedSecret(secret, publicKey).slice(1, 33);
51
+ }
@@ -0,0 +1,16 @@
1
+ type State<T> = ReturnType<typeof createState<T>>;
2
+ export declare function createState<T>(initial: T): {
3
+ read(): T;
4
+ write(value: T | ((prev: T) => T)): T;
5
+ reset(): void;
6
+ subscribe(fn: (value: T) => void): () => void;
7
+ onFirstSubscribe(callback: VoidFunction): import("nanoevents").Unsubscribe;
8
+ onLastUnsubscribe(callback: VoidFunction): import("nanoevents").Unsubscribe;
9
+ };
10
+ export declare function readonly<T>(state: State<T>): {
11
+ read: () => T;
12
+ subscribe: (fn: (value: T) => void) => () => void;
13
+ onFirstSubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
14
+ onLastUnsubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
15
+ };
16
+ export {};
@@ -0,0 +1,51 @@
1
+ import { createNanoEvents } from 'nanoevents';
2
+ export function createState(initial) {
3
+ const events = createNanoEvents();
4
+ let currentValue = initial;
5
+ return {
6
+ read() {
7
+ return currentValue;
8
+ },
9
+ write(value) {
10
+ const actialValue = typeof value === 'function' ? value(currentValue) : value;
11
+ if (currentValue !== actialValue) {
12
+ currentValue = actialValue;
13
+ events.emit('value', actialValue);
14
+ }
15
+ return actialValue;
16
+ },
17
+ reset() {
18
+ if (currentValue !== initial) {
19
+ currentValue = initial;
20
+ events.emit('value', initial);
21
+ }
22
+ },
23
+ subscribe(fn) {
24
+ if (!events.events.value || events.events.value.length === 0) {
25
+ events.emit('first');
26
+ }
27
+ const unsubscribe = events.on('value', fn);
28
+ fn(currentValue);
29
+ return () => {
30
+ unsubscribe();
31
+ if (!events.events.value || events.events.value.length === 0) {
32
+ events.emit('last');
33
+ }
34
+ };
35
+ },
36
+ onFirstSubscribe(callback) {
37
+ return events.on('first', callback);
38
+ },
39
+ onLastUnsubscribe(callback) {
40
+ return events.on('last', callback);
41
+ },
42
+ };
43
+ }
44
+ export function readonly(state) {
45
+ return {
46
+ read: state.read,
47
+ subscribe: state.subscribe,
48
+ onFirstSubscribe: state.onFirstSubscribe,
49
+ onLastUnsubscribe: state.onLastUnsubscribe,
50
+ };
51
+ }
@@ -0,0 +1,4 @@
1
+ type InferArrayItem<T> = T extends Array<infer U> ? U : never;
2
+ type InferArgs<Args extends unknown[][]> = Args extends [infer Head, ...infer Tail extends unknown[][]] ? [InferArrayItem<Head>, ...InferArgs<Tail>] : [];
3
+ export declare function zipWith<const Args extends unknown[][], R>(arrays: Args, iteratee: (values: InferArgs<Args>) => R): R[];
4
+ export {};
@@ -0,0 +1,11 @@
1
+ export function zipWith(arrays, iteratee) {
2
+ if (arrays.length === 0)
3
+ return [];
4
+ const minLength = Math.min(...arrays.map(arr => arr.length));
5
+ const result = [];
6
+ for (let i = 0; i < minLength; i++) {
7
+ const values = arrays.map(arr => arr[i]);
8
+ result.push(iteratee(values));
9
+ }
10
+ return result;
11
+ }
@@ -0,0 +1,6 @@
1
+ import type { StorageAdapter } from '@novasamatech/storage-adapter';
2
+ import type { IdentityAdapter, IdentityRepository } from './types.js';
3
+ export declare function createIdentityRepository({ adapter, storage, }: {
4
+ adapter: IdentityAdapter;
5
+ storage: StorageAdapter;
6
+ }): IdentityRepository;
@@ -0,0 +1,68 @@
1
+ import { Result, ResultAsync, err, ok, okAsync } from 'neverthrow';
2
+ import { toError } from '../helpers/utils.js';
3
+ export function createIdentityRepository({ adapter, storage, }) {
4
+ const cachedRequester = createCachedIdentityRequester(storage, accountId => `identity_${accountId}`);
5
+ return {
6
+ getIdentity(accountId) {
7
+ return cachedRequester([accountId], adapter.readIdentities).map(map => map[accountId] ?? null);
8
+ },
9
+ getIdentities(accounts) {
10
+ return cachedRequester(accounts, adapter.readIdentities);
11
+ },
12
+ };
13
+ }
14
+ function createCachedIdentityRequester(storage, getKey) {
15
+ function readSingleCacheRecord(accountId) {
16
+ return storage.read(getKey(accountId)).andThen(raw => {
17
+ if (!raw) {
18
+ return ok(null);
19
+ }
20
+ try {
21
+ return ok(JSON.parse(raw));
22
+ }
23
+ catch (e) {
24
+ return err(toError(e));
25
+ }
26
+ });
27
+ }
28
+ function writeSingleCacheRecord(accountId, identity) {
29
+ if (identity === null) {
30
+ return okAsync(undefined);
31
+ }
32
+ return storage.write(getKey(accountId), JSON.stringify(identity));
33
+ }
34
+ function readCache(accounts) {
35
+ if (accounts.length === 0) {
36
+ return okAsync({});
37
+ }
38
+ const identities = ResultAsync.combine(accounts.map(readSingleCacheRecord));
39
+ return identities.map(identities => {
40
+ return Object.fromEntries(identities.map((x, i) => {
41
+ const accountId = accounts.at(i);
42
+ if (!accountId) {
43
+ throw new Error(`Identity not found`);
44
+ }
45
+ return [accountId, x];
46
+ }));
47
+ });
48
+ }
49
+ function writeCache(identities) {
50
+ return ResultAsync.combine(Object.entries(identities).map(args => writeSingleCacheRecord(...args))).map(() => identities);
51
+ }
52
+ return (accounts, request) => {
53
+ return readCache(accounts).andThen(existing => {
54
+ const emptyIdentities = Object.entries(existing)
55
+ .filter(([, identity]) => identity === null)
56
+ .map(([accountId]) => accountId);
57
+ if (emptyIdentities.length === 0) {
58
+ return okAsync(existing);
59
+ }
60
+ return request(accounts)
61
+ .andThen(writeCache)
62
+ .map(fetched => ({
63
+ ...existing,
64
+ ...fetched,
65
+ }));
66
+ });
67
+ };
68
+ }
@@ -0,0 +1,3 @@
1
+ import type { LazyClient } from '@novasamatech/statement-store';
2
+ import type { IdentityAdapter } from './types.js';
3
+ export declare function createIdentityRpcAdapter(lazyClient: LazyClient): IdentityAdapter;
@@ -0,0 +1,38 @@
1
+ import { AccountId } from '@polkadot-api/substrate-bindings';
2
+ import { errAsync, fromPromise, ok } from 'neverthrow';
3
+ import { toError } from '../helpers/utils.js';
4
+ import { zipWith } from '../helpers/zipWith.js';
5
+ export function createIdentityRpcAdapter(lazyClient) {
6
+ const accCodec = AccountId();
7
+ return {
8
+ readIdentities(accounts) {
9
+ const client = lazyClient.getClient();
10
+ const unsafeApi = client.getUnsafeApi();
11
+ const method = unsafeApi.query.Resources?.Consumers;
12
+ if (!method) {
13
+ return errAsync(new Error('Method Resources.Consumers not found'));
14
+ }
15
+ const results = fromPromise(method.getValues([accounts.map(accCodec.dec)]), toError);
16
+ return results.andThen(results => {
17
+ if (!results) {
18
+ return ok({});
19
+ }
20
+ return ok(Object.fromEntries(zipWith([accounts, results], x => x).map(([accountId, raw]) => {
21
+ if (!raw) {
22
+ return [accountId, null];
23
+ }
24
+ console.log(raw.credibility);
25
+ return [
26
+ accountId,
27
+ {
28
+ accountId: accountId,
29
+ fullUsername: raw.full_username ? raw.full_username.asText() : null,
30
+ liteUsername: raw.lite_username ? raw.lite_username.asText() : null,
31
+ credibility: raw.credibility.type,
32
+ },
33
+ ];
34
+ })));
35
+ });
36
+ },
37
+ };
38
+ }
@@ -0,0 +1,22 @@
1
+ import type { ResultAsync } from 'neverthrow';
2
+ type Credibility = {
3
+ type: 'Lite';
4
+ } | {
5
+ type: 'Person';
6
+ alias: number;
7
+ lastUpdate: bigint;
8
+ };
9
+ export type Identity = {
10
+ accountId: string;
11
+ fullUsername: string | null;
12
+ liteUsername: string;
13
+ credibility: Credibility;
14
+ };
15
+ export type IdentityAdapter = {
16
+ readIdentities(accounts: string[]): ResultAsync<Record<string, Identity | null>, Error>;
17
+ };
18
+ export type IdentityRepository = {
19
+ getIdentity(accountId: string): ResultAsync<Identity | null, Error>;
20
+ getIdentities(accounts: string[]): ResultAsync<Record<string, Identity | null>, Error>;
21
+ };
22
+ export {};
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type { PappAdapter } from './papp.js';
2
- export type { AuthentificationStatus } from './components/auth/types.js';
3
- export type { UserSession } from './components/user/userSessionStorage.js';
4
- export type { Identity } from './adapters/identity/types.js';
5
- export declare function createPappHostAdapter(appId: string, metadata: string): import("./papp.js").PappAdapter;
2
+ export { createPappAdapter } from './papp.js';
3
+ export type { AuthentificationStatus } from './sso/auth/types.js';
4
+ export type { UserSession } from './sso/sessionManager/userSession.js';
5
+ export type { Identity } from './identity/types.js';
package/dist/index.js CHANGED
@@ -1,7 +1 @@
1
- import { createPappAdapter } from './papp.js';
2
- export function createPappHostAdapter(appId, metadata) {
3
- return createPappAdapter({
4
- appId,
5
- metadata,
6
- });
7
- }
1
+ export { createPappAdapter } from './papp.js';
@@ -6,22 +6,18 @@ export type EncrPublicKey = Branded<Uint8Array, 'EncrPublicKey'>;
6
6
  export type EncrSecret = Branded<Uint8Array, 'EncrSecret'>;
7
7
  export type SharedSecret = Branded<Uint8Array, 'SharedSecret'>;
8
8
  export type SharedSession = Branded<Uint8Array, 'SharedSession'>;
9
+ export declare function BrandedBytesCodec<T extends Uint8Array>(length?: number): Codec<T>;
9
10
  export declare const SsPubKey: Codec<SsPublicKey>;
10
11
  export declare const EncrPubKey: Codec<EncrPublicKey>;
11
12
  export declare function stringToBytes(str: string): Uint8Array<ArrayBuffer>;
12
13
  export declare function bytesToString(bytes: Uint8Array): string;
13
- export declare function mergeBytes(...bytes: Uint8Array[]): Uint8Array<ArrayBuffer>;
14
14
  export declare const SS_SECRET_SEED_SIZE = 32;
15
- export declare function createSsSecret(seed: Uint8Array): SsSecret;
15
+ export declare function createSsSecret(seed?: Uint8Array): SsSecret;
16
16
  export declare function createSsHardDerivation(secret: SsSecret, derivation: string | number): SsSecret;
17
17
  export declare function getSsPub(secret: SsSecret): SsPublicKey;
18
18
  export declare function signWithSsSecret(secret: SsSecret, message: Uint8Array): Uint8Array<ArrayBufferLike>;
19
+ export declare function verifyWithSsSecret(message: Uint8Array, signature: Uint8Array, publicKey: Uint8Array): boolean;
19
20
  export declare const ENCR_SECRET_SEED_SIZE = 48;
20
- export declare function createEncrSecret(seed: Uint8Array): EncrSecret;
21
+ export declare function createEncrSecret(seed?: Uint8Array): EncrSecret;
21
22
  export declare function getEncrPub(secret: EncrSecret): EncrPublicKey;
22
- export declare function createRandomSeed(suffix: string, size: number): Uint8Array<ArrayBufferLike>;
23
- export declare function createStableSeed(value: string, size: number): Uint8Array<ArrayBufferLike>;
24
- export declare function khash(secret: Uint8Array, message: Uint8Array): Uint8Array<ArrayBufferLike>;
25
23
  export declare function createSharedSecret(secret: EncrSecret, publicKey: Uint8Array): SharedSecret;
26
- export declare function encrypt(secret: Uint8Array, cipherText: Uint8Array): Uint8Array<ArrayBufferLike>;
27
- export declare function decrypt(secret: Uint8Array, message: Uint8Array): Uint8Array<ArrayBufferLike>;