CognitoとAWS-Amplifyを使ってS3バケットにサーバーレスな認証をつける

Yoshino Masaya
13 min readMay 20, 2018

--

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上のファイルのユーザー認証でした。

こんなに簡単にユーザー認証と、ユーザーによるアクセス制限ができるのは、本当に便利です。

今後も使っていきたいと思います。

--

--