@novasamatech/host-papp 0.5.0-17 → 0.5.0-18
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/crypto.d.ts +2 -2
- package/dist/crypto.js +8 -7
- package/dist/helpers/abortError.d.ts +0 -1
- package/dist/helpers/abortError.js +0 -3
- package/dist/index.d.ts +1 -1
- package/dist/papp.js +2 -2
- package/dist/sso/auth/impl.d.ts +29 -3
- package/dist/sso/auth/impl.js +60 -35
- package/dist/sso/auth/types.d.ts +13 -2
- package/dist/sso/userSecretRepository.d.ts +1 -1
- package/dist/sso/userSecretRepository.js +2 -2
- package/package.json +4 -4
package/dist/crypto.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export declare const SsPubKey: Codec<SsPublicKey>;
|
|
|
11
11
|
export declare const EncrPubKey: Codec<EncrPublicKey>;
|
|
12
12
|
export declare function stringToBytes(str: string): Uint8Array<ArrayBuffer>;
|
|
13
13
|
export declare function bytesToString(bytes: Uint8Array): string;
|
|
14
|
-
export declare function createSsSecret(
|
|
14
|
+
export declare function createSsSecret(entropy: Uint8Array): SsSecret;
|
|
15
15
|
export declare function createSsDerivation(secret: SsSecret, derivation: string): SsSecret;
|
|
16
16
|
export declare function getSsPub(secret: SsSecret): SsPublicKey;
|
|
17
17
|
export declare function signWithSsSecret(secret: SsSecret, message: Uint8Array): Uint8Array<ArrayBufferLike>;
|
|
@@ -24,6 +24,6 @@ export type DerivedSr25519Account = {
|
|
|
24
24
|
verify(message: Uint8Array, signature: Uint8Array): boolean;
|
|
25
25
|
};
|
|
26
26
|
export declare function deriveSr25519Account(mnemonic: string, derivation: string): DerivedSr25519Account;
|
|
27
|
-
export declare function createEncrSecret(
|
|
27
|
+
export declare function createEncrSecret(entropy: Uint8Array): EncrSecret;
|
|
28
28
|
export declare function getEncrPub(secret: EncrSecret): EncrPublicKey;
|
|
29
29
|
export declare function createSharedSecret(secret: EncrSecret, publicKey: Uint8Array): SharedSecret;
|
package/dist/crypto.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { p256 } from '@noble/curves/nist.js';
|
|
2
|
-
import {
|
|
2
|
+
import { entropyToMiniSecret, mnemonicToEntropy } from '@polkadot-labs/hdkd-helpers';
|
|
3
3
|
import { HDKD as sr25519HDKD, getPublicKey as sr25519GetPublicKey, secretFromSeed as sr25519SecretFromSeed, sign as sr25519Sign, verify as sr25519Verify, } from '@scure/sr25519';
|
|
4
4
|
import { Bytes, str } from 'scale-ts';
|
|
5
5
|
// schemas
|
|
@@ -33,8 +33,8 @@ function createChainCode(derivation) {
|
|
|
33
33
|
return chainCode;
|
|
34
34
|
}
|
|
35
35
|
// statement store key pair
|
|
36
|
-
export function createSsSecret(
|
|
37
|
-
const miniSecret =
|
|
36
|
+
export function createSsSecret(entropy) {
|
|
37
|
+
const miniSecret = entropyToMiniSecret(entropy);
|
|
38
38
|
return sr25519SecretFromSeed(miniSecret);
|
|
39
39
|
}
|
|
40
40
|
export function createSsDerivation(secret, derivation) {
|
|
@@ -59,19 +59,20 @@ export function verifyWithSsSecret(message, signature, publicKey) {
|
|
|
59
59
|
return sr25519Verify(message, signature, publicKey);
|
|
60
60
|
}
|
|
61
61
|
export function deriveSr25519Account(mnemonic, derivation) {
|
|
62
|
-
const
|
|
62
|
+
const entropy = mnemonicToEntropy(mnemonic);
|
|
63
|
+
const secret = createSsDerivation(createSsSecret(entropy), derivation);
|
|
63
64
|
const publicKey = getSsPub(secret);
|
|
64
65
|
return {
|
|
65
66
|
secret,
|
|
66
67
|
publicKey,
|
|
67
|
-
entropy
|
|
68
|
+
entropy,
|
|
68
69
|
sign: message => signWithSsSecret(secret, message),
|
|
69
70
|
verify: (message, signature) => verifyWithSsSecret(message, signature, publicKey),
|
|
70
71
|
};
|
|
71
72
|
}
|
|
72
73
|
// encryption key pair
|
|
73
|
-
export function createEncrSecret(
|
|
74
|
-
const miniSecret =
|
|
74
|
+
export function createEncrSecret(entropy) {
|
|
75
|
+
const miniSecret = entropyToMiniSecret(entropy);
|
|
75
76
|
const seed = new Uint8Array(48);
|
|
76
77
|
seed.set(miniSecret);
|
|
77
78
|
const { secretKey } = p256.keygen(seed);
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { SS_STABLE_STAGE_ENDPOINTS, SS_UNSTABLE_STAGE_ENDPOINTS } from './constants.js';
|
|
2
2
|
export type { PappAdapter } from './papp.js';
|
|
3
3
|
export { createPappAdapter } from './papp.js';
|
|
4
|
-
export type {
|
|
4
|
+
export type { AttestationStatus, PairingStatus } from './sso/auth/types.js';
|
|
5
5
|
export type { UserSession } from './sso/sessionManager/userSession.js';
|
|
6
6
|
export type { Identity } from './identity/types.js';
|
|
7
7
|
export type { SignPayloadRequest } from './sso/sessionManager/scale/signPayloadRequest.js';
|
package/dist/papp.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLazyClient, createPapiStatementStoreAdapter } from '@novasamatech/statement-store';
|
|
2
2
|
import { createLocalStorageAdapter } from '@novasamatech/storage-adapter';
|
|
3
3
|
import { getWsProvider } from '@polkadot-api/ws-provider';
|
|
4
|
-
import {
|
|
4
|
+
import { SS_STABLE_STAGE_ENDPOINTS } from './constants.js';
|
|
5
5
|
import { createIdentityRepository } from './identity/impl.js';
|
|
6
6
|
import { createIdentityRpcAdapter } from './identity/rpcAdapter.js';
|
|
7
7
|
import { createAuth } from './sso/auth/impl.js';
|
|
@@ -9,7 +9,7 @@ import { createSsoSessionManager } from './sso/sessionManager/impl.js';
|
|
|
9
9
|
import { createUserSecretRepository } from './sso/userSecretRepository.js';
|
|
10
10
|
import { createUserSessionRepository } from './sso/userSessionRepository.js';
|
|
11
11
|
export function createPappAdapter({ appId, metadata, adapters }) {
|
|
12
|
-
const lazyClient = adapters?.lazyClient ?? createLazyClient(getWsProvider(
|
|
12
|
+
const lazyClient = adapters?.lazyClient ?? createLazyClient(getWsProvider(SS_STABLE_STAGE_ENDPOINTS));
|
|
13
13
|
const statementStore = adapters?.statementStore ?? createPapiStatementStoreAdapter(lazyClient);
|
|
14
14
|
const identities = adapters?.identities ?? createIdentityRpcAdapter(lazyClient);
|
|
15
15
|
const storage = adapters?.storage ?? createLocalStorageAdapter(appId);
|
package/dist/sso/auth/impl.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ type Params = {
|
|
|
11
11
|
lazyClient: LazyClient;
|
|
12
12
|
};
|
|
13
13
|
export declare function createAuth({ metadata, statementStore, ssoSessionRepository, userSecretRepository, lazyClient, }: Params): {
|
|
14
|
-
|
|
14
|
+
pairingStatus: {
|
|
15
15
|
read: () => {
|
|
16
16
|
step: "none";
|
|
17
17
|
} | {
|
|
@@ -22,7 +22,7 @@ export declare function createAuth({ metadata, statementStore, ssoSessionReposit
|
|
|
22
22
|
step: "pairing";
|
|
23
23
|
payload: string;
|
|
24
24
|
} | {
|
|
25
|
-
step: "
|
|
25
|
+
step: "pairingError";
|
|
26
26
|
message: string;
|
|
27
27
|
} | {
|
|
28
28
|
step: "finished";
|
|
@@ -38,7 +38,7 @@ export declare function createAuth({ metadata, statementStore, ssoSessionReposit
|
|
|
38
38
|
step: "pairing";
|
|
39
39
|
payload: string;
|
|
40
40
|
} | {
|
|
41
|
-
step: "
|
|
41
|
+
step: "pairingError";
|
|
42
42
|
message: string;
|
|
43
43
|
} | {
|
|
44
44
|
step: "finished";
|
|
@@ -47,6 +47,32 @@ export declare function createAuth({ metadata, statementStore, ssoSessionReposit
|
|
|
47
47
|
onFirstSubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
|
|
48
48
|
onLastUnsubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
|
|
49
49
|
};
|
|
50
|
+
attestationStatus: {
|
|
51
|
+
read: () => {
|
|
52
|
+
step: "none";
|
|
53
|
+
} | {
|
|
54
|
+
step: "attestation";
|
|
55
|
+
username: string;
|
|
56
|
+
} | {
|
|
57
|
+
step: "attestationError";
|
|
58
|
+
message: string;
|
|
59
|
+
} | {
|
|
60
|
+
step: "finished";
|
|
61
|
+
};
|
|
62
|
+
subscribe: (fn: (value: {
|
|
63
|
+
step: "none";
|
|
64
|
+
} | {
|
|
65
|
+
step: "attestation";
|
|
66
|
+
username: string;
|
|
67
|
+
} | {
|
|
68
|
+
step: "attestationError";
|
|
69
|
+
message: string;
|
|
70
|
+
} | {
|
|
71
|
+
step: "finished";
|
|
72
|
+
}) => void) => () => void;
|
|
73
|
+
onFirstSubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
|
|
74
|
+
onLastUnsubscribe: (callback: VoidFunction) => import("nanoevents").Unsubscribe;
|
|
75
|
+
};
|
|
50
76
|
authenticate(): ResultAsync<StoredUserSession | null, Error>;
|
|
51
77
|
abortAuthentication(): void;
|
|
52
78
|
};
|
package/dist/sso/auth/impl.js
CHANGED
|
@@ -10,37 +10,48 @@ import { createStoredUserSession } from '../userSessionRepository.js';
|
|
|
10
10
|
import { createAliceVerifier, createAttestationService } from './attestationService.js';
|
|
11
11
|
import { HandshakeData, HandshakeResponsePayload, HandshakeResponseSensitiveData } from './scale/handshake.js';
|
|
12
12
|
export function createAuth({ metadata, statementStore, ssoSessionRepository, userSecretRepository, lazyClient, }) {
|
|
13
|
-
const
|
|
13
|
+
const attestationStatus = createState({ step: 'none' });
|
|
14
|
+
const pairingStatus = createState({ step: 'none' });
|
|
14
15
|
let authResult = null;
|
|
15
16
|
let abort = null;
|
|
16
|
-
function
|
|
17
|
+
function attestAccount(account, signal) {
|
|
17
18
|
const attestationService = createAttestationService(lazyClient);
|
|
18
|
-
authStatus.write({ step: 'attestation' });
|
|
19
19
|
const verifier = createAliceVerifier();
|
|
20
20
|
const username = attestationService.claimUsername();
|
|
21
|
+
attestationStatus.write({ step: 'attestation', username });
|
|
21
22
|
return attestationService
|
|
22
23
|
.grantVerifierAllowance(verifier)
|
|
23
24
|
.andThrough(() => processSignal(signal))
|
|
24
25
|
.andThen(() => attestationService.registerLitePerson(username, account, verifier))
|
|
25
|
-
.andThrough(() => processSignal(signal))
|
|
26
|
+
.andThrough(() => processSignal(signal))
|
|
27
|
+
.andTee(() => {
|
|
28
|
+
attestationStatus.write({ step: 'finished' });
|
|
29
|
+
})
|
|
30
|
+
.orTee(e => {
|
|
31
|
+
if (!(e instanceof AbortError)) {
|
|
32
|
+
attestationStatus.write({ step: 'attestationError', message: e.message });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
26
35
|
}
|
|
27
|
-
function handshake(account,
|
|
36
|
+
function handshake(account, signal) {
|
|
28
37
|
const localAccount = createLocalSessionAccount(createAccountId(account.publicKey));
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
pairingStatus.write({ step: 'initial' });
|
|
39
|
+
const encrKeys = createEncrKeys(account.entropy);
|
|
40
|
+
const handshakePayload = encrKeys.andThen(({ publicKey }) => createHandshakePayloadV1({
|
|
41
|
+
ssPublicKey: account.publicKey,
|
|
42
|
+
encrPublicKey: publicKey,
|
|
43
|
+
metadata,
|
|
44
|
+
}));
|
|
45
|
+
const handshakeTopic = encrKeys.andThen(({ publicKey }) => createHandshakeTopic(localAccount, publicKey));
|
|
46
|
+
const dataPrepared = Result.combine([handshakePayload, handshakeTopic, encrKeys]).andTee(([payload]) => pairingStatus.write({ step: 'pairing', payload: createDeeplink(payload) }));
|
|
47
|
+
return dataPrepared.asyncAndThen(([, handshakeTopic, encrKeys]) => {
|
|
48
|
+
const pappResponse = waitForStatements(callback => statementStore.subscribeStatements([handshakeTopic], callback), signal, (statements, resolve) => {
|
|
38
49
|
for (const statement of statements) {
|
|
39
50
|
if (!statement.data)
|
|
40
51
|
continue;
|
|
41
52
|
const session = retrieveSession({
|
|
42
53
|
localAccount,
|
|
43
|
-
encrSecret,
|
|
54
|
+
encrSecret: encrKeys.secret,
|
|
44
55
|
payload: statement.data.asBytes(),
|
|
45
56
|
}).unwrapOr(null);
|
|
46
57
|
if (session) {
|
|
@@ -48,33 +59,46 @@ export function createAuth({ metadata, statementStore, ssoSessionRepository, use
|
|
|
48
59
|
break;
|
|
49
60
|
}
|
|
50
61
|
}
|
|
51
|
-
})
|
|
52
|
-
const secretesSaved = pappResponse.andThen(({ id }) =>
|
|
62
|
+
});
|
|
63
|
+
const secretesSaved = pappResponse.andThen(({ id }) => {
|
|
64
|
+
return userSecretRepository.write(id, {
|
|
65
|
+
ssSecret: account.secret,
|
|
66
|
+
encrSecret: encrKeys.secret,
|
|
67
|
+
entropy: account.entropy,
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
// secrets and sso session should be chained, or it can produce an incorrect state
|
|
53
71
|
const userCreated = secretesSaved.andThen(() => pappResponse.andThen(ssoSessionRepository.add));
|
|
54
|
-
const
|
|
55
|
-
return
|
|
72
|
+
const sessionReceived = ResultAsync.combine([userCreated, secretesSaved]).map(([session]) => session);
|
|
73
|
+
return sessionReceived
|
|
74
|
+
.andTee(session => {
|
|
75
|
+
pairingStatus.write(session ? { step: 'finished', session } : { step: 'none' });
|
|
76
|
+
})
|
|
77
|
+
.orTee(e => {
|
|
78
|
+
if (!(e instanceof AbortError)) {
|
|
79
|
+
pairingStatus.write({ step: 'pairingError', message: e.message });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
56
82
|
});
|
|
57
83
|
}
|
|
58
84
|
const authModule = {
|
|
59
|
-
|
|
85
|
+
pairingStatus: readonly(pairingStatus),
|
|
86
|
+
attestationStatus: readonly(attestationStatus),
|
|
60
87
|
authenticate() {
|
|
61
88
|
if (authResult) {
|
|
62
89
|
return authResult;
|
|
63
90
|
}
|
|
64
91
|
abort = new AbortController();
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.andTee(session => {
|
|
72
|
-
authStatus.write(session ? { step: 'finished', session } : { step: 'none' });
|
|
92
|
+
const account = deriveSr25519Account(generateMnemonic(), '//wallet//sso');
|
|
93
|
+
authResult = ResultAsync.combine([handshake(account, abort.signal), attestAccount(account, abort.signal)])
|
|
94
|
+
.map(([session]) => session)
|
|
95
|
+
.orElse(e => (e instanceof AbortError ? ok(null) : err(e)))
|
|
96
|
+
.andTee(() => {
|
|
97
|
+
abort = null;
|
|
73
98
|
})
|
|
74
|
-
.orTee(
|
|
99
|
+
.orTee(() => {
|
|
75
100
|
authResult = null;
|
|
76
101
|
abort = null;
|
|
77
|
-
authStatus.write({ step: 'error', message: e.message });
|
|
78
102
|
});
|
|
79
103
|
return authResult;
|
|
80
104
|
},
|
|
@@ -84,7 +108,8 @@ export function createAuth({ metadata, statementStore, ssoSessionRepository, use
|
|
|
84
108
|
abort = null;
|
|
85
109
|
}
|
|
86
110
|
authResult = null;
|
|
87
|
-
|
|
111
|
+
pairingStatus.reset();
|
|
112
|
+
attestationStatus.reset();
|
|
88
113
|
},
|
|
89
114
|
};
|
|
90
115
|
return authModule;
|
|
@@ -106,11 +131,11 @@ function parseHandshakePayload(payload) {
|
|
|
106
131
|
throw new Error('Unsupported handshake payload version');
|
|
107
132
|
}
|
|
108
133
|
}
|
|
109
|
-
const createEncrKeys = fromThrowable((
|
|
110
|
-
const
|
|
134
|
+
const createEncrKeys = fromThrowable((entropy) => {
|
|
135
|
+
const secret = createEncrSecret(entropy);
|
|
111
136
|
return {
|
|
112
|
-
|
|
113
|
-
|
|
137
|
+
secret,
|
|
138
|
+
publicKey: getEncrPub(secret),
|
|
114
139
|
};
|
|
115
140
|
}, toError);
|
|
116
141
|
function retrieveSession({ payload, encrSecret, localAccount, }) {
|
package/dist/sso/auth/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { StoredUserSession } from '../userSessionRepository.js';
|
|
2
|
-
export type
|
|
2
|
+
export type PairingStatus = {
|
|
3
3
|
step: 'none';
|
|
4
4
|
} | {
|
|
5
5
|
step: 'initial';
|
|
@@ -9,9 +9,20 @@ export type AuthentificationStatus = {
|
|
|
9
9
|
step: 'pairing';
|
|
10
10
|
payload: string;
|
|
11
11
|
} | {
|
|
12
|
-
step: '
|
|
12
|
+
step: 'pairingError';
|
|
13
13
|
message: string;
|
|
14
14
|
} | {
|
|
15
15
|
step: 'finished';
|
|
16
16
|
session: StoredUserSession;
|
|
17
17
|
};
|
|
18
|
+
export type AttestationStatus = {
|
|
19
|
+
step: 'none';
|
|
20
|
+
} | {
|
|
21
|
+
step: 'attestation';
|
|
22
|
+
username: string;
|
|
23
|
+
} | {
|
|
24
|
+
step: 'attestationError';
|
|
25
|
+
message: string;
|
|
26
|
+
} | {
|
|
27
|
+
step: 'finished';
|
|
28
|
+
};
|
|
@@ -6,7 +6,7 @@ type StoredUserSecrets = CodecType<typeof StoredUserSecretsCodec>;
|
|
|
6
6
|
declare const StoredUserSecretsCodec: import("scale-ts").Codec<{
|
|
7
7
|
ssSecret: SsSecret;
|
|
8
8
|
encrSecret: EncrSecret;
|
|
9
|
-
|
|
9
|
+
entropy: Uint8Array<ArrayBufferLike>;
|
|
10
10
|
}>;
|
|
11
11
|
export type UserSecretRepository = ReturnType<typeof createUserSecretRepository>;
|
|
12
12
|
export declare function createUserSecretRepository(salt: string, storage: StorageAdapter): {
|
|
@@ -2,13 +2,13 @@ import { gcm } from '@noble/ciphers/aes.js';
|
|
|
2
2
|
import { blake2b } from '@noble/hashes/blake2.js';
|
|
3
3
|
import { fromHex, toHex } from '@polkadot-api/utils';
|
|
4
4
|
import { fromThrowable } from 'neverthrow';
|
|
5
|
-
import {
|
|
5
|
+
import { Bytes, Struct } from 'scale-ts';
|
|
6
6
|
import { BrandedBytesCodec, stringToBytes } from '../crypto.js';
|
|
7
7
|
import { toError } from '../helpers/utils.js';
|
|
8
8
|
const StoredUserSecretsCodec = Struct({
|
|
9
9
|
ssSecret: BrandedBytesCodec(),
|
|
10
10
|
encrSecret: BrandedBytesCodec(),
|
|
11
|
-
|
|
11
|
+
entropy: Bytes(),
|
|
12
12
|
});
|
|
13
13
|
export function createUserSecretRepository(salt, storage) {
|
|
14
14
|
const baseKey = 'UserSecrets';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novasamatech/host-papp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.0-
|
|
4
|
+
"version": "0.5.0-18",
|
|
5
5
|
"description": "Polkadot app integration",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"@noble/ciphers": "2.1.1",
|
|
29
29
|
"@noble/curves": "2.0.1",
|
|
30
30
|
"@noble/hashes": "2.0.1",
|
|
31
|
-
"@novasamatech/host-api": "0.5.0-
|
|
32
|
-
"@novasamatech/statement-store": "0.5.0-
|
|
33
|
-
"@novasamatech/storage-adapter": "0.5.0-
|
|
31
|
+
"@novasamatech/host-api": "0.5.0-18",
|
|
32
|
+
"@novasamatech/statement-store": "0.5.0-18",
|
|
33
|
+
"@novasamatech/storage-adapter": "0.5.0-18",
|
|
34
34
|
"@polkadot-api/substrate-bindings": "^0.16.5",
|
|
35
35
|
"@polkadot-labs/hdkd-helpers": "^0.0.27",
|
|
36
36
|
"@scure/sr25519": "1.0.0",
|