使用AWS JS SDK for Cognito实现SPA用户登录遇认证流错误
问题概述
在SPA中使用AWS Cognito实现账号密码登录,切换至@aws-sdk/client-cognito-identity-provider后持续触发认证流错误,此前使用amazon-cognito-identity-js功能正常。
当前Cognito应用客户端启用的认证流:
ALLOW_REFRESH_TOKEN_AUTHALLOW_CUSTOM_AUTHALLOW_USER_SRP_AUTH
尝试多种AuthFlowType值后,浏览器控制台始终报错:
Uncaught (in promise) InvalidParameterException: Initiate Auth method not supported.
使用的登录代码:
command = new AdminInitiateAuthCommand({ ClientId: AWS_COGNITO_APP_CLIENT_ID, UserPoolId: AWS_COGNITO_USER_POOL_ID, AuthFlow: AuthFlowType.<WHAT_VALUE_TO_INSERT_?>, AuthParameters: { USERNAME: username, PASSWORD: password } });
更新#1:已在控制台为应用客户端添加ALLOW_USER_PASSWORD_AUTH标识,并使用AuthFlowType.USER_PASSWORD_AUTH登录,仍出现相同错误。
核心问题:误用AdminInitiateAuthCommand
AdminInitiateAuthCommand是服务器端专属API,需要具备Cognito用户池管理员权限的IAM凭证才能调用,前端SPA直接调用会触发权限或认证流不匹配的错误。前端应使用InitiateAuthCommand而非AdminInitiateAuthCommand。
同时,两种API对应的AuthFlow值存在差异:
- 前端
InitiateAuthCommand对应USER_PASSWORD_AUTH或USER_SRP_AUTH - 后端
AdminInitiateAuthCommand对应ADMIN_USER_PASSWORD_AUTH或ADMIN_USER_SRP_AUTH
解决方案
方案1:使用USER_PASSWORD_AUTH(仅测试用,不推荐生产环境)
若需临时用明文密码登录(存在密码泄露风险),确保应用客户端已开启ALLOW_USER_PASSWORD_AUTH,修改代码如下:
import { CognitoIdentityProviderClient, InitiateAuthCommand, AuthFlowType } from "@aws-sdk/client-cognito-identity-provider"; const client = new CognitoIdentityProviderClient({ region: "你的AWS区域" }); const command = new InitiateAuthCommand({ ClientId: AWS_COGNITO_APP_CLIENT_ID, AuthFlow: AuthFlowType.USER_PASSWORD_AUTH, AuthParameters: { USERNAME: username, PASSWORD: password } }); try { const response = await client.send(command); // 处理返回的AccessToken、IdToken等认证结果 } catch (error) { console.error(error); }
方案2:使用USER_SRP_AUTH(推荐,安全)
这是Cognito官方推荐的前端登录方式,无需明文传输密码。由于@aws-sdk/client-cognito-identity-provider未封装SRP参数计算逻辑,可结合amazon-cognito-identity-js的工具类实现:
- 安装依赖:
npm install amazon-cognito-identity-js
- 代码示例:
import { CognitoIdentityProviderClient, InitiateAuthCommand, RespondToAuthChallengeCommand, AuthFlowType } from "@aws-sdk/client-cognito-identity-provider"; import { SRP } from "amazon-cognito-identity-js"; const client = new CognitoIdentityProviderClient({ region: "你的AWS区域" }); const userPoolId = AWS_COGNITO_USER_POOL_ID; const clientId = AWS_COGNITO_APP_CLIENT_ID; const username = "用户账号"; const password = "用户密码"; // 生成SRP认证参数 const srp = new SRP({ username, password, poolId: userPoolId, clientId: clientId }); const authParams = await srp.getAuthParams(); // 发起初始认证请求 const initCommand = new InitiateAuthCommand({ ClientId: clientId, AuthFlow: AuthFlowType.USER_SRP_AUTH, AuthParameters: { USERNAME: username, SRP_A: authParams.SRP_A, SECRET_HASH: authParams.SECRET_HASH // 若应用客户端开启了Secret Hash则需传入 } }); try { const initResponse = await client.send(initCommand); // 处理PASSWORD_VERIFIER挑战 if (initResponse.ChallengeName === "PASSWORD_VERIFIER") { const challengeResponse = await srp.getPasswordVerifier(initResponse.ChallengeParameters); const challengeCommand = new RespondToAuthChallengeCommand({ ClientId: clientId, ChallengeName: "PASSWORD_VERIFIER", Session: initResponse.Session, ChallengeResponses: challengeResponse }); const finalResponse = await client.send(challengeCommand); // 获取最终认证令牌 console.log(finalResponse.AuthenticationResult); } else { // 直接获取认证结果 console.log(initResponse.AuthenticationResult); } } catch (error) { console.error(error); }
更新#1错误原因解析
你添加ALLOW_USER_PASSWORD_AUTH后仍报错,是因为使用了AdminInitiateAuthCommand,该API对应的AuthFlow应为AuthFlowType.ADMIN_USER_PASSWORD_AUTH,但前端不应使用管理员级API,切换至InitiateAuthCommand即可解决问题。
内容的提问来源于stack exchange,提问作者fudo

