Skip to content

Commit 9567099

Browse files
committed
fix(native): remove device share to reset state if private key reconstruction fails
1 parent 6a4b776 commit 9567099

File tree

3 files changed

+39
-3
lines changed

3 files changed

+39
-3
lines changed

packages/thirdweb/src/wallets/in-app/native/helpers/auth/middleware.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export async function postAuth({
8181
return storedToken;
8282
}
8383

84-
async function getRecoveryCode(args: {
84+
export async function getRecoveryCode(args: {
8585
storedToken: AuthStoredTokenWithCookieReturnType["storedToken"];
8686
client: ThirdwebClient;
8787
storage: ClientScopedStorage;

packages/thirdweb/src/wallets/in-app/native/helpers/storage/local.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,22 @@ export async function setDeviceShare({
112112
return deviceShare;
113113
}
114114

115+
export async function removeDeviceShare({
116+
clientId,
117+
}: {
118+
clientId: string;
119+
}): Promise<void> {
120+
const userDetails = await getWalletUserDetails(clientId);
121+
122+
if (!userDetails) {
123+
throw new Error("Missing wallet user ID");
124+
}
125+
126+
const name = DEVICE_SHARE_LOCAL_STORAGE_NAME(clientId, userDetails.userId);
127+
await removeItemInAsyncStorage(name);
128+
return;
129+
}
130+
115131
export async function getDeviceShare(clientId: string) {
116132
const cachedWalletUserId = await getWalletUserDetails(clientId);
117133
if (!cachedWalletUserId) {

packages/thirdweb/src/wallets/in-app/native/helpers/wallet/retrieval.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
DEVICE_SHARE_MISSING_MESSAGE,
1414
ROUTE_GET_USER_SHARES,
1515
} from "../constants.js";
16-
import { getDeviceShare } from "../storage/local.js";
16+
import { getDeviceShare, removeDeviceShare } from "../storage/local.js";
1717
import { storeShares } from "./creation.js";
1818
import { decryptShareWeb } from "./encryption.js";
1919

@@ -58,9 +58,27 @@ async function getAccountFromShares(args: {
5858
shares: string[];
5959
}): Promise<Account> {
6060
const { client, shares } = args;
61+
const privateKey = await (async () => {
62+
try {
63+
return await getWalletPrivateKeyFromShares(shares);
64+
} catch (e) {
65+
// If the private key reconstruction fails, try to reset the device share and prompt the user to try again
66+
// This can happen if a user's account has been migrated or otherwise modified in the backend to use a new wallet. In that case, we need to reset their device state to get a new share
67+
if (
68+
e instanceof Error &&
69+
e.message === "Invalid private key reconstructed from shares"
70+
) {
71+
await removeDeviceShare({ clientId: client.clientId });
72+
throw new Error("Invalid device state, please try again.");
73+
}
74+
// Otherwise this is a legitimate error, throw it
75+
throw e;
76+
}
77+
})();
78+
6179
return privateKeyToAccount({
6280
client,
63-
privateKey: await getWalletPrivateKeyFromShares(shares),
81+
privateKey,
6482
});
6583
}
6684

@@ -179,6 +197,7 @@ async function getShares<
179197
async function getAccountAddressFromShares(args: {
180198
client: ThirdwebClient;
181199
shares: string[];
200+
storage: ClientScopedStorage;
182201
}) {
183202
const wallet = await getAccountFromShares(args);
184203
return wallet.address;
@@ -205,6 +224,7 @@ export async function setUpShareForNewDevice({
205224
const walletAddress = await getAccountAddressFromShares({
206225
client,
207226
shares: [recoveryShare, authShare],
227+
storage,
208228
});
209229

210230
const maybeDeviceShare = await storeShares({

0 commit comments

Comments
 (0)