@oxy.so/core 1.2.1 → 1.3.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/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/crypto/identityMove.js +123 -0
- package/dist/cjs/crypto/keyManager.js +60 -21
- package/dist/cjs/crypto/webIdentityCarrier.js +262 -0
- package/dist/cjs/i18n/locales/ar-SA.json +16 -13
- package/dist/cjs/i18n/locales/ca-ES.json +16 -13
- package/dist/cjs/i18n/locales/de-DE.json +16 -13
- package/dist/cjs/i18n/locales/en-US.json +16 -17
- package/dist/cjs/i18n/locales/es-ES.json +16 -17
- package/dist/cjs/i18n/locales/fr-FR.json +16 -13
- package/dist/cjs/i18n/locales/it-IT.json +16 -13
- package/dist/cjs/i18n/locales/ja-JP.json +16 -13
- package/dist/cjs/i18n/locales/ko-KR.json +16 -13
- package/dist/cjs/i18n/locales/locales/ar-SA.json +16 -13
- package/dist/cjs/i18n/locales/locales/ca-ES.json +16 -13
- package/dist/cjs/i18n/locales/locales/de-DE.json +16 -13
- package/dist/cjs/i18n/locales/locales/en-US.json +16 -17
- package/dist/cjs/i18n/locales/locales/es-ES.json +16 -17
- package/dist/cjs/i18n/locales/locales/fr-FR.json +16 -13
- package/dist/cjs/i18n/locales/locales/it-IT.json +16 -13
- package/dist/cjs/i18n/locales/locales/ja-JP.json +16 -13
- package/dist/cjs/i18n/locales/locales/ko-KR.json +16 -13
- package/dist/cjs/i18n/locales/locales/pt-PT.json +16 -13
- package/dist/cjs/i18n/locales/locales/zh-CN.json +16 -13
- package/dist/cjs/i18n/locales/pt-PT.json +16 -13
- package/dist/cjs/i18n/locales/zh-CN.json +16 -13
- package/dist/cjs/index.js +38 -4
- package/dist/cjs/mixins/OxyServices.auth.js +14 -4
- package/dist/cjs/mixins/OxyServices.identity.js +14 -0
- package/dist/cjs/session/accountDialogController.js +333 -142
- package/dist/cjs/utils/authWebUrl.js +8 -1
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/crypto/identityMove.js +110 -0
- package/dist/esm/crypto/keyManager.js +60 -21
- package/dist/esm/crypto/webIdentityCarrier.js +243 -0
- package/dist/esm/i18n/locales/ar-SA.json +16 -13
- package/dist/esm/i18n/locales/ca-ES.json +16 -13
- package/dist/esm/i18n/locales/de-DE.json +16 -13
- package/dist/esm/i18n/locales/en-US.json +16 -17
- package/dist/esm/i18n/locales/es-ES.json +16 -17
- package/dist/esm/i18n/locales/fr-FR.json +16 -13
- package/dist/esm/i18n/locales/it-IT.json +16 -13
- package/dist/esm/i18n/locales/ja-JP.json +16 -13
- package/dist/esm/i18n/locales/ko-KR.json +16 -13
- package/dist/esm/i18n/locales/locales/ar-SA.json +16 -13
- package/dist/esm/i18n/locales/locales/ca-ES.json +16 -13
- package/dist/esm/i18n/locales/locales/de-DE.json +16 -13
- package/dist/esm/i18n/locales/locales/en-US.json +16 -17
- package/dist/esm/i18n/locales/locales/es-ES.json +16 -17
- package/dist/esm/i18n/locales/locales/fr-FR.json +16 -13
- package/dist/esm/i18n/locales/locales/it-IT.json +16 -13
- package/dist/esm/i18n/locales/locales/ja-JP.json +16 -13
- package/dist/esm/i18n/locales/locales/ko-KR.json +16 -13
- package/dist/esm/i18n/locales/locales/pt-PT.json +16 -13
- package/dist/esm/i18n/locales/locales/zh-CN.json +16 -13
- package/dist/esm/i18n/locales/pt-PT.json +16 -13
- package/dist/esm/i18n/locales/zh-CN.json +16 -13
- package/dist/esm/index.js +5 -1
- package/dist/esm/mixins/OxyServices.auth.js +14 -4
- package/dist/esm/mixins/OxyServices.identity.js +14 -0
- package/dist/esm/session/accountDialogController.js +334 -143
- package/dist/esm/utils/authWebUrl.js +7 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/crypto/identityMove.d.ts +59 -0
- package/dist/types/crypto/keyManager.d.ts +35 -5
- package/dist/types/crypto/webIdentityCarrier.d.ts +123 -0
- package/dist/types/index.d.ts +5 -2
- package/dist/types/mixins/OxyServices.auth.d.ts +12 -0
- package/dist/types/models/interfaces.d.ts +15 -0
- package/dist/types/session/accountDialogController.d.ts +169 -17
- package/dist/types/utils/authWebUrl.d.ts +7 -0
- package/package.json +3 -3
- package/src/crypto/__tests__/identityMove.test.ts +104 -0
- package/src/crypto/__tests__/keyManager.sharedSlotSync.test.ts +150 -0
- package/src/crypto/__tests__/webIdentityCarrier.test.ts +211 -0
- package/src/crypto/identityMove.ts +133 -0
- package/src/crypto/keyManager.ts +71 -21
- package/src/crypto/webIdentityCarrier.ts +326 -0
- package/src/i18n/locales/ar-SA.json +16 -13
- package/src/i18n/locales/ca-ES.json +16 -13
- package/src/i18n/locales/de-DE.json +16 -13
- package/src/i18n/locales/en-US.json +16 -17
- package/src/i18n/locales/es-ES.json +16 -17
- package/src/i18n/locales/fr-FR.json +16 -13
- package/src/i18n/locales/it-IT.json +16 -13
- package/src/i18n/locales/ja-JP.json +16 -13
- package/src/i18n/locales/ko-KR.json +16 -13
- package/src/i18n/locales/pt-PT.json +16 -13
- package/src/i18n/locales/zh-CN.json +16 -13
- package/src/index.ts +38 -1
- package/src/mixins/OxyServices.auth.ts +16 -3
- package/src/mixins/OxyServices.identity.ts +14 -0
- package/src/mixins/__tests__/OxyServices.rotateKey.test.ts +47 -0
- package/src/mixins/__tests__/commonsSignIn.test.ts +3 -0
- package/src/mixins/__tests__/verifyChallenge.test.ts +31 -0
- package/src/models/interfaces.ts +15 -0
- package/src/session/__tests__/accountDialogController.test.ts +407 -10
- package/src/session/accountDialogController.ts +406 -145
- package/src/utils/__tests__/oauthPkce.test.ts +50 -26
- package/src/utils/authWebUrl.ts +8 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity move — the crypto of taking a web identity into Commons.
|
|
3
|
+
*
|
|
4
|
+
* Both carriers run this: `id.oxy.so` (the initiator, holding the identity) and
|
|
5
|
+
* Commons (the responder, receiving it). The relay in between sees two ephemeral
|
|
6
|
+
* public keys and ciphertext; the 6-digit SAS both screens show is what makes a
|
|
7
|
+
* relay that swapped a key visible to the person.
|
|
8
|
+
*
|
|
9
|
+
* PURE: no storage, no network. Contract: `@oxy.so/contracts` `identityMove`.
|
|
10
|
+
*/
|
|
11
|
+
import './polyfill';
|
|
12
|
+
import { type OpenedWebIdentity } from './webIdentityCarrier';
|
|
13
|
+
/** Actions the identity key signs during a move. */
|
|
14
|
+
export declare const IDENTITY_MOVE_ACTIONS: {
|
|
15
|
+
readonly seal: "identity_move_seal";
|
|
16
|
+
readonly received: "identity_move_received";
|
|
17
|
+
};
|
|
18
|
+
/** A fresh ephemeral key pair for one move. Keep the private key in memory only. */
|
|
19
|
+
export declare function generateMoveEphemeralKeyPair(): {
|
|
20
|
+
privateKey: string;
|
|
21
|
+
publicKey: string;
|
|
22
|
+
};
|
|
23
|
+
/** The symmetric key both sides derive: HKDF(ECDH, salt = moveId). */
|
|
24
|
+
export declare function deriveMoveKey(ownEphemeralPrivateKey: string, otherEphemeralPublicKey: string, moveId: string): Uint8Array;
|
|
25
|
+
/** The 6-digit code both screens show, bound to the move and both ephemeral keys in their roles. */
|
|
26
|
+
export declare function deriveMoveSas(moveId: string, initiatorEphemeralPublicKey: string, responderEphemeralPublicKey: string): string;
|
|
27
|
+
/** Initiator: seal the identity's entropy for the responder. */
|
|
28
|
+
export declare function sealIdentityForMove(identity: Pick<OpenedWebIdentity, 'mnemonic' | 'publicKey'>, moveKey: Uint8Array, moveId: string): {
|
|
29
|
+
nonce: string;
|
|
30
|
+
ciphertext: string;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Responder: open the sealed identity. The derived public key must be the one
|
|
34
|
+
* the move declared — anything else is refused, never imported.
|
|
35
|
+
*/
|
|
36
|
+
export declare function openMovedIdentity(sealed: {
|
|
37
|
+
nonce: string;
|
|
38
|
+
ciphertext: string;
|
|
39
|
+
}, moveKey: Uint8Array, moveId: string, expectedPublicKey: string): OpenedWebIdentity;
|
|
40
|
+
/** The exact bytes a move proof or receipt signs. */
|
|
41
|
+
export declare function buildMoveMessage(action: string, moveId: string, timestamp: number): string;
|
|
42
|
+
/** Sign a move action with the identity key. */
|
|
43
|
+
export declare function signMoveAction(identity: Pick<OpenedWebIdentity, 'privateKey'>, action: string, moveId: string, timestamp?: number): Promise<{
|
|
44
|
+
signature: string;
|
|
45
|
+
timestamp: number;
|
|
46
|
+
}>;
|
|
47
|
+
/**
|
|
48
|
+
* Initiator: is this receipt really from the identity? Checked locally with the
|
|
49
|
+
* identity's public key, so a server that claims "completed" without Commons
|
|
50
|
+
* having the key cannot make the web destroy its copy.
|
|
51
|
+
*/
|
|
52
|
+
export declare function verifyMoveReceipt(publicKey: string, moveId: string, receipt: {
|
|
53
|
+
signature: string;
|
|
54
|
+
timestamp: number;
|
|
55
|
+
}): Promise<boolean>;
|
|
56
|
+
/** The QR payload for a move. */
|
|
57
|
+
export declare function buildMoveQrPayload(moveId: string): string;
|
|
58
|
+
/** The move id in a scanned payload, or `null` when it is not a move QR. */
|
|
59
|
+
export declare function parseMoveQrPayload(raw: string): string | null;
|
|
@@ -230,6 +230,32 @@ export declare class KeyManager {
|
|
|
230
230
|
* @returns Shared private key or null if no shared identity exists
|
|
231
231
|
*/
|
|
232
232
|
static getSharedPrivateKey(): Promise<string | null>;
|
|
233
|
+
/**
|
|
234
|
+
* Which identity key this device is actually acting as, per slot.
|
|
235
|
+
*
|
|
236
|
+
* There are two slots and they can disagree. The shared slot is what every
|
|
237
|
+
* cross-app reader takes first — `deriveScopedSeed` (so: the FairCoin wallet
|
|
238
|
+
* Peable derives) and `getSharedPrivateKey` — while the primary slot is what
|
|
239
|
+
* signing and the server-facing identity use. A disagreement is invisible to
|
|
240
|
+
* both sides on its own, and it is money: a wallet derived from a stale
|
|
241
|
+
* shared key watches addresses that the key published in the DID will never
|
|
242
|
+
* produce, so payments land where the recipient cannot see them.
|
|
243
|
+
*
|
|
244
|
+
* `activePublicKey` is the one money derives from — the same `shared ?? primary`
|
|
245
|
+
* order `deriveScopedSeed` uses, so a caller can compare it against the key
|
|
246
|
+
* published for the account and refuse to receive rather than watch the wrong
|
|
247
|
+
* addresses.
|
|
248
|
+
*
|
|
249
|
+
* Public keys are canonicalized, so two encodings of the same key compare
|
|
250
|
+
* equal. On web both slots are null and `inSync` is true: nothing is stored,
|
|
251
|
+
* so nothing can disagree.
|
|
252
|
+
*/
|
|
253
|
+
static getIdentityKeyState(): Promise<{
|
|
254
|
+
primaryPublicKey: string | null;
|
|
255
|
+
sharedPublicKey: string | null;
|
|
256
|
+
activePublicKey: string | null;
|
|
257
|
+
inSync: boolean;
|
|
258
|
+
}>;
|
|
233
259
|
/**
|
|
234
260
|
* Check if a shared identity exists (accessible across all Oxy apps)
|
|
235
261
|
*
|
|
@@ -279,14 +305,18 @@ export declare class KeyManager {
|
|
|
279
305
|
*/
|
|
280
306
|
static clearSharedSession(): Promise<void>;
|
|
281
307
|
/**
|
|
282
|
-
*
|
|
308
|
+
* Make the shared slot hold THIS device's identity key.
|
|
283
309
|
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
310
|
+
* The shared slot is the one every Oxy app on the device reads, so it has to
|
|
311
|
+
* agree with the primary slot. This creates it when it is empty (the original
|
|
312
|
+
* migration, for apps that predate the shared identity) and repairs it when it
|
|
313
|
+
* holds a different key.
|
|
287
314
|
*
|
|
288
|
-
* @returns True
|
|
315
|
+
* @returns True when the shared slot ends up holding the device's key, false
|
|
316
|
+
* when there is no identity to share.
|
|
289
317
|
*/
|
|
318
|
+
static syncSharedIdentity(): Promise<boolean>;
|
|
319
|
+
/** @deprecated Renamed to {@link KeyManager.syncSharedIdentity}. */
|
|
290
320
|
static migrateToSharedIdentity(): Promise<boolean>;
|
|
291
321
|
/**
|
|
292
322
|
* Atomically persist a key pair to secure storage with verification + backup.
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Web identity carrier — the crypto of "one identity, two carriers".
|
|
3
|
+
*
|
|
4
|
+
* An Oxy identity is a BIP-39 mnemonic; its seed's first 32 bytes are the
|
|
5
|
+
* secp256k1 key (`recoveryPhrase.ts`). Commons keeps that key in the device
|
|
6
|
+
* keychain. This module lets a browser carry the SAME identity without Oxy ever
|
|
7
|
+
* being able to use it:
|
|
8
|
+
*
|
|
9
|
+
* entropy ──AEAD(DEK)──▶ sealedEntropy
|
|
10
|
+
* DEK ──AEAD(KEK_i)─▶ wraps[i] KEK_i = HKDF(PRF output of passkey i)
|
|
11
|
+
*
|
|
12
|
+
* The PRF output is produced inside the user's authenticator, behind user
|
|
13
|
+
* verification, and never leaves the page that asked for it. The envelope is
|
|
14
|
+
* therefore safe to store anywhere — the identity origin's IndexedDB and the
|
|
15
|
+
* server copy alike — and opens only with a registered passkey or the phrase.
|
|
16
|
+
*
|
|
17
|
+
* PURE: no storage, no network, no WebAuthn call, no platform globals beyond the
|
|
18
|
+
* CSPRNG the AEAD polyfill guarantees. The caller runs the ceremony and hands the
|
|
19
|
+
* PRF output in; everything here is deterministic given its inputs, so it is
|
|
20
|
+
* identical on web, Node and React Native, and fully unit-testable.
|
|
21
|
+
*
|
|
22
|
+
* Contract: `WebIdentityEnvelope` in `@oxy.so/contracts`.
|
|
23
|
+
*/
|
|
24
|
+
import './polyfill';
|
|
25
|
+
import { type WebIdentityEnvelope, type WebIdentityWrap } from '@oxy.so/contracts';
|
|
26
|
+
/**
|
|
27
|
+
* The PRF input every identity-carrier ceremony evaluates (`prf.eval.first`).
|
|
28
|
+
*
|
|
29
|
+
* Fixed rather than per-wrap: discoverable sign-in has no credential list to key
|
|
30
|
+
* `evalByCredential` by, and each passkey's output is already distinct. A future
|
|
31
|
+
* scheme change bumps the label — and with it every KEK — instead of mutating it.
|
|
32
|
+
*/
|
|
33
|
+
export declare const WEB_IDENTITY_PRF_INPUT: Uint8Array;
|
|
34
|
+
/** Why an envelope did not open. Every value is a normal outcome a UI can explain. */
|
|
35
|
+
export type WebIdentityUnlockFailure =
|
|
36
|
+
/** The passkey used is not one of the envelope's wraps. */
|
|
37
|
+
'unknown-credential'
|
|
38
|
+
/**
|
|
39
|
+
* The passkey IS listed but its PRF output does not open its wrap — a synced
|
|
40
|
+
* copy returning a different PRF value, or a provider that silently changed it.
|
|
41
|
+
* Recoverable with another passkey or the phrase.
|
|
42
|
+
*/
|
|
43
|
+
| 'prf-mismatch'
|
|
44
|
+
/** The envelope itself is malformed or tampered with. */
|
|
45
|
+
| 'corrupt';
|
|
46
|
+
export declare class WebIdentityUnlockError extends Error {
|
|
47
|
+
readonly failure: WebIdentityUnlockFailure;
|
|
48
|
+
constructor(failure: WebIdentityUnlockFailure, message: string);
|
|
49
|
+
}
|
|
50
|
+
/** The identity in usable form. Hold it for one operation, then {@link wipeBytes}. */
|
|
51
|
+
export interface OpenedWebIdentity {
|
|
52
|
+
mnemonic: string;
|
|
53
|
+
/** 32-byte secp256k1 private key, lowercase hex. */
|
|
54
|
+
privateKey: string;
|
|
55
|
+
/** Uncompressed SEC1 public key, lowercase hex (Oxy's canonical form). */
|
|
56
|
+
publicKey: string;
|
|
57
|
+
}
|
|
58
|
+
/** Overwrite a buffer holding secret material. Best effort — JS gives no stronger guarantee. */
|
|
59
|
+
export declare function wipeBytes(bytes: Uint8Array): void;
|
|
60
|
+
/**
|
|
61
|
+
* Derive the identity from a mnemonic exactly as Commons does
|
|
62
|
+
* (`RecoveryPhraseService`): seed → first 32 bytes → secp256k1.
|
|
63
|
+
*/
|
|
64
|
+
export declare function deriveIdentityFromMnemonic(mnemonic: string): OpenedWebIdentity;
|
|
65
|
+
/** A brand-new 12-word identity. Nothing is persisted. */
|
|
66
|
+
export declare function generateWebIdentity(): OpenedWebIdentity;
|
|
67
|
+
/** A fresh random data key for a new envelope. */
|
|
68
|
+
export declare function generateDataKey(): Uint8Array;
|
|
69
|
+
/** The key-encryption key a passkey's PRF output yields for its own wrap. */
|
|
70
|
+
export declare function deriveKeyEncryptionKey(prfOutput: Uint8Array, credentialId: string): Uint8Array;
|
|
71
|
+
/** Wrap `dataKey` for one passkey. */
|
|
72
|
+
export declare function wrapDataKey(dataKey: Uint8Array, prfOutput: Uint8Array, credentialId: string, publicKey: string, now?: Date): WebIdentityWrap;
|
|
73
|
+
/**
|
|
74
|
+
* Seal an identity into a new envelope that opens with the given passkey.
|
|
75
|
+
*
|
|
76
|
+
* Returns the data key too, so the caller can add further wraps in the same
|
|
77
|
+
* session; wipe it when done.
|
|
78
|
+
*/
|
|
79
|
+
export declare function sealWebIdentity(identity: Pick<OpenedWebIdentity, 'mnemonic' | 'publicKey'>, firstWrap: {
|
|
80
|
+
prfOutput: Uint8Array;
|
|
81
|
+
credentialId: string;
|
|
82
|
+
}, now?: Date): {
|
|
83
|
+
envelope: WebIdentityEnvelope;
|
|
84
|
+
dataKey: Uint8Array;
|
|
85
|
+
};
|
|
86
|
+
/** Recover the data key with one passkey's PRF output. */
|
|
87
|
+
export declare function unwrapDataKey(envelope: WebIdentityEnvelope, prfOutput: Uint8Array, credentialId: string): Uint8Array;
|
|
88
|
+
/**
|
|
89
|
+
* Open an envelope with its data key.
|
|
90
|
+
*
|
|
91
|
+
* The derived public key is checked against the envelope's own: a sealed entropy
|
|
92
|
+
* that decrypts into a different identity is treated as corruption, never used.
|
|
93
|
+
*/
|
|
94
|
+
export declare function openWebIdentity(envelope: WebIdentityEnvelope, dataKey: Uint8Array): OpenedWebIdentity;
|
|
95
|
+
/** Unwrap and open in one step. */
|
|
96
|
+
export declare function unlockWebIdentity(envelope: WebIdentityEnvelope, prfOutput: Uint8Array, credentialId: string): OpenedWebIdentity;
|
|
97
|
+
/** Add (or replace) one passkey's wrap. Requires the data key of an unlocked envelope. */
|
|
98
|
+
export declare function addWrap(envelope: WebIdentityEnvelope, dataKey: Uint8Array, prfOutput: Uint8Array, credentialId: string, now?: Date): WebIdentityEnvelope;
|
|
99
|
+
/** Remove one passkey's wrap. The last wrap can never be removed — an envelope nobody can open is a loss. */
|
|
100
|
+
export declare function removeWrap(envelope: WebIdentityEnvelope, credentialId: string): WebIdentityEnvelope;
|
|
101
|
+
/**
|
|
102
|
+
* The message an identity key signs to authorize an account action
|
|
103
|
+
* (`link_identity`, `web_envelope_delete`, …): `JSON.stringify({ action, userId,
|
|
104
|
+
* timestamp })`, byte-identical to what the API reconstructs.
|
|
105
|
+
*/
|
|
106
|
+
export declare function buildIdentityActionMessage(action: string, userId: string, timestamp: number): string;
|
|
107
|
+
/** Sign an identity-action message with an opened identity's key. */
|
|
108
|
+
export declare function signIdentityAction(identity: Pick<OpenedWebIdentity, 'privateKey'>, action: string, userId: string, timestamp?: number): Promise<{
|
|
109
|
+
signature: string;
|
|
110
|
+
timestamp: number;
|
|
111
|
+
}>;
|
|
112
|
+
/**
|
|
113
|
+
* The 6-digit short authentication string both sides of an identity transfer show.
|
|
114
|
+
*
|
|
115
|
+
* Bound to the pairing and to BOTH ephemeral public keys in their roles, so a relay
|
|
116
|
+
* that substitutes either key produces a different code on each screen. The user
|
|
117
|
+
* comparing the two codes is what makes the transfer safe against the relay itself.
|
|
118
|
+
*/
|
|
119
|
+
export declare function deriveTransferSas(input: {
|
|
120
|
+
pairingId: string;
|
|
121
|
+
initiatorEphemeralPublicKey: string;
|
|
122
|
+
responderEphemeralPublicKey: string;
|
|
123
|
+
}): string;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -70,6 +70,9 @@ export { hkdfSha256 } from './crypto/kdf';
|
|
|
70
70
|
export { encryptAead, decryptAead, AEAD_KEY_LENGTH, AEAD_NONCE_LENGTH, } from './crypto/aead';
|
|
71
71
|
export type { AeadResult } from './crypto/aead';
|
|
72
72
|
export { deriveSharedSecret } from './crypto/ecdh';
|
|
73
|
+
export { WEB_IDENTITY_PRF_INPUT, WebIdentityUnlockError, addWrap, buildIdentityActionMessage, deriveIdentityFromMnemonic, deriveKeyEncryptionKey, deriveTransferSas, generateDataKey, generateWebIdentity, openWebIdentity, removeWrap, sealWebIdentity, signIdentityAction, unlockWebIdentity, unwrapDataKey, wipeBytes, wrapDataKey, } from './crypto/webIdentityCarrier';
|
|
74
|
+
export type { OpenedWebIdentity, WebIdentityUnlockFailure } from './crypto/webIdentityCarrier';
|
|
75
|
+
export { IDENTITY_MOVE_ACTIONS, buildMoveMessage, buildMoveQrPayload, deriveMoveKey, deriveMoveSas, generateMoveEphemeralKeyPair, openMovedIdentity, parseMoveQrPayload, sealIdentityForMove, signMoveAction, verifyMoveReceipt, } from './crypto/identityMove';
|
|
73
76
|
export { DeviceManager } from './utils/deviceManager';
|
|
74
77
|
export type { DeviceFingerprint, StoredDeviceInfo } from './utils/deviceManager';
|
|
75
78
|
export type { OxyConfig, PrivacySettings, NotificationPreferences, UserPreferences, User, LoginResponse, Notification, Wallet, Transaction, BlockedUser, RestrictedUser, TransferFundsRequest, PurchaseRequest, WithdrawalRequest, TransactionResponse, PaginationInfo, SearchProfilesResponse, ApiError, PaymentMethod, PaymentRequest, PaymentResponse, AnalyticsData, FollowerDetails, ContentViewer, FileMetadata, FileUploadResponse, FileListResponse, FileUpdateRequest, FileDeleteResponse, RNFileDescriptor, AssetUploadInput, FileVisibility, AssetLink, AssetMetadata, AssetVariant, Asset, AssetInitRequest, AssetInitResponse, AssetCompleteRequest, AssetLinkRequest, AssetUnlinkRequest, AssetUrlResponse, BatchFileAccessEntry, BatchFileAccessResponse, AssetDeleteSummary, AssetUpdateVisibilityRequest, AssetUpdateVisibilityResponse, ServiceAssetMetadata, ServiceAssetMetadataBySha, AccountStorageCategoryUsage, AccountStorageUsageResponse, SecurityEventType, SecurityEventSeverity, SecurityActivity, SecurityActivityResponse, AssetUploadProgress, DeviceLinkedSession, DeviceLinkedSessionsResponse, DeviceLinkedSessionLogoutResponse, UpdateDeviceNameResponse, } from './models/interfaces';
|
|
@@ -104,7 +107,7 @@ export { updateAvatarVisibility } from './utils/avatarUtils';
|
|
|
104
107
|
export { buildAccountsArray, createQuickAccount, getAccountDisplayName, getAccountFallbackHandle, formatPublicKeyHandle, getAccountColor, } from './utils/accountUtils';
|
|
105
108
|
export type { QuickAccount, DisplayNameUserShape } from './utils/accountUtils';
|
|
106
109
|
export { registrableApex } from './utils/registrableApex';
|
|
107
|
-
export { CENTRAL_IDP_APEX } from './utils/authWebUrl';
|
|
110
|
+
export { CENTRAL_IDP_APEX, IDENTITY_WEB_ORIGIN } from './utils/authWebUrl';
|
|
108
111
|
export { isOxyRpOrigin } from './utils/webauthnOrigin';
|
|
109
112
|
export { runColdBoot } from './utils/coldBoot';
|
|
110
113
|
export type { ColdBootStep, ColdBootStepResult, ColdBootSession, ColdBootSkip, ColdBootOutcome, RunColdBootOptions, } from './utils/coldBoot';
|
|
@@ -125,7 +128,7 @@ export { isSwitchTargetAccount, canSwitchIntoAccount, } from './session/accountS
|
|
|
125
128
|
export { resolveAccountDelegationAccess } from './session/accountDelegationAccess';
|
|
126
129
|
export type { AccountDelegationAccess, AccountDelegationNode, } from './session/accountDelegationAccess';
|
|
127
130
|
export { AccountDialogController, createAccountDialogController, } from './session/accountDialogController';
|
|
128
|
-
export type { AccountDialogControllerOptions, AccountDialogSnapshot, AccountDialogView, CommonsAvailability, PopupWindowHandle, SignInFlowPhase, SignInFlowState, SignInProgress, } from './session/accountDialogController';
|
|
131
|
+
export type { AccountDialogControllerOptions, AccountDialogSnapshot, AccountDialogView, CommonsAvailability, PopupWindowHandle, SignInFailureReason, SignInFlowPhase, SignInFlowState, SignInProgress, } from './session/accountDialogController';
|
|
129
132
|
export { createWebAuthStateStore, createNativeAuthStateStore, createMemoryAuthStateStore, AUTH_STATE_STORAGE_KEY, } from './session/authStateStore';
|
|
130
133
|
export type { PersistedAuthState, AuthStateStore, NativeKeyValueStorage, } from './session/authStateStore';
|
|
131
134
|
export { createSharedMirroringAuthStateStore, decideSharedDeviceJoin, decideSharedDevicePublish, normalizeSharedDeviceSessionRead, publishProvenDeviceCredential, readLocalDeviceCredential, } from './session/sharedDeviceCredential';
|
|
@@ -471,10 +471,16 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
|
|
|
471
471
|
* `timeout`). Interactive callers omit it (defaults keep retries); the
|
|
472
472
|
* cold-boot `shared-key-signin` step passes `{ retry: false }` so a slow
|
|
473
473
|
* network cannot multiply boot latency via the inner retry loop.
|
|
474
|
+
* @param options.plantTokens - Install the returned bearer on this client
|
|
475
|
+
* (default `true`). A caller that must decide LATER whether the session is
|
|
476
|
+
* still wanted (the account dialog, whose user may have cancelled while the
|
|
477
|
+
* request was in flight) passes `false` and plants it itself.
|
|
474
478
|
*/
|
|
475
479
|
verifyChallenge(publicKey: string, challenge: string, signature: string, timestamp: number, deviceName?: string, deviceFingerprint?: string, requestOptions?: {
|
|
476
480
|
retry?: boolean;
|
|
477
481
|
timeout?: number;
|
|
482
|
+
}, options?: {
|
|
483
|
+
plantTokens?: boolean;
|
|
478
484
|
}): Promise<SessionLoginResponse>;
|
|
479
485
|
/**
|
|
480
486
|
* Check if a public key is already registered
|
|
@@ -515,9 +521,13 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
|
|
|
515
521
|
* `POST /auth/session/create` at the start of the flow.
|
|
516
522
|
* @param options.deviceFingerprint - Optional fingerprint of the
|
|
517
523
|
* originating client device.
|
|
524
|
+
* @param options.plantTokens - Install the claimed bearer on this client
|
|
525
|
+
* (default `true`). Pass `false` to install it yourself once you know the
|
|
526
|
+
* session is still wanted — see `verifyChallenge`.
|
|
518
527
|
*/
|
|
519
528
|
claimSessionByToken(sessionToken: string, options?: {
|
|
520
529
|
deviceFingerprint?: string;
|
|
530
|
+
plantTokens?: boolean;
|
|
521
531
|
}): Promise<{
|
|
522
532
|
accessToken: string;
|
|
523
533
|
sessionId: string;
|
|
@@ -545,6 +555,7 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
|
|
|
545
555
|
* retries); the cold-boot `shared-key-signin` step passes `{ retry: false }`
|
|
546
556
|
* so this network step cannot multiply boot latency via the inner retry
|
|
547
557
|
* loop. The token-refresh scheduler / 401 lane still retry later.
|
|
558
|
+
* @param opts.plantTokens - Forwarded to `verifyChallenge` (default `true`).
|
|
548
559
|
*/
|
|
549
560
|
signInWithSharedIdentity(opts?: {
|
|
550
561
|
deviceName?: string;
|
|
@@ -553,6 +564,7 @@ export declare function OxyServicesAuthMixin<T extends typeof OxyServicesBase>(B
|
|
|
553
564
|
retry?: boolean;
|
|
554
565
|
timeout?: number;
|
|
555
566
|
};
|
|
567
|
+
plantTokens?: boolean;
|
|
556
568
|
}): Promise<SessionLoginResponse | null>;
|
|
557
569
|
/**
|
|
558
570
|
* MECHANISM B (relying party) — begin a "Sign in with Oxy" handoff.
|
|
@@ -101,7 +101,22 @@ export interface User {
|
|
|
101
101
|
description?: string;
|
|
102
102
|
phone?: string;
|
|
103
103
|
address?: string;
|
|
104
|
+
/** Legacy free-text birthday, kept for backward compatibility. Prefer `dateOfBirth`. */
|
|
104
105
|
birthday?: string;
|
|
106
|
+
/**
|
|
107
|
+
* The structured date of birth, `YYYY-MM-DD`. The new source of truth going
|
|
108
|
+
* forward — `birthday` above is unparsed free text with no guaranteed
|
|
109
|
+
* shape. Present only on the account's OWN profile (never on another
|
|
110
|
+
* account's public profile), same visibility as `phone`/`address`/`birthday`.
|
|
111
|
+
*/
|
|
112
|
+
dateOfBirth?: string | null;
|
|
113
|
+
/**
|
|
114
|
+
* Derived, non-PII signal: whether the account holder is at least 18.
|
|
115
|
+
* `undefined` when `dateOfBirth` is unset ("unknown"), distinct from
|
|
116
|
+
* `false` ("known, not yet 18"). Never stored — computed fresh by the API
|
|
117
|
+
* on every read of the owner's own profile.
|
|
118
|
+
*/
|
|
119
|
+
isAdult?: boolean;
|
|
105
120
|
website?: string;
|
|
106
121
|
createdAt?: string;
|
|
107
122
|
updatedAt?: string;
|
|
@@ -110,6 +110,21 @@ export type SignInFlowPhase = 'idle' | 'starting' | 'waiting' | 'authorized' | '
|
|
|
110
110
|
* evidence of an approval. Only `authorized` moves the flow forward.
|
|
111
111
|
*/
|
|
112
112
|
export type SignInProgress = 'idle' | 'preparing' | 'awaiting-approval' | 'delivered-to-commons' | 'opened-in-commons' | 'confirming-identity' | 'identity-confirmed';
|
|
113
|
+
/**
|
|
114
|
+
* WHY a sign-in attempt ended in `'error'` — a machine-readable reason the UI
|
|
115
|
+
* turns into localized copy (the controller ships no user-facing prose).
|
|
116
|
+
*
|
|
117
|
+
* - `'cancelled'` — the USER walked away (closed the passkey hub popup). A
|
|
118
|
+
* voluntary outcome, not a failure to report.
|
|
119
|
+
* - `'denied'` — the approver declined the request in Commons.
|
|
120
|
+
* - `'expired'` — the request outlived its server-authoritative expiry.
|
|
121
|
+
* - `'network'` — Oxy could not be reached (no response, timeout).
|
|
122
|
+
* - `'not-configured'` — the app has no registered `clientId`.
|
|
123
|
+
* - `'unsupported-flow'` — an OAuth-bound request, which this surface cannot finalize.
|
|
124
|
+
* - `'claim-failed'` — the request WAS approved, but the session could not be claimed.
|
|
125
|
+
* - `'unknown'` — anything else; the raw message stays on `error` for logs.
|
|
126
|
+
*/
|
|
127
|
+
export type SignInFailureReason = 'cancelled' | 'denied' | 'expired' | 'network' | 'not-configured' | 'unsupported-flow' | 'claim-failed' | 'unknown';
|
|
113
128
|
/**
|
|
114
129
|
* Minimal structural handle over a popup `Window` — just enough for the
|
|
115
130
|
* cross-origin passkey hub flow ({@link AccountDialogController.startPasskeyHubSignIn}):
|
|
@@ -147,8 +162,13 @@ export interface SignInFlowState {
|
|
|
147
162
|
qrPayload: string | null;
|
|
148
163
|
/** Server-authoritative expiry (epoch ms), or `null`. */
|
|
149
164
|
expiresAt: number | null;
|
|
150
|
-
/**
|
|
165
|
+
/**
|
|
166
|
+
* The raw, UNLOCALIZED failure message (diagnostics only), or `null`. A surface
|
|
167
|
+
* renders {@link failure} instead — this string is English or a server message.
|
|
168
|
+
*/
|
|
151
169
|
error: string | null;
|
|
170
|
+
/** Why the attempt failed, set together with `phase: 'error'`; `null` otherwise. */
|
|
171
|
+
failure: SignInFailureReason | null;
|
|
152
172
|
/**
|
|
153
173
|
* The ONE primary delivery route the controller chose for this request
|
|
154
174
|
* ({@link selectCommonsDelivery}), or `null` while it is still being resolved.
|
|
@@ -189,6 +209,14 @@ export interface SignInFlowState {
|
|
|
189
209
|
* cannot drift from them or run ahead of a real signal.
|
|
190
210
|
*/
|
|
191
211
|
progress: SignInProgress;
|
|
212
|
+
/**
|
|
213
|
+
* Identity of the sign-in ATTEMPT this state belongs to — a counter that moves
|
|
214
|
+
* whenever an attempt is started, cancelled, or the controller is destroyed.
|
|
215
|
+
* Not a secret. It lets a surface treat "this attempt failed" as one event (a
|
|
216
|
+
* re-notification of the same failure is not a second failure, while a new
|
|
217
|
+
* attempt failing with the same message is).
|
|
218
|
+
*/
|
|
219
|
+
attempt: number;
|
|
192
220
|
}
|
|
193
221
|
/** Immutable snapshot consumed by `useSyncExternalStore`. */
|
|
194
222
|
export interface AccountDialogSnapshot {
|
|
@@ -290,8 +318,16 @@ export interface AccountDialogControllerOptions {
|
|
|
290
318
|
*/
|
|
291
319
|
openPopup?: () => PopupWindowHandle | null;
|
|
292
320
|
/**
|
|
293
|
-
*
|
|
294
|
-
* `https://
|
|
321
|
+
* Origin of the web identity carrier (default `IDENTITY_WEB_ORIGIN`,
|
|
322
|
+
* `https://id.oxy.so`) — where a passkey sign-in or sign-up runs
|
|
323
|
+
* and the account's identity is kept sealed under the passkey. The popup opens
|
|
324
|
+
* `<identityOrigin>/continue?code=…`. Overridable for local/staging testing.
|
|
325
|
+
*/
|
|
326
|
+
identityOrigin?: string;
|
|
327
|
+
/**
|
|
328
|
+
* @deprecated Alias of {@link identityOrigin}, kept for existing
|
|
329
|
+
* configuration. The popup used to open `auth.oxy.so/hub-passkey`; it now opens
|
|
330
|
+
* the identity origin's `/continue`, so a value here must be that origin.
|
|
295
331
|
*/
|
|
296
332
|
hubBaseUrl?: string;
|
|
297
333
|
/**
|
|
@@ -319,7 +355,7 @@ export declare class AccountDialogController {
|
|
|
319
355
|
private readonly canOpenApp?;
|
|
320
356
|
private readonly socketFactory?;
|
|
321
357
|
private readonly openPopup?;
|
|
322
|
-
private readonly
|
|
358
|
+
private readonly identityOrigin;
|
|
323
359
|
private readonly platform;
|
|
324
360
|
private readonly listeners;
|
|
325
361
|
private view;
|
|
@@ -328,8 +364,24 @@ export declare class AccountDialogController {
|
|
|
328
364
|
private activatingContextId;
|
|
329
365
|
private removingContextId;
|
|
330
366
|
private removingPrincipalId;
|
|
367
|
+
/**
|
|
368
|
+
* `true` while an operation run through {@link runDeviceMutation} (a host
|
|
369
|
+
* sign-out) is in flight. The fourth member of the ONE device-mutation gate,
|
|
370
|
+
* alongside the three flags above.
|
|
371
|
+
*/
|
|
372
|
+
private exclusiveMutationInFlight;
|
|
331
373
|
private signIn;
|
|
332
374
|
private commonsAvailability;
|
|
375
|
+
/**
|
|
376
|
+
* The CURRENT sign-in attempt. Every asynchronous step of a sign-in captures
|
|
377
|
+
* it before awaiting and re-checks it after (`isCurrentAttempt`); cancelling,
|
|
378
|
+
* starting another attempt, or destroying the controller moves it, so a late
|
|
379
|
+
* response from an abandoned attempt can neither update the surface nor
|
|
380
|
+
* install a session.
|
|
381
|
+
*/
|
|
382
|
+
private signInAttempt;
|
|
383
|
+
/** How the current attempt was started, so a retry repeats the user's choice. */
|
|
384
|
+
private signInMethod;
|
|
333
385
|
/** The secret device-flow token of the active QR flow (never surfaced). */
|
|
334
386
|
private signInToken;
|
|
335
387
|
private pollTimer;
|
|
@@ -342,9 +394,10 @@ export declare class AccountDialogController {
|
|
|
342
394
|
/**
|
|
343
395
|
* Guards {@link pollOnce} against re-entrancy: the fallback timer and a socket
|
|
344
396
|
* `auth_update` wake can fire together — without this both could claim the
|
|
345
|
-
* single-use token concurrently.
|
|
397
|
+
* single-use token concurrently. Keyed by token, so a poll still running for an
|
|
398
|
+
* ABANDONED request never blocks the next request's first poll.
|
|
346
399
|
*/
|
|
347
|
-
private
|
|
400
|
+
private pollInFlightToken;
|
|
348
401
|
/** The popup opened by {@link startPasskeyHubSignIn}, or `null`. */
|
|
349
402
|
private activePopup;
|
|
350
403
|
/** Watches {@link activePopup}'s `closed` state; see {@link watchPopup}. */
|
|
@@ -412,8 +465,11 @@ export declare class AccountDialogController {
|
|
|
412
465
|
* — and the reconstruction was not merely redundant: it enumerated the
|
|
413
466
|
* CALLER's account graph, which on a device holding two people is one
|
|
414
467
|
* person's answer presented as the device's.
|
|
468
|
+
*
|
|
469
|
+
* Resolves `false` when THIS read failed (a 401 is the signed-out edge, not a
|
|
470
|
+
* failure). Never rejects.
|
|
415
471
|
*/
|
|
416
|
-
refresh(): Promise<
|
|
472
|
+
refresh(): Promise<boolean>;
|
|
417
473
|
/**
|
|
418
474
|
* Activate one `principal acting as account` context — the ADR 0002 switch,
|
|
419
475
|
* and the one that can express what an account id cannot: WHICH person's
|
|
@@ -427,6 +483,11 @@ export declare class AccountDialogController {
|
|
|
427
483
|
* A context id is not stable across a removal, so a stale one is an ordinary
|
|
428
484
|
* outcome rather than a bug: the server answers 404 or 403, heals the row, and
|
|
429
485
|
* the refresh below re-reads a directory that no longer offers it.
|
|
486
|
+
*
|
|
487
|
+
* Resolves `true` once the SWITCH happened — even if the directory re-read
|
|
488
|
+
* after it fails (that failure lands on `error`, but the subject did change).
|
|
489
|
+
* Resolves `false` when the switch failed, or was refused because another
|
|
490
|
+
* device mutation is in flight ({@link isDeviceMutationInFlight}).
|
|
430
491
|
*/
|
|
431
492
|
activateContext(contextId: string): Promise<boolean>;
|
|
432
493
|
/**
|
|
@@ -451,11 +512,46 @@ export declare class AccountDialogController {
|
|
|
451
512
|
* and elects a replacement active context in one transition.
|
|
452
513
|
*/
|
|
453
514
|
signOutPrincipal(principalId: string): Promise<boolean>;
|
|
515
|
+
/**
|
|
516
|
+
* Whether ANY operation that changes who is on this device, or which of them
|
|
517
|
+
* is active, is in flight: an activation, either removal, or a host operation
|
|
518
|
+
* run through {@link runDeviceMutation}.
|
|
519
|
+
*
|
|
520
|
+
* They share ONE gate because they mutate the same device set: a removal that
|
|
521
|
+
* elects a replacement active context racing a switch could leave either one
|
|
522
|
+
* active. A refused call is refused, never queued — a press that could not run
|
|
523
|
+
* must not fire later, after the user has moved on.
|
|
524
|
+
*/
|
|
525
|
+
isDeviceMutationInFlight(): boolean;
|
|
526
|
+
/**
|
|
527
|
+
* Run a device mutation the controller does not own (the host's sign-out)
|
|
528
|
+
* under the same gate as {@link activateContext} and the removals.
|
|
529
|
+
*
|
|
530
|
+
* Resolves `{ ran: false }` without calling `operation` when another mutation
|
|
531
|
+
* is in flight; otherwise `{ ran: true, value }`. A rejection from `operation`
|
|
532
|
+
* propagates, after the gate is released.
|
|
533
|
+
*/
|
|
534
|
+
runDeviceMutation<T>(operation: () => Promise<T>): Promise<{
|
|
535
|
+
ran: true;
|
|
536
|
+
value: T;
|
|
537
|
+
} | {
|
|
538
|
+
ran: false;
|
|
539
|
+
}>;
|
|
540
|
+
/**
|
|
541
|
+
* The shared body of the three controller-owned device mutations: take the
|
|
542
|
+
* gate, run the mutation, then re-read the directory.
|
|
543
|
+
*
|
|
544
|
+
* The mutation's outcome and the re-read's are kept apart. Once `operation`
|
|
545
|
+
* has resolved the device HAS changed, so this resolves `true` even when the
|
|
546
|
+
* re-read fails — that failure is the directory's (`error`), and reporting it
|
|
547
|
+
* as a failed switch would invite the user to repeat something that happened.
|
|
548
|
+
*/
|
|
549
|
+
private mutateDevice;
|
|
454
550
|
/**
|
|
455
551
|
* Start "Sign in with Oxy". Native devices with a shared identity mint a
|
|
456
552
|
* session silently (`signInWithSharedIdentity`); everything else (web, or a
|
|
457
553
|
* native device without a shared identity) falls through to the cross-device
|
|
458
|
-
* QR handoff.
|
|
554
|
+
* QR handoff — as the SAME attempt, so cancelling during either half stops both.
|
|
459
555
|
*/
|
|
460
556
|
signInWithOxy(): Promise<void>;
|
|
461
557
|
/**
|
|
@@ -466,11 +562,24 @@ export declare class AccountDialogController {
|
|
|
466
562
|
*/
|
|
467
563
|
showQr(): Promise<void>;
|
|
468
564
|
/**
|
|
469
|
-
*
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
565
|
+
* Start a NEW attempt the same way the last one was started — "Try again"
|
|
566
|
+
* repeats the user's choice rather than silently switching method. A failed
|
|
567
|
+
* passkey-hub attempt reopens the hub popup; a failed "Sign in with Oxy"
|
|
568
|
+
* retries that; an explicit QR request shows the QR.
|
|
569
|
+
*
|
|
570
|
+
* Call it straight from the press handler: the hub route opens its popup
|
|
571
|
+
* synchronously, before this method's first `await`, so the browser still
|
|
572
|
+
* attributes it to the click.
|
|
573
|
+
*/
|
|
574
|
+
retrySignIn(): Promise<void>;
|
|
575
|
+
/**
|
|
576
|
+
* Web-only: sign in (or create an account) with a passkey at the web identity
|
|
577
|
+
* carrier. The ceremony runs on `id.oxy.so`, never on the calling origin, for
|
|
578
|
+
* two reasons: a credential minted with `WEBAUTHN_RP_ID=oxy.so` can only be
|
|
579
|
+
* asserted from `oxy.so`/a subdomain/loopback (a browser-enforced boundary),
|
|
580
|
+
* and only the identity origin may unseal or create the account's identity
|
|
581
|
+
* (one identity, two carriers). Opens a popup at `<identityOrigin>/continue`,
|
|
582
|
+
* scoped to the SAME device-flow
|
|
474
583
|
* session {@link showQr} would create (same `authorizeCode`/`sessionToken`
|
|
475
584
|
* pair), and let the SAME poll/socket/claim engine complete it once the hub
|
|
476
585
|
* authorizes the session (`POST /auth/session/authorize-code/:authorizeCode`,
|
|
@@ -485,13 +594,26 @@ export declare class AccountDialogController {
|
|
|
485
594
|
* `showQr`'s plain QR rendering — no dead end.
|
|
486
595
|
*/
|
|
487
596
|
startPasskeyHubSignIn(): Promise<void>;
|
|
597
|
+
/**
|
|
598
|
+
* End whatever attempt is running (withdrawing its request) and begin a new
|
|
599
|
+
* one. Returns the new attempt's identity for the caller to re-check after
|
|
600
|
+
* every `await`.
|
|
601
|
+
*/
|
|
602
|
+
private beginSignInAttempt;
|
|
603
|
+
/** Whether `attempt` is still the one the surface is waiting on. */
|
|
604
|
+
private isCurrentAttempt;
|
|
488
605
|
/**
|
|
489
606
|
* Shared device-flow session creation for both {@link showQr} (renders the
|
|
490
607
|
* QR) and {@link startPasskeyHubSignIn} (also opens the hub popup) — the
|
|
491
608
|
* same `startCommonsSignIn` → poll/socket wiring either way. Returns the
|
|
492
|
-
* handle on success (already reflected in `signIn`), or `null`
|
|
493
|
-
* (already set as `signIn.
|
|
609
|
+
* handle on success (already reflected in `signIn`), or `null` when the
|
|
610
|
+
* attempt failed (already set as `signIn.failure`) or was abandoned while the
|
|
611
|
+
* request was being created.
|
|
494
612
|
*
|
|
613
|
+
* @param attempt - The attempt this request belongs to. A request that comes
|
|
614
|
+
* back for an abandoned attempt is withdrawn server-side and never wired up:
|
|
615
|
+
* closing the dialog on a slow connection must not leave an approvable
|
|
616
|
+
* request, a socket, and a poll behind it.
|
|
495
617
|
* @param opts.deliver - Whether to run automatic Commons delivery selection
|
|
496
618
|
* ({@link resolveDeliveryRoute}). `true` for the normal one-primary-action
|
|
497
619
|
* entry; `false` when the caller already owns the primary surface (the
|
|
@@ -574,6 +696,12 @@ export declare class AccountDialogController {
|
|
|
574
696
|
* cancel the request, not just stop listening to it. Without the withdrawal a
|
|
575
697
|
* dismissed QR would stay approvable until it expired, so a later scan of a
|
|
576
698
|
* stale code could authorize a session nobody is waiting for.
|
|
699
|
+
*
|
|
700
|
+
* It also ENDS the attempt: anything of it still awaiting a response (the
|
|
701
|
+
* request being created, a shared-identity mint, a claim) finds itself
|
|
702
|
+
* superseded when that response arrives, and neither touches the surface nor
|
|
703
|
+
* installs a session. The withdrawal itself is best effort — cancelling
|
|
704
|
+
* locally is not proof the server received it (see {@link withdrawRequest}).
|
|
577
705
|
*/
|
|
578
706
|
cancelSignIn(): void;
|
|
579
707
|
/**
|
|
@@ -603,8 +731,28 @@ export declare class AccountDialogController {
|
|
|
603
731
|
private recordDeliveryProgress;
|
|
604
732
|
private claimAndComplete;
|
|
605
733
|
/**
|
|
606
|
-
*
|
|
607
|
-
* by the shared-key
|
|
734
|
+
* Install an authorized session, notify, and return to the account list.
|
|
735
|
+
* Shared by the shared-key and QR paths so they cannot drift.
|
|
736
|
+
*
|
|
737
|
+
* THE install point, and so the last place an abandoned attempt is stopped:
|
|
738
|
+
* the bearer is planted and the session committed only while `attempt` is
|
|
739
|
+
* still current. A commit that fails restores the bearer the client had
|
|
740
|
+
* before, so a half-installed session never leaves requests going out as an
|
|
741
|
+
* account the device does not hold.
|
|
742
|
+
*
|
|
743
|
+
* A failed commit REJECTS (after that restore), so each caller keeps its own
|
|
744
|
+
* policy: the QR path fails the attempt, the shared-identity path falls
|
|
745
|
+
* through to the QR handoff.
|
|
746
|
+
*
|
|
747
|
+
* Once the commit has resolved the session IS this device's, whether or not
|
|
748
|
+
* the user moved on while it ran. It is reported (`onSignedIn`, re-read) either
|
|
749
|
+
* way; only the surface bookkeeping is skipped for a superseded attempt, so a
|
|
750
|
+
* newer attempt's state is never overwritten.
|
|
751
|
+
*
|
|
752
|
+
* Residual: an attempt abandoned after the server minted its session (a claim
|
|
753
|
+
* or shared-identity mint that was already in flight) leaves that server
|
|
754
|
+
* session un-installed on this device — it is never planted or committed here,
|
|
755
|
+
* but it is not revoked either.
|
|
608
756
|
*/
|
|
609
757
|
private completeSignIn;
|
|
610
758
|
/**
|
|
@@ -613,6 +761,10 @@ export declare class AccountDialogController {
|
|
|
613
761
|
* `SessionClient.registerAndActivate` (registration + activation only).
|
|
614
762
|
*/
|
|
615
763
|
private commitAuthorizedSession;
|
|
764
|
+
/**
|
|
765
|
+
* End the CURRENT attempt as failed. The attempt keeps its identity (the
|
|
766
|
+
* failure belongs to it); only a new attempt or a cancel moves it on.
|
|
767
|
+
*/
|
|
616
768
|
private failSignIn;
|
|
617
769
|
private clearPollTimer;
|
|
618
770
|
/**
|
|
@@ -14,3 +14,10 @@
|
|
|
14
14
|
* the CORS helper (and anything else that needs the central apex) never drifts.
|
|
15
15
|
*/
|
|
16
16
|
export declare const CENTRAL_IDP_APEX = "oxy.so";
|
|
17
|
+
/**
|
|
18
|
+
* The web identity carrier's origin — where a person signs in or creates an
|
|
19
|
+
* account with a passkey, and where their self-custody identity is kept sealed
|
|
20
|
+
* under that passkey (one identity, two carriers). Apps open it; they never
|
|
21
|
+
* handle the identity themselves.
|
|
22
|
+
*/
|
|
23
|
+
export declare const IDENTITY_WEB_ORIGIN = "https://id.oxy.so";
|