5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS Amplifyで認証+ユーザごとの画像フォルダ+API(GraphQL)+ホスティングの基盤を構築(Vue.jsコード例付き)

Last updated at Posted at 2021-09-04

目指すもの

  • 以下の様なサーバレスアーキテクチャを構築します。

    • Amazon Cognitoを使用してBasic認証およびGoogle認証を行う
    • WEBページはGitHubと連携してCI/CDを設定しホスティングする。
    • メディア用のS3バケットを作成しユーザごとのディレクトリを作成する
    • APIはAPI GatewayおよびAppSyncを用いた2種類を用意
      amplify構成.png
    • ※ 正確には今回はS3 メディアをCloudFrontに通してないです。
    • ※ REST API(Lambda+SESでのメール送信)は今回は扱わないです。
  • 扱わない事項

    • 要所でVue.jsのコード例を載せますが、1つのアプリを完成させるようなことはせず、非常に簡単な動作検証を確認する程度です。
    • ログの保管について
    • AWS WAFなどを使用したセキュリティ
    • 独自ドメインの利用
  • 料金について

  • 構築や開発をするだけであればかかりません。ほぼ0円です。

  • 料金について: https://aws.amazon.com/jp/cognito/pricing/

環境

  • Windows
  • amplify CLI 5.3.0
  • npm 6.12.0
  • node 12.13.0
  • エディタ: vscode
  • vue-cli 4.1.2

前提条件

  • AWSアカウントを取得しログインできる
  • Gitアカウントを持っている
  • (Vue.jsのコード例を参考にする場合は)vue-cliを使用できること

構築手順

1.Gitリポジトリ & Vueプロジェクトを作成

1-1. Gitリポジトリを新規作成

  • 適当なリポジトリを作成します。名前は何でも大丈夫です。
  • こちらは後にAmplifyのCI/CD環境と連携し、ここにpushされたファイルが自動でデプロイされるようになります。

1-2. Vueプロジェクトを作成

  • Vueプロジェクトを作成します。
vue create frontend

? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, Router, Vuex, Linter
? Use history mode for router? (Requires proper server setup for index fallback in production) Yes
? Pick a linter / formatter config: Basic
? Pick additional lint features: (Press <space> to select, <a> to toggle all, <i> to invert
selection)Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? (y/N) N
  • 実際には今回Vuexやvur-routerを使いません。

  • また必要なライブラリをインストールします。

  • 加えてAmplify APIを叩けるようにmain.jsを下記のように変更します。

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

// Amplify用設定
import Amplify from '@aws-amplify/core';
import Auth from '@aws-amplify/auth'
import Storage from '@aws-amplify/storage'
import API from '@aws-amplify/api'
import { Logger } from '@aws-amplify/core'
import { I18n } from '@aws-amplify/core'

import { AmplifyPlugin } from 'aws-amplify-vue'
import aws_exports from  './aws-exports'
Amplify.configure(aws_exports)
import appSyncConfig from "./aws-exports";

Amplify.configure(appSyncConfig);
Vue.use(AmplifyPlugin,{Auth,Storage,API,Logger,I18n})

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

1-3. Vueプロジェクトをリポジトリにpush

  • 一旦1-1で作成したリポジトリに1-2で作成したVueプロジェクトをpushしておきます。

2. Amplify インストール & 設定

2-1. Amplify CLIインストール

  • 下記でAmplify CLIをインストールしてamplifyコマンド(バックエンド/インフラの設定や構築を実施する)を使えるようにします。
    • npm i -g @aws-amplify/cli

2-2. Amplify 設定

  • 下記コマンドでamplifyを使用するユーザを作成します。
amplify configure
Follow these steps to set up access to your AWS account:

Sign in to your AWS administrator account:
https://console.aws.amazon.com/ ← ここでブラウザ上でコンソール画面が立ち上がるのでサインインします。
Press Enter to continue  ← サインインしたらEnterを押します。

Specify the AWS Region
? region:  ap-northeast-1
Specify the username of the new IAM user:
? user name:  amplify-test
Complete the user creation using the AWS console ← ここで再度ブラウザに移りIAMユーザ作成画面に移ります。
                                                   詳細は下の画像をご覧ください。
  • 下の画像の様にユーザを作成します。(名前は適当です。)

amplify_user.png

Create a user with AdministratorAccess to your account to provision AWS resources for you like AppSync, Cognito etc.

  • 再度CLIに戻って続きです。先のユーザのアクセスキーやシークレットキーを入力したら完了です。
Enter the access key of the newly created user:
? accessKeyId:  ********************
? secretAccessKey:  ****************************************
This would update/create the AWS Profile in your local machine
? Profile Name:  amplify-test

Successfully set up the new user.
  • 入力したキーとプロファイル名を用いて、C:\Users\{ユーザ名}\.aws\credentials の認証情報を設定または更新をしてくれています。

2-3. Amplifyプロジェクト初期化

  • vueプロジェクトのトップでAmplifyプロジェクトを作成します。
$ amplify init
Note: It is recommended to run this command from the root of your app directory
? Enter a name for the project testdir
The following configuration will be applied:

Project information
| Name: testdir
| Environment: dev
| Default editor: Visual Studio Code
| App type: javascript
| Javascript framework: none
| Source Directory Path: src
| Distribution Directory Path: dist
| Build Command: npm.cmd run-script build
| Start Command: npm.cmd run-script start

? Initialize the project with the above configuration? Yes
Using default provider  awscloudformation
? Select the authentication method you want to use: AWS profile

For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html

? Please choose the profile you want to use: amplify-test ← プロファイルは2-2の値を使用します。
~中略~
Your project has been successfully initialized and connected to the cloud!

3. 認証基盤を導入

  • ここからは実際に構築をしていきます。まずは認証からです。

3-1. GCPでOAuthクライアントID作成

  • アプリのユーザがGoogleアカウントでログインできるようにするため準備します。

  • GCPにログインし上部プロジェクト選択よりプロジェクトを新規作成します。
    gcp4.png

  • メニューより 認証情報 を選択します。
    gcp1.png

  • 次に、APIとサービスよりOAuth同意画面を選択し必要事項を入力していき、下記画面の様になります。
    gcp5.png

  • 次に +認証情報を作成 より OAuthクライアントID を選択します
    gcp2.png

  • 「アプリケーションの種類」「名前」を入力し作成を押します。

  • 「承認済みのリダイレクトURI」 「承認済みの JavaScript 生成元」は後ほど入力します。
    gcp3.png

  • 表示されるキーを保存しておきます。

3-2. Amazon Cognitoを追加

  • Amazon CognitoはBasic認証やOAuth認証の基盤を簡単に構築できるマネージドサービスです。
  • 月間5万月間アクティブユーザまでは無料です。
  • 下記のコマンドで構築します。OAuthの設定以外はデフォルトです。
$ amplify add auth
Using service: Cognito, provided by: awscloudformation
 
 The current configured provider is Amazon Cognito. 
 
 Do you want to use the default authentication and security configuration? Default configura
tion with Social Provider (Federation)
 Warning: you will not be able to edit these selections. 
 How do you want users to be able to sign in? Email
 Do you want to configure advanced settings? No, I am done.
 What domain name prefix do you want to use? testdir24052885-24052885
 Enter your redirect signin URI: http://localhost:8080/ ← サインイン後のURL。今回はローカルで試すためこの値。
? Do you want to add another redirect signin URI No
 Enter your redirect signout URI: http://localhost:8080/
? Do you want to add another redirect signout URI No
 Select the social providers you want to configure for your user pool: Google ← ソーシャルログインでGoogleを選択
  
 You've opted to allow users to authenticate via Google.  If you haven't already, you'll nee
d to go to https://developers.google.com/identity and create an App ID. 
 
 Enter your Google Web Client ID for your OAuth flow: {3-1で取得した値を使用する}
 Enter your Google Web Client Secret for your OAuth flow: {3-1で取得した値を使用する}
~中略~
Successfully added auth resource testdir24052885 locally
  • ローカルの設定が変わっただけで、実際に認証基盤はまだ構築されていないので、amplify pushで反映し認証基盤を構築します。
$ amplify push
√ Successfully pulled backend environment dev from the cloud.

Current Environment: dev

| Category | Resource name   | Operation | Provider plugin   |
| -------- | --------------- | --------- | ----------------- |
| Auth     | testdir24052885 | Create    | awscloudformation |
? Are you sure you want to continue? Yes
~中略~
√ All resources are updated in the cloud
  • この状態でAWSコンソール → Cognito → ユーザープールの管理 → ユーザプール を見ると、該当のユーザプール(ユーザ情報DBのようなモノ)が作成されています。
    cognito.png

3-3. GCP設定追加

  • 「承認済みのリダイレクトURI」 「承認済みの JavaScript 生成元」を編集します。

  • AWSコンソールより3-2で作成したユーザプールのドメイン名を確認します(下記画像)

  • 再度GCPにログインし、メニュー → APIとサービス → 認証情報 → 3-1で作成したOAuthクライアントの編集アイコンを選択 し、下記の値を入力して保存します。

    • 承認済みの JavaScript 生成元: https://{下記画像オレンジ線の値}.auth.{リージョン}.amazoncognito.com
    • 承認済みのリダイレクト URI: https://{下記画像オレンジ線の値}.auth.{リージョン}.amazoncognito.com/oauth2/idpresponse
      cognito1.png
  • 参考: https://aws.amazon.com/jp/premiumsupport/knowledge-center/cognito-google-social-identity-provider/#OAuth%202.0%20クライアントの認証情報を取得する

3-4. Vue.jsで試してみる

  • Vue.jsでログイン画面を作成し、ログインを試して、上記ユーザプールに登録されるのを確認します。
  • 以下のVueファイルを追加します。
<!-- 認証画面 -->
<template>
  <div>
    <div class="signin">
      <amplify-authenticator v-bind:authConfig="authConfig">
      </amplify-authenticator>
    </div>
    <button @click="signin">Googleでログイン</button>
  </div>
</template>

<script>
import Auth from '@aws-amplify/auth'
export default {
  name: 'Signin',
  components: {
  },
  methods: {
    signin: function() {
      Auth.federatedSignIn({ provider: 'Google' })
    },
  },
  data () {
    return {
      authConfig: {
        signUpConfig: {
          hideAllDefaults: true,
          defaultCountryCode: '1',
          signUpFields: [
            {
              label: 'Username',
              key: 'username',
              required: true,
              displayOrder: 1,
              type: 'string',
            },
            {
              label: 'email',
              key: 'email',
              required: true,
              displayOrder: 2,
              type: 'string',
            },
            {
              label: 'Password',
              key: 'password',
              required: true,
              displayOrder: 3,
              type: 'password'
            }
          ]
        }
      }
    }
  }
}
</script>
  • 下記のような画面が作成されます。

signin1.png

  • 操作は割愛しますが、上記画面より「Create account」や「Googleでログイン」を実行すると、以下画像の様にユーザプールに追加されます。
  • google_〇〇というユーザは「Googleでログイン」を実行したユーザです。
    cognito4.png

4. WEBページをホスティング & CI/CD設定(GitHub & amplify 連携)

  • WEBページをインターネットに公開できるように設定します
  • また1.で作成したGitリポジトリに変更をpushした際に、自動的にWEBページが更新されるように、CI/CDも設定します。

4-1. Gitと連携してCI/CDを設定する

  • 簡単にできます。

  • AWSコンソール → Amplify と行くとamplifyアプリが存在しているので、そちらを選択します。

  • 次に「Frontend enviroments」タブを選択すると下図の様になります。
    hosting1.png

  • GitHubを選択し次に進みます。認証が必要であれば実施し下図の様になるので、1.で作成しているリポジトリを選択します。
    hosting4.png

  • 次にビルド設定をします。ビルド用のロールは下図緑下線のボタンから作成します。
    hosting3.png

  • Advanced settingsにて下記環境変数を追加し、3-1.で取得したGoogle OAuthクライアントの値を入力します。

    • AMPLIFY_GOOGLE_CLIENT_ID
    • AMPLIFY_GOOGLE_CLIENT_SECRET
      hosting5.png
  • そのまま次へ進んでいき 「保存して次へ」をクリックするとCI/CDの連携が完了し、ビルドが走ります。

    • このタイミングでAWSの裏側に、(コンソール上では見えない)CloudFront+S3の環境が構築されホスティングされます。
      hosting6.png

4-2. ビルド設定の修正

4-3. ビルドファイルの作成

  • ディレクトリのトップにamplify.ymlというファイルを作成し以下の値を入力しpushします。
  • これはCI/CD環境のビルド動作を決めるファイルです。
version: 1
frontend:
  phases:
    preBuild:
      commands:
        - npm ci
    build:
      commands:
        - npm run build
  artifacts:
    baseDirectory: dist
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*
  • 対象ブランチへpushするたびにビルドが走るので、今回のpushでも走っています。ビルド終了後に再度見に行くと、以下の様になると思います。

hosting7.png

5. メディア用ストレージを作成(S3)

  • ユーザが画像を保存できるようなストレージを作成します。
  • 今回は各ユーザが各ユーザ用のフォルダしか見えないようにします。

5-1. S3の構築

  • 下記コマンドでS3バケットを作成します。
$ amplify add storage
? Please select from one of the below mentioned services: Content (Images, audio, video, etc.)
? Please provide a friendly name for your resource that will be used to label this category in the project: media
? Please provide bucket name: media
? Who should have access: Auth users only
? What kind of access do you want for Authenticated users? create/update, read, delete
? Do you want to add a Lambda Trigger for your S3 Bucket? N

$ amplify push

  • AWSコンソールを見るとS3バケットが作成されているのを確認できます。

5-2. S3ポリシー用IAMユーザ作成

  • 適当なIAMユーザを作成します。アクセス権限は下記の様に何もなくて大丈夫です。
    s32.png

5-3. S3のポリシーを変更

  • 上記で作成したS3バケットに対して、ユーザごとのフォルダが作成されるようなルールを作成したいと思います。

    • つまりユーザAのフォルダであるfolderAにはAしかアクセスできないような状態にします。
  • まず、AWSコンソール → S3 → 該当のバケット → アクセス許可タブ → バケットポリシー 編集を選択します。(下図赤線)
    s31.png

  • 次にバケットポリシーの値を以下に変更します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::{AWSアカウント}:user/{5-2で作成したIAMユーザ名}"
            },
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::{バケット名}",
            "Condition": {
                "StringLike": {
                    "s3:prefix": "/public/users/${cognito-identity.amazonaws.com:sub}/*"
                }
            }
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::{AWSアカウント}:user/{5-2で作成したIAMユーザ名}"
            },
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:PutObjectTagging"
            ],
            "Resource": "arn:aws:s3:::{バケット名}/public/users/${cognito-identity.amazonaws.com:sub}/*"
        }
    ]
}
  • 捕捉1: ポリシーの中で設定する値は簡単に下記です。
    • Action: どのようなアクションに対してルールを課すか。今回はバケットの中身をリスト表示したりオブジェクトを置いたりするアクション。
    • Effect: アクションした際の対応。許可するのか拒否するのか。今回は許可。
    • Resource: ルールを課す対象。今回はバケット。
    • Condition: ルールを課す条件。今回は特定の接頭辞という条件を付ける。
  • 捕捉2: ${cognito-identity.amazonaws.com:sub} には、cognito内で各ユーザに与えられる一意の値(sub)が入ります。これで各ユーザごとのフォルダが作成されます。
  • ポリシー内の${}のことをポリシー変数といいます。詳細はググってください。

5-4. Vue.jsのコード例

<!-- s3の画像アクセステスト -->
<template>
  <div>
    <div>
        cognito sub: {{userSub}}
    </div>
    <button @click="selectFile">
        ファイル認証後にアップ可能
    </button>
    <input style="display: none"
      ref="input" type="file"
      @change="uploadSelectedFile()">
    <br><br><br>
    マウント時コンソールにログインユーザの画像情報が出力
  </div>
</template>

<script>
import Auth from '@aws-amplify/auth';
import Storage from '@aws-amplify/storage';
export default {
  name: 's3',
  data:function(){
      return{
          userSub:''
      }
  },
  methods:{
    selectFile() {
      if(this.$refs.input != undefined){
        this.$refs.input.click();
      }
    },
    uploadSelectedFile() {
      let file = this.$refs.input.files[0];
      let filePath = 'users/' + this.userSub +'/'+ file.name
      Storage.put(filePath, file).then(result => {
        console.log(result);
      }).catch(err => console.log(err));
    }
  },
  mounted:async function(){
    // ユーザのsub情報を取得
    const { attributes } = await Auth.currentAuthenticatedUser();
    // 各ユーザのs3の格納ファイルを取得
    let userSub = attributes.sub
    this.userSub = attributes.sub
    Storage.list('users/'+userSub )
        .then(result => console.log(result))
        .catch(err => console.log(err));
    }
}
</script>
  • 非常に簡単な画面ができます。(画像表示もないです。画像情報はすべてコンソール上に文字列として出ます。)

  • ファイルボタンを押して、適当なファイルを選択すると、S3のバケット上に保存されます。
    s37.png

  • S3を見に行くと確かに保存されます。
    s38.png

  • また上記コード内にベタ打ちで他のユーザのsubを入れても見ることが出来ないはずです。

6. APIの作成(GraphQL)

  • AppSync(GraphQLを簡単に開発できるマネージドサービス)とDynamoDB(NoSQLのマネージドサービス)を利用します。
  • 今回はデフォルトで用意されるTODOアプリ用のAPI+DBを利用します。

6-1. APIの構築

  • 以下でAPIを作成します。
$ amplify add api
? Please select from one of the below mentioned services: GraphQL
? Provide API name: test
? Choose the default authorization type for the API Amazon Cognito User Pool
Use a Cognito user pool configured as a part of this project. ← APIの認証にCognitoユーザプールを使用
? Do you want to configure advanced settings for the GraphQL API No, I am done.
? Do you have an annotated GraphQL schema? No
? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, des
cription)
~中略~
Successfully added resource test locally
  • この時点で、amplify/backend/api/{API名}配下に色々作成されています。
    • 開発時に編集するのはschema.graphql(テーブル構造を決めるファイル)くらいです。
    • 詳細は省きますが、こちらを編集してamplify pushするだけでAppSyncのGraphQLの中身やDynamoDBのテーブル構造が変化します。
  • 次に以下でAWS上にAppSyncおよびDynamoDBを作成します。
$ amplify push
? Are you sure you want to continue? Yes
? Do you want to generate code for your newly created GraphQL API Yes
? Choose the code generation language target javascript
? Enter the file name pattern of graphql queries, mutations and subscriptions src\grap
hql\**\*.js ← デフォルト
? Do you want to generate/update all possible GraphQL operations - queries, mutations 
and subscriptions Yes ← 後述のVue.jsの例で使用するGraphQLのクエリなどを自動生成してくれます。
? Enter maximum statement depth [increase from default if your schema is deeply nested
] 2 ← デフォルト

~中略~

※ push中に赤文字でUPDATE_FAILED HostedUIProvidersCustomResourceInputs が出た場合、amplify update auth → Update OAuth social providers より再度3-1.で取得したGoogleのキーを入力してください。

  • AWSコンソールでそれぞれAppSyncとDynamoDBが作成されていることを確認できます。

6-2. Vue.jsのコード例

  • 以下を使用します。
  • これにより以下を実現します
    • TODOの名前と内容を入力し登録する
    • Vueのマウント時にそのユーザのTODO情報一覧を取得する
<!-- APIテスト画面 -->
<template>
  <div>
    <!-- TODOリストに追加 -->
    <form v-on:submit.prevent>
      <input type="text" v-model="name" placeholder="TODO名前">
      <input type="text" v-model="description" placeholder="TODO内容">
      <button v-on:click="create">Add Todo</button>
    </form>
    <div>
      追加結果
      {{result}}
    </div>
    <br/><br/><br/>
    <div>
      ログインユーザのTODOリスト取得結果
      {{resultList}}
    </div>
  </div>
</template>

<script>
import { API, graphqlOperation} from "aws-amplify";
import {listTodos} from '@/graphql/queries';
import {createTodo} from '@/graphql/mutations';
export default {
  name: 'Home',
  components: {
  },
  data () {
    return {
      name:"",
      description:"",
      result:'',
      resultList:[]
    }
  },
  methods: {
    // TODOに値を追加
    async create() {
        let input = {
          name:this.name,
          description:this.description
        }
        this.result = await API.graphql(graphqlOperation(createTodo,{input:input}));
    },
    async getList() {
      this.resultList = await API.graphql(graphqlOperation(listTodos));
    }
  },
  mounted:function(){
    this.getList()
  }
}
</script>
  • 非常に簡単な画面が出来上がります

    • 「追加結果」 に追加したTODOのresponseをそのまま載せています。
    • 「ログインユーザのTODOリスト取得結果」 にマウント時にユーザのTODO一覧を取得します。
      api1.png
  • またDynamoDBを見に行くとデータが保存されていることが確認できます。

api2.png

  • (捕捉) 今回は手軽さからGraphQLを扱いましたが、REST API(api gateway+Lambda)を使えば、Amazon SESを利用したメール送信や、Stripeを利用した決済機能の追加を実現できます。

7. 環境の削除

  • 不要な環境を削除します。
  • 以下をすれば大方削除できる、と思います。
    • AWSコンソール → AWS Amplify → 対象のアプリケーションを選択 → 右上のアクション▼ → アプリの削除
    • 今回使用したIAMを削除
    • +α GCPのOAuthクライアント

終わりに

  • AWS Amplify便利です。
5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?