IDV as 2FA at Coinbase
2021–2022Usually 2FA is a code from a text, an authenticator app, or a hardware key. But if the phone is lost, or a login looks suspicious, all of these fail. At Coinbase I made identity verification a second factor for exactly those cases.
Coinbase already asked for a passport, a national ID, or a photo at sign-up. We needed that same identity verification as a second factor in exactly those cases. Otherwise, users had to wait five days to get back into their account.
Document capture already lived in the old sign-up app. The 2FA work happened in a different, newer frontend monorepo, and I could not import the old screens: they were tightly coupled to old libraries and an old version of React, written as class components. So I pulled document capture into its own package, cut the old dependencies, moved it to the new design system library and a newer React with hooks, and installed it in the new app.
What I built
A new React package for document capture:
- Capture of a passport, a national ID, or a photo, from a webcam, a phone, or a file already on the machine.
- A submit path that sends the images to the Coinbase API, which stores them, passes them to the verification vendor, and returns a verification id.
- A callback that hands that id back to the sign-in flow.
- One responsive package for both desktop and mobile web.
The existing auth path assumed a credential that only exists after 2FA, and this flow was the 2FA. The package stops at the verification id. Sign-in owns the wait, then lets the person in or asks them to set up 2FA again.
Where it shipped
It went live on Coinbase sign-in and account recovery in 2022. That is the IDV as 2FA path: if you lost your authenticator, or a login looked risky, you proved who you were and you were back in.
The same library later became one of the web captures for onboarding too. By 2025 its consumers were sign-in 2FA, account recovery, and onboarding.
Outcome
The feature is still in production. Sign-in and account recovery still treat identity verification as a second factor. A locked-out user gets back in under a day instead of waiting five.
The library I built ran for four years, from 2022 to 2026. In April 2026 it was retired and marked deprecated, replaced by a package with the same API, a new name, and new internals. I shipped the first web capture library that made this flow possible.
What I learned
- How to build a reusable package, and how to link it locally with the app during development without publishing the package again and again.
- A UI can have many different scenarios, and they confuse developers and become a bottleneck later if they are not identified early, which leads to rework for everyone. It is always good to write down all the possible flows.
- It is important to clarify the ownership of screens in the mocks, and to identify how other teams will communicate with the screens we are building.
- It is good to keep a document of open questions and keep adding the answers there, and to keep updating it with new questions and doubts as they come.
- It is important to get translations started as soon as possible, because it takes time for translators to translate.
- It is good to identify which things in the interface are static and which are dynamic.
- Always look at the interface from a customer's perspective. We need to consider cases like a user coming from a different device, a different country, a different language, or coming back after a long, long time on the app.