好吧,我自己找到了一种方法。我必须说,我找不到任何关于此的文档,所以使用它需要您自担风险!
当然,此过程假设您有一个启用了 MFA 的用户池(我使用了 TOTP MFA)。
- 注册用户:
const cognito = new AWS.CognitoIdentityServiceProvider();
cognito.signUp({
ClientId,
Username: email,
Password,
}).promise();
一封电子邮件被发送到用户的地址(在前面的函数调用中称为用户名),其中包含代码。
用户读取代码并将代码提供给下一个函数调用:
cognito.confirmSignUp({
ClientId,
ConfirmationCode: code,
Username: email,
ForceAliasCreation: false,
}).promise();
- 第一次登录:
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
在登录之前需要遵循挑战(每次用户注册都会发生一次)。
- 为用户启用 TOTP MFA:
cognito.associateSoftwareToken({
Session,
}).promise();
需要上一个调用,因为有两个选项,通过发出给定调用,您告诉 Cognito 您希望您的用户启用 TOTP MFA(而不是 SMS MFA)。Session
输入是前一个函数调用的返回值。现在,这一次它将返回这个值:
{
"SecretCode": "...",
"Session": "..."
}
用户必须接受给定的信息SecretCode
并将其输入到“Google Authenticator”之类的应用程序中。添加后,应用程序将开始显示每分钟刷新一次的 6 位数字。
验证身份验证器应用程序:
cognito.verifySoftwareToken({
UserCode: '123456',
Session,
}).promise()
Session
输入将是第 5 步中返回的字符串,并且是UserCode
此时验证器应用程序上显示的 6 位数字。如果成功完成,您将获得以下返回值:
{
"Status": "SUCCESS",
"Session": "..."
}
我没有发现这个对象返回的会话有任何用处。现在,注册过程完成,用户可以登录了。
- 实际登录(每次用户想要进行身份验证时都会发生):
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
挑战过程。
- 通过提供 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": "..."
}
}