@metamask-previews/mfa-recovery-controller 0.0.0-preview-a2f6118d2
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 +26 -0
- package/LICENSE +6 -0
- package/LICENSE.APACHE2 +201 -0
- package/LICENSE.MIT +21 -0
- package/README.md +22 -0
- package/dist/MfaRecoveryController-method-action-types.d.ts +88 -0
- package/dist/MfaRecoveryController-method-action-types.d.ts.map +1 -0
- package/dist/MfaRecoveryController-method-action-types.js +6 -0
- package/dist/MfaRecoveryController-method-action-types.js.map +1 -0
- package/dist/MfaRecoveryController.d.ts +119 -0
- package/dist/MfaRecoveryController.d.ts.map +1 -0
- package/dist/MfaRecoveryController.js +456 -0
- package/dist/MfaRecoveryController.js.map +1 -0
- package/dist/crypto.d.ts +111 -0
- package/dist/crypto.d.ts.map +1 -0
- package/dist/crypto.js +268 -0
- package/dist/crypto.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +25 -0
- package/dist/errors.js.map +1 -0
- package/dist/escrow-utils.d.ts +42 -0
- package/dist/escrow-utils.d.ts.map +1 -0
- package/dist/escrow-utils.js +77 -0
- package/dist/escrow-utils.js.map +1 -0
- package/dist/identifier-auth.d.ts +83 -0
- package/dist/identifier-auth.d.ts.map +1 -0
- package/dist/identifier-auth.js +119 -0
- package/dist/identifier-auth.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/pending-operation-validation.d.ts +21 -0
- package/dist/pending-operation-validation.d.ts.map +1 -0
- package/dist/pending-operation-validation.js +181 -0
- package/dist/pending-operation-validation.js.map +1 -0
- package/dist/state-machine.d.ts +23 -0
- package/dist/state-machine.d.ts.map +1 -0
- package/dist/state-machine.js +34 -0
- package/dist/state-machine.js.map +1 -0
- package/dist/types.d.ts +235 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +76 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MFA recovery protocol types from the recovery ADR.
|
|
3
|
+
*/
|
|
4
|
+
export type Identifier = {
|
|
5
|
+
type: string;
|
|
6
|
+
namespace: string;
|
|
7
|
+
value: string;
|
|
8
|
+
verifier: unknown;
|
|
9
|
+
};
|
|
10
|
+
export type PoPChallenge = {
|
|
11
|
+
id: string;
|
|
12
|
+
escrowId: string;
|
|
13
|
+
/**
|
|
14
|
+
* Unix time in seconds.
|
|
15
|
+
*/
|
|
16
|
+
expiresAt: number;
|
|
17
|
+
};
|
|
18
|
+
export type IdentifierAuthMode = 'key-bound';
|
|
19
|
+
export type KeyBoundIdentifierToken = {
|
|
20
|
+
identifier: Identifier;
|
|
21
|
+
proofPublicKey: string;
|
|
22
|
+
requestHash: string;
|
|
23
|
+
providerAssertion: unknown;
|
|
24
|
+
};
|
|
25
|
+
export type ProofOfPossession = {
|
|
26
|
+
challengeId: string;
|
|
27
|
+
requestHash: string;
|
|
28
|
+
signature: string;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Identifier proof presented to an escrow. Only key-bound proofs are wired;
|
|
32
|
+
* escrow-challenge grants land with MFA-605 / MFA-606 / MFA-568.
|
|
33
|
+
*/
|
|
34
|
+
export type IdentifierAuthorization = {
|
|
35
|
+
kind: 'key-bound';
|
|
36
|
+
token: KeyBoundIdentifierToken;
|
|
37
|
+
proof: ProofOfPossession;
|
|
38
|
+
};
|
|
39
|
+
export type MutationOperation = 'register' | 'updateRecoverySecret' | 'updateIdentifiers';
|
|
40
|
+
export type Mutation = {
|
|
41
|
+
id: string;
|
|
42
|
+
profileId: string;
|
|
43
|
+
operation: MutationOperation;
|
|
44
|
+
expectedVersion: number;
|
|
45
|
+
newVersion: number;
|
|
46
|
+
payloadHash: string;
|
|
47
|
+
audiences: string[];
|
|
48
|
+
requestHash: string;
|
|
49
|
+
};
|
|
50
|
+
export type AuthControllerToken = {
|
|
51
|
+
profileId: string;
|
|
52
|
+
requestHash: string;
|
|
53
|
+
twoFactor?: true;
|
|
54
|
+
identifiersHash?: string;
|
|
55
|
+
identifierOwnershipApproved?: true;
|
|
56
|
+
issuer: string;
|
|
57
|
+
/**
|
|
58
|
+
* Unix time in seconds.
|
|
59
|
+
*/
|
|
60
|
+
expiresAt: number;
|
|
61
|
+
signature: string;
|
|
62
|
+
};
|
|
63
|
+
export type MutationReceipt = {
|
|
64
|
+
mutationId: string;
|
|
65
|
+
requestHash: string;
|
|
66
|
+
escrowId: string;
|
|
67
|
+
version: number;
|
|
68
|
+
/**
|
|
69
|
+
* SHA-256 of the uncompressed receipt public key, as `0x` hex.
|
|
70
|
+
*/
|
|
71
|
+
receiptKeyId: string;
|
|
72
|
+
signature: string;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* P-256 public JWK used as an ephemeral wrap key (`pkE`).
|
|
76
|
+
*/
|
|
77
|
+
export type EcPublicJwk = {
|
|
78
|
+
kty: 'EC';
|
|
79
|
+
crv: 'P-256';
|
|
80
|
+
x: string;
|
|
81
|
+
y: string;
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Result of first-factor identifier authentication, consumed by
|
|
85
|
+
* `getRecoverySecret`.
|
|
86
|
+
*
|
|
87
|
+
* The private keys are ephemeral and must only be kept in memory.
|
|
88
|
+
*/
|
|
89
|
+
export type IdentifierSession = {
|
|
90
|
+
token: KeyBoundIdentifierToken;
|
|
91
|
+
proofPrivateKey: string;
|
|
92
|
+
requestId: string;
|
|
93
|
+
ephemeralPrivateKey: string;
|
|
94
|
+
pkE: EcPublicJwk;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Recovery secret wrapped to one escrow wrap key for transit.
|
|
98
|
+
* `ciphertext` is unprefixed hex of `nonce || ChaCha20-Poly1305(ciphertext+tag)`.
|
|
99
|
+
*/
|
|
100
|
+
export type WrappedSecret = {
|
|
101
|
+
pkE: EcPublicJwk;
|
|
102
|
+
ciphertext: string;
|
|
103
|
+
};
|
|
104
|
+
export type WrappedSecretOutput = {
|
|
105
|
+
ciphertext: string;
|
|
106
|
+
};
|
|
107
|
+
export type RegisterPayload = {
|
|
108
|
+
recoverySecret: WrappedSecret;
|
|
109
|
+
identifiers: Identifier[];
|
|
110
|
+
};
|
|
111
|
+
export type UpdateRecoverySecretPayload = Pick<RegisterPayload, 'recoverySecret'>;
|
|
112
|
+
export type UpdateIdentifiersPayload = Pick<RegisterPayload, 'identifiers'>;
|
|
113
|
+
/**
|
|
114
|
+
* Escrow `applyMutation` body. Recovery secrets are wrapped to that escrow's
|
|
115
|
+
* wrap key; do not confuse with {@link PendingMutationPayload}.
|
|
116
|
+
*/
|
|
117
|
+
export type MutationPayload = RegisterPayload | UpdateRecoverySecretPayload | UpdateIdentifiersPayload;
|
|
118
|
+
/**
|
|
119
|
+
* 0x-hex of a plaintext recovery secret. Encrypted pending state stores this
|
|
120
|
+
* logical value; each escrow receives a {@link WrappedSecret} only at apply.
|
|
121
|
+
*/
|
|
122
|
+
export type PendingRecoverySecretHex = string;
|
|
123
|
+
/**
|
|
124
|
+
* Logical register payload stored in encrypted pending state.
|
|
125
|
+
*/
|
|
126
|
+
export type PendingRegisterPayload = {
|
|
127
|
+
identifiers: Identifier[];
|
|
128
|
+
recoverySecret: PendingRecoverySecretHex;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Logical secret-update payload stored in encrypted pending state.
|
|
132
|
+
*/
|
|
133
|
+
export type PendingUpdateRecoverySecretPayload = Pick<PendingRegisterPayload, 'recoverySecret'>;
|
|
134
|
+
/**
|
|
135
|
+
* Logical identifier-update payload stored in encrypted pending state.
|
|
136
|
+
*/
|
|
137
|
+
export type PendingUpdateIdentifiersPayload = Pick<PendingRegisterPayload, 'identifiers'>;
|
|
138
|
+
/**
|
|
139
|
+
* Logical mutation payload persisted in encrypted pending state. Distinct from
|
|
140
|
+
* {@link MutationPayload}, which is the per-escrow apply body.
|
|
141
|
+
*/
|
|
142
|
+
export type PendingMutationPayload = PendingRegisterPayload | PendingUpdateRecoverySecretPayload | PendingUpdateIdentifiersPayload;
|
|
143
|
+
type PendingOperationBase = {
|
|
144
|
+
mutation: Mutation;
|
|
145
|
+
identifier: Identifier | null;
|
|
146
|
+
payload: PendingMutationPayload;
|
|
147
|
+
};
|
|
148
|
+
export type AuthorizingPendingOperation = PendingOperationBase & {
|
|
149
|
+
phase: 'authorizing';
|
|
150
|
+
};
|
|
151
|
+
export type WritingPendingOperation = PendingOperationBase & {
|
|
152
|
+
phase: 'writing';
|
|
153
|
+
authControllerToken: AuthControllerToken;
|
|
154
|
+
receipts: MutationReceipt[];
|
|
155
|
+
};
|
|
156
|
+
export type PendingOperation = AuthorizingPendingOperation | WritingPendingOperation;
|
|
157
|
+
export type RecoveryPhase = 'idle' | PendingOperation['phase'];
|
|
158
|
+
/**
|
|
159
|
+
* Encoded ciphertext of {@link PendingOperation}. Wallet secure storage
|
|
160
|
+
* encrypts this blob (including any hex recovery secret). A raw recovery
|
|
161
|
+
* secret is never persisted in BaseController state.
|
|
162
|
+
*
|
|
163
|
+
* The recovery spec's pseudocode represents this as `Uint8Array`, but the
|
|
164
|
+
* controller stores the encoded ciphertext as a string so BaseController state
|
|
165
|
+
* persistence and serialization preserve it without binary coercion.
|
|
166
|
+
*/
|
|
167
|
+
export type EncryptedPendingOperation = string;
|
|
168
|
+
export type GetRecoverySecretResponse = {
|
|
169
|
+
recoverySecret: WrappedSecretOutput;
|
|
170
|
+
version: number;
|
|
171
|
+
lastMutationId: string;
|
|
172
|
+
wrapKeyId: string;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Recovery secret plus the version to use as `epoch` on later mutations.
|
|
176
|
+
*/
|
|
177
|
+
export type RecoveredSecret = {
|
|
178
|
+
recoverySecret: Uint8Array;
|
|
179
|
+
epoch: number;
|
|
180
|
+
};
|
|
181
|
+
/**
|
|
182
|
+
* Authenticates the MetaMask profile and issues request-bound attestations.
|
|
183
|
+
*/
|
|
184
|
+
export type RecoveryAuthProvider = {
|
|
185
|
+
getAuthenticatedProfileId: () => Promise<string>;
|
|
186
|
+
authorizeRecoveryRequest: (params: {
|
|
187
|
+
requestHash: string;
|
|
188
|
+
requireTwoFactor?: boolean;
|
|
189
|
+
identifiers?: Identifier[];
|
|
190
|
+
}) => Promise<AuthControllerToken>;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* Obtains a key-bound identifier assertion from an IdP (OIDC, Passkey, SIWE).
|
|
194
|
+
*/
|
|
195
|
+
export type RecoveryIdentifierAuthProvider = {
|
|
196
|
+
getKeyBoundIdentifierToken: (params: {
|
|
197
|
+
identifier: Identifier;
|
|
198
|
+
proofPublicKey: string;
|
|
199
|
+
requestHash: string;
|
|
200
|
+
}) => Promise<KeyBoundIdentifierToken>;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* One recovery escrow replica. Implementations may talk to different backends.
|
|
204
|
+
*/
|
|
205
|
+
export type RecoveryEscrowProvider = {
|
|
206
|
+
readonly id: string;
|
|
207
|
+
/**
|
|
208
|
+
* P-256 wrap public JWK JSON. Mutation payloads are encrypted to this key;
|
|
209
|
+
* `getSecret` responses are encrypted to the request's ephemeral key using
|
|
210
|
+
* the corresponding wrap private key.
|
|
211
|
+
*/
|
|
212
|
+
readonly wrapPublicKey: string;
|
|
213
|
+
isAvailable: () => Promise<boolean>;
|
|
214
|
+
generateChallenge: () => Promise<PoPChallenge>;
|
|
215
|
+
getSecret: (authorization: IdentifierAuthorization, requestId: string, pkE: EcPublicJwk) => Promise<GetRecoverySecretResponse>;
|
|
216
|
+
applyMutation: (mutation: Mutation, authControllerToken: AuthControllerToken, identifierAuthorization: IdentifierAuthorization | null, payload: MutationPayload) => Promise<MutationReceipt>;
|
|
217
|
+
/**
|
|
218
|
+
* Verifies a receipt cryptographically using the build-pinned receipt key
|
|
219
|
+
* and confirms it targets the expected escrow.
|
|
220
|
+
*
|
|
221
|
+
* @param receipt - Receipt returned by an escrow.
|
|
222
|
+
* @param mutation - Mutation acknowledged by the receipt.
|
|
223
|
+
* @param expectedEscrowId - Escrow identity expected by the caller.
|
|
224
|
+
*/
|
|
225
|
+
verifyReceipt: (receipt: MutationReceipt, mutation: Mutation, expectedEscrowId: string) => boolean;
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* Encrypts pending mutation state using wallet secure storage.
|
|
229
|
+
*/
|
|
230
|
+
export type PendingOperationEncryptor = {
|
|
231
|
+
encrypt: (operation: PendingOperation) => Promise<EncryptedPendingOperation>;
|
|
232
|
+
decrypt: (ciphertext: EncryptedPendingOperation) => Promise<PendingOperation>;
|
|
233
|
+
};
|
|
234
|
+
export {};
|
|
235
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * MFA recovery protocol types from the recovery ADR.\n */\n\nexport type Identifier = {\n type: string;\n namespace: string;\n value: string;\n verifier: unknown;\n};\n\nexport type PoPChallenge = {\n id: string;\n escrowId: string;\n /**\n * Unix time in seconds.\n */\n expiresAt: number;\n};\n\nexport type IdentifierAuthMode = 'key-bound';\n\nexport type KeyBoundIdentifierToken = {\n identifier: Identifier;\n proofPublicKey: string;\n requestHash: string;\n providerAssertion: unknown;\n};\n\nexport type ProofOfPossession = {\n challengeId: string;\n requestHash: string;\n signature: string;\n};\n\n/**\n * Identifier proof presented to an escrow. Only key-bound proofs are wired;\n * escrow-challenge grants land with MFA-605 / MFA-606 / MFA-568.\n */\nexport type IdentifierAuthorization = {\n kind: 'key-bound';\n token: KeyBoundIdentifierToken;\n proof: ProofOfPossession;\n};\n\nexport type MutationOperation =\n | 'register'\n | 'updateRecoverySecret'\n | 'updateIdentifiers';\n\nexport type Mutation = {\n id: string;\n profileId: string;\n operation: MutationOperation;\n expectedVersion: number;\n newVersion: number;\n payloadHash: string;\n audiences: string[];\n requestHash: string;\n};\n\nexport type AuthControllerToken = {\n profileId: string;\n requestHash: string;\n twoFactor?: true;\n identifiersHash?: string;\n identifierOwnershipApproved?: true;\n issuer: string;\n /**\n * Unix time in seconds.\n */\n expiresAt: number;\n signature: string;\n};\n\nexport type MutationReceipt = {\n mutationId: string;\n requestHash: string;\n escrowId: string;\n version: number;\n /**\n * SHA-256 of the uncompressed receipt public key, as `0x` hex.\n */\n receiptKeyId: string;\n signature: string;\n};\n\n/**\n * P-256 public JWK used as an ephemeral wrap key (`pkE`).\n */\nexport type EcPublicJwk = {\n kty: 'EC';\n crv: 'P-256';\n x: string;\n y: string;\n};\n\n/**\n * Result of first-factor identifier authentication, consumed by\n * `getRecoverySecret`.\n *\n * The private keys are ephemeral and must only be kept in memory.\n */\nexport type IdentifierSession = {\n token: KeyBoundIdentifierToken;\n proofPrivateKey: string;\n requestId: string;\n ephemeralPrivateKey: string;\n pkE: EcPublicJwk;\n};\n\n/**\n * Recovery secret wrapped to one escrow wrap key for transit.\n * `ciphertext` is unprefixed hex of `nonce || ChaCha20-Poly1305(ciphertext+tag)`.\n */\nexport type WrappedSecret = {\n pkE: EcPublicJwk;\n ciphertext: string;\n};\n\nexport type WrappedSecretOutput = {\n ciphertext: string;\n};\n\nexport type RegisterPayload = {\n recoverySecret: WrappedSecret;\n identifiers: Identifier[];\n};\n\nexport type UpdateRecoverySecretPayload = Pick<\n RegisterPayload,\n 'recoverySecret'\n>;\n\nexport type UpdateIdentifiersPayload = Pick<RegisterPayload, 'identifiers'>;\n\n/**\n * Escrow `applyMutation` body. Recovery secrets are wrapped to that escrow's\n * wrap key; do not confuse with {@link PendingMutationPayload}.\n */\nexport type MutationPayload =\n | RegisterPayload\n | UpdateRecoverySecretPayload\n | UpdateIdentifiersPayload;\n\n/**\n * 0x-hex of a plaintext recovery secret. Encrypted pending state stores this\n * logical value; each escrow receives a {@link WrappedSecret} only at apply.\n */\nexport type PendingRecoverySecretHex = string;\n\n/**\n * Logical register payload stored in encrypted pending state.\n */\nexport type PendingRegisterPayload = {\n identifiers: Identifier[];\n recoverySecret: PendingRecoverySecretHex;\n};\n\n/**\n * Logical secret-update payload stored in encrypted pending state.\n */\nexport type PendingUpdateRecoverySecretPayload = Pick<\n PendingRegisterPayload,\n 'recoverySecret'\n>;\n\n/**\n * Logical identifier-update payload stored in encrypted pending state.\n */\nexport type PendingUpdateIdentifiersPayload = Pick<\n PendingRegisterPayload,\n 'identifiers'\n>;\n\n/**\n * Logical mutation payload persisted in encrypted pending state. Distinct from\n * {@link MutationPayload}, which is the per-escrow apply body.\n */\nexport type PendingMutationPayload =\n | PendingRegisterPayload\n | PendingUpdateRecoverySecretPayload\n | PendingUpdateIdentifiersPayload;\n\ntype PendingOperationBase = {\n mutation: Mutation;\n identifier: Identifier | null;\n payload: PendingMutationPayload;\n};\n\nexport type AuthorizingPendingOperation = PendingOperationBase & {\n phase: 'authorizing';\n};\n\nexport type WritingPendingOperation = PendingOperationBase & {\n phase: 'writing';\n authControllerToken: AuthControllerToken;\n receipts: MutationReceipt[];\n};\n\nexport type PendingOperation =\n | AuthorizingPendingOperation\n | WritingPendingOperation;\n\nexport type RecoveryPhase = 'idle' | PendingOperation['phase'];\n\n/**\n * Encoded ciphertext of {@link PendingOperation}. Wallet secure storage\n * encrypts this blob (including any hex recovery secret). A raw recovery\n * secret is never persisted in BaseController state.\n *\n * The recovery spec's pseudocode represents this as `Uint8Array`, but the\n * controller stores the encoded ciphertext as a string so BaseController state\n * persistence and serialization preserve it without binary coercion.\n */\nexport type EncryptedPendingOperation = string;\n\nexport type GetRecoverySecretResponse = {\n recoverySecret: WrappedSecretOutput;\n version: number;\n lastMutationId: string;\n wrapKeyId: string;\n};\n\n/**\n * Recovery secret plus the version to use as `epoch` on later mutations.\n */\nexport type RecoveredSecret = {\n recoverySecret: Uint8Array;\n epoch: number;\n};\n\n/**\n * Authenticates the MetaMask profile and issues request-bound attestations.\n */\nexport type RecoveryAuthProvider = {\n getAuthenticatedProfileId: () => Promise<string>;\n authorizeRecoveryRequest: (params: {\n requestHash: string;\n requireTwoFactor?: boolean;\n identifiers?: Identifier[];\n }) => Promise<AuthControllerToken>;\n};\n\n/**\n * Obtains a key-bound identifier assertion from an IdP (OIDC, Passkey, SIWE).\n */\nexport type RecoveryIdentifierAuthProvider = {\n getKeyBoundIdentifierToken: (params: {\n identifier: Identifier;\n proofPublicKey: string;\n requestHash: string;\n }) => Promise<KeyBoundIdentifierToken>;\n};\n\n/**\n * One recovery escrow replica. Implementations may talk to different backends.\n */\nexport type RecoveryEscrowProvider = {\n readonly id: string;\n /**\n * P-256 wrap public JWK JSON. Mutation payloads are encrypted to this key;\n * `getSecret` responses are encrypted to the request's ephemeral key using\n * the corresponding wrap private key.\n */\n readonly wrapPublicKey: string;\n isAvailable: () => Promise<boolean>;\n generateChallenge: () => Promise<PoPChallenge>;\n getSecret: (\n authorization: IdentifierAuthorization,\n requestId: string,\n pkE: EcPublicJwk,\n ) => Promise<GetRecoverySecretResponse>;\n applyMutation: (\n mutation: Mutation,\n authControllerToken: AuthControllerToken,\n identifierAuthorization: IdentifierAuthorization | null,\n payload: MutationPayload,\n ) => Promise<MutationReceipt>;\n /**\n * Verifies a receipt cryptographically using the build-pinned receipt key\n * and confirms it targets the expected escrow.\n *\n * @param receipt - Receipt returned by an escrow.\n * @param mutation - Mutation acknowledged by the receipt.\n * @param expectedEscrowId - Escrow identity expected by the caller.\n */\n verifyReceipt: (\n receipt: MutationReceipt,\n mutation: Mutation,\n expectedEscrowId: string,\n ) => boolean;\n};\n\n/**\n * Encrypts pending mutation state using wallet secure storage.\n */\nexport type PendingOperationEncryptor = {\n encrypt: (operation: PendingOperation) => Promise<EncryptedPendingOperation>;\n decrypt: (ciphertext: EncryptedPendingOperation) => Promise<PendingOperation>;\n};\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@metamask-previews/mfa-recovery-controller",
|
|
3
|
+
"version": "0.0.0-preview-a2f6118d2",
|
|
4
|
+
"description": "Manages MFA recovery flows across MetaMask clients",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"Ethereum",
|
|
7
|
+
"MetaMask"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://github.com/MetaMask/core/tree/main/packages/mfa-recovery-controller#readme",
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/MetaMask/core/issues"
|
|
12
|
+
},
|
|
13
|
+
"license": "(MIT OR Apache-2.0)",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/MetaMask/core.git"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist/"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"default": "./dist/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./package.json": "./package.json"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public",
|
|
32
|
+
"registry": "https://registry.npmjs.org/"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsc --project tsconfig.build.json",
|
|
36
|
+
"build:all": "tsc --build tsconfig.build.json --verbose",
|
|
37
|
+
"build:clean": "yarn build:only-clean && yarn build",
|
|
38
|
+
"build:docs": "typedoc",
|
|
39
|
+
"build:only-clean": "rimraf './dist' './tsconfig.build.tsbuildinfo'",
|
|
40
|
+
"changelog:update": "../../scripts/update-changelog.sh @metamask/mfa-recovery-controller",
|
|
41
|
+
"changelog:validate": "../../scripts/validate-changelog.sh @metamask/mfa-recovery-controller",
|
|
42
|
+
"lint:tsconfigs": "tsx ../../scripts/lint-tsconfigs/lint-tsconfigs.mts",
|
|
43
|
+
"lint:tsconfigs:fix": "tsx ../../scripts/lint-tsconfigs/lint-tsconfigs.mts --fix",
|
|
44
|
+
"messenger-action-types:check": "tsx ../../packages/messenger-cli/src/cli.ts --formatter oxfmt --esm --check",
|
|
45
|
+
"messenger-action-types:generate": "tsx ../../packages/messenger-cli/src/cli.ts --formatter oxfmt --esm --generate",
|
|
46
|
+
"since-latest-release": "../../scripts/since-latest-release.sh",
|
|
47
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest --reporters=jest-silent-reporter",
|
|
48
|
+
"test:clean": "NODE_OPTIONS=--experimental-vm-modules jest --clearCache",
|
|
49
|
+
"test:verbose": "NODE_OPTIONS=--experimental-vm-modules jest --verbose",
|
|
50
|
+
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch"
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@metamask/base-controller": "^10.0.0",
|
|
54
|
+
"@metamask/messenger": "^3.0.0",
|
|
55
|
+
"@metamask/utils": "^12.0.0",
|
|
56
|
+
"@noble/ciphers": "^1.3.0",
|
|
57
|
+
"@noble/curves": "^1.9.2",
|
|
58
|
+
"@noble/hashes": "^1.8.0"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@metamask/auto-changelog": "^6.1.0",
|
|
62
|
+
"@types/jest": "^30.0.0",
|
|
63
|
+
"@typescript/native": "npm:typescript@^7.0.2",
|
|
64
|
+
"deepmerge": "^4.2.2",
|
|
65
|
+
"jest": "^30.4.2",
|
|
66
|
+
"rimraf": "^5.0.5",
|
|
67
|
+
"ts-jest": "^29.4.11",
|
|
68
|
+
"tsx": "^4.20.5",
|
|
69
|
+
"typedoc": "^0.25.13",
|
|
70
|
+
"typedoc-plugin-missing-exports": "^2.0.0",
|
|
71
|
+
"typescript": "npm:@typescript/typescript6@^6.0.2"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": "^22.14.0 || ^24"
|
|
75
|
+
}
|
|
76
|
+
}
|