【问题标题】:Setting up TOTP MFA with a QR Code using amazon-cognito-identity-js使用 amazon-cognito-identity-js 使用二维码设置 TOTP MFA
【发布时间】:2023-02-01 00:17:47
【问题描述】:

问题:我在从 amazon-cognito-identity-js 库实现 use case 27 时遇到困难,特别是在尝试修改它以使用 QR 码时。我能够从“associateSoftwareToken”接收密码,将其转换为二维码,并从验证器应用程序获取 TOTP 代码。但是,之后我很难将 TOTP 代码作为挑战答案传递给“verifySoftwareToken”。

目标:在库提供的示例中,他们使用“提示”(我已经尝试过并且有效)暂停工作流程,但我希望用户能够将 TOTP 键入页面本身的表单/输入字段而不是而不是弹出窗口。在表单上点击“提交”后,我想将 TOTP 输入提交到“verifySoftwareToken”,允许用户完成注册并被定向到主页。

我试过以这种方式分解身份验证流程,但它似乎不起作用:


  const [totpInput, setTotpInput] = useState(""); // controlled by an input field underneath the QR

  // Initial login event handler, which contains the main auth flow
  const onSubmit = (event) => {

    const authDetails = new AuthenticationDetails({
      Username: email,
      Password: password
    });

    const user = new CognitoUser({
      Username: email,
      Pool: UserPool
    });

    user.authenticateUser(authDetails, {

      ... success and error handling, as well as other callbacks

      mfaSetup: (challengeName) => {
        user.associateSoftwareToken({
          onFailure: (err) => {
            console.error(err);
          },
          associateSecretCode: (secretCode) => {
            setQRCodeSecret(secretCode);
            setQRCode(true); // renders the QR component on the page
            // the example uses a prompt here and continues to verifySoftwareToken immediately
            // after, which I'm trying to modify
          }
        });
      }
  }


// Runs when the QR form is submitted
const onSubmitTotp = (totpInput) => {

   user = new CognitoUser({
      Username: email,
      Pool: UserPool
    })

    user.verifySoftwareToken(totpInput, 'cry5', {
      onSuccess: (result) => {
        console.log(result);
      },
      onFailure: (err) => {
        console.log(err);
      }
    });
  }


我遇到的错误:

  1. 上述方法在提交表单时抛出网络错误
  2. 我尝试在 useState 挂钩中声明“user”和“authDetails”,以便它们的值在渲染中保持不变,但这似乎不起作用:
    ...globally:
    const [user, setUser] = useState();
    const [authDetails, setAuthDetails] = useState();
    
    
    ...at the start of onSubmit function:
      setUser(new CognitoUser({
          Username: email,
          Pool: UserPool
        }));
    
      setAuthDetails(new AuthenticationDetails({
          Username: email,
          Password: password
        }));
    
    

【问题讨论】:

    标签: javascript reactjs qr-code totp amazon-cognito-identity-js


    【解决方案1】:

    经过千辛万苦,我终于找到了解决办法。请注意,这是一个非常具体的解决方案,用于使用 amazon-cognito-identity-js 和完全在 React 中构建的登录流程(没有真正的 vanilla JS)。

    一般步骤

    • 将 Cognito 用户对象存储在全局变量中,最好使用 useState 挂钩
    • 在调用associateSoftwareToken 并获取密码以生成二维码后中断身份验证流程
    • onClickonSubmit 事件中(确保在后者的处理函数顶部使用 event.preventDefault())获取全局用户对象,调用 verifySoftwareToken,并使用用户的 TOTP 输入完成验证

    存储用户

    第一个问题是 Cognito SDK 改变了用户对象在其身份验证流程中。

    const user = new CognitoUser({
          Username: username,
          Pool: UserPool
        });
    

    存储在该用户对象中的特定信息对于任何后续步骤都是必需的。例如,如果您的流程使用 associateSoftwareTokenverifySoftwareToken 回调函数,您需要存储在用户来自 associateSoftwareToken/associateSecretCode 以成功完成 verifySoftwareToken。如果您尝试创建一个新的用户对象来初始化 verifySoftwareToken(即使您已经从从 associateSoftwareToken 收到的密码中获取了正确的 TOTP 代码),您的身份验证将失败。

    // WILL NOT WORK
    const user = new CognitoUser({
          Username: username,
          Pool: UserPool
        });
    
    user.verifySoftareToken(correctTotp, {
      onSuccess: (result) => {
        // code
      },
      onFailure: (error) => {
        // code
      }
    }
    

    那么解决方案是将用户存储在全局变量中在认证过程中。这样,您可以随时停止流程并通过引用变量返回到流程。在 React 中,你应该将用户存储在一个钩子中,这样它就不会在重新渲染时被擦除。如有必要,您还可以存储身份验证详细信息:

    const [user, SetUser] = useState()
    const [authDetails, setAuthDetails] = useState()
    

    最后,在身份验证流程的最开始将用户变量设置为新的CognitoUser

    注意事项:函数设置钩子的状态后,它才不是可以访问函数本身内的更改值。例子:

    const [hook, setHook] = useState()
    
    const testFunc = () => {
      setHook('a');
      console.log(hook) // this will print 'undefined'
    }
    

    因此,我需要编写一个辅助函数来改变用户变量的状态,并且将对用户对象的引用返回给身份验证函数,以便在初始身份验证流程中使用。完整示例:

    const [user, SetUser] = useState()
    
    const returnUser = () => {
      let new_user = new CognitoUser({
        Username: username,
        Pool: UserPool
      })
    
      setUser(new_user)
      return new_user
    }
    
    const onLoginStart = (event) => {
      event.preventDefault() // Necessary, stops the SDK from losing connection to AWS when the initial login form submits
      
      let user = returnUser()
    
      user.authenticateUser(authDetails, {
      ...etc.
      }
    }
    

    获取密码

    从那里继续身份验证流程,直到您点击 mfaSetup 回调函数。完成此流程,直到您获得密码,然后使用该密码创建和显示 QR 码:

    ...previous callbacks
        mfaSetup: (challengeName) => {
            user.associateSoftwareToken({
              associateSecretCode: (secretCode) => {
                setQRCodeSecret(secretCode);
                displayQRCode(true);
              }
            });
          }
    

    将 TOTP 输入传递给 verifySoftwareToken

    用户输入 TOTP 后,使用它来完成verifySoftwareToken。在我的例子中,用户会在输入字段中输入他们的 TOTP,然后单击一个按钮。然后我会将他们的 TOTP 传递给 onSubmit 处理程序函数,该函数将引用全局用户来完成 verifySoftwareToken:

    const onTotpFormSubmit = (event, totp) => {
      event.preventDefault();
      user.verifySoftwareToken(totp, {
        onSuccess: (result) => {
          // code
        },
        onFailure: (err) => {
          // code
        }
      });
    }
    

    用户现在已经过验证,可以重定向到所需的页面。

    【讨论】:

    • 很好地使用钩子 - 在很多方面,反应可能比香草 js 更复杂......
    猜你喜欢
    • 1970-01-01
    • 2021-09-13
    • 2020-12-09
    • 1970-01-01
    • 1970-01-01
    • 2018-07-14
    • 2022-01-24
    • 2018-12-15
    • 2019-12-11
    相关资源
    最近更新 更多