はじめに
Cognito について調べる機会があったので、実際に公式のハンズオンを試してみた!って記事です。
参照:サーバーレスのウェブアプリケーションを構築する
構成は下記の通り。
Amplify と Cognito は初見なので頑張ります。。
サーバーレスのウェブアプリケーションを構築する
① 静的ウェブサイトをホスティングする
AWS Apmlifyについては コチラ
今回、静的ウェブサイトのホスティングに AWS Apmlify を使用します。
調べたところ Apmlify 君はかなり便利そう。
AWS バックエンドを数分で作成してくれる上に、色々な機能まで兼ね備えてるとか。。
実際の業務では色々な要件があるので Apmlify 君を使う機会は少ないかもですが、このような便利なサービスがあるとハンズオンも捗るのでありがたいです。
少し話が脱線しましたのでハンズオンに戻ります。
リージョン選択
我らがap-northeast-1(東京リージョン)で作業をしていきます。
Gitリポジトリを作成
CodeCommit を使用してアプリケーション コードを保存します。(GitHubでも可)
AWSマネコンから CodeCommit > リポジトリ > リポジトリを作成 の順にクリック
今回はsample-repo
というリポジトリを作成しました。
CodeCommit の HTTPS Git 認証
IAM > ユーザ > 自身のIAMユーザ > セキュリティ認証情報 タブの順の開きます。
認証情報の生成
から認証情報を生成して、csvファイルをダウンロードするか、情報を控えておきます。
Gitリポジトリにデータを追加
筆者は Windows11 なので人によっては一部手順が異なります。
そこら辺は調べてください。
URL のクローンから HTTPSのクローン を選択し、URLをコピーします。
自身のPCに適当に作業用のフォルダを作成して、パスの部分にcmd
と入力しEnterを押下
ターミナルで git clone を実行します。
git コマンドが使えない方はインストールしてください。
git clone <HTTPS URL>
コマンドを実行すると下記の様に認証を促されます。
先ほど生成した認証情報を入力してContinue
をクリック。
リポジトリが空なので warning が出力されますが気にしない。
C:\Users\onish\OneDrive\デスクトップ\AWS\Cognito>git clone https://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/sample-repo
Cloning into 'sample-repo'...
warning: You appear to have cloned an empty repository.
ここにデータを追加していきます。
# ディレクトリをリポジトリに変更
cd sample-repo
# ソースコードのダウンロード(aws コマンドが実行できるようにしておいてください)
aws s3 cp s3://wildrydes-us-east-1/WebApplication/1_StaticWebHosting/website ./ --recursive
# add
git add .
# commit
git commit -m 'new'
# push
git push
push 出来たので CodeCommit のリポジトリを確認
ソースコードが追加されているので問題なし!
Amplifyコンソールでウェブホスティングを有効にする
やっと Amplify 君の出番。 お待たせ・・・。
使用を開始する
から右側の Amplify ホスティングを選択
CodeCommit を選択後、先ほどのリポジトリとブランチを選択し、次へ を押下
下図のチェックボックスにチェックをつけてデプロイします。
デプロイには数分かかります。
この間に次の作業の準備をします。
サイトを変更する
Amplify コンソールは、接続されたリポジトリへの変更を検出すると、アプリを再構築して再デプロイします。
index.html を少し修正して、実際に自動デプロイされるかを確認します。
修正内容はなんでもいいです。。
<!-- 修正前 (7行目) -->
<title>Wild Rydes</title>
<!-- 修正後 -->
<title>Wild Rydes - hands on!!</title>
修正後に再度 push します。
ただ、その前にAmplify によってウェブサイトがデプロイされていることを確認してから実施します。
デプロイまでチェックが付いていると、画像の左下辺りにURLが表示されておりますのでそちらをクリック
確認できたので push します。
# add
git add .
# commit
git commit -m 'update title'
# push
git push
デプロイ完了後に確認するとタイトルが変わってる!便利ですね。
② ユーザを管理する
検証済み ID の作成
Cognito でメール送信機能を使用するので、SESに検証済みのIDを作成しておきます。
Amazon SES > 設定: 検証済み ID > ID の作成 の順にクリック
IDタイプはEメールを選択し、自身が使用しているメールアドレスを入力。Gメールとかで OK です。
example が誤字ってるなんて言わないでくださいね。。
するとメールが送られてくるので URL をクリックして認証完了。
Cognito ユーザープールの作成
やっと Cognito です。
記事書きながらハンズオンしているので、ここまでで1時間弱掛かっており心が折れそうです(笑)
ユーザーのアカウントを管理するための Amazon Cognito ユーザープールを作成します。
顧客が新規ユーザーとして登録し、電子メール アドレスを確認し、サイトにサインインできるページを展開します。
Cognito は、ユーザーを認証するために 2 つの異なるメカニズムを提供します。
① Cognito ユーザー プール
Cognito ユーザー プール使用して、アプリケーションにサインアップおよびサインイン機能を追加可能
②Cognito ID プール
Facebook、Twitter、Amazon などのソーシャル ID プロバイダー、SAML ID ソリューション、または独自の ID システムを使用してユーザーを認証可能
Emailアドレスでサインインする場合は、Cognito ユーザプール、
Google や Apple のアカウントでサインインするのが Cognito ID プール ってイメージかな?
Cognito ユーザプールを作成し、アプリをユーザプールに統合する
Amazon Cognito > ユーザープール > ユーザープールを作成 の順にクリック
ここら辺の作業は公式ハンズオンと異なります。(コンソールのバージョンが異なるため)
・認証プロバイダー
ユーザ名
にチェックを付けて「次へ」を押下
・セキュリティ要件を設定
多要素認証 はMFAなし
を選択し「次へ」を押下
・サインアップエクスペリエンスを設定
デフォルトのまま「次へ」を押下
・メッセージ配信を設定
E メールプロバイダーは SES を選択し、先ほど作成した検証済み ID を選択して「次へ」を押下
・アプリケーションを統合
ユーザープール名:sample-user-pool
アプリケーションクライアント名:WildRydesWebApp
それ以外はデフォルトで「次へ」を押下しユーザプールを作成します。
Webサイト構成を更新する
sample-repo/js/config.js ファイルに Cognito の情報を記述していきます。
必要な情報は以下の2つ。
① userPoolId
作成したらすぐに確認できる個所にあります。
② userPoolClientId
作成したユーザプールを選択後、アプリケーションの結合タグを選択
一番下までスクロールするとあります。
window._config = {
cognito: {
userPoolId: 'ap-northeast-1_ZuNYdCNEU', // e.g. us-east-2_uXboG5pAb
userPoolClientId: '3e2a1i2ak2i7ph8vc8f1voceb4', // e.g. 25ddkmj4v6hfsfvruhpfi7n4hv
region: 'ap-northeast-1' // e.g. us-east-2
},
api: {
invokeUrl: '' // e.g. https://rc7nyt4tql.execute-api.us-west-2.amazonaws.com/prod',
}
};
修正後は push します。
# add
git add .
# commit
git commit -m "new config"
# push
git push
検証
Web サイトのドメインの下にある/register.html
にアクセスするか、Giddy Up!を押下します。
自身が所有しているEメールアドレスがある場合はそちらを入力してユーザ登録を行います。
(検証済みIDで使用したアドレスを使用する)
LET'S RYDE を押下すると、認証ページに飛ばされます。
6桁の認証コードが送信されているので、メールアドレスと認証コードを入力すると下記メッセージのポップアップが表示されます。
Verification successful. You will now be redirected to the login page
認証に成功したのでログインページにリダイレクトされますので、
メールアドレスとパスワード入力してログインを行います。
Cognito のユーザプールを確認すると、ユーザが登録されております。
そもそもですが、Cognito は 認証・認可の機能を提供するサービスなので、
認証に成功するとトークンが返ってくるって感じですね。
このトークンを利用して API Gateway で API を呼び出すことが出来るって感じかー!と少し理解してきた。
③ サーバレスバックエンドを構築する
続いてはバックエンドの構築。
今回は DynamoDB と Lambda を構築します。
DynamoDB テーブルを作成する
DynamoDB > テーブル > テーブルの作成 の順にクリックします。
下記のパラメータを設定し、残りはデフォルトでテーブルを作成します。
パラメータ | 設定値 |
---|---|
テーブル名 | Rides |
パーティションキー | RideId |
作成後に、テーブルの概要から ARN を控えておきます。(後で使います)
Lambda 関数の IAM ロールを作成する
Lambda 関数にアタッチする IAM ロールを作成します。
IAM > ロール > ロールを作成 の順にクリックします。
・信頼されたエンティティを選択
下記のパラメータを設定し「次へ」を押下
パラメータ | 設定値 |
---|---|
信頼されたエンティティタイプ | AWS のサービス |
ユースケース | Lambda |
・信頼されたエンティティを選択
AWSLambdaBasicExecutionRole
にチェックを付けて「次へ」を押下
・名前、確認、および作成
下記のパラメータを設定し「作成」を押下
パラメータ | 設定値 |
---|---|
ロール名 | WildRydesLambda |
IAM ロールにインラインポリシーを追加します。
IAM > ロール > WildRydesLambda の順にクリックします。
許可ポリシーの右側に「許可を追加」というメニューがあるので、そちらからインラインポリシーを作成
サービス検索にDynamoDB
と入力しDynamoDB を選択。
検索欄にPutItem
と入力し、PutItemにチェックを付ける。
「次へ」を押下
下記のパラメータを設定し「ポリシーを作成」を押下
パラメータ | 設定値 |
---|---|
ポリシー名 | DynamoDBWriteAccess |
これで、Cloudwatch Logs にログを出力するポリシーと、DynamoDB へ書き込むポリシーが付与された、Lambda 用のIAMロールが完成です。
Lambda 関数を作成する
Lambda > 関数 > 関数の作成 の順にクリックします。
下記のパラメータを設定し「関数の作成」を押下
パラメータ | 設定値 |
---|---|
関数名 | RequestUnicorn |
ランタイム | Node.js 16.x |
デフォルトの実行ロールの変更 | WildRydesLambda |
作成したLambda関数のコードを修正します。
index.js の内容を下記に修正し、「Deploy」を押下
const randomBytes = require('crypto').randomBytes;
const AWS = require('aws-sdk');
const ddb = new AWS.DynamoDB.DocumentClient();
const fleet = [
{
Name: 'Bucephalus',
Color: 'Golden',
Gender: 'Male',
},
{
Name: 'Shadowfax',
Color: 'White',
Gender: 'Male',
},
{
Name: 'Rocinante',
Color: 'Yellow',
Gender: 'Female',
},
];
exports.handler = (event, context, callback) => {
if (!event.requestContext.authorizer) {
errorResponse('Authorization not configured', context.awsRequestId, callback);
return;
}
const rideId = toUrlString(randomBytes(16));
console.log('Received event (', rideId, '): ', event);
// Because we're using a Cognito User Pools authorizer, all of the claims
// included in the authentication token are provided in the request context.
// This includes the username as well as other attributes.
const username = event.requestContext.authorizer.claims['cognito:username'];
// The body field of the event in a proxy integration is a raw string.
// In order to extract meaningful values, we need to first parse this string
// into an object. A more robust implementation might inspect the Content-Type
// header first and use a different parsing strategy based on that value.
const requestBody = JSON.parse(event.body);
const pickupLocation = requestBody.PickupLocation;
const unicorn = findUnicorn(pickupLocation);
recordRide(rideId, username, unicorn).then(() => {
// You can use the callback function to provide a return value from your Node.js
// Lambda functions. The first parameter is used for failed invocations. The
// second parameter specifies the result data of the invocation.
// Because this Lambda function is called by an API Gateway proxy integration
// the result object must use the following structure.
callback(null, {
statusCode: 201,
body: JSON.stringify({
RideId: rideId,
Unicorn: unicorn,
Eta: '30 seconds',
Rider: username,
}),
headers: {
'Access-Control-Allow-Origin': '*',
},
});
}).catch((err) => {
console.error(err);
// If there is an error during processing, catch it and return
// from the Lambda function successfully. Specify a 500 HTTP status
// code and provide an error message in the body. This will provide a
// more meaningful error response to the end client.
errorResponse(err.message, context.awsRequestId, callback)
});
};
// This is where you would implement logic to find the optimal unicorn for
// this ride (possibly invoking another Lambda function as a microservice.)
// For simplicity, we'll just pick a unicorn at random.
function findUnicorn(pickupLocation) {
console.log('Finding unicorn for ', pickupLocation.Latitude, ', ', pickupLocation.Longitude);
return fleet[Math.floor(Math.random() * fleet.length)];
}
function recordRide(rideId, username, unicorn) {
return ddb.put({
TableName: 'Rides',
Item: {
RideId: rideId,
User: username,
Unicorn: unicorn,
RequestTime: new Date().toISOString(),
},
}).promise();
}
function toUrlString(buffer) {
return buffer.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=/g, '');
}
function errorResponse(errorMessage, awsRequestId, callback) {
callback(null, {
statusCode: 500,
body: JSON.stringify({
Error: errorMessage,
Reference: awsRequestId,
}),
headers: {
'Access-Control-Allow-Origin': '*',
},
});
}
実装を検証する
Deploy
の左にある、Test
をクリックします。
イベント名にTestRequestEvent
と入力し、イベントJsonには下記をコピペし「保存」を押下
{
"path": "/ride",
"httpMethod": "POST",
"headers": {
"Accept": "*/*",
"Authorization": "eyJraWQiOiJLTzRVMWZs",
"content-type": "application/json; charset=UTF-8"
},
"queryStringParameters": null,
"pathParameters": null,
"requestContext": {
"authorizer": {
"claims": {
"cognito:username": "the_username"
}
}
},
"body": "{\"PickupLocation\":{\"Latitude\":47.6174755835663,\"Longitude\":-122.28837066650185}}"
}
その後、Test を実行し下記のレスポンスが出力されれば OK です。
{
"statusCode": 201,
"body": "{\"RideId\":\"ctgrS1HMmBFsrBUkiCgNGQ\",\"Unicorn\":{\"Name\":\"Rocinante\",\"Color\":\"Yellow\",\"Gender\":\"Female\"},\"Eta\":\"30 seconds\",\"Rider\":\"the_username\"}",
"headers": {
"Access-Control-Allow-Origin": "*"
}
}
④ RESTful API をデプロイする
新しい REST API を作成する
API Gateway > API > REST API > 構築 の順にクリックします。
下記のパラメータを設定し「APIの作成」を押下
パラメータ | 設定値 |
---|---|
プロトコルを選択する | REST |
新しい API の作成 | 新しい API |
API 名 | WildRydes |
エンドポイントタイプ | エッジ最適化 |
オーソライザーを作成する
左側のナビゲーションでオーソライザーを選択後、下記のパラメータを設定し「作成」を押下
パラメータ | 設定値 |
---|---|
名前 | CognitoAuth |
タイプ | Cognito |
ユーザープール | sample-user-pool |
トークンのソース | Authorization |
新しいリソースとメソッドを作成する
左側のナビゲーションでリソースを選択後、
アクションのドロップダウンからリソースの作成
を選択する。
下記のパラメータを設定し「リソースの作成」を押下
パラメータ | 設定値 |
---|---|
リソース名 | ride(リソース パスが[ride]に設定されていることを確認します) |
API Gateway CORS を有効にする | 有効 |
作成したride
リソースを選択した状態で、アクションからメソッドの作成
を選択する。
プルダウンからPOST
を選択し、チェックマークをクリック
下記のパラメータを設定し「保存」を押下
パラメータ | 設定値 |
---|---|
統合タイプ | Lambda関数 |
Lambda プロキシ統合の使用 | 有効 |
Lambda 関数 | RequestUnicorn |
認可:なし
の設定を先ほど作成したCognitoAuth
に変更します。
API をデプロイする
下記のパラメータを設定し「デプロイ」を押下
パラメータ | 設定値 |
---|---|
デプロイされるステージ | 新しいステージ |
ステージ名 | prod |
左ナビゲーションでステージを選択
prod をクリックするとprod ステージエディター
が表示されるので、こちらの URL をメモします。
Web サイト構成を更新する
config.js のinvokeUrl
に先ほどメモした URL を記述します。
window._config = {
cognito: {
userPoolId: 'ap-northeast-1_ZuNYdCNEU', // e.g. us-east-2_uXboG5pAb
userPoolClientId: '3e2a1i2ak2i7ph8vc8f1voceb4', // e.g. 25ddkmj4v6hfsfvruhpfi7n4hv
region: 'ap-northeast-1' // e.g. us-east-2
},
api: {
invokeUrl: 'https://dgo212off5.execute-api.ap-northeast-1.amazonaws.com/prod' // e.g. https://rc7nyt4tql.execute-api.us-west-2.amazonaws.com/prod',
}
};
修正後は push します。
# add
git add .
# commit
git commit -m "update config"
# push
git push
動作確認
ArcGISのバージョンが上がっているため、ride.htmlを修正する必要があるらしい。
2か所修正する必要があるので、下記の通り修正してください。(4.3 ⇒ 4.6 になっただけ)
修正前:https://js.arcgis.com/4.3
修正後:https://js.arcgis.com/4.6
修正後はいつもの git push まで実施します。(割愛)
Web サイトのドメインの下にある/ride.htmlにアクセスします。
アクセス後に下記の画面が表示された場合は、ArcGIS アカウント作成ページから作成してください。
適当にどこかクリックした後、
右上のRequest Unicorn
をクリックするとユニコーンが飛んできます(笑)
ユニコーン到着後に DynamoDB を確認するとレコードが追加されてます。
⑤ リソースを終了する
作成したリソースを削除していきます。
削除は難しくないので、コチラを参考にして下さい。。
削除しないと料金がかかるので気を付けてください。
また、使用しなければ料金はかかりませんが、CodeCommitの削除も忘れずに。
感想
結局 Cognito って何ぞや!という話ですが、
Cognitoは認証・認可機能を提供するAWSサービスという理解でいいですかね。
API Gateway で認可を使うのであれば、紐づけ可能な Cognito はものすごく便利ですね!
認証・認可については コチラ を参考にしてください。
さいごに
無事ハンズオンを終えることが出来ました。
記事にするのはかなり疲れましたがいざ完走するといい気分です(笑)
やっぱりAWSは便利ですし初心者でも使いやすいので本当にいいサービスですね!
定期的にAWSの記事を更新していくので他の記事もぜひご覧ください。