Open7

JavaScript V3 srp login cognito

Ryusuke NaritaRyusuke Narita

Email でもログインできるが、内部的なユーザー名は Email でない、というセットアップだったのでハマった

import {
  createPasswordHash,
  createSrpSession,
  signSrpSession,
  wrapAuthChallenge,
  wrapInitiateAuth,
} from "cognito-srp-helper";
import {
  CognitoIdentityProviderClient,
  InitiateAuthCommand,
  RespondToAuthChallengeCommand,
} from "@aws-sdk/client-cognito-identity-provider";

interface CognitoSrpSecrets {
  userPoolId: string;
  clientId: string;
  username: string;
  password: string;
}

export const userPoolSrpSignIn = async ({userPoolId, clientId, username, password}: CognitoSrpSecrets) => {
  const passwordHash = createPasswordHash(username, password, userPoolId);
  const initialSrpSession = createSrpSession(username, passwordHash, userPoolId);
  const client = new CognitoIdentityProviderClient();
  const initiateAuthRes = await client.send(
    new InitiateAuthCommand(
      wrapInitiateAuth(initialSrpSession, {
        ClientId: clientId,
        AuthFlow: "USER_SRP_AUTH",
        AuthParameters: {
          CHALLENGE_NAME: "SRP_A",
          USERNAME: username,
        },
      }),
    ),
  );
  const srpSession = (() => {
    const userId = initiateAuthRes.ChallengeParameters!.USER_ID_FOR_SRP;
    const passwordHash = createPasswordHash(userId, password, userPoolId);
    return {
      ...initialSrpSession,
      username: userId,
      passwordHash,
    }
  })()

  const signedSrpSession = signSrpSession(srpSession, initiateAuthRes);
  const respondToAuthChallengeRes = await client.send(
    new RespondToAuthChallengeCommand(
      wrapAuthChallenge(signedSrpSession, {
        ClientId: clientId,
        ChallengeName: initiateAuthRes.ChallengeName,
        ChallengeResponses: {
          USERNAME: initiateAuthRes.ChallengeParameters!.USERNAME,
        },
      }),
    ),
  )
  return respondToAuthChallengeRes.AuthenticationResult!
}
Ryusuke NaritaRyusuke Narita

library に PR だして修正してもらうまでは↑のやり方で。

Ryusuke NaritaRyusuke Narita

amazon-cognito-identity-js の authenticateUser を async/await で利用する:

const asyncAuthenticateUser = async (cognitoUser: CognitoUser, cognitoAuthenticationDetails: AuthenticationDetails) => {
  const result = await new Promise(function(resolve, reject) {
    cognitoUser.authenticateUser(cognitoAuthenticationDetails, {
      onSuccess: resolve,
      onFailure: reject,
      newPasswordRequired: resolve
    })
  });
  if (!(result instanceof CognitoUserSession)) {
    throw new Error('Failed to authenticate user');
  }
  return result;
}