Multi-factor authentication is the single highest-return security control for most applications, because the overwhelming majority of account compromises begin with a valid password used by the wrong person. The design question is not whether to have it but which factors to offer, and that is a genuine trade between security and the proportion of users who will complete setup.
The options rank clearly on security. Passkeys and hardware security keys are phishing-resistant by design: the credential is bound to your domain, so a convincing fake site cannot obtain anything usable. Authenticator apps generating time-based codes are strong against password reuse and credential stuffing, and phishable - a user can be persuaded to type the code into a fake page. SMS codes are the weakest, vulnerable to SIM swap and interception, and are still enormously better than nothing.
That last point deserves emphasis, because security advice often reads as though SMS is worthless. An account protected by SMS is protected against every attack that starts with a leaked password from another site, which is the attack your users actually face. If your population will not install an authenticator app - and for many consumer products in India they will not - offering SMS is the difference between most users having a second factor and almost none.
So offer several and let users choose, with a preference order in the interface. Passkeys first, since browser and platform support is now good and they remove the password entirely for many users. Authenticator apps next. SMS as the accessible fallback. And allow more than one method per account, because a user with only one factor and a lost phone becomes a support problem you must solve carefully.
Recovery is where implementations are compromised, and it is worth designing before the happy path. If your account recovery can be completed with a password reset by email, then email is your real second factor and everything above it is decoration. Generate single-use recovery codes at enrolment, insist they are stored, and make any human-assisted recovery path deliberately slow and well-verified - because that path is what an attacker will use once the technical controls are sound.
Two implementation details that matter. Verify time-based codes with a small window of tolerance for clock drift, and reject a code that has already been used, or an intercepted code remains valid for its full period. And rate-limit verification attempts per account: a six-digit code is a million possibilities, which is a lot for a human and not much for a script given unlimited attempts.