-
Notifications
You must be signed in to change notification settings - Fork 31
Fix OTP verification error handling to properly propagate errors to UI #1463
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Fix OTP verification error handling to properly propagate errors to UI #1463
Conversation
- Modified verifyOtp() to explicitly return Promise<void> and properly throw errors - Modified sendMessageWithOtp() to throw errors instead of only rejecting internal promise - Updated React UI CrossmintWalletProvider to re-throw errors so they reach BaseCodeInput - Errors now properly display to users when OTP verification fails or rate limits occur - Maintains backward compatibility with existing onAuthRequired callback signature Co-Authored-By: jorge@paella.dev <jorge@paella.dev>
Original prompt from jorge@paella.dev |
🦋 Changeset detectedLatest commit: 4f2f73e The changes in this PR will be included in the next version bump. Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Co-Authored-By: jorge@paella.dev <jorge@paella.dev>
| } | ||
|
|
||
| private async sendMessageWithOtp() { | ||
| private async sendMessageWithOtp(): Promise<void> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's remove the Promise<void> return type to stay consistent with the rest of the codebase style
| } | ||
|
|
||
| private async verifyOtp(encryptedOtp: string) { | ||
| private async verifyOtp(encryptedOtp: string): Promise<void> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here: let's remove the Promise<void> return type to stay consistent with the rest of the codebase style
| setEmailSignerDialogStep("otp"); | ||
| } catch (error) { | ||
| console.error("Failed to send email OTP", error); | ||
| rejectRef.current(new Error("Failed to send email OTP")); | ||
| throw error; | ||
| } | ||
| }; | ||
|
|
||
| const emailsigners_handleOTPSubmit = async (otp: string) => { | ||
| try { | ||
| await verifyOtpRef.current(otp); | ||
| setEmailSignerDialogOpen(false); | ||
| } catch (error) { | ||
| console.error("Failed to verify OTP", error); | ||
| rejectRef.current(new Error("Failed to verify OTP")); | ||
| throw error; | ||
| } | ||
| }; | ||
|
|
||
| // Phone authentication handlers (for TEE handshake) | ||
| const phonesigners_handleSendPhoneOTP = async () => { | ||
| try { | ||
| await sendPhoneWithOtpRef.current(); | ||
| setPhoneSignerDialogStep("otp"); | ||
| } catch (error) { | ||
| console.error("Failed to send phone OTP", error); | ||
| rejectRef.current(new Error("Failed to send phone OTP")); | ||
| throw error; | ||
| } | ||
| }; | ||
|
|
||
| const phonesigners_handleOTPSubmit = async (otp: string) => { | ||
| try { | ||
| await verifyPhoneOtpRef.current(otp); | ||
| setPhoneSignerDialogOpen(false); | ||
| } catch (error) { | ||
| console.error("Failed to verify phone OTP", error); | ||
| rejectRef.current(new Error("Failed to verify phone OTP")); | ||
| throw error; | ||
| } | ||
| }; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the difference between throwing here and calling rejectRef.current(new Error("Failed to send email OTP"))? does the latter effectively throw as well?
| this._needsAuth = true; | ||
| this._authPromise?.reject(err as Error); | ||
| throw err; | ||
| const error = err as Error; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to create a new variable
|
Good question! The key difference is in the error propagation chain: Before (calling
After (throwing the error):
The In response to: #1463 (comment) |
…iable Co-Authored-By: jorge@paella.dev <jorge@paella.dev>
Description
Fixes a critical error handling bug where OTP verification failures in the non-custodial signer were not being displayed to users. When users entered an incorrect OTP or hit rate limits, the SDK would silently restart the auth flow without showing any error message, causing the UI to appear hung or stuck in a loop.
Root Cause:
verifyOtp()andsendMessageWithOtp()inncs-signer.tswere only rejecting an internal promise but not throwing errors to the callerChanges:
sendMessageWithOtp()to throw errors after rejecting the internal promiseverifyOtp()properly throws errors to callersCrossmintWalletProviderhandlers to re-throw errors so they reachBaseCodeInputfor displayPromise<void>return types for clarityImpact: Users will now see appropriate error messages when OTP verification fails, instead of experiencing silent failures.
Test plan
Critical to test:
Error propagation chain to verify:
ncs-signer.tsthrows errorCrossmintWalletProviderre-throws errorBaseCodeInputcatches and displays errorBackward compatibility: Existing apps using the SDK should continue to work without changes
Package updates
The following packages are affected and will need changesets:
@crossmint/client-sdk-wallets- Core error handling fix@crossmint/client-sdk-react-ui- UI error propagationNeed to run
pnpm change:addto create appropriate changesets with:Human Review Checklist
🔴 High Priority Items:
ncs-signer.tslines 209-211 and 245-246 - is it safe to both reject the promise AND throw the error?packages/client/ui/react-native/src/providers/CrossmintWalletProvider.tsxalso be updated? I assumed no changes needed since it uses context pattern, but verify this.Session Info: https://app.devin.ai/sessions/d4fa7bab226846e9b736967f6438328e
Requested by: jorge@paella.dev (@jorge2393)