AWS Cognito SMS 2FA报错:无效验证码或用户认证状态异常
解决AWS Cognito SMS 2FA认证的两个异常问题
我来帮你梳理下这两个错误的根源,以及对应的修复方案:
问题1:「Missing required parameter SMS_MFA_CODE」错误
这个错误的核心原因很直接:当mfaRequired回调触发时,你立刻调用了cognitoUser.sendMFACode(context.state.code, this),但此时用户还没来得及输入验证码,context.state.code是空字符串,Cognito自然会提示缺少必要参数。
你不需要在mfaRequired里马上提交验证码,这个回调的作用只是通知你需要显示验证码输入框,让用户输入收到的短信验证码。
问题2:「Invalid code or auth state for the user」错误
当Cognito返回mfaRequired后,当前的cognitoUser实例已经处于MFA验证的会话状态中。你此时再次调用authenticateUser会重置整个认证流程,破坏了之前的MFA上下文,导致Cognito无法识别当前的认证状态,所以抛出这个错误。
正确的做法是复用同一个cognitoUser实例,在用户输入验证码后直接调用sendMFACode完成验证,而不是重新走一遍登录流程。
修改后的完整代码
下面是调整后的React组件代码,我标记了关键修改点:
import React, { Component } from 'react'; import { BrowserRouter as Router, Route, Link, Redirect } from "react-router-dom"; import * as AWS from 'aws-sdk/global'; import { AuthenticationDetails, CognitoUserPool, CognitoUserAttribute, CognitoUser } from 'amazon-cognito-identity-js'; import PropTypes from 'prop-types'; import '../../stylesheets/Authentication.css'; export default class CognitoIdentitySignIn extends Component { constructor(props) { super(props); this.state = { password: "", email: "", code: "", submissionAnnouncement: "", readyForCode: false }; // 关键修改1:保存cognitoUser实例到组件属性,方便后续复用 this.cognitoUser = null; this.handleChangeEmail = this.handleChangeEmail.bind(this); this.handleChangePassword = this.handleChangePassword.bind(this); this.handleChangeCode = this.handleChangeCode.bind(this); this.submitCredentials = this.submitCredentials.bind(this); // 关键修改2:新增提交验证码的方法 this.submitCode = this.submitCode.bind(this); } handleChangeEmail(event) { this.setState({email: event.target.value}); } handleChangePassword(event) { this.setState({password: event.target.value}); } handleChangeCode(event) { this.setState({code: event.target.value}); } submitCredentials(event) { this.signIn(this, this.state.email, this.state.password); } // 关键修改3:提交验证码的方法,复用之前的cognitoUser实例 submitCode() { if (!this.cognitoUser || !this.state.code) { this.setState({ submissionAnnouncement: "请先输入验证码" }); return; } this.cognitoUser.sendMFACode(this.state.code, { onSuccess: (session) => { var accessToken = session.getAccessToken().getJwtToken(); AWS.config.region = 'us-west-2'; var Logins = {} Logins[`cognito-idp.${AWS.config.region}.amazonaws.com/${'us-west-2_xxxxxxxx'}`] = accessToken; AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId : 'us-west-2:XXXXXXX-XXXX-XXXXXXXX', Logins : Logins }); AWS.config.credentials.refresh((error) => { if (error) { alert('onSuccess:\n' + JSON.stringify(error)); this.setState({ submissionAnnouncement: error.message, readyForCode: false }) console.error(error); } else { this.setState({ submissionAnnouncement: "SUCCESS", readyForCode: false }); console.log(session); } }); }, onFailure: (err) => { alert('MFA验证失败:\n' + JSON.stringify(err)); this.setState({ submissionAnnouncement: err.message }) } }); } signIn(context, email, password) { var authenticationData = { Username : email, Password : password }; var authenticationDetails = new AuthenticationDetails(authenticationData); var poolData = { UserPoolId: 'us-west-2_xxxxxxxx', ClientId: 'XXXXXXXXXXXXXXXXXXXXXX', }; var userPool = new CognitoUserPool(poolData); var userData = { Username : email, Pool : userPool }; // 关键修改4:把cognitoUser实例保存到组件属性 this.cognitoUser = new CognitoUser(userData); console.log(authenticationDetails); console.log(this.cognitoUser); this.cognitoUser.authenticateUser(authenticationDetails, { onSuccess: function (session) { var accessToken = session.getAccessToken().getJwtToken(); AWS.config.region = 'us-west-2'; var Logins = {} Logins[`cognito-idp.${AWS.config.region}.amazonaws.com/${poolData.UserPoolId}`] = accessToken; AWS.config.credentials = new AWS.CognitoIdentityCredentials({ IdentityPoolId : 'us-west-2:XXXXXXX-XXXX-XXXXXXXX', Logins : Logins }); AWS.config.credentials.refresh((error) => { if (error) { alert('onSuccess:\n' + JSON.stringify(error)); context.setState({ submissionAnnouncement: error.message, readyForCode: false }) console.error(error); } else { context.setState({ submissionAnnouncement: "SUCCESS", readyForCode: false }); console.log(session); } }); }, onFailure: function(err) { alert('登录失败:\n' + JSON.stringify(err)); context.setState({ submissionAnnouncement: err.message, readyForCode: false }) }, mfaRequired: function(codeDeliveryDetails) { console.log(codeDeliveryDetails) // 关键修改5:这里只更新UI,让用户输入验证码,不调用sendMFACode context.setState({ submissionAnnouncement: "请输入短信验证码", readyForCode: true }) } }); } render() { return ( <div className='SignIn'> <div className='sign-in-input-box'> <label> <p>Email</p> <input className='sign-in-email-input' value={this.state.email} onChange={this.handleChangeEmail} type='text' /> </label> </div> <div className='sign-in-input-box'> <label> <p>Password</p> <input className='sign-in-password-input' value={this.state.password} onChange={this.handleChangePassword} type='password' /> </label> </div> <div className="click-text" onClick={this.submitCredentials}> SIGN IN </div> {(this.state.submissionAnnouncement === "" ? null : <div><span>{this.state.submissionAnnouncement}</span></div>)} {(this.state.readyForCode) ? ( <div className='sign-in-input-box'> <label> <p>SMS验证码</p> <input className='sign-in-code-input' value={this.state.code} onChange={this.handleChangeCode} type='text' /> </label> {/* 关键修改6:添加提交验证码的按钮 */} <div className="click-text" onClick={this.submitCode}> 提交验证码 </div> </div> ): null} </div> ) } } CognitoIdentitySignIn.propTypes = { }
关键修复总结
- 保存
cognitoUser实例:把登录时创建的cognitoUser存在组件属性里,避免MFA阶段重新创建实例导致会话丢失。 - 拆分登录和MFA验证流程:登录触发
mfaRequired后,只显示验证码输入框,等用户输入后再调用sendMFACode提交。 - 避免重复调用
authenticateUser:MFA阶段不需要重新走登录流程,直接用已有的cognitoUser实例完成验证。
内容的提问来源于stack exchange,提问作者AlleyOOP
相关产品推荐
相关产品推荐

