LoginSignup
9
1

More than 1 year has passed since last update.

【AWS】Nuxt.js + AWS Cognito で認証付きサイトをS3にデプロイ

Last updated at Posted at 2021-12-15

この記事は、AWS Advent Calendar 2021 カレンダー2の16日目の記事です。

はじめに

サーバーレスアプリケーションを構築するにあたり、Amplifyを使うことで「AWSインフラの構築」「CRUDのAPIの自動生成」「フロントエンドへの SDK 提供」など、開発を効率化させるので非常に便利ですが、各々で利用しているAWSサービスが見えにくくなるのが欠点かと思います。

本記事では、Amplifyを使わずにNuxt.jsとAWS Cognitoを使用して認証機能を備えたウェブサイトを構築し、そのサイトをAWS S3にデプロイする手順について説明します。また、CircleCIを使用してCI/CDのパイプラインを設定し、コードがmainブランチにpushされると、自動的にNuxt.jsアプリがビルドされてS3にデプロイされるようにします。

目次

前提事項

  • Nuxt.jsを使ってみたい人
  • AWS Cognitoを使ってみたい人
  • ログイン処理を作るのが面倒な人
  • aws-cliは事前にインストール済であること
  • CI/CDはCircleCIを利用します

全体構成図

MailDemo.drawio (1).png

作成したNuxt.jsはS3上でホスティングしております。

作成する機能

  • ログイン画面
    screencapture-s3nuxt-kemper0530-signin-2021-12-13-09_46_14.png

  • トップページ
    screencapture-s3nuxt-kemper0530-2021-12-14-08_31_47.png

ユーザ登録画面でアカウントを作成し、ログイン画面でログインに成功したらトップページへ
遷移させます。

実際のソースはこちらになります。

Cognitoの準備

AWS Cognito は、ウェブおよびモバイルアプリの認証、承認、およびユーザー管理機能を提供します。
ユーザーは、ユーザー名とパスワードを使用して直接ログインするか、Facebook、Amazon、Google、Apple などのサードパーティーを通じてログインすることができます。

Coginitoのユーザプールは、AWSのコンソール画面からユーザを作成できるものの、仮パスワードを設定してユーザにパスワードを強制変更してもらわないといけないので、今回はaws-cliを利用して作成します。

1.ユーザプールの作成

「pool-name」部分は任意でお願いします

$ aws cognito-idp create-user-pool --pool-name test_pool

2.ユーザプール確認

$ aws cognito-idp list-user-pools --max-results 10 | jq ".UserPools[] | {Id, Name}"

以下は出力例です。出力される実行結果からIDを控えておきます。

{
  "Id": "ap-northeast-1_XXXXXXX",
  "Name": "test_pool"
}

3.ユーザ作成

2.控えたIDを利用してユーザを作成します。こちらは後続の「認証ミドルウェアの準備」でも利用いたします。

$ aws cognito-idp admin-create-user \
--user-pool-id "ap-northeast-1_XXXXXXX" \
--username "test_mail@example.com" \
--user-attributes Name=email,Value="test_mail@example.com" Name=email_verified,Value=true \
--message-action SUPPRESS

user-pool-id : 2.で控えたuser-pool-idを入力
username : ログイン用のメールアドレスを入力
user-attributes : 認証時のオプション(ここではメールアドレスのみ認証とさせてます)
message-action : SUPPRESS (対象ユーザへの通知を止める)

ユーザ作成結果

{
    "User": {
        "Username": "test_mail@example.com", 
        "Enabled": true, 
        "UserStatus": "FORCE_CHANGE_PASSWORD", 
        "UserCreateDate": 1639385020.166, 
        "UserLastModifiedDate": 1639385020.166, 
        "Attributes": [
            {
                "Name": "sub", 
                "Value": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
            }, 
            {
                "Name": "email_verified", 
                "Value": "true"
            }, 
            {
                "Name": "email", 
                "Value": "test_mail@example.com"
            }
        ]
    }
}

4.パスワードの恒久化

現状UserStatusFORCE_CHANGE_PASSWORD となっているため、このままではログインできません。admin-set-user-passwordを使ってパスワードを設定します。ユーザプールID、ユーザネーム、パスワードをそれぞれ指定します。

$ aws cognito-idp admin-set-user-password \
--user-pool-id "ap-northeast-1_XXXXXXX" \
--username "test_mail@example.com" \
--password 'P@ssw0rd' \
--permanent

--permanent オプションで、パスワードを恒久化します。--no-permanentにすると仮パスワードになります。

5.ユーザ確認

$ aws cognito-idp admin-get-user \
--user-pool-id "ap-northeast-1_XXXXXXX" \
--username "test_mail@example.com"

確認結果

{
    "Username": "test_mail@example.com", 
    "Enabled": true, 
    "UserStatus": "CONFIRMED", 
    "UserCreateDate": 1639385434.063, 
    "UserAttributes": [
        {
            "Name": "sub", 
            "Value": "XXXXXXXXXXXX-XXXX-XXX-XXX-XXXXXXXXXXXX"
        }, 
        {
            "Name": "email_verified", 
            "Value": "true"
        }, 
        {
            "Name": "email", 
            "Value": "test_mail@example.com"
        }
    ], 
    "UserLastModifiedDate": 1639385519.298
}

UserStatusCONFIRMED になっています。これで ユーザ名test_mail@example.com、パスワードP@ssw0rdでログインできるようになります。

6.アプリクライアントの追加

$ aws cognito-idp create-user-pool-client --user-pool-id "ap-northeast-1_XXXXXXX" --client-name "test-pool" \
--explicit-auth-flows "ALLOW_ADMIN_USER_PASSWORD_AUTH" "ALLOW_USER_SRP_AUTH" "ALLOW_REFRESH_TOKEN_AUTH" \
--prevent-user-existence-errors "ENABLED"

出力結果
ClientIdを控えて置きます。後続の「認証ミドルウェアの準備」で利用します。

{
    "UserPoolClient": {
        "UserPoolId": "ap-northeast-1_XXXXXXX", 
        "LastModifiedDate": 1639401558.11, 
        "ClientId": "XXXXXXXXXXXXXXXXXXXXXXXXXX", 
        "AllowedOAuthFlowsUserPoolClient": false, 
        "PreventUserExistenceErrors": "ENABLED", 
        "ExplicitAuthFlows": [
            "ALLOW_ADMIN_USER_PASSWORD_AUTH", 
            "ALLOW_USER_SRP_AUTH", 
            "ALLOW_REFRESH_TOKEN_AUTH"
        ], 
        "RefreshTokenValidity": 30, 
        "CreationDate": 1639401558.11, 
        "ClientName": "test-pool"
    }
}

S3の準備

こちらもaws-cliを利用して構築していきます。

1.公開用のS3バケットを作成

$ aws s3 mb s3://[backet-name]

2.バケットのアクセスブロックを設定(公開できる状態へ)

$ aws s3api put-public-access-block --bucket [backet-name] --public-access-block-configuration  "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"

3.バケットポリシーの作成

下記のjsonファイルをローカルに作成します。
[backet-name]のところは作成した名称へ置き換えてください。

setting-policy.json

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow", 
            "Principal": "*", 
            "Action": "s3:GetObject", 
            "Resource": "arn:aws:s3:::[backet-name]/*" 
        } 
    ] 
}

S3バケットに上記ポリシーファイルをアタッチします。

$ aws s3api put-bucket-policy --bucket [backet-name] --policy file://setting-policy.json

4.静的ウェブサイトホスティングの設定

$ aws s3 website s3://[backet-name] --index-document index.html

これでS3の準備は完了です。


Nuxt.js プロジェクトのセットアップ

まず、Nuxt.js プロジェクトをセットアップします。Nuxt.jsはVue.jsのフレームワークであり、サーバーサイドレンダリング(SSR)、Vue Router、Vuexなどの設定を自動的に行います。以下のコマンドでNuxt.jsプロジェクトを生成します。

$ npx create-nuxt-app test-form

create-nuxt-app v3.6.0
✨  Generating Nuxt.js project in nuxt-inquiry-form
? Project name: nuxt-inquiry-form
? Programming language: TypeScript
? Package manager: Npm
? UI framework: Tailwind CSS
? Nuxt.js modules: Axios - Promise based HTTP client
? Linting tools: ESLint, Prettier
? Testing framework: Jest
? Rendering mode: Single Page App
? Deployment target: Static (Static/Jamstack hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Continuous integration: GitHub Actions (GitHub only)
? What is your GitHub username? GitHubに登録されているユーザー名
? Version control system: Git

Nuxt.jsとCognitoの統合

Nuxt.jsとCognitoを統合するために、@nuxtjs/authモジュールを利用します。このモジュールは、簡単な設定で多くの認証プロバイダと統合することができます。@nuxtjs/authモジュールをインストールし、nuxt.config.jsに設定を追加します。ここではCognitoを認証プロバイダとして設定し、ユーザープールのIDとクライアントIDを設定します。

1.nuxt/authの利用

authモジュールはNuxt.jsで利用できる認証モジュールです。authを使うことによって非常に簡単にログイン・ログアウト等の機能を実装することができます。

2.authおよびaxiosモジュールを追加

$ yarn add --exact @nuxtjs/auth-next
$ yarn add @nuxtjs/axios

3.@a1ter/nuxt-auth-aws-cognito-scheme のインストール

nuxt/auth はデフォルトでは Cognito に対応していないため、 @a1ter/nuxt-auth-aws-cognito-scheme をインストールします。

$ yarn add @a1ter/nuxt-auth-aws-cognito-scheme

4.nuxt.config.jsの設定

ビルドしてS3で環境変数を利用できるようにする設定します

$ yarn add @nuxtjs/dotenv
require("dotenv").config()
const {
    AWS_APPSYNC_REIGION,
    AWS_APPSYNC_GRAPHQL_ENDPOINT,
    AWS_APPSYNC_AUTHENTICATION_TYPE,
    AWS_APPSYNC_APIKEY,
    AWS_COGNITO_REIGION,
    AWS_COGNITO_USERPOOLID,
    AWS_COGNITO_USERPOOLWEBCLIENTID
} = process.env

export default {
    env: {
        AWS_APPSYNC_REIGION,
        AWS_APPSYNC_GRAPHQL_ENDPOINT,
        AWS_APPSYNC_AUTHENTICATION_TYPE,
        AWS_APPSYNC_APIKEY,
        AWS_COGNITO_REIGION,
        AWS_COGNITO_USERPOOLID,
        AWS_COGNITO_USERPOOLWEBCLIENTID
    },

amplify.jsの設定、Amplifyは使いませんがライブラリは利用します

// Disable server-side rendering: https://go.nuxtjs.dev/ssr-mode
ssr: false,

// Target: https://go.nuxtjs.dev/config-target
target: 'static',

// Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins
plugins: [{ src: '@/plugins/amplify.js', mode: 'client' }, ],

認証ミドルウェアを設定します

// Modules: https://go.nuxtjs.dev/config-modules
modules: [
    // https://go.nuxtjs.dev/axios
    '@nuxtjs/axios',
    '@nuxtjs/auth-next'
],
router: {
    middleware: ['auth'],
},

authモジュール内でCognitoを利用できるように設定します

auth: {
    redirect: {
        login: '/signin', // 未ログイン時に認証ルートへアクセスした際のリダイレクトURL
        logout: '/signin', // ログアウト時のリダイレクトURL
        home: false, // ログイン後のリダイレクトURL
    },
    localStorage: false,
    strategies: {
        cognito: {
            scheme: '@a1ter/nuxt-auth-aws-cognito-scheme/scheme/scheme',
            credentials: {
                userPoolId: process.env.AWS_COGNITO_USERPOOLID,
                userPoolWebClientId: process.env.AWS_COGNITO_USERPOOLWEBCLIENTID,
                region: process.env.AWS_COGNITO_REIGION
            },
            endpoints: {
                user: false,
            },
        },
    },
},

次にplugins/amplify.jsを作成します

import Vue from 'vue'
import Amplify, * as AmplifyModules from 'aws-amplify'
import { AmplifyPlugin, components } from 'aws-amplify-vue'
import awsconfig from '@/src/appsync-exports'
import Auth from '@aws-amplify/auth';

export default () => {
    Auth.configure({
        Auth: {
            region: process.env.AWS_COGNITO_REIGION,
            userPoolId: process.env.AWS_COGNITO_USERPOOLID,
            userPoolWebClientId: process.env.AWS_COGNITO_USERPOOLWEBCLIENTID,
        },
    })
}

Amplify.configure(awsconfig)

Vue.use(AmplifyPlugin, AmplifyModules)
Vue.component(components)

環境変数の設定値

  • process.env.AWS_COGNITO_REIGION : ap-north-east-1
  • process.env.AWS_COGNITO_USERPOOLID : COGNITOのユーザプールID
  • process.env.AWS_COGNITO_USERPOOLWEBCLIENTID : COGNITOのユーザクライアントID

ログイン画面の作成

1.トップページの作成

まず/pages/index.vueを作成します。
ログイン後に遷移する画面となります。ログイン後に認証をクリアするログアウトボタンを設定します。

<template>
  <div class="m-auto flex justify-center min-h-screen items-center text-center bg-black">
      <div>
        <div class="flex flex-col">
          <button class="py-4 px-8 mt-8 text-2xl text-gray-50 bg-red-500 hover:bg-red-600 focus:ring-red-600 focus:ring-offset-red-600 text-white transition ease-in duration-200 text-center font-semibold shadow-md focus:outline-none focus:ring-2 focus:ring-offset-2 rounded-full" @click.prevent="$auth.logout()">Logout</button>
        </div>
      </div>
  </div>
</template>

2.ログイン画面の作成

次に/pages/Signin.vueを作成します。

<template>
  <div  class="m-auto flex justify-center min-h-screen items-center text-center bg-black">
    <Signin />
  </div>
</template>

詳細の/component/Signin.vueを作成します。
cssはgithubを参照ください。

<template>
  <div class="form-wrapper">
    <h1><b>Sign In</b></h1>
    <div>
      <div class="form-item">
        <label for="email"></label>
        <input type="email" name="email" required="required" placeholder="Email Address" v-model="email">
      </div>
      <div class="form-item">
        <label for="password"></label>
        <input type="password" name="password" required="required" placeholder="Password" v-model="password">
      </div>
      <div class="button-panel">
        <button class="signin-button" title="Sign In" @click="signin">Sign In</button>
      </div>
    </div>
    <div class="form-footer">
      <nuxt-link to="/signup">Create an account</nuxt-link>
    </div>
  </div>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  data: () => ({
    email: '' as string,
    password: '' as string,
  }),
  head() {
    return {
    }
  },
  methods: {
    async signin() {
      // try {
        const username = this.email;
        const password  = this.password;
        await this.$auth.loginWith('cognito', {
          data: {
            username,
            password,
          }
        }).then((success) => {
           console.log("success sign in.... ");
           // @ts-ignore
           this.$router.push({ path: '/' });
        }).catch((error) => {
           alert(error.message);
           console.log('error signing in:', error.code + ":" + error.message + ":" + error.name);
           return;
        });
    },
  },
})
</script>

3.ローカルでの確認

ここまでできたらローカルでも確認することができます。
下記コマンドを実行し画面を立ち上げて、「Cognitoの準備」で設定したID、パスワードでログイン処理を実施してください。

$ yarn dev

CI/CDの準備

CircleCIを使ってCI/CDパイプラインを設定します。以下の.circleci/config.ymlファイルの設定を使用します。この設定により、コードがmainブランチにpushされるたびにCircleCIがNuxt.jsをビルドし、S3にデプロイします。

version: 2.1
orbs:
  aws-s3: circleci/aws-s3@3.0
defaults: &defaults
  working_directory: ~/aws-rough
  docker:
    - image: cimg/python:3.9.6-browsers
filter_only_main: &filter_only_main
  filters:
    branches:
      only: main
jobs:
  install:
    <<: *defaults
    steps:
      - checkout
      - restore_cache:
          name: Restore Yarn Package Cache
          keys:
            - yarn-packages-{{ checksum "yarn.lock" }}
      - run: yarn install
      - save_cache:
          name: Save Yarn Package Cache
          key: yarn-packages-{{ checksum "yarn.lock" }}
          paths:
            - ~/.cache/yarn
      - persist_to_workspace:
          root: ~/aws-rough
          paths:
            - ./*
  test:
    <<: *defaults
    steps:
      - attach_workspace:
          at: ~/aws-rough
      - run: yarn test

  build:
    <<: *defaults
    steps:
      - attach_workspace:
          at: ~/aws-rough
      - run: yarn generate
      - persist_to_workspace:
          root: ~/aws-rough
          paths:
            - dist

  deploy:
    <<: *defaults
    steps:
      - attach_workspace:
          at: ~/aws-rough
      - aws-s3/sync:
          arguments: |
            --cache-control "max-age=86400"
          from: ./dist/
          to: 's3://${BACKET_NAME}'

workflows:
  version: 2
  test-deploy:
    jobs:
      - install
      - test:
          requires:
            - install
      - build:
          <<: *filter_only_main
          requires:
            - install
            - test
      - deploy:
          <<: *filter_only_main
          requires:
            - install
            - test
            - build

CircleCIに設定する環境変数

  • AWS_ACCESS_KEY_ID : アクセスキー
  • AWS_SECRET_ACCESS_KEY : シークレットアクセスキー
  • AWS_REGION : S3を利用しているリージョン
  • AWS_COGNITO_REIGION : Cognitoを利用しているリージョン
  • AWS_COGNITO_USERPOOLID : CognitoのユーザプールID
  • AWS_COGNITO_USERPOOLWEBCLIENTID : CognitoのユーザクライアントID
  • BACKET_NAME : S3で作成したバケット名

CircleCIの実行
スクリーンショット 2021-12-15 21.41.49.png

S3へデプロイ完了!

S3上での確認

S3にアップロードし、ブラウザ上で表示されるか、確認します。
URLは下記となります。
http://[bucket-name].s3-website-[region].amazonaws.com

screencapture-s3nuxt-kemper0530-signin-2021-12-13-09_46_14.png

表示されました!

まとめ

  • 今回Amplifyを使わなくても「nuxt/auth」を利用することで簡単にCognitoと連携してログイン処理を作成することができました。
  • 詳細な認証の仕組みを本記事では説明しておりませんので参考文献の「nuxt/authの利用方法」を確認頂ければと思います。

最後まで読んでいただきありがとうございました。

参考文献

9
1
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
9
1