4

我正在使用 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。我只是在询问它如何影响我的注册/登录过程。

4

1 回答 1

8

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

当然,此过程假设您有一个启用了 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();

此时,返回值将有所不同(与未强制执行 MFA 的情况相比)。返回值将类似于:

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

返回的对象是说用户MFA_SETUP在登录之前需要遵循挑战(每次用户注册都会发生一次)。

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

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

{
  "SecretCode": "...",
  "Session": "..."
}
  1. 用户必须接受给定的信息SecretCode并将其输入到“Google Authenticator”之类的应用程序中。添加后,应用程序将开始显示每分钟刷新一次的 6 位数字。

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

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

Session输入将是第 5 步中返回的字符串,并且是UserCode此时验证器应用程序上显示的 6 位数字。如果成功完成,您将获得以下返回值:

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

我没有发现这个对象返回的会话有任何用处。现在,注册过程完成,用户可以登录了。

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

当然,这与第 4 步相同。但它的返回值不同:

{
  "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 步返回的输入,是需要mfa从验证器应用程序读取的 6 位数字。调用该函数后,它将返回令牌:

{
  "ChallengeParameters": {},
  "AuthenticationResult": {
    "AccessToken": "...",
    "ExpiresIn": 3600,
    "TokenType": "Bearer",
    "RefreshToken": "...",
    "IdToken": "..."
  }
}
于 2020-01-16T00:42:30.073 回答