CognitoとAWS-Amplifyを使ってS3バケットにサーバーレスな認証をつける
AWSCognito、認証が簡単に作成できて便利です。
また、それにjavascriptのAWS-Amplifyを組み合わせると、AWS-SDKを使うより色んな設定を省くことができて、非常に楽ですね。
その二つを使用して、S3バケットに対しサーバーレスなユーザー認証機能をつけたいと思います。
Cognitoユーザープールをつくる
まずはユーザープールをつくります。
追加属性なども指定できたり、便利です。
今回は、
- ユーザー名
- パスワード
のみの認証で進めます。
ちなみにCognitoユーザープールでは、他にも
- パスワードの強度
- サインアップを許可するかどうか
- 多要素認証
- メールアドレスの検証
など、様々な設定が可能です。
アプリクライアントの作成の画面では、
「クライアントシークレットを生成」のチェックははずします。
Node.jsはまだクライアントシークレットに対応していないようで、ここにチェックを入れてしまうと、ユーザープールを使用できません。
ユーザープールの作成が終わると、最終的にIDが発行されます。
Cognitoフェデレーティッドアイデンティティを作る
続いてフェデレーティッドアイデンティティを作ります。
Cognitoには実は大きく分けて三つほど機能があるようで、今回使うのはそのうちの二つ。
- ユーザープール
- フェデレーティッドアイデンティティ
です。
この二つの違いは
ユーザープールは
ユーザー名とパスワードのデータセットや、ログイン機構を簡単に作成できる。
であるのに対し、
フェデレーティッドアイデンティティは
ユーザープールのログインユーザーや、FacebookやTwitterなどのログインユーザーに対して、AWSのIAMロールを付与できる。
という違いではないかと認識してます。
ここが全然わからなくて、最初戸惑いました、、、
今回はフェデレーティッドアイデンティティに、
先ほど作成したユーザープールのユーザーに対して、
指定したS3バケットにアクセスできるようなIAMを付与してもらいたいので、
ここも設定をしていきます。
認証プロバイダーは先ほど作成したCognitoのIDを入力します。
認証したIAMRoleと 認証してないIAMRoleを作成してくれます。
これで、フェデレーティッドアイデンティティの設定は完了です。
S3バケットを作成
使用するS3バケットを作成します。 今回はyoshino-cognitoというバケットをつくりました。
また、CORSの設定を以下のように変更します。
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule>
<AllowedOrigin>*</AllowedOrigin> <AllowedMethod>HEAD</AllowedMethod> <AllowedMethod>GET</AllowedMethod> <AllowedMethod>PUT</AllowedMethod> <AllowedMethod>POST</AllowedMethod> <AllowedMethod>DELETE</AllowedMethod> <MaxAgeSeconds>3000</MaxAgeSeconds>
<ExposeHeader>x-amz-server-side-encryption</ExposeHeader> <ExposeHeader>x-amz-request-id</ExposeHeader>
<ExposeHeader>x-amz-id-2</ExposeHeader>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
IAMロールに対してS3バケットへのアクセス権限を付与する
次にAuth_Roleに対して、S3バケットへのアクセス権限を付与します。
IAMポリシーを以下のように編集。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"mobileanalytics:PutEvents",
"cognito-sync:*",
"cognito-identity:*"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::yoshino-cognito/private/${cognito-identity.amazonaws.com:sub}",
"arn:aws:s3:::yoshino-cognito/private/${cognito-identity.amazonaws.com:sub}/*"
]
}
]
}
yoshino-cognitoというバケットの中の、CognitoログインしているユーザーのIDに対して、GetやPutができるような設定になっています。
サインアップ、サインイン機構をAWS-Amplifyで作る
vue-cliを使って作っていきます。
$ vue init webpack sample
$ cd sample
$ yarn add aws-amplify
aws-amplifyのマニュアル(https://aws.github.io/aws-amplify/media/authentication_guide)通り、aws-amplifyの設定ファイルaws-exports.jsを作成します。
import Amplify from 'aws-amplify';Amplify.configure({
Auth: {
// フェデレーションアイデンティティのID
identityPoolId: 'ap-northeast-1:******************',
// リージョン
region: 'ap-northeast-1',
// ユーザープールのID
userPoolId: 'ap-northeast-1_************',
// ユーザープールのウェブクライアントID
userPoolWebClientId: '*******************',
mandatorySignIn: true,
},
Storage: {
bucket: 'yoshino-cognito', //使用するS3バケット名
region: 'ap-northeast-1', //リージョン
},
});
HelloWorld.vueを編集して、サインアップとログインができるようにします。
<template>
<div class="page">
<div class="login-form">
<p>ログイン</p>
<p>{{ status }}</p>
<p>{{ message_text }} </p>
<label>ユーザー名</label>
<input type="text" v-model="userInfo.username"/>
<label>パスワード</label>
<input type="password" v-model="userInfo.password"/>
<button class="btn btn-primary" @click="signIn()">ログイン</button>
<button class="btn btn-primary" @click="signOut()">ログアウト</button>
<button class="btn btn-primary" @click="put()">PUT</button>
<button class="btn btn-primary" @click="get()">GET</button>
<img :src=url>
</div>
<div class="login-form">
<p>サインアップ</p>
<label>ユーザー名</label>
<input type="text" v-model="userInfo.username"/>
<label>パスワード</label>
<input type="password" v-model="userInfo.password"/>
<button class="btn btn-primary" @click="signUp()">サインアップ</button>
</div>
</div>
</template><script>
import Amplify, { Auth, Storage } from 'aws-amplify';
import aws_exports from '../../aws-exports';
Amplify.configure(aws_exports);export default {
data () {
return {
status: '',
userInfo: {
username: '',
password: '',
},
message_text: '',
url: '',
};
},
created() {
Auth.currentSession()
.then((data) => {
this.status = 'ログインしています'
}).catch((err) => {
this.status = 'ログインしていません'
});
},
methods:{
put: function () {
Storage.put('test.txt', 'Hello', { level: 'private', contentType: 'text/plain'})
.then (result => console.log(result))
.catch(err => console.log(err))
},
get: function () {
Storage.configure({ level: 'private' });
Storage.get('bird.png')
.then ((result) => {
console.log(result)
this.url = result;
})
.catch(err => console.log(err));
},
signUp: function () {
Auth.signUp(this.userInfo.username, this.userInfo.password)
.then((data) => console.log(data))
.catch((err) => console.log(err));
},
signIn: function () {
Auth.signIn(this.userInfo.username, this.userInfo.password)
.then((data) => {
this.message_text = 'ログインしました';
this.status = 'こんにちは、'+data.username+'さん';
}).catch((err) => {
this.message_text = 'ログインできませんでした';
});
},
signOut: function () {
Auth.signOut()
.then((data) => {
this.message_text = 'ログアウトしました';
}).catch((err) => {
this.message_text = 'ログアウトできませんでした';
});
},
}
}
</script><style scoped>
.login-form {
margin: 20;
}
</style>
こんな画面になります。
ユーザーを作成、ログインしてみる
まず、サインアップからユーザーを作成します。
サインアップに成功すると、このような形でトークンが発行されます。
ここでそのままログインしようとすると、以下のようなエラーがでます。
ユーザーの確認ができていない
とのことなので、ユーザープールにアクセスします。
このような感じでユーザーが作成されているので、「ユーザーの確認」を押します。
再度ログインしてみると、ログインに成功します。
ローカルストレージを見てみると、Cognitoの情報が保存されているのがわかります。
S3にオブジェクトをPut,Getしてみる
Putボタンを押すと、「text.txt」が指定したS3に保存されます。
S3を確認すると
保存されているのが確認できます。
パスは、privateファイルの設定の場合
バケット名/private/ユーザー固有のID/ファイル名
に自動的に設定してくれます。
では次にS3の同じ場所にbird.pngという画像ファイルを設置します。
この状態で、GETボタンを押すと
S3から画像を取得してくれます。
ユーザーごとの認証になっているか確認する
ここで一度ログアウトして、リロードした上で、画像の取得をしてみます。
「No Credential」という表示がでて、取得ができません。
また、他のユーザーでログインをしても、同じく取得ができません。
以上、CognitoとAWS-Amplifyを用いたS3上のファイルのユーザー認証でした。
こんなに簡単にユーザー認証と、ユーザーによるアクセス制限ができるのは、本当に便利です。
今後も使っていきたいと思います。