Amplify Vue.jsアプリのフロントエンドからAWSリソースに直接リクエストしてみた
今回はAmplifyを使用して、Vue.jsアプリのフロントエンドから、AWSリソースに直接リクエストする方法を紹介します。
きっかけ
AWS SDK for JavaScriptで、ブラウザからリクエストできることは知っていましたが、やり方が分からなかったので、すべてバックエンドからリクエストをしていました。
具体的には、Amplify CLIでAPI GatewayとLambda関数を作成し、Lambda関数からSDKを使用してリクエストしていました。
複数の関数の実装が大変
特定の処理ごとに複数のLambda関数を作成していたため、実装が大変な部分がありました。
- Amplify CLIでAPI GatewayのパスとLambda関数追加
- 関数コードのコピペの繰り返しも必要
- Lambda関数ごとに作成されるIAMロールに対するIAMポリシーのアタッチ
関数が増えるほど手間も増え、作業ミスも起きやすくなってしまうので、何とかしたいと思っていました。
単純にフロントエンドからのリクエスト方法を知りたい
これもきっかけの1つです。
今までバックエンドでの実装しかやったことがなかったので、フロントエンドからのリクエスト方法も知りたいと思っていました。
ブラウザスクリプトの使用開始などを参考にすれば、単純なJavaScriptでのリクエストはできそうでしたが、AmplifyでVue.jsを使ったアプリだったので、Amplifyの認証機能を使えないかと考えました。
実装の概要
- Amplify CLIで
add auth
を実行し、認証機能を追加 -
add auth
により、CognitoユーザープールとIDプールができるので、これを利用 - フロントエンドからIDプールに紐づくIAMロールの認証情報を取得
- IAMロールの認証情報と権限でAWSリソースにリクエスト
Amplify CLIで認証機能を追加
amplify add auth
で認証機能を追加すれば、自動的にCognitoユーザープールやIDプールが作成されるので、まずはAmplify CLIでadd auth
します。
僕の場合、設定はすべてデフォルトのままとしています。
amplify add auth
ユーザーの作成とIAMロールの編集
amplify add auth
により作成されたCognitoユーザープールにユーザーを作成します。
こちらはコンソール上で行いました。
続いて、IDプールと紐づくIAMロールを編集します。
まずはIDプールを見てみましょう。
IDプールの編集画面で、「認証されたロール 」と「認証されていないロール」のIAMロール名を確認できます。
認証されたロールは、ログインしたユーザー用に割り当てられます。
認証されていないロールは、ログインしていないユーザー用に割り当てられますが、デフォルトでは無効になっているので、必要な場合は有効にします。
ユースケースとしては、ログインした有料会員はS3バケットへPUTを許可して画像のアップロードはできるけど、ログインしていない無料会員にはS3バケットのRead Only権限で画像の表示しかできないみたいなかんじだとおもっています。
IAMコンソールで、認証されたロールに必要なポリシーをアタッチします。
今回はAmazonEC2FullAccess
をアタッチしました。
これでログインしたユーザーにはこのロールにアタッチしたEC2へのフルアクセス権限が与えられます。
フロントエンドからIDプールに紐づくIAMロールの認証情報を取得
ここが一番わからなかったのですが、サポートに助けて頂き、以下のAmplifyドキュメントを紹介して頂きました。
Working with AWS service objects
import Route53 from 'aws-sdk/clients/route53';
Auth.currentCredentials()
.then(credentials => {
const route53 = new Route53({
apiVersion: '2013-04-01',
credentials: Auth.essentialCredentials(credentials)
});
// more code working with route53 object
// route53.changeResourceRecordSets();
})
IAMロールの認証情報を取得するためには、Auth.currentCredentials()
を呼び出す必要がありました。このメソッドの戻り値として、credentials
にIAMロールの一時認証情報であるアクセスキーなどが入っていました。
IAMロールの認証情報と権限でAWSリソースにリクエスト
ここまでくれば、リクエスト方法はバックエンドとほぼ同じなので、SDKに認証情報を渡してリクエストすればOKです。サンプルとして、以下のような方法を試してみました。
import AWS from "aws-sdk";
import { Auth } from "aws-amplify";
~諸々の処理は割愛~
async test() {
const credentials = await this.getCredentials();
AWS.config.region = "ap-northeast-1";
AWS.config.apiVersions = {
ec2: "2016-11-15"
};
const ec2 = new AWS.EC2(credentials);
const params = {
Filters: [
{
Name: "is-public",
Values: [
"false"
/* more items */
]
}
/* more items */
]
};
const result = await ec2.describeImages(params).promise();
console.log(result);
},
getCredentials() {
return new Promise((resolve, reject) => {
Auth.currentCredentials()
.then(credentials => {
resolve(credentials);
})
.catch(err => {
reject(err);
});
});
}
Vue.jsなのでテンプレート側で適当に以下のようなボタンを作って試しました。
<button @click="test()">テスト</button>
ボタンをクリックすると、プライベートAMI一覧を取得するというシンプルなリクエストをしています。ただし、最初にgetCredentials()
を呼び出し、この中でAuth.currentCredentials()
で認証情報を取得して返しています。
その後、
const ec2 = new AWS.EC2(credentials);
で、EC2のクラスを呼び出す際に取得した認証情報を渡しています。
これにより、describeImages()
などのメソッドもIAMロールの権限で呼び出すことができます。
あとの実装は要件しだいで
認証情報を取得してどこに保存するのか、ポリシーはどうするのかなどは要件次第で変わってくると思うので、そのへんはご自由にというかんじです。
とりあえず僕は、vuexを使用して、ページ読み込み時に認証情報をstateに保持し、ログアウト時に破棄するようにしています。これが安全なのかどうか、正攻法なのかどうかはわかりませんが、より良い方法があったら少しずつ改善していこうと思っています。
気になること
フロントから直接リクエストするので、ブラウザの開発者ツールを使うと、アカウントIDとかリソースIDが見えてしまいます。もしかしたら隠すこともできるかもしれませんが、そのへんはまだよくわかりません。
安全を考慮すると、やはりバックエンドで処理を実装するというのも捨てることはできないので、どちらか一択という考え方ではないと思っています。
まとめ
今回はAmplifyのVue.jsアプリでフロントエンドからAWSリソースに直接リクエストする方法を紹介しました。改めて実装手順をまとめておきます。
- Amplify CLIで
add auth
を実行し、認証機能を追加 -
add auth
により、CognitoユーザープールとIDプールができるので、これを利用 - フロントエンドからIDプールに紐づくIAMロールの認証情報を取得
- IAMロールの認証情報と権限でAWSリソースにリクエスト
Amplify CLIで認証周りの基礎は自動的に作成してくれるので、あとはIAMロールへのポリシーアタッチとアプリ側のコードでの認証情報取得処理だけで済みます。
まだまだ改善の余地はありそうですが、ひとまず今回は以上です。
参考になれば幸いです。
Discussion