@novasamatech/host-papp 0.9.3 → 0.9.4
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/sso/auth/impl.d.ts +9 -0
- package/dist/sso/auth/impl.js +11 -0
- package/dist/sso/deviceIdentityStore.d.ts +1 -0
- package/dist/sso/deviceIdentityStore.js +3 -0
- package/dist/sso/sessionManager/impl.d.ts +6 -1
- package/dist/sso/sessionManager/impl.js +25 -9
- package/dist/sso/sessionManager/userSession.d.ts +1 -0
- package/dist/sso/sessionManager/userSession.js +3 -1
- package/package.json +5 -5
package/dist/sso/auth/impl.d.ts
CHANGED
|
@@ -76,5 +76,14 @@ export declare function createAuth({ hostMetadata, deviceIdentity, deviceIdentit
|
|
|
76
76
|
};
|
|
77
77
|
authenticate(): ResultAsync<StoredUserSession | null, Error>;
|
|
78
78
|
abortAuthentication(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Discard the persisted device keypair and the processed-handshake marker
|
|
81
|
+
* stored with it, so the next pairing runs on a new topic with a new key —
|
|
82
|
+
* which makes any cached on-chain HandshakeSuccess undecryptable.
|
|
83
|
+
*
|
|
84
|
+
* No-op for hosts supplying their own `deviceIdentity` factory, and a
|
|
85
|
+
* pairing already in flight keeps the old identity.
|
|
86
|
+
*/
|
|
87
|
+
resetDeviceIdentity(): ResultAsync<void, Error>;
|
|
79
88
|
};
|
|
80
89
|
export {};
|
package/dist/sso/auth/impl.js
CHANGED
|
@@ -146,6 +146,17 @@ export function createAuth({ hostMetadata, deviceIdentity, deviceIdentityStore,
|
|
|
146
146
|
authResult = null;
|
|
147
147
|
pairingStatus.reset();
|
|
148
148
|
},
|
|
149
|
+
/**
|
|
150
|
+
* Discard the persisted device keypair and the processed-handshake marker
|
|
151
|
+
* stored with it, so the next pairing runs on a new topic with a new key —
|
|
152
|
+
* which makes any cached on-chain HandshakeSuccess undecryptable.
|
|
153
|
+
*
|
|
154
|
+
* No-op for hosts supplying their own `deviceIdentity` factory, and a
|
|
155
|
+
* pairing already in flight keeps the old identity.
|
|
156
|
+
*/
|
|
157
|
+
resetDeviceIdentity() {
|
|
158
|
+
return deviceIdentityStore.reset();
|
|
159
|
+
},
|
|
149
160
|
};
|
|
150
161
|
function persistAndNotify(identity, success, _flowId) {
|
|
151
162
|
const localAccount = createLocalSessionAccount(createAccountId(identity.statementAccountPublicKey));
|
|
@@ -9,6 +9,7 @@ export type DeviceIdentityStore = {
|
|
|
9
9
|
loadOrCreate(): ResultAsync<DeviceIdentity, Error>;
|
|
10
10
|
readLastProcessedHandshakeStatement(): ResultAsync<string | null, Error>;
|
|
11
11
|
writeLastProcessedHandshakeStatement(hex: string): ResultAsync<void, Error>;
|
|
12
|
+
reset(): ResultAsync<void, Error>;
|
|
12
13
|
};
|
|
13
14
|
export declare function createDeviceIdentityStore(salt: string, storage: StorageAdapter): DeviceIdentityStore;
|
|
14
15
|
export declare const awaitDeviceIdentity: (store: DeviceIdentityStore) => Promise<DeviceIdentity>;
|
|
@@ -66,6 +66,9 @@ export function createDeviceIdentityStore(salt, storage) {
|
|
|
66
66
|
return write({ ...existing, lastProcessedHandshakeStatement: hex });
|
|
67
67
|
});
|
|
68
68
|
},
|
|
69
|
+
reset() {
|
|
70
|
+
return storage.clear(KEY);
|
|
71
|
+
},
|
|
69
72
|
};
|
|
70
73
|
}
|
|
71
74
|
// Re-export the awaitable form for convenience, since most call sites already
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { StatementStoreAdapter } from '@novasamatech/statement-store';
|
|
2
2
|
import type { StorageAdapter } from '@novasamatech/storage-adapter';
|
|
3
|
+
import { ResultAsync } from 'neverthrow';
|
|
3
4
|
import type { IdentityRepository } from '../../identity/types.js';
|
|
4
5
|
import type { Callback } from '../../types.js';
|
|
5
6
|
import type { AllowanceRepository } from '../allowance/index.js';
|
|
@@ -20,7 +21,11 @@ export declare function createSsoSessionManager({ ssoSessionRepository, userSecr
|
|
|
20
21
|
read: () => UserSession[];
|
|
21
22
|
subscribe: (callback: Callback<UserSession[]>) => () => void;
|
|
22
23
|
};
|
|
23
|
-
disconnect(userSession: StoredUserSession):
|
|
24
|
+
disconnect(userSession: StoredUserSession): ResultAsync<undefined, Error>;
|
|
25
|
+
/**
|
|
26
|
+
* Teardown by id alone, without notifying the peer.
|
|
27
|
+
* */
|
|
28
|
+
forget(sessionId: string): ResultAsync<undefined, Error>;
|
|
24
29
|
dispose(): void;
|
|
25
30
|
};
|
|
26
31
|
export {};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createEncryption } from '@novasamatech/statement-store';
|
|
2
|
-
import { okAsync } from 'neverthrow';
|
|
2
|
+
import { ResultAsync, okAsync } from 'neverthrow';
|
|
3
3
|
import { emitHostPappDebugMessage } from '../../debugBus.js';
|
|
4
4
|
import { createState } from '../../helpers/state.js';
|
|
5
5
|
import { createSsoStatementProver } from '../ssoSessionProver.js';
|
|
6
|
-
import { createUserSession } from './userSession.js';
|
|
6
|
+
import { createUserSession, processedMessagesKey } from './userSession.js';
|
|
7
7
|
export function createSsoSessionManager({ ssoSessionRepository, userSecretRepository, allowanceRepository, identityRepository, statementStore, storage, }) {
|
|
8
8
|
const localSessions = createState({});
|
|
9
9
|
const sessionUnsubscribes = new Map();
|
|
@@ -11,9 +11,16 @@ export function createSsoSessionManager({ ssoSessionRepository, userSecretReposi
|
|
|
11
11
|
sessionUnsubscribes.get(id)?.();
|
|
12
12
|
sessionUnsubscribes.delete(id);
|
|
13
13
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
// The session-list write goes first: it is what notifies the subscription
|
|
15
|
+
// above to unsubscribe and dispose the live session.
|
|
16
|
+
const purgeSession = (sessionId) => ssoSessionRepository
|
|
17
|
+
.filter(s => s.id !== sessionId)
|
|
18
|
+
.andThen(() => ResultAsync.combine([
|
|
19
|
+
userSecretRepository.clear(sessionId),
|
|
20
|
+
allowanceRepository.clearSession(sessionId),
|
|
21
|
+
storage.clear(processedMessagesKey(sessionId)),
|
|
22
|
+
]))
|
|
23
|
+
.map(() => undefined);
|
|
17
24
|
ssoSessionRepository.subscribe(userSessions => {
|
|
18
25
|
const activeSessions = localSessions.read();
|
|
19
26
|
const toRemove = new Set(Object.keys(activeSessions));
|
|
@@ -36,7 +43,7 @@ export function createSsoSessionManager({ ssoSessionRepository, userSecretReposi
|
|
|
36
43
|
case 'v1': {
|
|
37
44
|
switch (message.data.value.tag) {
|
|
38
45
|
case 'Disconnected':
|
|
39
|
-
return
|
|
46
|
+
return purgeSession(userSession.id).map(() => false);
|
|
40
47
|
}
|
|
41
48
|
}
|
|
42
49
|
}
|
|
@@ -76,9 +83,18 @@ export function createSsoSessionManager({ ssoSessionRepository, userSecretReposi
|
|
|
76
83
|
const session = createSession(userSession, statementStore, storage, userSecretRepository, allowanceRepository, identityRepository);
|
|
77
84
|
return session
|
|
78
85
|
.sendDisconnectMessage()
|
|
79
|
-
.
|
|
80
|
-
.
|
|
81
|
-
|
|
86
|
+
.orElse(error => {
|
|
87
|
+
console.warn('[host-papp] disconnect: peer notification failed, tearing down locally anyway', error);
|
|
88
|
+
return okAsync(undefined);
|
|
89
|
+
})
|
|
90
|
+
.andTee(() => session.dispose())
|
|
91
|
+
.andThen(() => purgeSession(userSession.id));
|
|
92
|
+
},
|
|
93
|
+
/**
|
|
94
|
+
* Teardown by id alone, without notifying the peer.
|
|
95
|
+
* */
|
|
96
|
+
forget(sessionId) {
|
|
97
|
+
return purgeSession(sessionId);
|
|
82
98
|
},
|
|
83
99
|
dispose() {
|
|
84
100
|
for (const session of Object.values(localSessions.read())) {
|
|
@@ -12,6 +12,7 @@ import { RemoteMessageCodec } from './scale/remoteMessage.js';
|
|
|
12
12
|
import type { ApAllocationOutcome, ResourceAllocationRequest } from './scale/resourceAllocation.js';
|
|
13
13
|
import type { SignVrfRequest } from './scale/signVrf.js';
|
|
14
14
|
import type { SignRawLegacyRequest, SigningPayloadRequest, SigningPayloadResponseData, SigningRawRequest } from './scale/signing.js';
|
|
15
|
+
export declare const processedMessagesKey: (sessionId: string) => string;
|
|
15
16
|
export type UserSession = StoredUserSession & {
|
|
16
17
|
/** Read this session's persisted allowance slot-account key for a product/resource. */
|
|
17
18
|
readAllowance(productId: string, resource: AllowanceResourceKind): ResultAsync<Uint8Array | null, Error>;
|
|
@@ -16,6 +16,8 @@ import { RemoteMessageCodec } from './scale/remoteMessage.js';
|
|
|
16
16
|
const QUEUE_TASK_TIMEOUT_MS = 240_000;
|
|
17
17
|
// Mobile SSO statements allow 500 KiB total; keep headroom for statement/session overhead.
|
|
18
18
|
const MAX_SSO_REQUEST_SIZE = 498 * 1024;
|
|
19
|
+
// Per-session dedup log of already-handled peer message ids.
|
|
20
|
+
export const processedMessagesKey = (sessionId) => `sso_processed_${sessionId}`;
|
|
19
21
|
function withQueueTimeout(resultAsync, label) {
|
|
20
22
|
const timeoutPromise = new Promise(resolve => setTimeout(() => resolve(err(new Error(`${label} timed out — queue freed`))), QUEUE_TASK_TIMEOUT_MS));
|
|
21
23
|
return ResultAsync.fromPromise(Promise.race([resultAsync, timeoutPromise]), toError).andThen(r => r);
|
|
@@ -106,7 +108,7 @@ export function createUserSession({ userSession, statementStore, encryption, sto
|
|
|
106
108
|
});
|
|
107
109
|
const processedMessages = fieldListView({
|
|
108
110
|
storage,
|
|
109
|
-
key:
|
|
111
|
+
key: processedMessagesKey(userSession.id),
|
|
110
112
|
from: JSON.parse,
|
|
111
113
|
to: JSON.stringify,
|
|
112
114
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@novasamatech/host-papp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.4",
|
|
5
5
|
"description": "Polkadot app integration",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"@noble/ciphers": "2.2.0",
|
|
39
39
|
"@noble/curves": "2.2.0",
|
|
40
40
|
"@noble/hashes": "2.2.0",
|
|
41
|
-
"@novasamatech/host-api": "0.9.
|
|
42
|
-
"@novasamatech/scale": "0.9.
|
|
43
|
-
"@novasamatech/statement-store": "0.9.
|
|
44
|
-
"@novasamatech/storage-adapter": "0.9.
|
|
41
|
+
"@novasamatech/host-api": "0.9.4",
|
|
42
|
+
"@novasamatech/scale": "0.9.4",
|
|
43
|
+
"@novasamatech/statement-store": "0.9.4",
|
|
44
|
+
"@novasamatech/storage-adapter": "0.9.4",
|
|
45
45
|
"@polkadot-api/utils": "^0.4.0",
|
|
46
46
|
"@polkadot-labs/hdkd-helpers": "^0.0.31",
|
|
47
47
|
"nanoevents": "10.0.0",
|