サーバレスWebアプリケーションを手軽に構築

これまでの問題

  • WebアプリでCognito Identity Pool + Cognito User Poolを使う場合、複数のJSを読み込む必要がある
  • API GatewayのIAM認証をしている場合、SignV4での署名が複雑になる
  • API Gatewayで生成されるSDKを使ってリクエストをする場合、複数のAPIを使用することがやりづらい

特に署名についてはあまりドキュメントも多くなく、試行錯誤されている方を多く見かけます。

AWS Amplifyが出ました!!

そんな悩めるフロントエンドエンジニアに待望のライブラリが発表されました。

AWS Amplifyの機能

AWS Amplifyは下記の機能を備えているようです。

  • Authentication
    • Cognito Identity Pool と Cognito User Poolを使用したユーザ認証、ユーザサインアップ等のAPIを提供します
  • Analytics
    • Amazon Pinpointを使用したアクセス解析を行う機能を提供します。Pinpointの設定を行うだけでセッションデータの集計は可能。
    • カスタムイベントやユーザ属性等の送信を簡単に行うAPIが提供されます。
  • API
    • API GatewayへのGET、POST、PUT、DELETE、HEADリクエストを簡単に行うAPIを提供します
    • AWS_IAM認証を行っているAPIに対して、Cognitoにより認証したクレデンシャルを使用し、AWS Signature Version 4による署名を自動で行います。
  • Storage
    • S3へのオブジェクトのPUT、GETを簡単に行うAPIを提供します。
    • Private,PublicのACLを容易に設定できます。
  • Cache
    • キーバリュー構造のデータを容易にLRUキャッシュとして管理することができるAPIを提供します。
    • Webで使用する場合、LocalStorageにキャッシュデータが保存されます。
  • Utilities
    • 多言語対応を行うためのl18nモジュール
    • ロガーモジュール
    • moduleとcomponent間でイベントをpub/subにより共有できるhubモジュール

使ってみた

今回使うもの

Cognito + API Gateway(AWS_IAM認証)をWebアプリケーションとして実装したいと思います。
そのため、下記のモジュールを使用します。

  • Authentication
  • API

環境

  • Windows10
  • Visual Studio Code
  • JavaScript(TypeScript)

インストール

今回は、ブラウザ上で実行するWebアプリケーションを構築するため、
必要なファイルは aws-amplify.js のみとなります。
github上からダウンロードしてくるか、下記の通りnpmにてインストールし、該当ファイルを取得してください。

npm install aws-amplify --save

ファイル構成

/
├─ index.html
├─ main.ts
├─ package.json
├─ require.js
├─ style.css
├─ tsconfig.json
└─ src
  └─ aws-amplify.js

index.html

index.html内では、TSファイルの読み込みを行うため、require.jsにてロードさせる

<script data-main="./src/main.js" src="require.js"></script>

main.ts

今回の対象スクリプトはこのファイルに記述する
ファイルの先頭でAWS Amplifyの読み込みを行う

main.ts
import Amplify,{ Auth,API } from 'aws-amplify';

Cognito Identity Pool / Cognito User Poolの作成

Cognito Identity Pool と Cognito User Poolを作成します。
設定内容を合わせるため、要点を記載します。

Cognito User Pool

サインインの方法として今回はEメールアドレスを使用する形で設定します。

image.png

アプリクライアントの作成をする部分では、クライアントシークレットの作成を行わないようチェックを外してください

image.png

その他は基本的にデフォルトの内容で作成します。

Cognito Identity Pool

Cognito Identity Poolについては、認証プロバイダーに先程作成したUser Poolの情報を入力し、認証されていないIDに対するアクセスも許可するように設定を行ってください。

image.png

また、今回は認証済ユーザのみAPI GatewayへのAPIを実行できる権限を持たせるため、
認証済みユーザのIAMポリシーを下記のように設定します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "mobileanalytics:PutEvents",
                "cognito-sync:*",
                "cognito-identity:*",
                "execute-api:Invoke"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

API Gatewayの作成

今回は下記の内容で、設定を行います。

作成するリソース

  • GET /guest
    • リクエストタイプ Mock
    • 認証なし
  • GET /member
    • リクエストタイプ Mock
    • AWS_IAM認証あり

統合レスポンスとしてそれぞれ下記の通りの設定をしました

guest
{
    "Message": "Hello! Guest"
}
member
{
    "Message": "Hello! Member"
}

Webからリクエストを行えるようにするため、CORSを有効に設定します。

設定が完了したら下記のリソースになります。
image.png

ステージ名をv1としてデプロイしました

Amplify.configure

Amplify.configure()によって、各種設定情報を与えます。
今回使用するのはCognitoとAPI Gatewayなので、AuthモジュールとAPIモジュールの設定を行います。

main.ts
Amplify.configure({
  Auth: {
      identityPoolId: 'ap-northeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', //REQUIRED - Amazon Cognito Identity Pool ID
      region: 'ap-northeast-1', // REQUIRED - Amazon Cognito Region
      userPoolId: 'ap-northeast-1_xxxxxxxxx', //OPTIONAL - Amazon Cognito User Pool ID
      userPoolWebClientId: 'xxxxxxxxxxxxxxxxxxxxxxxxx', //OPTIONAL - Amazon Cognito Web Client ID
  },
  API: {
    endpoints: [
      {
          name: "AmplifyTest",
          endpoint: "https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/v1"
      }
    ]
  }
});

Authモジュール

サインアップ

今回の設定では、メールアドレスをユーザー登録時の必須属性としています。
その為、メールアドレスとパスワードにてサインアップを行うことができます。

image.png

適当なフォームを作成し、入力を受け付けます。

main.ts
Auth.signUp(email, password)
.then((data) => { 
  alert('登録メールアドレスに検証コードを送信しました。');
})
.catch((err) => {
  console.log(err);
  alert('エラーが発生しました');
  return;
});

サインアップを行うためのAPIは、
Auth.signUp(username, password, email, phone_number)
となり、今回はusernameの代わりにemailを使用しているため、
email, passwordの2つを渡すことでサインアップが行われます。

メールアドレスが必須となっているため、MFAによりメールアドレス宛に
検証コードが送信されます。
送信された検証コードを受け付けるフォームを表示します。

image.png

main.ts
Auth.confirmSignUp(email, code)
.then((data) => { 
  alert('ユーザ登録が完了しました。');
})
.catch((err) => {
  console.log(err);
  alert('検証に失敗しました');
  return;
});

検証のためのAPIは
Auth.confirmSignUp(username , code)
となります。

とても明快で簡潔に処理が記述できました。
ユーザー登録が完了しました。
AWSコンソールのCognito User Poolからユーザとグループの参照を行うと、
先程登録したユーザが存在することがわかります。

image.png

サインイン

続いてサインインです。
メールアドレスとパスワードを入力してサインインをするフォームを作成します。

image.png

main.ts
Auth.signIn(email, password)
.then((data) => { 
  alert('サインインに成功しました');
})
.catch((err) => {
  console.log(err);
  alert('サインインに失敗しました');
  return;
});

サインインを行うAPIは
Auth.signIn(username , password)
です。
サインインに成功するとCognitoUserの情報が取得できます。

サインアウト

認証の最後にサインアウトを実装します。

main.ts
Auth.signOut()
.then((data) => { 
  alert('サインアウトしました');
})
.catch((err) => {
  console.log(err);
  alert('サインアウトに失敗しました');
  return;
});

ここまでで、Cognito User Poolを使用したサインアップ、サインイン、サインアウトがとても簡単に実装できました。

APIモジュール

確認

今回の実装では未認証時はAPI GatewayのInvoke権限がないため、AWS_IAM認証が必要な /member へはコールができない想定です。
また、認証を実装していない /guest へは認証の有無に関わらずAPIコールが行える想定となります。

まずはAPI Gatewayの設定が正しく行われているか確認をするため、ブラウザから直接エンドポイントを叩いてみます。

/v1/guest:
image.png

/v1/member:
image.png

上記の通り、/guest は正常にレスポンスが返ってきており、/member は署名がされていないリクエストのため、リクエストは拒否されます。

/guest へのリクエスト

Authモジュールにて作成したページで、サインアウトした状態で、
下記のリクエストを実行します。

main.ts
let apiName = 'AmplifyTest';
let guest_path = '/guest'; 

API.get(apiName, guest_path)
.then(response => {
  console.log(response);
})
.catch(err => {
  console.log(err);
});

結果はこちら
image.png

正常にAPIからの応答を表示できています。

未認証時の /member へのリクエスト

続いて未認証時の /member へリクエストを行います。
先程とほぼ変わらず下記のコードとなります。

main.ts
let apiName = 'AmplifyTest';
let member_path = '/guest'; 

API.get(apiName, member_path)
.then(response => {
  console.log(response);
})
.catch(err => {
  console.log(err);
});

結果はこちら
image.png

403ステータスが返されてエラーとなっています。

認証済みの /member へのリクエスト

それではサインインを行った状態で再度 /member へのリクエストを同じコードで実行してみましょう。

結果は下記の通り
image.png

見事にHello! Memberと返ってきてAWS_IAM認証を必要とするAPIへのコールに成功しています。

このように、AWS Amplifyを使用すると、Sign V4の署名リクエストを意識することなく簡単に行うことができます。

まとめ

  • AWS Amplifyを使うことでCognitoを使用したサーバレスWebアプリケーションを簡単に実装することができる
  • AWS_IAM認証を行うことが簡単!
  • 今回実装したもの以外にも多くの機能が使用可能

また、APIも今回紹介したもの以外に数多く存在します。
https://aws.github.io/aws-amplify/packages/aws-amplify-react-native/docs/index.html

サンプル

今回実装したアプリのソースコードを下記に置いておきました。
https://github.com/cwknakayama/amplify-test