@novasamatech/host-papp 0.7.9 → 0.8.0-1
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/README.md +170 -1
- package/dist/debugTypes.d.ts +3 -5
- package/dist/identity/rpcAdapter.js +12 -5
- package/dist/index.d.ts +2 -1
- package/dist/papp.d.ts +21 -16
- package/dist/papp.js +6 -2
- package/dist/sso/auth/impl.d.ts +25 -7
- package/dist/sso/auth/impl.js +126 -188
- package/dist/sso/auth/scale/handshakeV2.d.ts +260 -0
- package/dist/sso/auth/scale/handshakeV2.js +176 -0
- package/dist/sso/auth/v2/envelope.d.ts +18 -0
- package/dist/sso/auth/v2/envelope.js +23 -0
- package/dist/sso/auth/v2/proposal.d.ts +23 -0
- package/dist/sso/auth/v2/proposal.js +43 -0
- package/dist/sso/auth/v2/service.d.ts +64 -0
- package/dist/sso/auth/v2/service.js +184 -0
- package/dist/sso/auth/v2/state.d.ts +90 -0
- package/dist/sso/auth/v2/state.js +65 -0
- package/dist/sso/auth/v2/topic.d.ts +17 -0
- package/dist/sso/auth/v2/topic.js +21 -0
- package/dist/sso/deviceIdentityStore.d.ts +14 -0
- package/dist/sso/deviceIdentityStore.js +76 -0
- package/dist/sso/sessionManager/userSession.js +1 -4
- package/dist/sso/userSecretRepository.d.ts +1 -0
- package/dist/sso/userSecretRepository.js +16 -1
- package/dist/sso/userSessionRepository.d.ts +29 -1
- package/dist/sso/userSessionRepository.js +22 -5
- package/package.json +9 -6
package/README.md
CHANGED
|
@@ -60,7 +60,11 @@ const papp = createPappAdapter({
|
|
|
60
60
|
Custom adapters (statement store, identity RPC, storage, lazy chain client) can be supplied
|
|
61
61
|
via the `adapters` option for testing or non-browser environments.
|
|
62
62
|
|
|
63
|
-
## Authentication and pairing
|
|
63
|
+
## Authentication and pairing (V1)
|
|
64
|
+
|
|
65
|
+
The V1 SSO flow described in this section is the single-device pairing protocol used by
|
|
66
|
+
`papp.sso.authenticate()`. For the multi-device V2 protocol see
|
|
67
|
+
[V2 SSO handshake](#v2-sso-handshake) below.
|
|
64
68
|
|
|
65
69
|
`papp.sso.authenticate()` runs the full pairing + attestation flow and resolves with the
|
|
66
70
|
stored user session, or `null` if the flow was aborted. The flow is idempotent — calling it
|
|
@@ -216,3 +220,168 @@ const lookup = async (accountId: string) => {
|
|
|
216
220
|
// Batch lookup
|
|
217
221
|
await papp.identity.getIdentities([accountIdA, accountIdB]);
|
|
218
222
|
```
|
|
223
|
+
|
|
224
|
+
## V2 SSO handshake
|
|
225
|
+
|
|
226
|
+
V2 is a redesign of the SSO pairing flow that supports the same user identity across
|
|
227
|
+
multiple devices. The host generates a stable device keypair locally, emits a
|
|
228
|
+
`VersionedHandshakeProposal::V2` via QR/deeplink, and an authorising peer (e.g. the user's
|
|
229
|
+
existing Polkadot App) responds over the Statement Store with the user identity keys signed
|
|
230
|
+
to authorise this device. Subsequent devices belonging to the same user reuse the same
|
|
231
|
+
identity, so contacts, chats, and roster events are shared between them.
|
|
232
|
+
|
|
233
|
+
V2 is **not interoperable with V1**: a V1-only peer can't decode a V2 proposal QR and vice
|
|
234
|
+
versa. Hosts that want to support both should branch on which protocol the peer advertises.
|
|
235
|
+
|
|
236
|
+
### Shape of the flow
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
host peer (authorising device)
|
|
240
|
+
────────────────────────────────────────────────────────────────────────
|
|
241
|
+
buildPairingDeeplink(device, metadata)
|
|
242
|
+
→ polkadotapp://pair?handshake=<hex>
|
|
243
|
+
scan QR, decode proposal
|
|
244
|
+
compute pairing topic from
|
|
245
|
+
the host's pubkeys
|
|
246
|
+
ECDH-encrypt + post:
|
|
247
|
+
Pending(AllowanceAllocation)
|
|
248
|
+
Success { encryptionKey,
|
|
249
|
+
accountId,
|
|
250
|
+
identitySignature }
|
|
251
|
+
Failed(reason)
|
|
252
|
+
service.subscribeStatements(topic) +
|
|
253
|
+
poll the topic every 2s
|
|
254
|
+
↓
|
|
255
|
+
decode VersionedHandshakeResponse::V2
|
|
256
|
+
→ ECDH-decrypt envelope with the
|
|
257
|
+
device encryption private key
|
|
258
|
+
→ SCALE-decode inner payload
|
|
259
|
+
→ state machine: Submitted → Pending →
|
|
260
|
+
Success | Failed
|
|
261
|
+
→ on Success persist user identity
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
The user identity carried in `Success` is the chat encryption pubkey + the user's identity
|
|
265
|
+
sr25519 accountId. The host verifies the 64-byte sr25519 `identitySignature` against the
|
|
266
|
+
canonical 97 bytes `statementAccountId || encryptionPublicKey`
|
|
267
|
+
(see `IDENTITY_SIGNATURE_PAYLOAD_BYTES`).
|
|
268
|
+
|
|
269
|
+
### Building and rendering the QR
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
import { buildPairingDeeplink } from '@novasamatech/host-papp';
|
|
273
|
+
|
|
274
|
+
const deeplink = buildPairingDeeplink(
|
|
275
|
+
{
|
|
276
|
+
statementAccountPublicKey: device.statementAccountPublicKey, // sr25519 device pubkey, 32 bytes
|
|
277
|
+
encryptionPublicKey: device.encryptionPublicKey, // P-256 device pubkey, 65 bytes uncompressed
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
hostName: 'My Host App',
|
|
281
|
+
hostVersion: '1.0.0',
|
|
282
|
+
platformType: 'macOS',
|
|
283
|
+
platformVersion: '15.4',
|
|
284
|
+
},
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
renderQrCode(deeplink); // 'polkadotapp://pair?handshake=<hex>'
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Driving the handshake
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
import { startPairingV2 } from '@novasamatech/host-papp';
|
|
294
|
+
|
|
295
|
+
const pairing = startPairingV2({
|
|
296
|
+
statementStore: papp.adapters.statementStore, // any StatementStoreAdapter
|
|
297
|
+
deviceIdentity: {
|
|
298
|
+
statementAccountPublicKey: device.statementAccountPublicKey,
|
|
299
|
+
encryptionPublicKey: device.encryptionPublicKey,
|
|
300
|
+
encryptionPrivateKey: device.encryptionPrivateKey, // P-256 priv key, 32 bytes
|
|
301
|
+
},
|
|
302
|
+
metadata: {
|
|
303
|
+
hostName: 'My Host App',
|
|
304
|
+
hostVersion: '1.0.0',
|
|
305
|
+
platformType: 'macOS',
|
|
306
|
+
platformVersion: '15.4',
|
|
307
|
+
},
|
|
308
|
+
persistOnSuccess: async success => {
|
|
309
|
+
// success.identityChatPublicKey, success.userIdentityAccountId,
|
|
310
|
+
// success.identitySignature — persist in your secureStore.
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
pairing.qrPayload; // 'polkadotapp://pair?handshake=<hex>' for the QR UI
|
|
315
|
+
|
|
316
|
+
pairing.state$.subscribe(state => {
|
|
317
|
+
switch (state.tag) {
|
|
318
|
+
case 'Submitted':
|
|
319
|
+
// QR shown, waiting for the peer to scan
|
|
320
|
+
return;
|
|
321
|
+
case 'Pending':
|
|
322
|
+
// peer acknowledged; allocating Statement Store allowance on-chain
|
|
323
|
+
return;
|
|
324
|
+
case 'Success':
|
|
325
|
+
// identity received, device authorised
|
|
326
|
+
return;
|
|
327
|
+
case 'Failed':
|
|
328
|
+
// peer rejected (declined / duplicate / no-slot / tx-failed)
|
|
329
|
+
console.error(state.reason);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// Cancel mid-flight (the Observable completes, polling stops, subscription closes):
|
|
335
|
+
pairing.abort();
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Surviving reloads / proper logout
|
|
339
|
+
|
|
340
|
+
The chain holds the most recent statement on the pairing topic indefinitely, so on cold
|
|
341
|
+
start the service will see the previous Success and replay it. To distinguish a stale
|
|
342
|
+
replay from a fresh re-pair, callers can pass byte-level dedupe state:
|
|
343
|
+
|
|
344
|
+
```ts
|
|
345
|
+
const pairing = startPairingV2({
|
|
346
|
+
// ...
|
|
347
|
+
initialProcessedDataHex: await secureStore.get('lastProcessedHandshakeStatement'),
|
|
348
|
+
onStatementProcessed: hex => {
|
|
349
|
+
void secureStore.set('lastProcessedHandshakeStatement', hex);
|
|
350
|
+
},
|
|
351
|
+
});
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
The service skips any incoming statement whose bytes match `initialProcessedDataHex`. PApp
|
|
355
|
+
re-encrypts every Success with a fresh ephemeral key + AES-GCM nonce, so a genuine re-pair
|
|
356
|
+
always produces different bytes and passes the dedupe.
|
|
357
|
+
|
|
358
|
+
### Pairing topic / channel
|
|
359
|
+
|
|
360
|
+
If the host needs to derive the pairing topic or channel itself (for example to subscribe
|
|
361
|
+
in-line, or to verify a statement source):
|
|
362
|
+
|
|
363
|
+
```ts
|
|
364
|
+
import { computePairingTopic, computePairingChannel } from '@novasamatech/host-papp';
|
|
365
|
+
|
|
366
|
+
const topic = computePairingTopic(statementAccountId, encryptionPublicKey);
|
|
367
|
+
const channel = computePairingChannel(statementAccountId, encryptionPublicKey);
|
|
368
|
+
// topic = blake2b256_keyed(encryptionPublicKey || "topic", key=statementAccountId)
|
|
369
|
+
// channel = blake2b256_keyed(encryptionPublicKey || "channel", key=statementAccountId)
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Codec exports
|
|
373
|
+
|
|
374
|
+
The SCALE codecs are exported as plain `Codec<T>` values for callers that need to
|
|
375
|
+
encode/decode statements outside the orchestrator:
|
|
376
|
+
|
|
377
|
+
| Export | Description |
|
|
378
|
+
| --------------------------------- | -------------------------------------------------------------------------------------------- |
|
|
379
|
+
| `VersionedHandshakeProposal` | Outer enum; V2 at SCALE discriminant 1, with `_v1Reserved` at 0. |
|
|
380
|
+
| `HandshakeProposalV2` | `{ device, metadata }` — what the QR encodes. |
|
|
381
|
+
| `Device` | `{ statementAccountId(32), encryptionPublicKey(65) }`. |
|
|
382
|
+
| `MetadataKey`, `MetadataEntry` | Metadata enum + `(MetadataKey, str)` tuple. |
|
|
383
|
+
| `VersionedHandshakeResponse` | Outer enum for the answer; `V1` legacy + `V2`. |
|
|
384
|
+
| `HandshakeResponseV2` | `{ encrypted, tmpKey(65) }` — the ECDH-wrapped envelope. |
|
|
385
|
+
| `EncryptedHandshakeResponseV2` | Inner payload after envelope decrypt: `Pending` (1 byte), `Success` (161 bytes), `Failed`. |
|
|
386
|
+
| `HandshakeSuccessV2` | `{ encryptionKey(65), accountId(32), identitySignature(64) }`. |
|
|
387
|
+
| `IDENTITY_SIGNATURE_PAYLOAD_BYTES`| `97` — the bytes the user identity sr25519 signs over. |
|
package/dist/debugTypes.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export type SsoDebugEvent = {
|
|
|
25
25
|
flowId: string;
|
|
26
26
|
timestamp: number;
|
|
27
27
|
payload: {
|
|
28
|
-
metadata:
|
|
28
|
+
metadata: unknown;
|
|
29
29
|
};
|
|
30
30
|
} | {
|
|
31
31
|
layer: 'sso';
|
|
@@ -40,16 +40,14 @@ export type SsoDebugEvent = {
|
|
|
40
40
|
event: 'awaiting_response';
|
|
41
41
|
flowId: string;
|
|
42
42
|
timestamp: number;
|
|
43
|
-
payload:
|
|
44
|
-
topic: string;
|
|
45
|
-
};
|
|
43
|
+
payload: Record<string, never>;
|
|
46
44
|
} | {
|
|
47
45
|
layer: 'sso';
|
|
48
46
|
event: 'response_received';
|
|
49
47
|
flowId: string;
|
|
50
48
|
timestamp: number;
|
|
51
49
|
payload: {
|
|
52
|
-
|
|
50
|
+
identityAccountId: Uint8Array;
|
|
53
51
|
};
|
|
54
52
|
} | {
|
|
55
53
|
layer: 'sso';
|
|
@@ -18,10 +18,16 @@ export function createIdentityRpcAdapter(lazyClient) {
|
|
|
18
18
|
if (!results) {
|
|
19
19
|
return ok({});
|
|
20
20
|
}
|
|
21
|
-
return ok(Object.fromEntries(zipWith([accounts, results], x => x).map(([accountId,
|
|
22
|
-
if (!
|
|
21
|
+
return ok(Object.fromEntries(zipWith([accounts, results], x => x).map(([accountId, typedRaw]) => {
|
|
22
|
+
if (!typedRaw) {
|
|
23
23
|
return [accountId, null];
|
|
24
24
|
}
|
|
25
|
+
// Runtime metadata may expose fields in snake_case (V1) or
|
|
26
|
+
// camelCase (V2 multi-device). Read defensively. The .papi
|
|
27
|
+
// descriptor only types snake_case, so widen here.
|
|
28
|
+
const raw = typedRaw;
|
|
29
|
+
const fullUsername = raw.full_username ?? raw.fullUsername;
|
|
30
|
+
const liteUsername = raw.lite_username ?? raw.liteUsername;
|
|
25
31
|
const credibility = raw.credibility.type == 'Lite'
|
|
26
32
|
? {
|
|
27
33
|
type: 'Lite',
|
|
@@ -29,14 +35,15 @@ export function createIdentityRpcAdapter(lazyClient) {
|
|
|
29
35
|
: {
|
|
30
36
|
type: 'Person',
|
|
31
37
|
alias: raw.credibility.value.alias,
|
|
32
|
-
lastUpdate: raw.credibility.value.last_update
|
|
38
|
+
lastUpdate: (raw.credibility.value.last_update ??
|
|
39
|
+
raw.credibility.value.lastUpdate).toString(),
|
|
33
40
|
};
|
|
34
41
|
return [
|
|
35
42
|
accountId,
|
|
36
43
|
{
|
|
37
44
|
accountId: accountId,
|
|
38
|
-
fullUsername:
|
|
39
|
-
liteUsername: textDecoder.decode(
|
|
45
|
+
fullUsername: fullUsername ? textDecoder.decode(fullUsername) : null,
|
|
46
|
+
liteUsername: liteUsername ? textDecoder.decode(liteUsername) : '',
|
|
40
47
|
credibility,
|
|
41
48
|
},
|
|
42
49
|
];
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { SS_PASEO_STABLE_STAGE_ENDPOINTS, SS_PREVIEW_STAGE_ENDPOINTS, SS_STABLE_STAGE_ENDPOINTS } from './constants.js';
|
|
2
2
|
export type { PappAdapter } from './papp.js';
|
|
3
3
|
export { createPappAdapter } from './papp.js';
|
|
4
|
-
export type { HostMetadata } from './sso/auth/impl.js';
|
|
4
|
+
export type { AuthComponent, HostMetadata, OnAuthSuccess } from './sso/auth/impl.js';
|
|
5
5
|
export type { PairingStatus } from './sso/auth/types.js';
|
|
6
|
+
export type { DeviceIdentityForPairing } from './sso/auth/v2/service.js';
|
|
6
7
|
export type { UserSession } from './sso/sessionManager/userSession.js';
|
|
7
8
|
export type { StoredUserSession } from './sso/userSessionRepository.js';
|
|
8
9
|
export type { Identity } from './identity/types.js';
|
package/dist/papp.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { LazyClient, StatementStoreAdapter } from '@novasamatech/statement-store';
|
|
2
2
|
import type { StorageAdapter } from '@novasamatech/storage-adapter';
|
|
3
3
|
import type { IdentityAdapter, IdentityRepository } from './identity/types.js';
|
|
4
|
-
import type { AuthComponent, HostMetadata } from './sso/auth/impl.js';
|
|
4
|
+
import type { AuthComponent, HostMetadata, OnAuthSuccess } from './sso/auth/impl.js';
|
|
5
|
+
import type { DeviceIdentityForPairing } from './sso/auth/v2/service.js';
|
|
5
6
|
import type { SsoSessionManager } from './sso/sessionManager/impl.js';
|
|
6
7
|
import type { UserSecretRepository } from './sso/userSecretRepository.js';
|
|
7
8
|
export type PappAdapter = {
|
|
@@ -18,27 +19,31 @@ type Adapters = {
|
|
|
18
19
|
};
|
|
19
20
|
type Params = {
|
|
20
21
|
/**
|
|
21
|
-
* Host app Id.
|
|
22
|
-
*
|
|
22
|
+
* Host app Id. CAUTION! This value should be stable across launches — it
|
|
23
|
+
* seeds the storage prefix that backs every persisted SSO blob.
|
|
23
24
|
*/
|
|
24
25
|
appId: string;
|
|
25
26
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* interface Metadata {
|
|
30
|
-
* name: string;
|
|
31
|
-
* icon: string; // url for icon. Icon should be a rasterized image with min size 256x256 px.
|
|
32
|
-
* }
|
|
33
|
-
* ```
|
|
27
|
+
* Host environment metadata embedded inside the V2 pairing proposal QR so
|
|
28
|
+
* the paired device can render a request screen with the host name / icon /
|
|
29
|
+
* platform. All fields are optional — absence must not break pairing.
|
|
34
30
|
*/
|
|
35
|
-
|
|
31
|
+
hostMetadata?: HostMetadata;
|
|
36
32
|
/**
|
|
37
|
-
* Optional
|
|
38
|
-
*
|
|
33
|
+
* Optional override for the device identity. Default: the SDK persists a
|
|
34
|
+
* fresh identity to the configured `StorageAdapter` on first run and reuses
|
|
35
|
+
* it on subsequent launches. Pass a factory only if you need a different
|
|
36
|
+
* persistence backend (Electron Keychain, native secure storage, etc.).
|
|
39
37
|
*/
|
|
40
|
-
|
|
38
|
+
deviceIdentity?: () => Promise<DeviceIdentityForPairing> | DeviceIdentityForPairing;
|
|
39
|
+
/**
|
|
40
|
+
* Optional caller hook fired after a successful handshake — after the SDK
|
|
41
|
+
* has already written the session + secrets to its own repositories. Use it
|
|
42
|
+
* for consumer-specific bookkeeping (telemetry, custom peer caches, device-
|
|
43
|
+
* sync seeding). Throwing fails the `sso.authenticate()` call.
|
|
44
|
+
*/
|
|
45
|
+
onAuthSuccess?: OnAuthSuccess;
|
|
41
46
|
adapters?: Partial<Adapters>;
|
|
42
47
|
};
|
|
43
|
-
export declare function createPappAdapter({ appId,
|
|
48
|
+
export declare function createPappAdapter({ appId, hostMetadata, deviceIdentity, onAuthSuccess, adapters, }: Params): PappAdapter;
|
|
44
49
|
export {};
|
package/dist/papp.js
CHANGED
|
@@ -5,10 +5,11 @@ 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';
|
|
8
|
+
import { createDeviceIdentityStore } from './sso/deviceIdentityStore.js';
|
|
8
9
|
import { createSsoSessionManager } from './sso/sessionManager/impl.js';
|
|
9
10
|
import { createUserSecretRepository } from './sso/userSecretRepository.js';
|
|
10
11
|
import { createUserSessionRepository } from './sso/userSessionRepository.js';
|
|
11
|
-
export function createPappAdapter({ appId,
|
|
12
|
+
export function createPappAdapter({ appId, hostMetadata, deviceIdentity, onAuthSuccess, adapters, }) {
|
|
12
13
|
const lazyClient = adapters?.lazyClient ??
|
|
13
14
|
createLazyClient(getWsProvider(SS_STABLE_STAGE_ENDPOINTS, { heartbeatTimeout: Number.POSITIVE_INFINITY }));
|
|
14
15
|
const statementStore = adapters?.statementStore ?? createPapiStatementStoreAdapter(lazyClient);
|
|
@@ -16,13 +17,16 @@ export function createPappAdapter({ appId, metadata, hostMetadata, adapters }) {
|
|
|
16
17
|
const storage = adapters?.storage ?? createLocalStorageAdapter(appId);
|
|
17
18
|
const ssoSessionRepository = createUserSessionRepository(storage);
|
|
18
19
|
const userSecretRepository = createUserSecretRepository(appId, storage);
|
|
20
|
+
const deviceIdentityStore = createDeviceIdentityStore(appId, storage);
|
|
19
21
|
return {
|
|
20
22
|
sso: createAuth({
|
|
21
|
-
metadata,
|
|
22
23
|
hostMetadata,
|
|
24
|
+
deviceIdentity,
|
|
25
|
+
deviceIdentityStore,
|
|
23
26
|
statementStore,
|
|
24
27
|
ssoSessionRepository,
|
|
25
28
|
userSecretRepository,
|
|
29
|
+
onAuthSuccess,
|
|
26
30
|
}),
|
|
27
31
|
sessions: createSsoSessionManager({ storage, statementStore, ssoSessionRepository, userSecretRepository }),
|
|
28
32
|
secrets: userSecretRepository,
|
package/dist/sso/auth/impl.d.ts
CHANGED
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
import type { StatementStoreAdapter } from '@novasamatech/statement-store';
|
|
2
2
|
import { ResultAsync } from 'neverthrow';
|
|
3
|
+
import type { DeviceIdentityStore } from '../deviceIdentityStore.js';
|
|
3
4
|
import type { UserSecretRepository } from '../userSecretRepository.js';
|
|
4
5
|
import type { StoredUserSession, UserSessionRepository } from '../userSessionRepository.js';
|
|
6
|
+
import type { HandshakeMetadata } from './v2/proposal.js';
|
|
7
|
+
import type { DeviceIdentityForPairing } from './v2/service.js';
|
|
8
|
+
export type HostMetadata = HandshakeMetadata;
|
|
5
9
|
export type AuthComponent = ReturnType<typeof createAuth>;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Optional caller hook fired once the V2 handshake reaches Success, after the
|
|
12
|
+
* SDK has persisted the session and secrets and before `authenticate()`
|
|
13
|
+
* resolves. Receives both the persisted `StoredUserSession` and the sensitive
|
|
14
|
+
* `identityChatPrivateKey` (which lives in `UserSecretRepository` and isn't
|
|
15
|
+
* surfaced on the session shape). Throwing fails the `authenticate()` call.
|
|
16
|
+
*/
|
|
17
|
+
export type OnAuthSuccess = (event: {
|
|
18
|
+
session: StoredUserSession;
|
|
19
|
+
identityChatPrivateKey: Uint8Array;
|
|
20
|
+
}) => Promise<void> | void;
|
|
11
21
|
type Params = {
|
|
12
|
-
metadata: string;
|
|
13
22
|
hostMetadata?: HostMetadata;
|
|
23
|
+
/**
|
|
24
|
+
* Optional override for the device identity. If absent, the SDK uses an
|
|
25
|
+
* internal `deviceIdentityStore` backed by the host's `StorageAdapter` —
|
|
26
|
+
* fine for web hosts. Electron / native consumers can plug in a Keychain-
|
|
27
|
+
* backed identity by passing a factory that returns the same shape.
|
|
28
|
+
*/
|
|
29
|
+
deviceIdentity?: () => Promise<DeviceIdentityForPairing> | DeviceIdentityForPairing;
|
|
30
|
+
deviceIdentityStore: DeviceIdentityStore;
|
|
14
31
|
statementStore: StatementStoreAdapter;
|
|
15
32
|
ssoSessionRepository: UserSessionRepository;
|
|
16
33
|
userSecretRepository: UserSecretRepository;
|
|
34
|
+
onAuthSuccess?: OnAuthSuccess;
|
|
17
35
|
};
|
|
18
|
-
export declare function createAuth({
|
|
36
|
+
export declare function createAuth({ hostMetadata, deviceIdentity, deviceIdentityStore, statementStore, ssoSessionRepository, userSecretRepository, onAuthSuccess, }: Params): {
|
|
19
37
|
pairingStatus: {
|
|
20
38
|
read: () => {
|
|
21
39
|
step: "none";
|