Amazon Cognito × Nuxt.js でユーザー認証を試してみる(サインアップ編)

こんにちは。しげぞうです!

みなさんはWebアプリケーションやスマホアプリを開発する際、ユーザー管理や認証はどうしていますか?
個人としては最近、ユーザー認証基盤を提供するAWSサービス Amazon Cognito がいい感じだなって思っています!

先日の業務打ち合わせでも「Cognito 使ってみたいよね~」という話も出てきて、試したい欲が急上昇!!
手始めにCognitoを使ったユーザーのサインアップ機能を実装してみました!

そもそもCognitoとは

Cognitoは、Webアプリケーション/モバイルアプリケーションで利用可能なユーザー認証機能を提供するAWSのフルマネージドサービスです。単にユーザーのサインイン/サインアップ機能を提供するだけでなく、ユーザーに対して他AWSサービスへのアクセスコントロールを提供することができます。特に後者の機能が便利で、AWS SDKを利用して開発するときに直接コードにIAMの認証情報(AccessKeyId,SecretAccessKey)を埋め込む必要がなくなるので、セキュアなAPIアクセスが実現できます!

Cognitoを利用する上で理解しておくべき概念として、ユーザープールIDプール があります。これらについて簡単にさらっておきます。

ユーザープール(UserPool)

  • Cognitoユーザーを管理するユーザーディレクトリとしての役割を担う
  • ユーザーのサインアップ/サインインはユーザープールのみで実現可能で、外部IDプロバイダとの連携にも対応
  • 管理者によるユーザーの作成とユーザー自身によるサインアップの両方に対応
  • Cognitoで管理するユーザーは基本情報(ユーザー名, 誕生日, 性別 etc...)を属性として保持できる
  • 属性はユーザープールごとにカスタムで追加もできるが、更新頻度の高いデータを持たせるのはアンチパターン

IDプール(Federated Identities)

  • Cognitoで管理するユーザーに一時的なAWS認証情報(Temporary AWS Credentials)を提供する役割を担う
  • 認証されたユーザーだけでなく非認証ユーザー(いわゆるゲストユーザー)もサポート
  • Temporary AWS Credentialsとして AccessKeyId, SecretAccessKey, SessionToken の3つを提供
  • AWSサービスへのアクセスコントロールは独自のIAMロールで管理
  • ゲストユーザーに対してもIAMロールを設定できる(←これ面白い)

さっそく試してみる!

まずCognitoを利用する環境整備から

Cognitoを利用してユーザー管理やAWSサービスへのアクセスコントロールを実現するには、まず前章で出てきたユーザープールやIDプールなどの環境が必要になります。

これらはCognitoの管理コンソールをポチポチして用意できるのですが、せっかくなので、CloudFormation テンプレートを使って環境を整備できるようにしました。(コードで管理することで再利用性も高く、変更を差分管理できるのがGood!)

ここでテンプレートをぜんぶ載せすると膨大な量になってしまうので、GitHub に挙げておきます。ここでは、今回のメインになるユーザープール(UserPool)とユーザープールクライアント(UserPoolClient)の設定項目について説明します。

まず、ユーザープール(UserPool)

{
  "CognitoUserPool": {
    "Type": "AWS::Cognito::UserPool",
    "Properties": {
      "UserPoolName": "nuxt-cognito-user-pool",
      "Policies": {
        # パスワード認証で利用するパスワードポリシーを設定します
        # 今回の場合、大文字/小文字/数値をそれぞれ含んだ8桁以上の文字列
        "PasswordPolicy": {
          "MinimumLength": 8,
          "RequireUppercase": true,
          "RequireLowercase": true,
          "RequireNumbers": true,
          "RequireSymbols": false,
          "TemporaryPasswordValidityDays": 7
        }
      },
      # ユーザープールに設定する属性を設定します
      # 今回は、ニックネーム(nickname)を必須項目に設定
      "Schema": [
        {
          "Name": "nickname",
          "AttributeDataType": "String",
          "DeveloperOnlyAttribute": false,
          "Mutable": true,
          "Required": true,
          "StringAttributeConstraints": {
            "MinLength": "0",
            "MaxLength": "2048"
          }
        }
      ],
      # ユーザーの検証を何で行うか(メールor電話番号)
      "AutoVerifiedAttributes": [
        "email"
      ],
      # サインアップ時に何でユーザーを特定するか(メールor電話番号)
      "UsernameAttributes": [
        "email"
      ],
      # Cognitoのメールに関する設定
      "EmailConfiguration": {
        # 送信元メールアカウントをどれにするか(Amazon SESも設定可能)
        # 今回は、from: no-reply@verificationemail.com となるように設定
        "EmailSendingAccount": "COGNITO_DEFAULT"
      },

      ・・・

      # 検証用メッセージについての設定
      "VerificationMessageTemplate": {
        "DefaultEmailOption": "CONFIRM_WITH_CODE"
      }
    }
  }
}

次に、ユーザープールクライアント(UserPoolClient)

{
  "CognitoUserPoolClient": {
    "Type": "AWS::Cognito::UserPoolClient",
    "Properties": {
      "ClientName": "nuxt-cognito-user-pool-client",
      # 利用アプリに対して認証を行うか
      "GenerateSecret": false,
      # トークンの有効期限(日)
      "RefreshTokenValidity": 7,
      # 認証フローに関する設定
      "ExplicitAuthFlows": [
        "ALLOW_REFRESH_TOKEN_AUTH",
        # 認証で利用するJavaScript SDKがSRPプロトコルベースを採用しているので今回指定
        "ALLOW_USER_SRP_AUTH"
      ],
      # ユーザー存在エラーの設定
      # SighUpの時、既に存在する username を登録しようとするとエラーとして扱う
      "PreventUserExistenceErrors": "ENABLED",
      "UserPoolId": {
        "Ref": "CognitoUserPool"
      }
    }
  }
}

Cognitoを使ってユーザーのサインアップを試してみる

Cognitoを使ったユーザー認証を試してみるのに、今回はWebアプリアーキテクチャの主流になりつつあるJamstackも意識して、実務でも利用したことのある Nuxt のフレームワークでやってみることにしました。

サインアップですが、以下の流れで実現します。

シンプルなサインアップ画面です。ユーザープール作成時、属性にnicknameを必須に設定したのでそちらの入力欄も用意しました。ちなみに画面の装飾には、Tailwind CSS というCSSフレームワークを利用しています。(TailwindUI というコンポーネント集がとても便利なので、今回はそちらを利用させてもらいました)

Cognitoでのサインアップ処理ですが、 amazon-cognito-identity-js というnpmライブラリを利用して実装します。

import { Component, Vue } from "nuxt-property-decorator";
import {
  CognitoUserPool,
  CognitoUserAttribute,
} from "amazon-cognito-identity-js";

@Component({ auth: false })
export default class Signup extends Vue {
  email = "";
  password = "";
  confirmPassword = "";
  nickName = "";

  async signup(): Promise<void> {
    // 入力チェック
    if (this.password != this.confirmPassword) {
      this.errorMsg = "パスワードと確認用パスワードが一致しません。";
      return;
    }
    // ユーザープールクライアントを初期化
    const userPool = new CognitoUserPool({
      ClientId: process.env.COGNITO_CLIENT_ID || "",
      UserPoolId: process.env.COGNITO_USER_POOL_ID || "",
    });

    // ニックネームを登録用パラメータに整形
    const attributeList: CognitoUserAttribute[] = [];
    const attributeUserNickName = new CognitoUserAttribute({
      Name: "nickname",
      Value: this.nickName,
    });
    attributeList.push(attributeUserNickName);

    // 入力したユーザー情報をもとに登録処理を実施
    const self = this;
    await userPool.signUp(
      this.email,
      this.password,
      attributeList,
      [],
      function (err) {
        // エラー時
        if (err) return;
        // アクティベーション画面に遷移
        self.$router.push("/activate");
      }
    );
  }
}

①仮ユーザー登録 ですが、ユーザープール構築時に生成された認証キーを使ってクライアントの初期化(new CognitoUserPool)を行った後、登録用パラメータを渡して登録処理(userPool.signUp)を呼び出せばOKです。

登録処理にはコールバック関数が用意されているので、処理結果に応じて以降の処理を独自にハンドリングできます。今回の場合、登録に成功すると入力したメールアドレス宛にユーザー登録用の確認コードが送信されるので、その確認コードを入力する専用画面に遷移させています。

②本登録 のロジックですが、基本の流れはユーザー登録処理と同じですのでここでは省略します。全体のソースコードをGitHubにアップしてますので、詳細はそちらを覗いてみてください。

サンプルプロジェクト全体のソースコード

ちなみにクライアントIDやユーザープールIDなどのCognito認証キーは、dotenvライブラリを利用して環境変数から参照しています。

# プロジェクト直下に.envファイルを作成して以下のように設定
COGNITO_USER_POOL_ID = 'ap-northeast-1_xxxxxxxx'
COGNITO_CLIENT_ID = 'xxxxxxx...'

実際にサインアップを試してみます。

まず仮登録画面で必要なユーザー情報を入力して仮登録を行います。

仮登録完了後、本登録画面でメールアドレスと確認コード(仮登録時に入力したメールアドレスに配信される)を入力しユーザーを検証します。

本登録成功!(サンプルでは、本登録完了が分かるようwindow.alertを出すようにしています)

本登録も完了したので、実際にユーザーが登録されているかCLIコマンドで確認してみます。

aws cognito-idp list-users --user-pool-id ap-northeast-1_xxxxxxxx --profile xxxxxxxxx

用意したユーザープールにユーザーが追加されていることが確認できました!

最後に

今回Cognitoを試してみましたが、圧倒的に早く、しかも簡単にユーザー認証の仕組みを整えれるすげえサービスだと実感しました。。Cognitoを利用すればサーバ側のフレームワークでユーザー認証を作りこむ必要がなくなるし、外部IDフェデレーションも標準で利用できるし、SDKも充実しているので、今後ユーザー認証に使わない選択肢はないなと思いました。

また、Webアプリの基盤を簡単に作れる Amplify では、このCognitoを内包しているので、より簡単にユーザー認証の仕組みをアプリに組みこめるそうです。Amplifyも試してみたい。。

今回はCognitoを利用したサインアップを試してみましたが、この延長で今後は以下のこともやってみようと思っています!

  • ユーザーによるサインイン処理
  • 認証ユーザーによる他AWSサービスの呼び出し

以上、最後まで読んでいただきありがとうございました!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

%d人のブロガーが「いいね」をつけました。