如何在 AWS Cognito 身份验证过程中包含 TOTP MFA

How to include TOTP MFA in AWS Cognito authentication process

我正在使用 Cognito 用户池来验证我的 Web 应用程序。我现在一切正常,但现在我需要为其启用 MFA。我现在就是这样做的(所有提供的代码都是服务器端代码):

  1. 注册用户:
const cognito = new AWS.CognitoIdentityServiceProvider();
cognito.signUp({
    ClientId,
    Username: email,
    Password,
}).promise();
  1. 一封电子邮件被发送到用户的地址(在之前的函数调用中被称为用户名),其中包含一个代码。

  2. 用户读取代码并提供代码给下一个函数调用:

cognito.confirmSignUp({
    ClientId,
    ConfirmationCode,
    Username: email,
    ForceAliasCreation: false,
}).promise();
  1. 用户登录:
const tokens = await cognito.adminInitiateAuth({
    AuthFlow: 'ADMIN_NO_SRP_AUTH',
    ClientId,
    UserPoolId,
    AuthParameters: {
        'USERNAME': email,
        'PASSWORD': password,
    },
}).promise();

我对这个过程很满意。但现在我需要为此添加 TOTP MFA 功能。如果我想这样做,有人可以告诉我如何更改这些步骤吗?顺便说一句,我知道在创建用户池时需要为它启用 TOTP MFA。我只是想问一下它如何影响我的 sign-up/log-in 流程。

好的,我自己找到了方法。我必须说,我找不到任何关于此的文档,所以使用它需要您自担风险!

当然,此过程假设您有一个启用了 MFA 的用户池(我使用了 TOTP MFA)。

  1. 注册用户:
const cognito = new AWS.CognitoIdentityServiceProvider();
cognito.signUp({
    ClientId,
    Username: email,
    Password,
}).promise();
  1. 一封电子邮件被发送到用户的地址(在之前的函数调用中被称为用户名),其中包含一个代码。

  2. 用户读取代码并提供代码给下一个函数调用:

cognito.confirmSignUp({
    ClientId,
    ConfirmationCode: code,
    Username: email,
    ForceAliasCreation: false,
}).promise();
  1. 首次登录:
await cognito.adminInitiateAuth({
    AuthFlow: 'ADMIN_NO_SRP_AUTH',
    ClientId,
    UserPoolId,
    AuthParameters: {
        'USERNAME': email,
        'PASSWORD': password,
    },
}).promise();

此时,return 值将有所不同(与未强制执行 MFA 时您将获得的值相比)。 return 值类似于:

{
  "ChallengeName": "MFA_SETUP",
  "Session": "...",
  "ChallengeParameters": {
    "MFAS_CAN_SETUP": "[\"SOFTWARE_TOKEN_MFA\"]",
    "USER_ID_FOR_SRP": "..."
  }
}

returned 对象说用户需要先完成 MFA_SETUP 挑战才能登录(每个用户注册都会发生一次)。

  1. 为用户启用 TOTP MFA:
cognito.associateSoftwareToken({
  Session,
}).promise();

需要之前的调用,因为有两个选项,通过发出给定的调用,您告诉 Cognito 您希望您的用户启用 TOTP MFA(而不是 SMS MFA)。 Session 输入是前一个函数调用的 return。现在,这次它将 return 这个值:

{
  "SecretCode": "...",
  "Session": "..."
}
  1. 用户必须获取给定的 SecretCode 并将其输入到 "Google Authenticator" 等应用程序中。添加后,该应用程序将开始显示每分钟刷新一次的 6 位数字。

  2. 验证验证器应用程序:

cognito.verifySoftwareToken({
  UserCode: '123456',
  Session,
}).promise()

Session 输入将是在第 5 步中输入的字符串 return,UserCode 是目前验证器应用程序上显示的 6 位数字。如果成功完成,您将获得此 return 值:

{
  "Status": "SUCCESS",
  "Session": "..."
}

我没有发现此对象对 return 会话有任何用处。现在,注册过程已完成,用户可以登录了。

  1. 实际登录(每次用户想要验证自己时都会发生):
await cognito.adminInitiateAuth({
    AuthFlow: 'ADMIN_NO_SRP_AUTH',
    ClientId,
    UserPoolId,
    AuthParameters: {
        'USERNAME': email,
        'PASSWORD': password,
    },
}).promise();

当然,这与第 4 步相同。但其 returned 值不同:

{
  "ChallengeName": "SOFTWARE_TOKEN_MFA",
  "Session": "...",
  "ChallengeParameters": {
    "USER_ID_FOR_SRP": "..."
  }
}

这是告诉您,为了完成登录过程,您需要遵循 SOFTWARE_TOKEN_MFA 挑战过程。

  1. 通过提供 MFA 完成登录过程:
cognito.adminRespondToAuthChallenge({
  ChallengeName: "SOFTWARE_TOKEN_MFA",
  ClientId,
  UserPoolId,
  ChallengeResponses: {
    "USERNAME": config.username,
    "SOFTWARE_TOKEN_MFA_CODE": mfa,
  },
  Session,
}).promise()

Session 输入是第 8 步 return 编辑的输入,mfa 是需要从身份验证器应用程序读取的 6 位数字。调用该函数后,它将 return 标记:

{
  "ChallengeParameters": {},
  "AuthenticationResult": {
    "AccessToken": "...",
    "ExpiresIn": 3600,
    "TokenType": "Bearer",
    "RefreshToken": "...",
    "IdToken": "..."
  }
}