Skip to content

Commit a7d0a3f

Browse files
authored
feat: add validation to oidc validator (#187)
Contracts include scripts to fix, but at the least we can deploy and start fresh. It's a little overkill, but this took me forever to find because it wasn't easy to reproduce locally!
1 parent 6c1d374 commit a7d0a3f

File tree

3 files changed

+52
-7
lines changed

3 files changed

+52
-7
lines changed

packages/auth-server/components/account-recovery/oidc-recovery-flow/Step4.vue

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,14 @@
3636
<script setup lang="ts">
3737
import { useAppKitAccount } from "@reown/appkit/vue";
3838
import { type Address, bytesToBigInt, type Hex, pad } from "viem";
39+
import { waitForTransactionReceipt } from "viem/actions";
3940
import { sendTransaction } from "viem/zksync";
41+
import { OidcRecoveryValidatorAbi } from "zksync-sso/abi";
4042
import { createNonceV2 } from "zksync-sso-circuits";
4143
4244
import { GOOGLE_CERTS_URL } from "./constants";
4345
44-
const { getWalletClient, defaultChain, getOidcClient } = useClientStore();
46+
const { getWalletClient, getPublicClient, defaultChain, getOidcClient } = useClientStore();
4547
const { startGoogleOauth } = useGoogleOauth();
4648
const accountData = useAppKitAccount();
4749
const {
@@ -92,6 +94,7 @@ function buildBlindingFactor(): bigint {
9294
9395
async function go() {
9496
const client = await getWalletClient({ chainId: defaultChain.id });
97+
const publicClient = getPublicClient({ chainId: defaultChain.id });
9598
const blindingFactor = buildBlindingFactor();
9699
const oidcData = await getOidcAccounts(userAddress.value);
97100
if (oidcData === undefined) {
@@ -150,22 +153,63 @@ async function go() {
150153
timeLimit,
151154
);
152155
156+
// Preflight checks
157+
const recoveryAddress = contractsByChain[defaultChain.id].recoveryOidc as Address;
158+
const senderAddress = client.account.address as Address;
159+
160+
// Ensure OIDC validator is initialized and wired correctly
161+
const [webAuthValidatorAddr, keyRegistryAddr, verifierAddr] = await Promise.all([
162+
publicClient.readContract({ address: recoveryAddress, abi: OidcRecoveryValidatorAbi, functionName: "webAuthValidator", args: [] }),
163+
publicClient.readContract({ address: recoveryAddress, abi: OidcRecoveryValidatorAbi, functionName: "keyRegistry", args: [] }),
164+
publicClient.readContract({ address: recoveryAddress, abi: OidcRecoveryValidatorAbi, functionName: "verifier", args: [] }),
165+
]) as [Address, Address, Address];
166+
167+
if (
168+
webAuthValidatorAddr === "0x0000000000000000000000000000000000000000"
169+
|| keyRegistryAddr === "0x0000000000000000000000000000000000000000"
170+
|| verifierAddr === "0x0000000000000000000000000000000000000000"
171+
) {
172+
throw new Error(`OIDC recovery validator at ${recoveryAddress} is not initialized`);
173+
}
174+
175+
const expectedPasskey = contractsByChain[defaultChain.id].passkey as Address | undefined;
176+
if (expectedPasskey && webAuthValidatorAddr.toLowerCase() !== expectedPasskey.toLowerCase()) {
177+
throw new Error(`webAuthValidator mismatch: on-chain=${webAuthValidatorAddr}, expected=${expectedPasskey}`);
178+
}
179+
180+
// Ensure sender has some balance for gas
181+
const balance = await publicClient.getBalance({ address: senderAddress });
182+
if (balance === 0n) {
183+
throw new Error("Insufficient balance to pay gas for recovery transaction");
184+
}
185+
153186
const sendTransactionArgs = {
154187
account: client.account,
155188
to: contractsByChain[defaultChain.id].recoveryOidc,
156189
data: calldata,
157-
gas: 20_000_000,
190+
value: 0n,
191+
gas: 20_000_000n,
158192
// eslint-disable-next-line @typescript-eslint/no-explicit-any
159193
} as any;
160-
await sendTransaction(client, sendTransactionArgs);
161194
195+
const sentTx = await sendTransaction(client, sendTransactionArgs);
196+
197+
const startRecoveryReceipt = await waitForTransactionReceipt(client, { hash: sentTx, confirmations: 1 });
198+
if (startRecoveryReceipt.status !== "success") {
199+
throw new Error(`Recovery transaction ${startRecoveryReceipt.status}`);
200+
}
162201
const oidcClient = getOidcClient({ chainId: defaultChain.id, address: userAddress.value });
163202
164-
await oidcClient.addNewPasskeyViaOidc({
203+
const addedPasskey = await oidcClient.addNewPasskeyViaOidc({
165204
credentialId: passkey.value.credentialId,
166205
passkeyPubKey: passkey.value.passkeyPubKey,
167206
passkeyDomain: window.location.origin,
168207
});
208+
209+
if (addedPasskey.status !== "success") {
210+
throw new Error(`Failed to add passkey via OIDC: ${addedPasskey.status}`);
211+
}
212+
169213
recoverySuccessful.value = true;
170214
}
171215
</script>

packages/auth-server/stores/era-sepolia.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"accountFactory": "0x7230ae6D4a2C367ff8493a76c15F8832c62f9fE9",
55
"accountPaymaster": "0xD055d87D3E7f3d28E8E9075342991FDdcaea4E63",
66
"recovery": "0x6AA83E35439D71F28273Df396BC7768dbaA9849D",
7-
"recoveryOidc": "0x116A07f88d03bD3982eBD5f2667EB08965aAe98c",
8-
"oidcKeyRegistry": "0x0EEeA31EA37959316dc6b50307BaF09528d3fcc4"
7+
"recoveryOidc": "0xB9DAF3eF41154Bbcf8d55e404627c2D1B35efC45",
8+
"oidcKeyRegistry": "0x0EEeA31EA37959316dc6b50307BaF09528d3fcc4",
9+
"oidcVerifier": "0x271e7A4E14d5950ea12B1Fc8a6B79B71225A40a1"
910
}

0 commit comments

Comments
 (0)