Top View


Author yoshino

Vuejs、Cognito、AWS-Amplifyを使ってS3にサーバーレスな認証をつける

2018/11/07

Cognitoユーザープールをつくる

まずはユーザープールをつくります。

今回は、

  • ユーザー名
  • パスワード

のみの認証で進めます。

パスワードの強度や、ユーザーへのサインアップの許可の選択
多要素認証や、メールアドレスの検証など、多くの機能が提供されています。

一点、注意点で、今回の開発はNode.jsを用いるのですが、その場合は上記の画面でアプリクライアントの作成をする際に

「クライアントシークレットを生成」

のチェックをはずす必要があります。

ユーザープール作成を終えると、IDが発行されます。

Cognitoフェデレーティッドアイデンティティを作る

続いてフェデレーティッドアイデンティティを作ります。

認証プロバイダーは先ほど作成した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ロールに対して、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.jsで作っていきます。

$ vue init webpack sample
 $ cd sample
 $ yarn add aws-amplify

aws-amplifyの設定ファイルaws-exports.jsを作成します。

import Amplify from 'aws-amplify';
 
 Amplify.configure({
 Auth: {
 // フェデレーションアイデンティティのID
 identityPoolId: 'ap-northeast-1:自分のID',
 // リージョン
 region: 'ap-northeast-1',
 // ユーザープールのID
 userPoolId: 'ユーザープールのID',
 // ユーザープールのウェブクライアント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>

このような画面になります。

ユーザーを作成、ログインしてみる

まず、サインアップからユーザーを作成します。

サインアップに成功すると、トークンが発行されます。

ここでそのままログインしようとすると、

UserNotConfirmedExceptionというエラーが発生します。

ユーザーの確認ができていない、とのことなので、ユーザープールにアクセスします。

以下のようにユーザーが作成されているので、「ユーザーの確認」を押します。

再度ログインしてみると、ログインに成功します。

S3にオブジェクトをput,getしてみる

Putボタンを押すと、「text.txt」が指定したS3に保存されます。

S3を確認すると

保存されているのが確認できます。

パスは、privateファイルの設定の場合

バケット名/private/ユーザー固有のID/ファイル名

に自動的に設定してくれます。

では次にS3の同じ場所にbird.pngという画像ファイルを設置し、状態で、GETボタンを押すと

S3から画像を取得してくれます。

ユーザーごとの認証になっているか確認する

ここで一度ログアウトして、リロードした上で、画像の取得をしてみます。

「No Credential」という表示がでて、取得ができません。
また、他のユーザーでログインをしても、同じく取得ができません。

以上、Vuejs、Cognito、AWS-Amplifyを用いたS3上のファイルのユーザー認証でした。

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

今後も使っていきたいと思いました!

yoshino

yoshino

Twitter X

Fusicエンジニア。Webアプリケーションやブロックチェーンプログラミングをしています。PHP, Vue.js, Solidity etc..