@shuttle.technology/authorization 0.0.0-stage → 0.2.0
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/CHANGELOG.md +191 -0
- package/README.md +1124 -2
- package/dist/accountLists.d.ts +28 -0
- package/dist/accountLists.js +27 -0
- package/dist/aesGcmSiv.d.ts +14 -0
- package/dist/aesGcmSiv.js +160 -0
- package/dist/auth.d.ts +68 -0
- package/dist/auth.js +189 -0
- package/dist/balances.d.ts +31 -0
- package/dist/balances.js +44 -0
- package/dist/canonicalize.d.ts +19 -0
- package/dist/canonicalize.js +107 -0
- package/dist/client.d.ts +152 -0
- package/dist/client.js +781 -0
- package/dist/clientTypes.d.ts +56 -0
- package/dist/clientTypes.js +5 -0
- package/dist/configuration.d.ts +48 -0
- package/dist/configuration.js +72 -0
- package/dist/consentDebit.d.ts +36 -0
- package/dist/consentDebit.js +95 -0
- package/dist/credentials.d.ts +116 -0
- package/dist/credentials.js +315 -0
- package/dist/debitPermissionWire.d.ts +90 -0
- package/dist/debitPermissionWire.js +118 -0
- package/dist/debitPermissions.d.ts +39 -0
- package/dist/debitPermissions.js +117 -0
- package/dist/deviceKey.d.ts +13 -0
- package/dist/deviceKey.js +28 -0
- package/dist/deviceRecovery.d.ts +40 -0
- package/dist/deviceRecovery.js +143 -0
- package/dist/deviceShare.d.ts +9 -0
- package/dist/deviceShare.js +24 -0
- package/dist/deviceShareEnvelope.d.ts +13 -0
- package/dist/deviceShareEnvelope.js +139 -0
- package/dist/deviceShares.d.ts +91 -0
- package/dist/deviceShares.js +204 -0
- package/dist/errors.d.ts +280 -0
- package/dist/errors.js +85 -0
- package/dist/linkedChecks.d.ts +17 -0
- package/dist/linkedChecks.js +31 -0
- package/dist/linkedRecovery.d.ts +40 -0
- package/dist/linkedRecovery.js +49 -0
- package/dist/linkedRegistrations.d.ts +11 -0
- package/dist/linkedRegistrations.js +109 -0
- package/dist/linkedSendDisplay.d.ts +73 -0
- package/dist/linkedSendDisplay.js +84 -0
- package/dist/linkedWalletSignals.d.ts +22 -0
- package/dist/linkedWalletSignals.js +101 -0
- package/dist/linkedWallets.d.ts +401 -0
- package/dist/linkedWallets.js +436 -0
- package/dist/me.d.ts +27 -0
- package/dist/me.js +30 -0
- package/dist/mediatedOperation.d.ts +27 -0
- package/dist/mediatedOperation.js +68 -0
- package/dist/mobile.d.ts +64 -0
- package/dist/mobile.js +69 -0
- package/dist/modeC.d.ts +131 -0
- package/dist/modeC.js +248 -0
- package/dist/modeCLinked.d.ts +207 -0
- package/dist/modeCLinked.js +270 -0
- package/dist/pages.d.ts +22 -0
- package/dist/pages.js +45 -0
- package/dist/poll.d.ts +15 -0
- package/dist/poll.js +76 -0
- package/dist/redirects.d.ts +45 -0
- package/dist/redirects.js +108 -0
- package/dist/requestGrant.d.ts +169 -0
- package/dist/requestGrant.js +243 -0
- package/dist/sends.d.ts +89 -0
- package/dist/sends.js +127 -0
- package/dist/serviceGrants.d.ts +52 -0
- package/dist/serviceGrants.js +46 -0
- package/dist/slice4.d.ts +187 -0
- package/dist/slice4.js +299 -0
- package/dist/streams.d.ts +212 -0
- package/dist/streams.js +282 -0
- package/dist/trackedWallets.d.ts +53 -0
- package/dist/trackedWallets.js +104 -0
- package/dist/transferStatus.d.ts +45 -0
- package/dist/transferStatus.js +43 -0
- package/dist/walletCreateGrant.d.ts +62 -0
- package/dist/walletCreateGrant.js +89 -0
- package/dist/walletDisplayParts.d.ts +17 -0
- package/dist/walletDisplayParts.js +47 -0
- package/dist/walletEvents.d.ts +43 -0
- package/dist/walletEvents.js +97 -0
- package/dist/walletLimits.d.ts +65 -0
- package/dist/walletLimits.js +102 -0
- package/dist/walletOperations.d.ts +209 -0
- package/dist/walletOperations.js +317 -0
- package/dist/webRenewal.d.ts +92 -0
- package/dist/webRenewal.js +179 -0
- package/package.json +65 -3
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { type NextAction } from "./auth.ts";
|
|
2
|
+
import { type DisplayCommon } from "./walletDisplayParts.ts";
|
|
3
|
+
import { type LinkedRecoverCreate, type LinkedRecoverDisplay } from "./linkedRecovery.ts";
|
|
4
|
+
import { type WalletCreateGrant, type WalletCreateGrantOutcome, type WalletCreateGrantWire } from "./walletCreateGrant.ts";
|
|
5
|
+
import { type ConsentDebitOutcome, type DebitTerms } from "./consentDebit.ts";
|
|
6
|
+
import { type LinkedSendDisplay, type LinkedWalletIdentity } from "./linkedSendDisplay.ts";
|
|
7
|
+
import { type MediatedDisplay, type MediatedPurgedDisplay } from "./mediatedOperation.ts";
|
|
8
|
+
export { LinkedSendAssets, MaxTransferCommentBytes, ReplaceActions, linkedSendAsset, replaceAction, transferComment } from "./linkedSendDisplay.ts";
|
|
9
|
+
export type { LinkedSendAsset, LinkedSendDisplay, LinkedWalletIdentity, ReplaceAction } from "./linkedSendDisplay.ts";
|
|
10
|
+
export type { LinkedRecoverCreate, LinkedRecoverDisplay } from "./linkedRecovery.ts";
|
|
11
|
+
export type { MediatedDisplay, MediatedLinkedSendDisplay, MediatedPurgedDisplay, MediatedSendDisplay } from "./mediatedOperation.ts";
|
|
12
|
+
export type UserWalletOperationKind = "wallet.create" | "wallet.sign" | "wallet.send" | "wallet.export" | "wallet.recover";
|
|
13
|
+
export type WalletResultKind = "signature" | "transfer_receipt" | "wallet_created" | "device_share_recovered";
|
|
14
|
+
export type UserWalletState = "awaiting_confirmation" | "authorized" | "dispatching" | "outcome_unknown" | "succeeded" | "failed" | "denied" | "canceled" | "expired";
|
|
15
|
+
export type UserWalletEffectState = "no_effect" | "possible" | "known";
|
|
16
|
+
/**
|
|
17
|
+
* session_authorized: the account's live session authorized the operation
|
|
18
|
+
* with no passkey. A send from a linked wallet, plain or a replacement, is
|
|
19
|
+
* authorized so (owner decision, 14 October 2026), as is the registration of
|
|
20
|
+
* a linked wallet for tracking; every other wallet operation is confirmed
|
|
21
|
+
* with a passkey (user_confirmed). user_request_signed: a service forwarded
|
|
22
|
+
* the send on the person's signed request (Mode C); its display is a
|
|
23
|
+
* MediatedDisplay, with no result recipient.
|
|
24
|
+
*/
|
|
25
|
+
export type UserWalletAuthorizationBasis = "" | "user_confirmed" | "session_authorized" | "user_request_signed";
|
|
26
|
+
/**
|
|
27
|
+
* A wallet operation's result goes to its owner account only, so no create
|
|
28
|
+
* body names a result recipient: the device share of a created, exported or
|
|
29
|
+
* recovered wallet reaches this device alone, and the server refuses a
|
|
30
|
+
* service named as the recipient with INVALID_REQUEST.
|
|
31
|
+
*/
|
|
32
|
+
type UserWalletCommon = {
|
|
33
|
+
owner_type: "account";
|
|
34
|
+
owner_id: string;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* UserWalletOperationCreate is the closed request body of source §2.2: the
|
|
38
|
+
* top-level wallet ID is required for every kind except wallet.create, and
|
|
39
|
+
* each payload carries exactly its kind's fields.
|
|
40
|
+
*/
|
|
41
|
+
export type UserWalletOperationCreate = (UserWalletCommon & {
|
|
42
|
+
kind: "wallet.create";
|
|
43
|
+
payload: {
|
|
44
|
+
network: string;
|
|
45
|
+
recipient_public_key: string;
|
|
46
|
+
};
|
|
47
|
+
grant?: WalletCreateGrantWire;
|
|
48
|
+
}) | (UserWalletCommon & {
|
|
49
|
+
kind: "wallet.sign";
|
|
50
|
+
wallet_id: string;
|
|
51
|
+
payload: {
|
|
52
|
+
network: string;
|
|
53
|
+
message_base64url: string;
|
|
54
|
+
};
|
|
55
|
+
}) | (UserWalletCommon & {
|
|
56
|
+
kind: "wallet.send";
|
|
57
|
+
wallet_id: string;
|
|
58
|
+
payload: {
|
|
59
|
+
network: string;
|
|
60
|
+
asset: "native";
|
|
61
|
+
recipient: string;
|
|
62
|
+
amount_raw: string;
|
|
63
|
+
max_fee_raw: string;
|
|
64
|
+
};
|
|
65
|
+
}) | (UserWalletCommon & {
|
|
66
|
+
kind: "wallet.export";
|
|
67
|
+
wallet_id: string;
|
|
68
|
+
payload: {
|
|
69
|
+
recipient_public_key: string;
|
|
70
|
+
};
|
|
71
|
+
}) | (UserWalletCommon & {
|
|
72
|
+
kind: "wallet.recover";
|
|
73
|
+
wallet_id: string;
|
|
74
|
+
payload: {
|
|
75
|
+
expected_generation: string;
|
|
76
|
+
recipient_public_key: string;
|
|
77
|
+
};
|
|
78
|
+
}) | LinkedRecoverCreate;
|
|
79
|
+
/** UserWalletPurgedDisplay is the safe identity left after material retention expires. */
|
|
80
|
+
export type UserWalletPurgedDisplay = DisplayCommon & {
|
|
81
|
+
kind: UserWalletOperationKind;
|
|
82
|
+
materialPurged: true;
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* UserWalletDisplay is the safe display of the frozen material. A purged
|
|
86
|
+
* record keeps its identity and reports materialPurged instead of inventing
|
|
87
|
+
* material; it never carries signing output or custody material. Narrow with
|
|
88
|
+
* `walletMaterialPurged` before reading any per-kind material field.
|
|
89
|
+
*/
|
|
90
|
+
export type UserWalletDisplay = UserWalletPurgedDisplay | (DisplayCommon & {
|
|
91
|
+
kind: "wallet.create";
|
|
92
|
+
network: string;
|
|
93
|
+
recipientPublicKey: string;
|
|
94
|
+
preparedSummary?: string;
|
|
95
|
+
grant?: WalletCreateGrant;
|
|
96
|
+
debit?: DebitTerms;
|
|
97
|
+
}) | (DisplayCommon & {
|
|
98
|
+
kind: "wallet.sign";
|
|
99
|
+
network: string;
|
|
100
|
+
messageBase64url: string;
|
|
101
|
+
preparedSummary?: string;
|
|
102
|
+
}) | (DisplayCommon & {
|
|
103
|
+
kind: "wallet.send";
|
|
104
|
+
network: string;
|
|
105
|
+
asset: string;
|
|
106
|
+
recipient: string;
|
|
107
|
+
amountRaw: string;
|
|
108
|
+
maxFeeRaw: string;
|
|
109
|
+
preparedSummary?: string;
|
|
110
|
+
}) | LinkedSendDisplay | (DisplayCommon & {
|
|
111
|
+
kind: "wallet.export";
|
|
112
|
+
recipientPublicKey: string;
|
|
113
|
+
preparedSummary?: string;
|
|
114
|
+
}) | (DisplayCommon & {
|
|
115
|
+
kind: "wallet.recover";
|
|
116
|
+
recipientPublicKey: string;
|
|
117
|
+
expectedGeneration: string;
|
|
118
|
+
preparedSummary?: string;
|
|
119
|
+
}) | LinkedRecoverDisplay | MediatedDisplay;
|
|
120
|
+
/** walletMaterialPurged separates the purged display from every material display. */
|
|
121
|
+
export declare function walletMaterialPurged(display: UserWalletDisplay): display is UserWalletPurgedDisplay | MediatedPurgedDisplay;
|
|
122
|
+
export interface UserWalletOperation {
|
|
123
|
+
id: string;
|
|
124
|
+
version: string;
|
|
125
|
+
kind: UserWalletOperationKind;
|
|
126
|
+
state: UserWalletState;
|
|
127
|
+
effectState: UserWalletEffectState;
|
|
128
|
+
authorizationBasis: UserWalletAuthorizationBasis;
|
|
129
|
+
ownerType: "account";
|
|
130
|
+
ownerId: string;
|
|
131
|
+
requesterId: string;
|
|
132
|
+
walletId?: string;
|
|
133
|
+
/** Present exactly when the operation names a linked wallet instead of walletId. */
|
|
134
|
+
linkedWallet?: LinkedWalletIdentity;
|
|
135
|
+
requestFingerprint: string;
|
|
136
|
+
operationFingerprint: string;
|
|
137
|
+
display: UserWalletDisplay;
|
|
138
|
+
/** UTC RFC 3339 date-time string (source §2.2), not NumericDate seconds. */
|
|
139
|
+
createdAt: string;
|
|
140
|
+
/** UTC RFC 3339 date-time string (source §2.2), not NumericDate seconds. */
|
|
141
|
+
expiresAt: string;
|
|
142
|
+
confirmationLocation?: string;
|
|
143
|
+
resultKind?: WalletResultKind;
|
|
144
|
+
nextAction: NextAction;
|
|
145
|
+
}
|
|
146
|
+
/** CreatedWallet is the public identity of a wallet the own-wallet system created. */
|
|
147
|
+
export interface CreatedWallet {
|
|
148
|
+
walletId: string;
|
|
149
|
+
keyId: string;
|
|
150
|
+
network: string;
|
|
151
|
+
address: string;
|
|
152
|
+
algorithm: string;
|
|
153
|
+
publicKey: string;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* DeviceSharePackage is the device share exactly as the own-wallet system
|
|
157
|
+
* sealed it to the recipient key frozen in the operation. The provider never
|
|
158
|
+
* opens it; only the holder of the matching device private key can. Every
|
|
159
|
+
* binary member is lowercase hexadecimal.
|
|
160
|
+
*/
|
|
161
|
+
export interface DeviceSharePackage {
|
|
162
|
+
ciphertext: string;
|
|
163
|
+
nonce: string;
|
|
164
|
+
ephemeralPublicKey: string;
|
|
165
|
+
encryptionAlgorithm: string;
|
|
166
|
+
encryptionContext: string;
|
|
167
|
+
}
|
|
168
|
+
export interface WalletResult {
|
|
169
|
+
requestId: string;
|
|
170
|
+
result: {
|
|
171
|
+
operationId: string;
|
|
172
|
+
kind: WalletResultKind;
|
|
173
|
+
receiptId: string;
|
|
174
|
+
outputBase64url?: string;
|
|
175
|
+
/** Present exactly for a wallet_created result. */
|
|
176
|
+
wallet?: CreatedWallet;
|
|
177
|
+
/** Present exactly for a wallet_created and a device_share_recovered result. */
|
|
178
|
+
deviceShare?: DeviceSharePackage;
|
|
179
|
+
/** Present exactly for a device_share_recovered result: the linked wallet whose share was recovered. */
|
|
180
|
+
linkedWalletId?: string;
|
|
181
|
+
/** Present for a wallet_created result whose creation named a grant (walletCreateGrant.ts). */
|
|
182
|
+
grant?: WalletCreateGrantOutcome;
|
|
183
|
+
/** Present when the grant's service holds wallet:debit:user: the debit permission the creation's passkey also created (consentDebit.ts). */
|
|
184
|
+
debit?: ConsentDebitOutcome;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
/** MaxWalletMessageBytes is the decoded wallet.sign payload bound of source §2.8. */
|
|
188
|
+
export declare const MaxWalletMessageBytes = 4096;
|
|
189
|
+
/**
|
|
190
|
+
* validateUserWalletCreate rejects every locally detectable fault before any
|
|
191
|
+
* request leaves the client: a foreign owner, a wallet ID present or absent
|
|
192
|
+
* against its kind, a non-compressed recipient key, a noncanonical or
|
|
193
|
+
* out-of-range quantity, a non-native asset, and a sign message outside
|
|
194
|
+
* 1..4096 decoded bytes. It returns the exact value it was given so the
|
|
195
|
+
* caller sends the same closed body it validated.
|
|
196
|
+
*/
|
|
197
|
+
export declare function validateUserWalletCreate(value: UserWalletOperationCreate, expectedOwnerId: string): UserWalletOperationCreate;
|
|
198
|
+
export declare function parseUserWalletOperationEnvelope(payload: unknown): {
|
|
199
|
+
requestId: string;
|
|
200
|
+
operation: UserWalletOperation;
|
|
201
|
+
};
|
|
202
|
+
export declare function parseUserWalletOperation(payload: unknown): UserWalletOperation;
|
|
203
|
+
/**
|
|
204
|
+
* parseWalletResult accepts the protected wallet output of source §2.2:
|
|
205
|
+
* output_base64url is present for a signature and forbidden for a transfer
|
|
206
|
+
* receipt. A wallet_created result is its own closed shape: the created
|
|
207
|
+
* wallet and its sealed device share, with no output member.
|
|
208
|
+
*/
|
|
209
|
+
export declare function parseWalletResult(payload: unknown, expectedOperationId: string): WalletResult;
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
// Mode B user wallet operations (AUTH-009-A03, source §1.3). Closed types,
|
|
2
|
+
// client-side validation of the five wallet payloads, and strict parsers for
|
|
3
|
+
// the safe status projection and the protected wallet result. These helpers
|
|
4
|
+
// compose the same /v1 operation routes as the local operations; they add no
|
|
5
|
+
// authority. Local faults raise ClientFlowError; server denials stay
|
|
6
|
+
// AuthorizationError and are produced by the caller's transport.
|
|
7
|
+
import { ClientFlowError, canonicalDecimal, deadlineAfter, isNextAction, opaqueId, record, rfc3339UTC, } from "./auth.js";
|
|
8
|
+
import { bindingRevision, displayCommon, lowerHexBytes, lowerUUID, rawQuantity, summaryOf, walletRecipient } from "./walletDisplayParts.js";
|
|
9
|
+
import { parseLinkedRecoverDisplay, parseRecoveredShareResult, validLinkedRecoverCreate } from "./linkedRecovery.js";
|
|
10
|
+
import { parseWalletCreateGrant, parseWalletCreateGrantOutcome, validWalletCreateGrant } from "./walletCreateGrant.js";
|
|
11
|
+
import { parseConsentDebitOutcome, parseDebitTerms } from "./consentDebit.js";
|
|
12
|
+
import { parseLinkedSendDisplay } from "./linkedSendDisplay.js";
|
|
13
|
+
import { parseMediatedOperation } from "./mediatedOperation.js";
|
|
14
|
+
export { LinkedSendAssets, MaxTransferCommentBytes, ReplaceActions, linkedSendAsset, replaceAction, transferComment } from "./linkedSendDisplay.js";
|
|
15
|
+
/** walletMaterialPurged separates the purged display from every material display. */
|
|
16
|
+
export function walletMaterialPurged(display) {
|
|
17
|
+
return "materialPurged" in display;
|
|
18
|
+
}
|
|
19
|
+
/** MaxWalletMessageBytes is the decoded wallet.sign payload bound of source §2.8. */
|
|
20
|
+
export const MaxWalletMessageBytes = 4096;
|
|
21
|
+
const maxWalletGeneration = (1n << 64n) - 1n;
|
|
22
|
+
const compressedP256Key = /^(02|03)[0-9a-f]{64}$/;
|
|
23
|
+
const walletNetwork = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
24
|
+
const base64urlValue = /^[A-Za-z0-9_-]+$/;
|
|
25
|
+
/**
|
|
26
|
+
* validateUserWalletCreate rejects every locally detectable fault before any
|
|
27
|
+
* request leaves the client: a foreign owner, a wallet ID present or absent
|
|
28
|
+
* against its kind, a non-compressed recipient key, a noncanonical or
|
|
29
|
+
* out-of-range quantity, a non-native asset, and a sign message outside
|
|
30
|
+
* 1..4096 decoded bytes. It returns the exact value it was given so the
|
|
31
|
+
* caller sends the same closed body it validated.
|
|
32
|
+
*/
|
|
33
|
+
export function validateUserWalletCreate(value, expectedOwnerId) {
|
|
34
|
+
const common = record(value, ["kind", "owner_type", "owner_id", "payload"], ["wallet_id", "linked_wallet_id", "grant"]);
|
|
35
|
+
// A grant is named beside a creation alone.
|
|
36
|
+
if (common.owner_type !== "account" || !opaqueId(common.owner_id) || common.owner_id !== expectedOwnerId ||
|
|
37
|
+
(common.grant !== undefined && (common.kind !== "wallet.create" || !validWalletCreateGrant(common.grant)))) {
|
|
38
|
+
throw new ClientFlowError("invalid_operation_create");
|
|
39
|
+
}
|
|
40
|
+
// A linked wallet is named by a recovery of its device share alone (linkedRecovery.ts).
|
|
41
|
+
if (common.linked_wallet_id !== undefined) {
|
|
42
|
+
if (!validLinkedRecoverCreate(common))
|
|
43
|
+
throw new ClientFlowError("invalid_operation_create");
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
const walletRequired = common.kind !== "wallet.create";
|
|
47
|
+
if (walletRequired !== opaqueId(common.wallet_id))
|
|
48
|
+
throw new ClientFlowError("invalid_operation_create");
|
|
49
|
+
switch (common.kind) {
|
|
50
|
+
case "wallet.create": {
|
|
51
|
+
const payload = record(common.payload, ["network", "recipient_public_key"]);
|
|
52
|
+
if (!network(payload.network) || !recipientPublicKey(payload.recipient_public_key))
|
|
53
|
+
throw new ClientFlowError("invalid_operation_create");
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case "wallet.sign": {
|
|
57
|
+
const payload = record(common.payload, ["network", "message_base64url"]);
|
|
58
|
+
if (!network(payload.network) || !boundedMessage(payload.message_base64url))
|
|
59
|
+
throw new ClientFlowError("invalid_operation_create");
|
|
60
|
+
break;
|
|
61
|
+
}
|
|
62
|
+
case "wallet.send": {
|
|
63
|
+
const payload = record(common.payload, ["network", "asset", "recipient", "amount_raw", "max_fee_raw"]);
|
|
64
|
+
if (!network(payload.network) || payload.asset !== "native" || typeof payload.recipient !== "string" || !walletRecipient.test(payload.recipient) ||
|
|
65
|
+
!rawQuantity(payload.amount_raw, true) || !rawQuantity(payload.max_fee_raw, false)) {
|
|
66
|
+
throw new ClientFlowError("invalid_operation_create");
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case "wallet.export": {
|
|
71
|
+
const payload = record(common.payload, ["recipient_public_key"]);
|
|
72
|
+
if (!recipientPublicKey(payload.recipient_public_key))
|
|
73
|
+
throw new ClientFlowError("invalid_operation_create");
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case "wallet.recover": {
|
|
77
|
+
const payload = record(common.payload, ["expected_generation", "recipient_public_key"]);
|
|
78
|
+
if (!recipientPublicKey(payload.recipient_public_key) || !walletGeneration(payload.expected_generation))
|
|
79
|
+
throw new ClientFlowError("invalid_operation_create");
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
default:
|
|
83
|
+
throw new ClientFlowError("invalid_operation_create");
|
|
84
|
+
}
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
export function parseUserWalletOperationEnvelope(payload) {
|
|
88
|
+
const root = record(payload, ["request_id", "operation"]);
|
|
89
|
+
if (!opaqueId(root.request_id))
|
|
90
|
+
throw new ClientFlowError("invalid_server_response");
|
|
91
|
+
return { requestId: root.request_id, operation: parseUserWalletOperation(root.operation) };
|
|
92
|
+
}
|
|
93
|
+
export function parseUserWalletOperation(payload) {
|
|
94
|
+
// A Mode C send is read in the mediated projection the provider serves for it.
|
|
95
|
+
if (payload !== null && typeof payload === "object" && payload.authorization_basis === "user_request_signed")
|
|
96
|
+
return parseMediatedOperation(payload);
|
|
97
|
+
const root = record(payload, [
|
|
98
|
+
"id", "version", "kind", "state", "effect_state", "authorization_basis", "owner_type", "owner_id", "requester_id",
|
|
99
|
+
"request_fingerprint", "operation_fingerprint", "display", "created_at", "expires_at", "next_action",
|
|
100
|
+
], ["wallet_id", "confirmation_location", "result_kind", "binding_source", "connection_id", "linked_wallet_id", "binding_revision"]);
|
|
101
|
+
if (!opaqueId(root.id) || !canonicalDecimal(root.version) || !userWalletKind(root.kind) || !userWalletState(root.state) ||
|
|
102
|
+
!userWalletEffectState(root.effect_state) || !userWalletBasis(root.authorization_basis) ||
|
|
103
|
+
root.owner_type !== "account" || !opaqueId(root.owner_id) || !opaqueId(root.requester_id) ||
|
|
104
|
+
(root.wallet_id !== undefined && !opaqueId(root.wallet_id)) || !lowerHexDigest(root.request_fingerprint) ||
|
|
105
|
+
!lowerHexDigest(root.operation_fingerprint) || !rfc3339UTC(root.created_at) || !rfc3339UTC(root.expires_at) ||
|
|
106
|
+
!deadlineAfter(root.created_at, root.expires_at) || (root.confirmation_location !== undefined && !httpsURL(root.confirmation_location)) ||
|
|
107
|
+
(root.result_kind !== undefined && !walletResultKind(root.result_kind)) || !isNextAction(root.next_action)) {
|
|
108
|
+
throw new ClientFlowError("invalid_server_response");
|
|
109
|
+
}
|
|
110
|
+
// A linked wallet is named by all three of its members, only for a send or
|
|
111
|
+
// the recovery of its device share, never beside an own wallet; only a send
|
|
112
|
+
// is authorized by the session alone.
|
|
113
|
+
const linked = root.binding_source !== undefined;
|
|
114
|
+
if (linked && (root.binding_source !== "linked_wallet" || (root.kind !== "wallet.send" && root.kind !== "wallet.recover") || root.wallet_id !== undefined ||
|
|
115
|
+
!lowerUUID(root.connection_id) || !lowerUUID(root.linked_wallet_id) || !bindingRevision(root.binding_revision) ||
|
|
116
|
+
(root.kind === "wallet.recover" && root.authorization_basis === "session_authorized"))) {
|
|
117
|
+
throw new ClientFlowError("invalid_server_response");
|
|
118
|
+
}
|
|
119
|
+
if (!linked && (root.connection_id !== undefined || root.linked_wallet_id !== undefined || root.binding_revision !== undefined || root.authorization_basis === "session_authorized")) {
|
|
120
|
+
throw new ClientFlowError("invalid_server_response");
|
|
121
|
+
}
|
|
122
|
+
const display = linked && !purgedDisplay(root.display) ? (root.kind === "wallet.recover" ? parseLinkedRecoverDisplay(root.display) : parseLinkedSendDisplay(root.display)) : parseUserWalletDisplay(root.display, root.kind);
|
|
123
|
+
if (display.ownerId !== root.owner_id || display.requesterId !== root.requester_id || display.walletId !== root.wallet_id) {
|
|
124
|
+
throw new ClientFlowError("invalid_server_response");
|
|
125
|
+
}
|
|
126
|
+
if ("linkedWalletId" in display && (display.linkedWalletId !== root.linked_wallet_id || display.connectionId !== root.connection_id || display.bindingRevision !== root.binding_revision)) {
|
|
127
|
+
throw new ClientFlowError("invalid_server_response");
|
|
128
|
+
}
|
|
129
|
+
const operation = {
|
|
130
|
+
id: root.id,
|
|
131
|
+
version: root.version,
|
|
132
|
+
kind: root.kind,
|
|
133
|
+
state: root.state,
|
|
134
|
+
effectState: root.effect_state,
|
|
135
|
+
authorizationBasis: root.authorization_basis,
|
|
136
|
+
ownerType: root.owner_type,
|
|
137
|
+
ownerId: root.owner_id,
|
|
138
|
+
requesterId: root.requester_id,
|
|
139
|
+
requestFingerprint: root.request_fingerprint,
|
|
140
|
+
operationFingerprint: root.operation_fingerprint,
|
|
141
|
+
display,
|
|
142
|
+
createdAt: root.created_at,
|
|
143
|
+
expiresAt: root.expires_at,
|
|
144
|
+
nextAction: root.next_action,
|
|
145
|
+
};
|
|
146
|
+
if (root.wallet_id !== undefined)
|
|
147
|
+
operation.walletId = root.wallet_id;
|
|
148
|
+
if (linked)
|
|
149
|
+
operation.linkedWallet = { connectionId: root.connection_id, linkedWalletId: root.linked_wallet_id, bindingRevision: root.binding_revision };
|
|
150
|
+
if (root.confirmation_location !== undefined)
|
|
151
|
+
operation.confirmationLocation = root.confirmation_location;
|
|
152
|
+
if (root.result_kind !== undefined)
|
|
153
|
+
operation.resultKind = root.result_kind;
|
|
154
|
+
return operation;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* parseWalletResult accepts the protected wallet output of source §2.2:
|
|
158
|
+
* output_base64url is present for a signature and forbidden for a transfer
|
|
159
|
+
* receipt. A wallet_created result is its own closed shape: the created
|
|
160
|
+
* wallet and its sealed device share, with no output member.
|
|
161
|
+
*/
|
|
162
|
+
export function parseWalletResult(payload, expectedOperationId) {
|
|
163
|
+
const root = record(payload, ["request_id", "result"]);
|
|
164
|
+
const resultKind = root.result !== null && typeof root.result === "object" ? root.result.kind : undefined;
|
|
165
|
+
if (resultKind === "wallet_created")
|
|
166
|
+
return parseCreatedWalletResult(root.request_id, root.result, expectedOperationId);
|
|
167
|
+
if (resultKind === "device_share_recovered")
|
|
168
|
+
return parseRecoveredShareResult(root.request_id, root.result, expectedOperationId);
|
|
169
|
+
const result = record(root.result, ["operation_id", "kind", "receipt_id"], ["output_base64url"]);
|
|
170
|
+
if (!opaqueId(root.request_id) || !opaqueId(result.operation_id) || result.operation_id !== expectedOperationId ||
|
|
171
|
+
!walletResultKind(result.kind) || result.kind === "wallet_created" || result.kind === "device_share_recovered" || !opaqueId(result.receipt_id)) {
|
|
172
|
+
throw new ClientFlowError("invalid_server_response");
|
|
173
|
+
}
|
|
174
|
+
if (result.kind === "signature") {
|
|
175
|
+
if (typeof result.output_base64url !== "string" || !base64urlValue.test(result.output_base64url))
|
|
176
|
+
throw new ClientFlowError("invalid_server_response");
|
|
177
|
+
return { requestId: root.request_id, result: { operationId: result.operation_id, kind: result.kind, receiptId: result.receipt_id, outputBase64url: result.output_base64url } };
|
|
178
|
+
}
|
|
179
|
+
if (result.output_base64url !== undefined)
|
|
180
|
+
throw new ClientFlowError("invalid_server_response");
|
|
181
|
+
return { requestId: root.request_id, result: { operationId: result.operation_id, kind: result.kind, receiptId: result.receipt_id } };
|
|
182
|
+
}
|
|
183
|
+
function parseCreatedWalletResult(requestId, value, expectedOperationId) {
|
|
184
|
+
const result = record(value, ["operation_id", "kind", "receipt_id", "wallet", "device_share"], ["grant", "debit"]);
|
|
185
|
+
const wallet = record(result.wallet, ["wallet_id", "key_id", "network", "address", "algorithm", "public_key"]);
|
|
186
|
+
const share = record(result.device_share, ["ciphertext", "nonce", "ephemeral_public_key", "encryption_algorithm", "encryption_context"]);
|
|
187
|
+
// The receipt of a creation is the created wallet ID; a result naming two
|
|
188
|
+
// different wallets is not one creation.
|
|
189
|
+
if (!opaqueId(requestId) || result.operation_id !== expectedOperationId || !lowerUUID(wallet.wallet_id) || result.receipt_id !== wallet.wallet_id ||
|
|
190
|
+
!lowerUUID(wallet.key_id) || typeof wallet.network !== "string" || !/^[a-z0-9_.-]{1,64}$/.test(wallet.network) ||
|
|
191
|
+
typeof wallet.address !== "string" || !walletRecipient.test(wallet.address) || !algorithmName(wallet.algorithm) || !lowerHexBytes(wallet.public_key) ||
|
|
192
|
+
!lowerHexBytes(share.ciphertext) || !lowerHexBytes(share.nonce) || !lowerHexBytes(share.ephemeral_public_key) ||
|
|
193
|
+
!algorithmName(share.encryption_algorithm) || !lowerHexBytes(share.encryption_context)) {
|
|
194
|
+
throw new ClientFlowError("invalid_server_response");
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
requestId,
|
|
198
|
+
result: {
|
|
199
|
+
operationId: expectedOperationId, kind: "wallet_created", receiptId: wallet.wallet_id,
|
|
200
|
+
wallet: { walletId: wallet.wallet_id, keyId: wallet.key_id, network: wallet.network, address: wallet.address, algorithm: wallet.algorithm, publicKey: wallet.public_key },
|
|
201
|
+
deviceShare: { ciphertext: share.ciphertext, nonce: share.nonce, ephemeralPublicKey: share.ephemeral_public_key, encryptionAlgorithm: share.encryption_algorithm, encryptionContext: share.encryption_context },
|
|
202
|
+
...(result.grant === undefined ? {} : { grant: parseWalletCreateGrantOutcome(result.grant) }),
|
|
203
|
+
...(result.debit === undefined ? {} : { debit: parseConsentDebitOutcome(result.debit) }),
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function purgedDisplay(value) {
|
|
208
|
+
return value !== null && typeof value === "object" && Object.prototype.hasOwnProperty.call(value, "material_purged");
|
|
209
|
+
}
|
|
210
|
+
function parseUserWalletDisplay(value, kind) {
|
|
211
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
212
|
+
throw new ClientFlowError("invalid_server_response");
|
|
213
|
+
if (Object.prototype.hasOwnProperty.call(value, "material_purged")) {
|
|
214
|
+
const purged = record(value, ["kind", "owner_id", "requester_id", "result_recipient", "material_purged"], ["wallet_id"]);
|
|
215
|
+
if (purged.kind !== kind || purged.material_purged !== true)
|
|
216
|
+
throw new ClientFlowError("invalid_server_response");
|
|
217
|
+
return { ...displayCommon(purged), kind, materialPurged: true };
|
|
218
|
+
}
|
|
219
|
+
switch (kind) {
|
|
220
|
+
case "wallet.create": {
|
|
221
|
+
const display = record(value, ["kind", "owner_id", "requester_id", "result_recipient", "network", "recipient_public_key"], ["wallet_id", "prepared_summary", "grant", "debit"]);
|
|
222
|
+
if (display.kind !== kind || !network(display.network) || !recipientPublicKey(display.recipient_public_key))
|
|
223
|
+
throw new ClientFlowError("invalid_server_response");
|
|
224
|
+
return { ...displayCommon(display), kind, network: display.network, recipientPublicKey: display.recipient_public_key, ...summaryOf(display.prepared_summary),
|
|
225
|
+
...(display.grant === undefined ? {} : { grant: parseWalletCreateGrant(display.grant) }), ...(display.debit === undefined ? {} : { debit: parseDebitTerms(display.debit) }) };
|
|
226
|
+
}
|
|
227
|
+
case "wallet.sign": {
|
|
228
|
+
const display = record(value, ["kind", "owner_id", "requester_id", "result_recipient", "network", "message_base64url"], ["wallet_id", "prepared_summary"]);
|
|
229
|
+
if (display.kind !== kind || !network(display.network) || !boundedMessage(display.message_base64url))
|
|
230
|
+
throw new ClientFlowError("invalid_server_response");
|
|
231
|
+
return { ...displayCommon(display), kind, network: display.network, messageBase64url: display.message_base64url, ...summaryOf(display.prepared_summary) };
|
|
232
|
+
}
|
|
233
|
+
case "wallet.send": {
|
|
234
|
+
const display = record(value, ["kind", "owner_id", "requester_id", "result_recipient", "network", "asset", "recipient", "amount_raw", "max_fee_raw"], ["wallet_id", "prepared_summary"]);
|
|
235
|
+
if (display.kind !== kind || !network(display.network) || display.asset !== "native" || typeof display.recipient !== "string" ||
|
|
236
|
+
!walletRecipient.test(display.recipient) || !rawQuantity(display.amount_raw, true) || !rawQuantity(display.max_fee_raw, false)) {
|
|
237
|
+
throw new ClientFlowError("invalid_server_response");
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
...displayCommon(display), kind, network: display.network, asset: display.asset, recipient: display.recipient,
|
|
241
|
+
amountRaw: display.amount_raw, maxFeeRaw: display.max_fee_raw, ...summaryOf(display.prepared_summary),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
case "wallet.export": {
|
|
245
|
+
const display = record(value, ["kind", "owner_id", "requester_id", "result_recipient", "recipient_public_key"], ["wallet_id", "prepared_summary"]);
|
|
246
|
+
if (display.kind !== kind || !recipientPublicKey(display.recipient_public_key))
|
|
247
|
+
throw new ClientFlowError("invalid_server_response");
|
|
248
|
+
return { ...displayCommon(display), kind, recipientPublicKey: display.recipient_public_key, ...summaryOf(display.prepared_summary) };
|
|
249
|
+
}
|
|
250
|
+
default: {
|
|
251
|
+
const display = record(value, ["kind", "owner_id", "requester_id", "result_recipient", "recipient_public_key", "expected_generation"], ["wallet_id", "prepared_summary"]);
|
|
252
|
+
if (display.kind !== "wallet.recover" || !recipientPublicKey(display.recipient_public_key) || !walletGeneration(display.expected_generation)) {
|
|
253
|
+
throw new ClientFlowError("invalid_server_response");
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
...displayCommon(display), kind: "wallet.recover", recipientPublicKey: display.recipient_public_key,
|
|
257
|
+
expectedGeneration: display.expected_generation, ...summaryOf(display.prepared_summary),
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
function network(value) {
|
|
263
|
+
return typeof value === "string" && walletNetwork.test(value);
|
|
264
|
+
}
|
|
265
|
+
function recipientPublicKey(value) {
|
|
266
|
+
return typeof value === "string" && compressedP256Key.test(value);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* boundedMessage accepts unpadded base64url whose decoded length is 1..4096
|
|
270
|
+
* bytes. A base64url group of exactly one residual character encodes no byte,
|
|
271
|
+
* so that length is rejected outright.
|
|
272
|
+
*/
|
|
273
|
+
function boundedMessage(value) {
|
|
274
|
+
if (typeof value !== "string" || !base64urlValue.test(value) || value.length % 4 === 1)
|
|
275
|
+
return false;
|
|
276
|
+
const decoded = Math.floor((value.length * 3) / 4);
|
|
277
|
+
return decoded >= 1 && decoded <= MaxWalletMessageBytes;
|
|
278
|
+
}
|
|
279
|
+
function walletGeneration(value) {
|
|
280
|
+
if (!canonicalDecimal(value))
|
|
281
|
+
return false;
|
|
282
|
+
const generation = BigInt(value);
|
|
283
|
+
return generation > 0n && generation <= maxWalletGeneration;
|
|
284
|
+
}
|
|
285
|
+
function userWalletKind(value) {
|
|
286
|
+
return value === "wallet.create" || value === "wallet.sign" || value === "wallet.send" || value === "wallet.export" || value === "wallet.recover";
|
|
287
|
+
}
|
|
288
|
+
function walletResultKind(value) {
|
|
289
|
+
return value === "signature" || value === "transfer_receipt" || value === "wallet_created" || value === "device_share_recovered";
|
|
290
|
+
}
|
|
291
|
+
function algorithmName(value) {
|
|
292
|
+
return typeof value === "string" && /^[a-z0-9_-]{1,128}$/.test(value);
|
|
293
|
+
}
|
|
294
|
+
function userWalletState(value) {
|
|
295
|
+
return value === "awaiting_confirmation" || value === "authorized" || value === "dispatching" || value === "outcome_unknown" ||
|
|
296
|
+
value === "succeeded" || value === "failed" || value === "denied" || value === "canceled" || value === "expired";
|
|
297
|
+
}
|
|
298
|
+
function userWalletEffectState(value) {
|
|
299
|
+
return value === "no_effect" || value === "possible" || value === "known";
|
|
300
|
+
}
|
|
301
|
+
function userWalletBasis(value) {
|
|
302
|
+
return value === "" || value === "user_confirmed" || value === "session_authorized";
|
|
303
|
+
}
|
|
304
|
+
function lowerHexDigest(value) {
|
|
305
|
+
return typeof value === "string" && /^[0-9a-f]{64}$/.test(value);
|
|
306
|
+
}
|
|
307
|
+
function httpsURL(value) {
|
|
308
|
+
if (typeof value !== "string")
|
|
309
|
+
return false;
|
|
310
|
+
try {
|
|
311
|
+
const parsed = new URL(value);
|
|
312
|
+
return parsed.protocol === "https:" && parsed.username === "" && parsed.password === "";
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { FetchLike } from "./client.ts";
|
|
2
|
+
/** The code of the typed error that asks the application to start the top-level restore. */
|
|
3
|
+
export declare const webRenewalRequiresNavigation = "web_renewal_requires_navigation";
|
|
4
|
+
interface WebSessionTarget {
|
|
5
|
+
authorizationOrigin: string;
|
|
6
|
+
clientId: string;
|
|
7
|
+
/** The name the tabs of one application take turns under. */
|
|
8
|
+
lockName: string;
|
|
9
|
+
signal?: AbortSignal;
|
|
10
|
+
/** Throws once the renewal or restore was superseded; asked under the lock right before each step that asks the provider. */
|
|
11
|
+
current?: () => void;
|
|
12
|
+
}
|
|
13
|
+
export interface WebRenewalRequest extends WebSessionTarget {
|
|
14
|
+
fetch: FetchLike;
|
|
15
|
+
/** The session's CSRF token, held in memory only; without one the renewal starts from the cookie. */
|
|
16
|
+
csrfToken: string | undefined;
|
|
17
|
+
}
|
|
18
|
+
export interface WebRestoreRequest extends WebSessionTarget {
|
|
19
|
+
redirectUri: string;
|
|
20
|
+
crypto: Crypto;
|
|
21
|
+
/** The host's top-level launcher; it answers the registered redirect the restore came back to. */
|
|
22
|
+
launch: (url: string, signal?: AbortSignal) => Promise<string | void>;
|
|
23
|
+
/** POST /v1/auth/restore with this body, answering the session payload. */
|
|
24
|
+
exchange: (body: string) => Promise<unknown>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The tabs of one application share the cookie, and the provider ends a
|
|
28
|
+
* session whose refresh value it is shown twice. Every renewal and every
|
|
29
|
+
* restore therefore runs under one Web Lock per client: each sends the value
|
|
30
|
+
* the previous one left. A restore holds it until the host's launcher
|
|
31
|
+
* settles or the restore's signal aborts it; the host gets the same signal
|
|
32
|
+
* and closes its window, so no abandoned navigation rotates the value later.
|
|
33
|
+
* A sign-in's exchange, which sets the cookie too, goes out under the same
|
|
34
|
+
* lock (client.ts), so a renewal's late answer never follows a newer
|
|
35
|
+
* sign-in's.
|
|
36
|
+
*/
|
|
37
|
+
export declare function underRenewalLock<T>(target: WebSessionTarget & {
|
|
38
|
+
whenWaiting?: () => void;
|
|
39
|
+
}, work: () => Promise<T>): Promise<T>;
|
|
40
|
+
/**
|
|
41
|
+
* renewWebSession is the background call: POST /v1/auth/renew with the cookie
|
|
42
|
+
* (credentials: "include"). A page that holds the session's CSRF token sends
|
|
43
|
+
* it; a page that holds none (a reload, a new tab), or whose token a renewal
|
|
44
|
+
* or restore in another tab replaced, renews from the cookie alone with the
|
|
45
|
+
* fixed X-Authorization-Renew header and gets a new token. It answers the
|
|
46
|
+
* session payload.
|
|
47
|
+
*/
|
|
48
|
+
export declare function renewWebSession(request: WebRenewalRequest): Promise<unknown>;
|
|
49
|
+
/**
|
|
50
|
+
* staleCsrfRefusal is the answer a web lifecycle call gets when a renewal or
|
|
51
|
+
* restore in another tab replaced the session's CSRF token since this page
|
|
52
|
+
* last renewed.
|
|
53
|
+
*/
|
|
54
|
+
export declare function staleCsrfRefusal(error: unknown): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* revokeWithCurrentCredentials is logout's way past a CSRF token another tab
|
|
57
|
+
* replaced. Under the tabs' lock, held until the revocation answers, it
|
|
58
|
+
* renews from the cookie alone and revokes with the tokens that answer, but
|
|
59
|
+
* only when they belong to the session the logout was asked for: a cookie
|
|
60
|
+
* another sign-in replaced is left alone and the revocation is not confirmed.
|
|
61
|
+
*/
|
|
62
|
+
export declare function revokeWithCurrentCredentials(request: Omit<WebRenewalRequest, "csrfToken"> & {
|
|
63
|
+
sessionId: string;
|
|
64
|
+
whenWaiting?: () => void;
|
|
65
|
+
}, revoke: (accessToken: string, csrfToken: string) => Promise<unknown>): Promise<unknown>;
|
|
66
|
+
/** revokedAnswer says a logout answer confirms the revocation. */
|
|
67
|
+
export declare function revokedAnswer(payload: unknown): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* revokeUnclaimedSession ends the session of a sign-in answer this page does
|
|
70
|
+
* not install, because a logout or a newer sign-in overtook it while it was
|
|
71
|
+
* asked for. The answer set the cookie, so without it a reload would take up
|
|
72
|
+
* a session nobody holds, one the person may have logged out of. The
|
|
73
|
+
* provider clears the cookie only while it holds that session. A CSRF token
|
|
74
|
+
* another tab replaced meanwhile (it renewed that session from the cookie) is
|
|
75
|
+
* followed by the logout's recovery: one renewal from the cookie under the
|
|
76
|
+
* tabs' lock, checked against the answer's session, and the revocation with
|
|
77
|
+
* its tokens. It answers true only for a confirmed revocation of exactly that
|
|
78
|
+
* session; a cookie that holds another session is left alone and answers
|
|
79
|
+
* false, as does any refusal or failure. whenWaiting is told when the
|
|
80
|
+
* recovery has to wait for the lock (a restore may hold it while its window
|
|
81
|
+
* is open): the revocation goes on, but nobody needs to wait for its verdict.
|
|
82
|
+
*/
|
|
83
|
+
export declare function revokeUnclaimedSession(request: Omit<WebRenewalRequest, "csrfToken"> & {
|
|
84
|
+
whenWaiting?: () => void;
|
|
85
|
+
}, payload: unknown, revoke: (accessToken: string, csrfToken: string) => Promise<unknown>): Promise<boolean>;
|
|
86
|
+
/**
|
|
87
|
+
* restoreWebSession is the top-level restore: a fresh state and S256 verifier
|
|
88
|
+
* in memory, the provider restore URL through the host's launcher, and the
|
|
89
|
+
* one-use handoff of the redirect it returns exchanged for the session.
|
|
90
|
+
*/
|
|
91
|
+
export declare function restoreWebSession(request: WebRestoreRequest): Promise<unknown>;
|
|
92
|
+
export {};
|