この記事は ハンズラボ Advent Calendar 2021 22日目の記事です。
バックエンドさんがAWS cognitoの環境構築をしてくれたなら、
我々フロントエンドは10分でログイン/ログアウトを実装できます。そう、Amplifyならね。
というわけで、この記事では既存AWSリソースがある場合の
・ログイン/ログアウト
・REST API呼び出し
・S3読み書き
を、爆速実装する方法を紹介します。
Amplify使い始めて2年が経とうとしていますが、社内にあまり浸透していないので
「これを読めばだいたい使いこなせるようになるぞ!」という記事を目指して書けたらと思います。
簡単さも伝わればいいな。
良い子のみんなは気づいたと思いますがVue.jsでやります(が、Vue.js以外でも応用できると思います)。
- この記事を読む必要がない人
- 公式ドキュメントを読んで自力で実装できる人
VueCLIを使うよ
言い忘れましたがVue.jsをそこそこ触っている人に向けて書きますので、細かい説明は省きます。
VueCLIで作成したプロジェクトでAmplifyを使います。
バージョンはVueは2系、CLIは3〜4で動くと思います。
$ vue create sample-project
ログイン・ログアウトを実装するよ
カンタン3ステップ
- VueプロジェクトにAmplifyをインストール
- 設定
- ログイン・ログアウト処理のコーディング
1. VueプロジェクトにAmplifyをインストール
- プロジェクトフォルダで以下コマンドを実行
$ npm install aws-amplify
または
$ yarn add aws-amplify
2. 設定
- envファイルに既存cognitoリソースの情報を記載
VUE_APP_REGION='ap-northeast-1' // 東京リージョンの場合
VUE_APP_USER_POOL_ID='ユーザープールID'
VUE_APP_USER_POOL_WEB_CLIENT_ID='ウェブクライアントID'
VUE_APP_IDENTITY_POOL_ID='フェデレーションアイデンティティID'
フェデレーションアイデンティティIDはIDプールを使用している場合のみ必要です。
(参考までに)既存cognitoリソースの情報を調べる方法
-
/src
下のどこかに構成ファイルを配置
$ touch aws-exports.js
import Amplify from 'aws-amplify'
Amplify.configure({
Auth: {
// リージョン
region: process.env.VUE_APP_REGION,
// ユーザープールのID
userPoolId: process.env.VUE_APP_USER_POOL_ID,
// ユーザープールのウェブクライアントID
userPoolWebClientId: process.env.VUE_APP_USER_POOL_WEB_CLIENT_ID,
// フェデレーションアイデンティティのID
identityPoolId: process.env.VUE_APP_IDENTITY_POOL_ID,
// AWSリソースにアクセスする前にユーザー認証を行うかどうか
mandatorySignIn: true
},
})
- main.jsで構成ファイルをインポート&ロード
import Amplify, { Auth } from 'aws-amplify';
import awsconfig from './aws-exports';
Amplify.configure(awsconfig);
3. ログイン・ログアウト処理のコーディング
公式ドキュメント:Sign up, Sign in & Sign out
3-1. ログイン
- サンプルで簡単なログイン画面を作ります
<template>
<div>
<input v-model="id" placeholder="ID"/>
<input v-model="password" placeholder="PASSWORD"/>
<button @click="login()">login</button>
</div>
</template>
<script>
export default {
data() {
return {
id: "",
password: "",
};
},
methods: {
login() {}
},
}
</script>
- Amplifyの認証ライブラリをインポートします
<script>
import { Auth } from 'aws-amplify';
export default {...
- ログインボタンクリック時のイベントでsignIn関数を使います
第一引数にユーザーid、第二引数にパスワードを渡してログインします。
ユーザーidは、メールアドレスだったりユーザーネームだったりcognito側の設定に合わせます。
<script>
...
methods: {
async login() {
try {
const user = await Auth.signIn(this.id, this.password)
console.log(user);
} catch (error) {
console.log('error signing in', error);
}
}
}
コンソールログにユーザー情報が表示されていればログイン完了です!(ちなみにレスポンスとしてユーザー情報に何を載せるかはcognito側の設定で変えられます)
エラーだった場合、エラーコードを確認すると原因がわかります。
<script>
...
} catch (error) {
if(error.code === 'NotAuthorizedException') {
alert('パスワードが誤っています');
} else if(error.code === 'UserNotFoundException') {
alert('ユーザーIDは存在しません');
} else {
alert('ログインに失敗しました')
}
}
エラーコード一覧が公式ドキュメントで見つかりませんでした…orz
こちらの記事が参考になると思います:Cognito のエラーコードまとめ
3-2. ログアウト
- ログアウトボタンクリック時のイベントでsignOut関数を使います
Auth.signOut()
3-3. (おまけ)よく使う認証系関数
currentAuthenticatedUser
Auth.currentAuthenticatedUser()
認証情報を返します。
認証されていない・セッション期限切れの場合、エラーを返します。
→ログイン画面で認証済みかどうか確認し、認証済みだったらログイン処理を飛ばしてトップ画面にリダイレクトさせる、ということができる
changePassword
Auth.changePassword(user, 'oldPassword', 'newPassword');
パスワード変更用の関数です。
第一引数のuser
は、前述したcurrentAuthenticatedUser
関数で取得した認証情報になります。
他にもカスタムオーソライザーを使った二段階認証なども簡単に実装できるぞ!
https://docs.amplify.aws/lib/auth/switch-auth/q/platform/js/#customauth-flow
REST API呼び出しを実装するよ
公式ドキュメント:API(REST)
前項で認証したcognitoユーザーが呼び出すという前提でご紹介します。
- 設定
- API呼び出し処理のコーディング
1. 設定
- さっき作ったaws-exports.jsを編集します
import Amplify from 'aws-amplify'
Amplify.configure({
Auth: {
...
},
API: {
endpoints: [
{
name: process.env.VUE_APP_API_NAME,
endpoint: process.env.VUE_APP_API_ENDPOINT
}
]
}
})
API名とエンドポイントのURLをenvファイルに追加しておいてください。
API名はなんでもいいです。あとでAPI実行時に使います。
2. API呼び出し処理のコーディング
- 設定のインポート
import { Auth, API } from 'aws-amplify'
const API_NAME = process.env.VUE_APP_API_NAME
- getしてみる
const token = (Auth.currentSession()).getIdToken().getJwtToken()
const response = API
.get(API_NAME, `/users`, {
headers: {Authorization: token}
})
.get()の第一引数にAPI名を入れることで、おそらくエンドポイントを参照してます。
第二引数はエンドポイントより後のパス、
第三引数のヘッダーに(Auth.currentSession()).getIdToken().getJwtToken()
で取得したtokenを入れます。
以上でデータの受け取りができました。ね?簡単でしょ??
簡単さが伝われば良いので今回はここまでにしておきます。updateやdeleteの処理にも興味があれば公式ドキュメントを読んでみてください。
また、APIの作りによっても実装方法が変わる可能性がありますので、バックエンドの人と相談してみてください。
もしAccess to XMLHttpRequest at 'https://...' has been blocked by CORS policy
というエラーメッセージが出たら、ユーザーにAPIを叩く権限がない可能性が高いです。
S3読み書きを実装するよ
公式ドキュメント:Storage - Using a Custom Plugin
前々項で認証したcognitoユーザーが読み書きするという前提でご紹介します。
classとかいう苦手概念が出てくるのであまり説明できませんが、コピペでできるように書きます。
- S3アップロード処理のコーディング
- 画面からアップロード
- S3ダウンロード処理のコーディング
- 画面からダウンロード
1. S3アップロード処理のコーディング
- 別ファイルに書きましょう。
/src
下のどこかにファイルを配置
$ touch storage-service.js
- 最初にStorageライブラリをインポートします
import { Storage } from 'aws-amplify'
- classを作成し、中に処理を書きます
ファイルを特定のバケットにアップロードする処理を書いてみます。
export default class StorageService {
constructor() {}
static putFile(file_name, file) {
Storage.configure({
AWSS3: {
bucket: process.env.VUE_APP_FILE_PUT_BUCKET, // アップロード先のバケットを指定
region: process.env.VUE_APP_REGION, // バケットのあるリージョンを指定
},
level: 'public' // バケットのアクセスレベル。publicはデフォルト
})
return (
Storage.put(file_name, file, { // 第一引数:S3に置く時のオブジェクト名 第二引数:置くファイル
progressCallback(progress) {
console.log(`Uploaded: ${progress.loaded}/${progress.total}`) // opt.アップロード進行度
},
})
.then (result => console.log(result))
.catch(err => console.log(err))
)
}
}
Storage.configure
で操作したいバケットの情報を設定します。
Storage.put
でファイルをアップロードしています。
2. 画面からアップロード
- inputで
file
をv-modelし、ボタンなどのトリガーでファイルをアップロードします
<script>
import StorageService from '@/storage-service.js'
export default {
data() {
return {
file: null,
}
},
methods: {
upload() {
StorageService.putFile(this.file.name, this.file) // 第一引数にファイル名、第二引数にファイル
}
},
}
</script>
まず、さきほど作ったclassをインポートします。
StorageService.putFileを呼び、アップロード処理を実行します。
指定したバケットにファイルが配置されていれば成功です。
3. S3ダウンロード処理のコーディング
次にダウンロードも書いていきます。
ドキュメント:Download files
- 先ほど作ったclassに追加していきます
export default class StorageService {
constructor() {}
static putFile(file_name, file) {
...
},
static getFile(file_name) {
Storage.configure({
AWSS3: {
bucket: process.env.VUE_APP_FILE_GET_BUCKET, // アップロード先のバケットを指定
region: process.env.VUE_APP_REGION, // バケットのあるリージョンを指定
},
level: 'public' // バケットのアクセスレベル。publicはデフォルト
})
return (
Storage.get(file_name) // 第一引数:ダウンロードしたいオブジェクトの名前
.then ((result) => { return result })
.catch(err => console.log(err))
)
}
}
アップロードの時と同じくバケット情報を設定後、Storage.get
を使います。
レスポンスはデフォルトで署名付きURLになります。
4. 画面からダウンロード
<script>
import StorageService from '@/storage-service.js'
export default {
methods: {
download() {
location.href = StorageService.getFile('ファイル名')
}
},
}
</script>
classをインポートし、StorageService.getFileで署名付きURLを取得して画面からダウンロードさせます。
署名付きURLは期限があるので、ダウンロードボタンが押されるたびに取得するのがいいかと思います。
ダウンロードできない場合、以下のことを確認してみてください。(私がハマったポイントです)
- ファイル名の文字コードがバケット上のファイルと同じか?
- バケット上のファイルのメタデータ:Content-Typeが
binary/octet-stream
になっているか?
また、APIと同じくAccess to XMLHttpRequest at 'https://...' has been blocked by CORS policy
というエラーメッセージが出たら、ユーザーにAPIを叩く権限がない可能性が高いです。
まとめ
爆速実装できましたでしょうか?
Amplifyの公式ドキュメント、最初は英語が嫌でウワーーーーッとなっていましたが
chromeの日本語翻訳でも十分読めるし、わりと読みやすく書かれているので一番参考になりました。
読んでいただきありがとうございました!
明日の記事もお楽しみに