むだいありー

Vue3のComposition APIでCognito認証したい 後編

概要

Vue3のComposition APIでCognito認証したい 前編の続き。

前回はamplifyコマンドを使って、CloudFormationの設定とvueプロジェクトのセットアップを行った。

Cognito認証管理クラスを作る

認証関連機能を持ったクラスを作成。

準備

src/plugins/cognito-authenticator/index.tsを作成。

class CognitoAuthenticator {
  // ここに実装していく
}

// インスタンスをエクスポート
export const cognitoAuthenticator = new CognitoAuthenticator()

接続準備(コンストラクタ)

必要そうなものをimportして、Cognito認証をするための初期セットアップを行う。

import authAwsConfig from "@/aws-exports";
import {
  AuthenticationDetails,
  CognitoUser,
  CognitoUserAttribute,
  CognitoUserPool,
  CognitoUserSession
} from "amazon-cognito-identity-js";
import Aws from "aws-sdk";

class CognitoAuthenticator {
  private userpool: CognitoUserPool;

  constructor() {
    console.log("cognitoAuthenticatorClass - constructor");
    this.userpool = new CognitoUserPool({
      UserPoolId: authAwsConfig.aws_user_pools_id,
      ClientId: authAwsConfig.aws_user_pools_web_client_id
    });

    Aws.config.update({
      region: authAwsConfig.aws_cognito_region,
      credentials: new Aws.CognitoIdentityCredentials({
        IdentityPoolId: authAwsConfig.aws_cognito_identity_pool_id
      })
    });
  }

  get currentUser() {
    return this.userpool.getCurrentUser();
  }
}

Signin / Signout

サインインとサインアウトの処理。 ユーザーをAWSコンソールで作った場合、新たなパスワードを設定する必要がある。 また、サインアップ時の必須属性にNameを設定したのでそのパラメータが必要になる。

このコードでは決め打ちしているけどパスワードは入力したほうが良いし、必要な属性はrequiredAttributesから取得したほうが良いと思う。

class CognitoAuthenticator {

  /* 省略 */

  signin(email: string, password: string) {
    const cognitoUser = new CognitoUser({
      Username: email,
      Pool: this.userpool
    });
    const authenticationDetails = new AuthenticationDetails({
      Username: email,
      Password: password
    });

    return new Promise<CognitoUserSession>((resolve, reject) => {
      cognitoUser.authenticateUser(authenticationDetails, {
        onSuccess: result => {
          resolve(result);
        },
        onFailure: error => {
          reject(error);
        },
        // eslint-disable-next-line
        newPasswordRequired: (userAttributes, requiredAttributes) => {
          cognitoUser.completeNewPasswordChallenge(
            password,
            { name: "ZAKI" },
            {
              onSuccess: result => {
                resolve(result);
              },
              onFailure: error => {
                reject(error);
              }
            }
          );
        }
      });
    });
  }

  signout(): void {
    if (this.currentUser) {
      this.currentUser.signOut();
    }
  }
}

ユーザー属性の取得

ユーザーの属性値(Nameとか)を取得するにはgetUserAttributesメソッドを実行する。 getCurrentUserの戻り値であるCognitoUserにあるメソッドだけど、そのまま実行すると401エラーになる。 getSessionでセッション情報を取っている中で属性値を取得する。

class CognitoAuthenticator {

  /* 省略 */

  getUserAttributes() {
    return new Promise<CognitoUserAttribute[] | undefined>(
      (resolve, reject) => {
        const currentUser = this.currentUser;
        if (currentUser) {
          // eslint-disable-next-line
          currentUser.getSession((error: Error | null, session: CognitoUserSession | null) => {
              if (error) {
                reject(error);
              } else {
                currentUser.getUserAttributes((error, result) => {
                  if (error) {
                    reject(error);
                  } else {
                    resolve(result);
                  }
                });
              }
            }
          );
        } else {
          reject(new Error("no user"));
        }
      }
    );
  }
}

view

実際にCognitoAuthenticatorを使うところ。 サインインに成功した後、ユーザーの属性を取得してストアに保存する。

import { defineComponent, ref } from "vue";
import { cognitoAuthenticator } from "@/plugins/cognito-authenticator";
import { useRouter } from "vue-router";
import { useStore } from "vuex";

export default defineComponent({
  name: "Signin",
  setup() {
    const userName = ref<string>("");
    const password = ref<string>("");
    const router = useRouter();
    const store = useStore();

    const signin = () => {
      cognitoAuthenticator
        .signin(userName.value, password.value)
        .then(result => {
          cognitoAuthenticator
            .getUserAttributes()
            .then(attrs => {
              let username, displayName, email;
              if (attrs) {
                attrs.forEach(attr => {
                  switch (attr.getName()) {
                    case "sub":
                      username = attr.getValue();
                      break;
                    case "name":
                      displayName = attr.getValue();
                      break;
                    case "email":
                      email = attr.getValue();
                      break;
                    default:
                      break;
                  }
                });
              }

              store.dispatch("setUser", {
                idToken: result.getIdToken(),
                accessToken: result.getAccessToken(),
                refreshToken: result.getRefreshToken(),
                displayName: displayName,
                username: username,
                email: email
              });

              router.push({ name: "AdminHome" });
            })
            .catch(error => {
              console.log(error);
            });
        })
        .catch(error => {
          console.error(error);
        });
    };

    return {
      userName,
      password,
      signin
    };
  }
});

最後に

最近になってようやくAWSを自分で使い始めた。 仕事で使うようになったって言うのが大きいけどね。

すっごい便利だと思うし、色々できるんだろうけど、むつかしいなぁ...。