Invalid OTP error when using front end, when I test API with swagger it works fine.
I am trying to implement asp.net 2FA authentication but i am getting error Invalid OTP when i try to use the front end, even though I received the email with the OTP. When i test the API using swagger i am able to get the JWT token and login.
OTPVerification.jsx
function OTPVerification({ isOpen, onOpenChange, username, length = 6 }) {
const { setIsLoading, isLoading, verifyOTP } = useAuth();
const handleOtpSubmit = async (otp) => {
setIsLoading(true);
try {
if (otp.length !== 6 || isNaN(otp)) {
toast.error('Please enter a valid 6-digit OTP.');
return;
}
console.log("Username: ", username);
console.log("OTP: ", otp);
const response = await verifyOTP(username,otp);
console.log("Response: ", response);
if (response.success) {
toast.success('OTP Verified Successfully!');
onOpenChange(false);
} else {
toast.error(response.message || 'OTP Verification Failed. Please try again.');
}
} catch (error) {
console.error('Error during OTP verification:', error);
const message = error.response?.data?.message || 'An error occurred during OTP verification. Please try again.';
toast.error(message);
} finally {
setIsLoading(false);
}
};
AuthContext,jsx
async function verifyOTP(username, otp) {
setIsLoading(true); // Start loading state
try {
const response = await axios.post('/api/Auth/login-2FA', {
Username: username,
OTP: otp
});
console.log('Sent OTP request:', { username, otp });
if (response.data.Status === 'Success') {
login(response.data.token, response.data.user);
return { success: true, token: response.data.token, user: response.data.user };
} else {
return { success: false, message: response.data.Message };
}
} catch (error) {
console.error('Error verifying OTP:', error);
const message = error.response?.data?.message || 'An error occurred. Please try again.';
return { success: false, message };
} finally {
setIsLoading(false); // Stop loading state
}
}
AuthController.cs
[HttpPost]
[Route("login-2FA")]
public async Task<IActionResult> LoginWithOTP([FromBody] OtpRequest request)
{
// Validate the request
if (request == null || string.IsNullOrEmpty(request.OTP) || string.IsNullOrEmpty(request.Username))
{
_logger.LogWarning("Invalid OTP request: Missing code or username.");
return BadRequest(new { Status = "Error", Message = "Code and Username are required." });
}
// Find the user by username
var user = await _userManager.FindByNameAsync(request.Username.ToLower());
if (user == null)
{
_logger.LogWarning("OTP login attempt for non-existent user: {Username}", request.Username);
return Unauthorized(new { Status = "Error", Message = "Invalid username or OTP ." });
}
if (await _userManager.IsLockedOutAsync(user))
{
_logger.LogWarning("User is locked out: {Username}", user.UserName);
return Unauthorized(new { Status = "Error", Message = "User account is locked out." });
}
// Verify the OTP token
var signInResult = await _signInManager.TwoFactorSignInAsync("Email", request.OTP, rememberClient: false, isPersistent: false);
if (signInResult.Succeeded)
{
// Generate JWT token
var authClaims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.UserName),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
// Add user roles to claims
var userRoles = await _userManager.GetRolesAsync(user);
foreach (var role in userRoles)
{
authClaims.Add(new Claim(ClaimTypes.Role, role));
}
// Generate token string using claims
var jwtToken = GenerateTokenString(authClaims);
_logger.LogInformation("2FA login successful for user: {Username}", user.UserName);
return Ok(new
{
token = jwtToken,
expiration = new JwtSecurityTokenHandler().ReadJwtToken(jwtToken).ValidTo
});
}
_logger.LogWarning("Invalid OTP provided for user: {Username}.", request.Username);
return Unauthorized(new { Status = "Error", Message = "Invalid OTP." });
}