この記事は、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にデプロイされるようにします。
目次
- 前提事項
- 全体構成図
- 作成する機能
- Cognitoの準備
- S3の準備
- [Nuxt.js プロジェクトのセットアップ](#Nuxt.js プロジェクトのセットアップ)
- Nuxt.jsとCognitoの統合
- ログイン画面の作成
- CI/CDの準備
- S3上での確認
- まとめ
- 参考文献
前提事項
- Nuxt.jsを使ってみたい人
- AWS Cognitoを使ってみたい人
- ログイン処理を作るのが面倒な人
- aws-cliは事前にインストール済であること
- CI/CDはCircleCIを利用します
全体構成図
作成したNuxt.jsはS3上でホスティングしております。
作成する機能
ユーザ登録画面でアカウントを作成し、ログイン画面でログインに成功したらトップページへ
遷移させます。
実際のソースはこちらになります。
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.パスワードの恒久化
現状UserStatus
が FORCE_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
}
UserStatus
が CONFIRMED
になっています。これで ユーザ名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で作成したバケット名
S3へデプロイ完了!
S3上での確認
S3にアップロードし、ブラウザ上で表示されるか、確認します。
URLは下記となります。
http://[bucket-name].s3-website-[region].amazonaws.com
表示されました!
まとめ
- 今回Amplifyを使わなくても「nuxt/auth」を利用することで簡単にCognitoと連携してログイン処理を作成することができました。
- 詳細な認証の仕組みを本記事では説明しておりませんので参考文献の「nuxt/authの利用方法」を確認頂ければと思います。
最後まで読んでいただきありがとうございました。