Modern web applications must protect user accounts from password theft, phishing, and credential-stuffing attacks. One of the most effective ways to strengthen authentication security is by implementing Two-Factor Authentication (2FA).
In addition to traditional 2FA methods such as email codes, SMS codes, and authenticator apps, modern applications are increasingly adopting Passkeys, which enable passwordless authentication based on the FIDO2/WebAuthn standard.
Why Two-Factor Authentication Matters
Passwords alone are no longer sufficient for securing accounts. Attackers frequently obtain passwords through phishing, database leaks, or brute-force attacks.
Two-Factor Authentication (2FA) introduces a second verification factor, typically categorized as:
- Something you know - Password or PIN
- Something you have - Phone, authenticator app, security key
- Something you are - Biometrics such as fingerprint or Face ID
With 2FA enabled, even if a password is compromised, attackers cannot access the account without the second factor.
2FA Methods in ASP.NET Core
ASP.NET Core Identity provides built-in support for several 2FA mechanisms:
- Email verification codes
- SMS verification codes
- Authenticator apps (TOTP)
- Recovery codes
During login, if 2FA is enabled, the authentication process is split into two steps.
Step 1 - Username & Password Login
var result = await _signInManager.PasswordSignInAsync(
model.Email,
model.Password,
model.RememberMe,
lockoutOnFailure: true
);
if (result.RequiresTwoFactor)
{
return RedirectToAction("VerifyCode");
}
If the user has 2FA enabled, the login flow redirects to the verification step.
Step 2 - Verify the 2FA Code
ASP.NET Core Identity can generate verification tokens using different providers such as email, SMS, or authenticator apps.
var code = await _userManager.GenerateTwoFactorTokenAsync(user, "Email");
This code is sent to the user through the selected provider.
To verify the code:
var result = await _userManager.VerifyTwoFactorTokenAsync(
user,
_userManager.Options.Tokens.AuthenticatorTokenProvider,
"Email",
code
);
Authenticator App Setup (TOTP)
Authenticator apps such as:
- Google Authenticator
- Microsoft Authenticator
- Authy
generate time-based one-time passwords (TOTP).
When enabling authenticator-based 2FA, the system generates a secret key and displays a QR code.
var authenticatorKey = await _userManager.GetAuthenticatorKeyAsync(user);
if (string.IsNullOrEmpty(authenticatorKey))
{
await _userManager.ResetAuthenticatorKeyAsync(user);
authenticatorKey = await _userManager.GetAuthenticatorKeyAsync(user);
}
The QR code is then scanned by the authenticator app, allowing it to generate rotating verification codes every 30 seconds.
Recovery Codes
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
These codes should be stored securely by the user.
Introducing Passkeys (Passwordless Authentication)
While traditional 2FA strengthens security, the industry is moving toward passwordless authentication using passkeys.
Passkeys are based on the FIDO2 / WebAuthn standard and use public-key cryptography instead of passwords.
When a passkey is created:
- A private key is stored securely on the user's device.
- A public key is stored on the server.
During login, the server sends a cryptographic challenge that can only be signed using the user's private key.
This makes phishing attacks extremely difficult because the private key never leaves the user's device.
Passkeys vs 2FA
| Feature | 2FA | Passkey |
|---|---|---|
| Password required | Yes | No |
| User experience | Multi-step login | Single step |
| Security model | Shared secrets | Public-key cryptography |
| Phishing resistance | Moderate | Very high |
| Device integration | Authenticator apps | Biometrics, device security |
Passkeys in ASP.NET Core
ASP.NET Core supports passkey authentication based on the FIDO2 / WebAuthn standard, enabling modern, passwordless, and phishing-resistant authentication mechanisms.
The implementation can be achieved using the Fido2.AspNet package, which is available on NuGet and provides a ready-to-use library for integrating passkey registration and authentication flows into ASP.NET Core applications.
dotnet add package Fido2.AspNet --version 4.0.0
Basic registration flow:
- User clicks Register Passkey
- Server generates a WebAuthn challenge
- Browser prompts biometric verification
- Device creates a cryptographic key pair
- Public key is stored on the server
Login flow:
- User selects Sign in with Passkey
- Browser prompts biometric verification
- Device signs the challenge using private key
- Server validates the signature
Security Best Practices
When implementing authentication systems in ASP.NET Core, follow these recommendations:
- Enable account lockout after multiple failed attempts
- Require email confirmation
- Store recovery codes securely
- Enforce HTTPS
- Log authentication events
- Allow users to manage 2FA/passkey methods from a dashboard
Conclusion
Two-Factor Authentication significantly improves account security by requiring additional verification beyond passwords. ASP.NET Core Identity provides built-in support for implementing multiple 2FA methods including email, SMS, and authenticator apps.
However, the future of authentication is shifting toward passwordless systems like passkeys, which offer stronger protection against phishing and credential theft while improving user experience.
By combining 2FA with passkey authentication, developers can build modern, secure, and user-friendly authentication systems.
Demo Project
You can explore the complete working example here:
https://github.com/TechPdo/TwoFactorAuth
The project demonstrates how to build a production-style authentication system in ASP.NET Core MVC with 2FA support and extensibility for modern authentication methods like passkeys.