【入門】Amazon Cognitoの使い方!Nest.jsでユーザー認証してみました

こんにちは!Insight Edge で Developer をしている Kobori です。 本記事では、Amazon Cognito と Nest.js を使用した認証認可について、調査で得た使い方とノウハウを紹介します! 初めて使用される人でも、本記事を見てユーザープールの設定からユーザー認証まで一通りの機能が使えるようになればと思いを込めて書きました。 ぜひ、最後までご覧いただけたらと思います。

この記事でわかること

  • Cognito のユーザープールの作り方
  • Nest.js の Guard でのユーザー認証方法

Cognito とは?

AWS のユーザー認証、ユーザーの管理ができるサービスです。

ユーザー認証に関わる下記のような充実した機能があります。 実装面においても、AWS ライブラリが用意されているので、フロントエンド、バックエンド問わず、工数を削減してセキュリティの高い機能を実装できます。

Cognito のユーザープールとは?

Cognito ユーザープールは、Cognito のサービスの一部でありユーザー管理、属性管理、認証するサービスです。 ユーザー情報(ID、パスワード、メールアドレスなど)やグループ情報の保持、トークン管理、アクセス制限などの設定ができます。 開発者は、ユーザー認証に係る機能を代替えして使用することで、よりアプリ開発に注力することが可能となります。

詳しくは、Amazon Cognito プール 参照

Cognito の主な提供機能

  • サインアップ
  • サインイン
  • パスワードリセット
  • パスワード変更
  • ユーザー属性の管理
  • JWT トークン(アクセストークン、リフレッシュトークン、リフレッシュトークン)発行
  • ユーザー固有の UUID 発行
  • 多要素認証
  • リスクの検出

Cognito の利用料金

月間 50,000 アクティブユーザまで無料で使用できます。

※ リスクの検出の機能である「高度なセキュリティ」については、追加の料金が発生します。

詳しくは、Amazon Cognito 料金 参照

Cognito ユーザープールの作成

Cognito でユーザー管理をするには、ユーザープールの作成が必要です。

ステップ別の主な設定内容

  • ステップ 1:サインイン時に使用するメールアドレス、電話番号の有無の設定
  • ステップ 2:パスワード要件や多要素認証の有無、アカウント復旧方法の設定
  • ステップ 3:サインアップ時の確認方法やユーザーに付与する属性情報の設定
  • ステップ 4:Cognito から送信する E メールの設定
  • ステップ 5:ユーザープール名、認証方式、トークンの有効期限の設定

作成時の各種設定

  • ステップ 1:サインインエクスペリエンスを設定

  • ステップ 2:セキュリティ要件を設定

Authenticator アプリケーション、SMS メッセージでの確認も可能

  • ステップ 3:サインアップエクスペリエンスを設定

デフォルトのまま次へ

  • ステップ 4:メッセージ配信を設定

一時的な設定として、Cognito での動作確認を優先するのであれば「Cognito で E メールを送信」を選択

SES で E メール送信を行う場合は、AWS SES の設定が必要です

  • ステップ 5:アプリケーションを統合

ユーザープール名の入力

「Cognito のホストされた UI を使用」にチェックと「ドメイン」の指定

アプリケーションクライアント名の入力

ユーザープールにユーザー作成

「E メールで招待を送信」を選択すると仮パスワードが送信される

※ 招待を送信しない場合、仮パスワードの設定をする

コマンド で認証確認

サインイン

下記コマンド実行でユーザー名とパスワードを使ってサインインできる

  • クライアント ID
    • クライアントアプリケーションの ID を指定 この ID は、Cognito のコンソール画面のアプリケーションの統合のタブで確認できる
  • ユーザー名
    • サインインするユーザーのユーザー名を指定します。
  • パスワード
    • サインインするユーザーのパスワードを指定します。
aws cognito-idp initiate-auth --client-id ”クライアントID” --auth-flow USER_PASSWORD_AUTH --auth-parameters USERNAME=”ユーザー名”,PASSWORD=”パスワード”

初期パスワード変更

初回サインイン時に返された「Session」を使って初期パスワードを変更する

aws cognito-idp respond-to-auth-challenge --client-id "クライアントID" --challenge-name NEW_PASSWORD_REQUIRED --challenge-responses NEW_PASSWORD="新しいパスワード",USERNAME="ユーザー名" --session "初回サインイン時に返されたsession"

ログアウト

下記コマンド実行でログアウトできる

aws cognito-idp global-sign-out --access-token "アクセストークン"

Nest.js で認証と認可

トークンを使ったアプリ側での認可

Cognito では、コマンドや API でサインインした際に 3 つのトークンが発行され返されます。 サインインでユーザー認証し、レスポンス(ユーザー情報)に含まれるアクセストークンを使用し、ユーザーの有無やトークンの有効期限を判定してアプリ側での認可をします。 リフレッシュトークンを使うと有効期限の切れたアクセストークンを再発行できるので、有効期限が切れた場合に再ログインさせるか、自動で再発行するかアプリ側で使い分けができます。

取得できるトークンの種類

  • アクセストークン
    • アクセス制限や、ユーザーの認可に使用するトークン
    • グループ情報も含まれている
    • 有効期限は、5分~1日
  • ID トークン
    • ユーザーの属性情報(例:ユーザー名、メールアドレス)が含まれるトークン
    • 有効期限は、5分~1日
  • リフレッシュトークン(更新トークン)
    • 再認証なしでアクセストークンと ID トークンの再発行を取得するためのトークン
    • 有効期限は、60分~10年

事前準備

下記をインストール

npm install @aws-sdk/client-cognito-identity-provider

下記を import

import { AdminInitiateAuthResponse } from 'aws-sdk/clients/cognitoidentityserviceprovider';

サインイン

JavaScript でのサインイン処理例

const cognito = new CognitoIdentityServiceProvider();
const data = await cognito
  .initiateAuth({
    AuthFlow: 'USER_PASSWORD_AUTH',
    ClientId: "クライアントID",
    AuthParameters: {
      USERNAME: "ログインユーザー名",
      PASSWORD: "ログインパスワード",
    },
  })
  .promise();
data.AuthenticationResult.AccessToken;

ログアウト

JavaScript でのログアウト処理例

const cognito = new CognitoIdentityServiceProvider();
await cognito
  .globalSignOut({
    AccessToken: "アクセストークン",
  })
  .promise();

Guard を使ったユーザー検証

Nest.js の Guard でユーザー検証する

アクセストークンを使ったユーザー取得をすることで、ユーザーの有無とアクセストークンの有効期限が検証できる

※ユーザーが取得できない場合やアクセストークンの有効期限切れの場合は、例外が発生します

const cognito = new CognitoIdentityServiceProvider();
this.user = await cognito
  .getUser({
    AccessToken: "アクセストークン",
  })
  .promise();

Nest.js の Guard でユーザーのグループ検証する

グループ情報は、アクセストークンに含まれるのでデコードすることでユーザーに設定したグループ情報が取得できる

下記のように取得後に対象のグループが含まれているか判定をすることで、グループの検証ができる

jsonwebtoken のインストール

npm install jsonwebtoken

アクセストークンをデコードしてグループ情報取得

import jwt from 'jsonwebtoken';

・・・

const decodedToken = jwt.decode("アクセストークン");
return decodedToken ? decodedToken["cognito:groups"] : [];

Cognito を使ってみた感想

難しそうと思っていた認証部分ですが、Cognito の導入により必要な機能を補うことができるので、開発者は、心置きなくアプリ開発に専念できると思いました。 また、ユーザー認証に必要な基本的な機能が揃っており、無料から使える点やライブラリや UI が用意されているため 手軽かつスピーディーな開発に繋がるでしょう。