Udemy 13. フロントエンドのログイン認証 #08
2020年11月03日に公開

【Rails×Nuxt.js】デモプロジェクトの作成とHerokuデプロイ(ログイン認証完)

今回達成すること

今回は、Railsにデモプロジェクトを返すコントローラーの作成し、ここまでの変更をHerokuにデプロイします。

これにてログイン認証が完成です。

Railsにデモプロジェクトを用意する

現状のNuxt.jsは、Vuexに用意したプロジェクト一覧を表示させています。

これを、Railsにリクエストを行いAPIを介して取得するよう変更します。

Railsにデモプロジェクトを返すコントローラーを作成します。

root $ docker-compose run --rm api rails g controller Api::V1::Projects

作成したコントローラーに、ユーザーが持っているであろうデモプロジェクトを用意します。

本来は、ユーザーと関連づいたプロジェクト一覧をデータベースから取得します。

api/app/controllers/api/v1/projects_controller.rb
class Api::V1::ProjectsController < ApplicationController
  # 以下、追加
  before_action :authenticate_user

  def index
    # 本来はDBから取得する => current_user.projects
    projects = [
      { id: 1, name: 'Rails MyProject01', updatedAt: '2020-04-01T12:00:00+09:00' },
      { id: 2, name: 'Rails MyProject02', updatedAt: '2020-04-05T12:00:00+09:00' },
      { id: 3, name: 'Rails MyProject03', updatedAt: '2020-04-03T12:00:00+09:00' },
      { id: 4, name: 'Rails MyProject04', updatedAt: '2020-04-04T12:00:00+09:00' },
      { id: 5, name: 'Rails MyProject05', updatedAt: '2020-04-01T12:00:00+09:00' }
    ]
    render json: projects
  end
end

このコントローラーにアクセスするルートを追加します。

api/config/routes.rb
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      ...
      # login, logout
      resources :user_token, only: [:create] do
        delete :destroy, on: :collection
      end

      # 追加
      # projects
      resources :projects, only: [:index]
    end
  end
end

curlコマンドで確認する

Railsのレスポンスが正しく返ってきているか、ターミナルから確認してみましょう。

コンテナを起動してログインし、Cookieからトークンをコピーします。

2020-10-29 16-08-31

このトークンをヘッダーに付けてcurlコマンドを実行します。

root $ curl http://localhost:3000/api/v1/projects \
-H "Authorization: Bearer \
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.\
eyJleHAiOjE2MDUxNjQxOTAsImF1ZCI6ImxvY2FsaG9zdDo4MDgwIiwic3ViIjoyfQ.\
x7aP9wywAvTuZ5acZEswCk-ov0Ihwic77iaf0YJBQGI" | python -mjson.tool
  • python -mjson.tool … レスポンスJSONを見やすく整形する。

OK!プロジェクト一覧が返ってきてますね。

[
    {
        "id": 1,
        "name": "Rails MyProject01",
        "updatedAt": "2020-04-01T12:00:00+09:00"
    },
    {
...

これでRails側の準備が整いました。

Nuxt.jsからプロジェクト一覧を取得する

ここからは、Nuxt.jsからRailsにプロジェクト一覧をリクストする処理を行います。

ミドルウェアファイルを作成する

Nuxt.jsでは、ログイン後ぺージに遷移した場合にRailsへリクエストを行い、ユーザーが持つプロジェクト一覧を取得します。

この実装には、ミドルウェアを使えば簡単に実装できます。

「middleware」ディレクトリ内にgetProjects.jsを作成しましょう。

root $ touch front/middleware/getProjects.js

ミドルウェアを追加する

作成したミドルウェアを、

  • ログイン後のレイアウトファイルであるdefault.vue
  • ログイン後のトップページを表示するindex.vueに追加します。
front/layouts/default.vue
...
<script>
export default {
  // 変更
  middleware: ['authenticator', 'getProjects']
  // 削除
  // middleware: 'authenticator'
}
</script>
front/pages/index.vue
...
<script>
import homeImg from '~/assets/images/loggedIn/home.png'

export default {
  // 追加
  middleware: ['authenticator', 'getProjects'],
  layout ({ $auth }) {
    ...
  },
  ...
}
</script>

これでミドルウェアの読み込み設定ができました。

ミドルウェアからRailsにリクエストする

getProjects.jsからRailsにリクエストを送ります。

リクエストを送るタイミングは、

  • ログインしている、かつ
  • Vuexにプロジェクトが存在しない場合です。

ミドルウェアは、設定したレイアウト内のページ遷移毎に呼ばれるため、無駄なAPIリクエストを行わないように分岐処理をしています。

front/middleware/getProjects.js
export default async ({ $auth, store, $axios }) => {
  // ログイン済みかつ、プロジェクト一覧が存在しないとき
  if ($auth.loggedIn && !store.state.projects.length) {
    await $axios.$get('/api/v1/projects')
      .then(response => store.dispatch('getProjects', response))
  }
}

Vuexにアクションを追加する

リクエスト成功時に取得したプロジェクト一覧をVuexに保存するために、index.jsにアクションとミューテーションを追加します。

front/store/index.js
export const state = () => ({
  ...
  // 空の配列に変更
  projects: [],
  // 削除
  // projects: [
  //   { id: 1, name: 'MyProject01', updatedAt: '2020-04-01T12:00:00+09:00' },
  //   { id: 2, name: 'MyProject02', updatedAt: '2020-04-05T12:00:00+09:00' },
  //   { id: 3, name: 'MyProject03', updatedAt: '2020-04-03T12:00:00+09:00' },
  //   { id: 4, name: 'MyProject04', updatedAt: '2020-04-04T12:00:00+09:00' },
  //   { id: 5, name: 'MyProject05', updatedAt: '2020-04-01T12:00:00+09:00' }
  // ],
  ...
})

export const getters = {}

export const mutations = {
  ...
  setToast (state, payload) {
    state.toast = payload
  },
  // 追加
  setProjects (state, payload) {
    state.projects = payload
  }
}

export const actions = {
  ...
  // トーストデータをセットする
  getToast ({ commit }, toast) {
    toast.color = toast.color || 'error'
    toast.timeout = toast.timeout || 4000
    commit('setToast', toast)
  },
  // 追加
  // ユーザーのプロジェクト一覧をセットする
  getProjects ({ commit }, projects) {
    commit('setProjects', projects)
  }
}

確認してみよう

ここまでの変更ができたら、ログイン後のトップページを確認してみましょう。

Railsからのプロジェクトが返ってきていたら成功です。

2020-10-29 16-13-57

プロジェクトを表示するdefault.vue上のページでも、リロードしても正しくプロジェクトが表示されています。

2020-10-29 16-17-03

これは、ページ描画前にミドルウェアが稼働し、Railsにリクエストを行なっているためです。

ログアウト処理を行う

最後にログアウト時にプロジェクト一覧を空にします。

auth.jsのログアウトメソッドにアクションを追加しましょう。

front/plugins/auth.js
...
class Authentication {
	...
  // ログアウト業務
  logout () {
    this.$axios.$delete('/api/v1/user_token')
    this.removeStorage()
    this.store.dispatch('getCurrentUser', null)
    // 追加
    this.store.dispatch('getProjects', [])
  }
	...
}
...

以上でログイン認証の実装が完了です。

RailsとNuxt.jsのリポジトリをデプロイする

Rails、Nuxt.js共にデプロイを行います。

まずは「api」ディレクトリに移動してHerokuにpushします。

root $ cd api
api $ git add -A && git commit -m "add_projects_controller.rb"
api $ git push && git push heroku && cd ../front

「front」ディレクトリ ではブランチを切っているので、マージした後にpushします。

front $ git add -A && git commit -m "add_getProjects_middleware"
front $ git checkout master
front $ git merge 20201013_login_authentication
front $ git push && git push heroku && cd ..

Nuxt.jsデプロイ時の警告メッセージへの対応

Nuxtデプロイ時に下記のようなエラーメッセージが出た場合。

WARN  mode option is deprecated. Please use ssr: true for universal mode or ssr: false for spa mode and remove mode from nuxt.config

Nuxt v2.14で変更があり、mode: 'spa'書き方が非推奨となりました。

この変更はNuxt v2.14以下でも適用されます。(筆者の場合 v.2.13.3)

対応するには、nuxt.config.jsを以下のように変更します。

front/nuxt.config.js
export default {
  // 変更
  ssr: false,
  // 削除
  // mode: 'spa',
  ...
}

ちなみに、universalモードを指定する場合は、ssr: trueにします。

ただ、このプロパティはデフォルト値がtrue なので、追加しなくても自動でuniversalモードとなります。

参考: ssrプロパティ - Nuxt.js

変更した場合は、「front」ディレクトリ をもう一度pushしてください。

root $ cd front && git commit -am "edit_nuxt.config.js mode ssr: false"
front $ git push && git push heroku

本番環境を確認してみよう

「front」ディレクトリ以下のでHerokuを開きましょう。

front $ heroku open

ログインしてみると…。

あれ、有効なユーザーなのにログインできない…!!


これは、Cookieが正しく保存できていないことが原因です。

RailsのCookieにSameSite属性を追加する

2020年の2月よりセキュリティ強化のため、Chrome 80においてCookieにSameSite(セイムサイト)属性のデフォルト値が追加されました。

デフォルトはLaxで、この値はGET以外のリクエストにCookieを含めることができません。

クロスサイトでCookieを使用するには、SameSiteの値をNoneにし、かつSecure(セキュア)属性をtrueにする必要があります。

しかし、(現状の)RailsのコードではSameSite属性の変更ができません。

そこでSameSiteをNoneに設定するGemをインストールします。

api/Gemfile
...
gem 'jwt', '~> 2.2'

# 追加
# CookieのSameSite属性をNoneにする(Chrome 80対応)
# Doc: https://qiita.com/ahera/items/0c8276da6b0bed2b580c
gem 'rails_same_site_cookie'

group :development, :test do
  ...

apiサービスを再ビルドして、Gemをインストールしましょう。

root $ docker-compose build api

ビルドが完了したら、Gemのインストール確認を行います。

root $ docker-compose run --rm api bundle info rails_same_site_cookie
  * rails_same_site_cookie (0.1.8)
	...

それでは、「api」ディレクトリ上で再デプロイを行いましょう。

root $ cd api
api $ git add -A && git commit -m "add_gem_rails_same_site_cookie"
api $ git push && git push heroku

完了したら、もう一度アプリを開いてログインを確認してください。

api $ cd ../front && heroku open && cd ..

参考:

Chrome 80が密かに呼び寄せる地獄 ~ SameSite属性のデフォルト変更を調べてみた - Qiita

rails_same_site_cookie gemで、RailsアプリにChrome 80向けのSameSite属性を指定する - Qiita

新しい Cookie 設定 SameSite=None; Secure の準備を始めましょう - Google

本番環境のCookieを確認する

本番環境のCookieを確認するには、デベロッパーツールの

  1. 「Network」タブから
  2. 左の検索バーよりapiドメインを検索
  3. そのURLをクリックした状態で
  4. 「Cookie」タブを選択します

Cookieが

  • HttpOnlyにチェック
  • Secureにチェック
  • SameSiteがNone

になっていることを確認してください。

Cookieの有効期限内であれば、リロードしてもログインが維持できていますね。

2020-10-31 01-31-58

最後は「root」ディレクトリをpushして終わりましょう。

root $ git commit -am "finished_login_authentication" && git push

まとめ

今回は、Railsにデモプロジェクトを返すコントローラーを作成し、Nuxt.jsのミドルウェアから取得する設定を行いました。

これにより、本番の挙動に近いアプリケーションができました。


今回はログイン認証を目的にここまでの記事を書いてきたので、当初はデモプロジェクトを返すコントローラーの用意はありませんでした。

しかし、実際のアプリケーションでは、プロジェクトの取得は認証直後の動作になります。

より本番に近づけた方が良いだろうと思い今回の実装を行いました。。。結果!!

Safari上で致命的なバグを見つけることになりました。とほほ。。

これは次の記事で対応します。(より本番環境に近づけるって大切ね。)

このチャプターまとめ

チャプター「フロントエンドのログイン認証」では以下のことを行いました。

さて次回は?

この記事でログイン認証の実装が終われないのは、Safariのブラウザでログインできない問題を抱えているためです。

次回は、「Safariでログインできない問題」に対応します。

現状のherokuapp.comのドメインから、SSL化した独自ドメインに変更するため月$7の料金が発生します。

1ヶ月だけなら…という方だけお試しください。

あなたの力になれること
私自身が独学でプログラミングを勉強してきたので、一人で学び続ける苦しみは痛いほど分かります。そこで、当時の私がこんなのあったら良いのにな、と思っていたサービスを立ち上げました。周りに質問できる人がいない、答えの調べ方が分からない、ここを聞きたいだけなのにスクールは高額すぎる...。そんな方に向けた単発・短期間メンターサービスを行っています。下のサービスへお進みください。
独学プログラマのサービス
次の記事はこちら
1. このカテゴリーの歩き方 #01
【お知らせ】UdemyでRails × Nuxt.jsの動画を公開することになりました
1. このカテゴリーの歩き方 #02
アプリケーション仕様書
1. このカテゴリーの歩き方 #03
このカテゴリーの歩き方(まずはここをチェック)
1. このカテゴリーの歩き方 #04
(Docker+Rails6+Nuxt.js+PostgreSQL)=>Heroku 環境構築~デプロイまでの手順書
2. Docker入門 #01
Docker for Macをインストールする手順
2. Docker入門 #02
分かるDocker解説。仮想環境・コンテナ・Dockerイメージ・Dockerfileとは何か?
2. Docker入門 #03
分かるDocker解説。DockerComposeとは何か?
3. Dockerを使ったRails+Nuxt.js環境構築 #01
【Docker+Rails6+Nuxt.js】今回作成するアプリの開発環境の全体像を知ろう
3. Dockerを使ったRails+Nuxt.js環境構築 #02
【MacOS】Homebrew経由でGitをインストールする方法
3. Dockerを使ったRails+Nuxt.js環境構築 #03
Rails6を動かすAlpineベースのDockerfileを作成する(AlpineLinuxとは何か)
3. Dockerを使ったRails+Nuxt.js環境構築 #04
Nuxt.jsを動かすAlpineベースのDockerfileを作成する(C.UTF-8とは何か)
3. Dockerを使ったRails+Nuxt.js環境構築 #05
.envファイルを使ったdocker-compose.ymlの環境変数設計
3. Dockerを使ったRails+Nuxt.js環境構築 #06
Rails6・Nuxt.js・PostgreSQLを動かすdocker-compose.ymlファイルを作成する
3. Dockerを使ったRails+Nuxt.js環境構築 #07
docker-compose.ymlを使ってRails6を構築する(PostgreSQLパスワード変更方法)
3. Dockerを使ったRails+Nuxt.js環境構築 #08
docker-compose.ymlを使ってNuxt.jsを構築する
4. 複数プロジェクトのGit管理 #01
複数プロジェクトで行うGit管理の全体像を理解しよう(Gitサブモジュール解説)
4. 複数プロジェクトのGit管理 #02
【Git】既存の子ディレクトリをサブモジュール管理に変更する手順
4. 複数プロジェクトのGit管理 #03
【GitHub】秘密鍵の生成・公開鍵を追加・SSH接続するまでを画像で分かりやすく
4. 複数プロジェクトのGit管理 #04
【GitHub】リモートリポジトリの追加・サブモジュールのリンク設定を行う
5. RailsAPI×Nuxt.js初めてのAPI通信 #01
【Rails6】"Hello" jsonを返すコントローラを作成する
5. RailsAPI×Nuxt.js初めてのAPI通信 #02
【Nxut.js】axiosの初期設定を行う(baseURL・browserBaseURLを解説)
5. RailsAPI×Nuxt.js初めてのAPI通信 #03
【Rails6】Gem rack-corsを導入してCORS設定を行う(オリジン・CORSとは何か)
6. Heroku.ymlを使ったDockerデプロイ #01
デプロイ準備。Herokuへ新規会員登録を行いHerokuCLIをインストールする
6. Heroku.ymlを使ったDockerデプロイ #02
heroku.yml解説編。Docker環境のRails6をHerokuにデプロイする(1/2)
6. Heroku.ymlを使ったDockerデプロイ #03
HerokuCLI-manifestのデプロイ解説編。Docker環境のRails6をHerokuにデプロイする(2/2)
6. Heroku.ymlを使ったDockerデプロイ #04
Dockerfile解説編。Docker環境のNuxt.jsをHerokuにデプロイする(1/2)
6. Heroku.ymlを使ったDockerデプロイ #05
デプロイ完結編。Docker環境のNuxt.jsをHerokuにデプロイする(2/2)
7. モデル開発事前準備 #01
【Rails6】application.rbの初期設定(タイムゾーン・I18n・Zeitwerk)
7. モデル開発事前準備 #02
【Rails6】モデル開発に必要なGemのインストールとHirb.enableの自動化
7. モデル開発事前準備 #03
【Docker+Rails】A server is already running. Check /tmp/pids/server.pidエラーの対応
7. モデル開発事前準備 #04
【Docker】<none>タグのイメージを一括削除する & Rails .gitignoreの編集
8. ユーザーモデル開発 #01
Railsユーザーモデル作成。テーブル設計・ユーザー認証設計を理解する
8. ユーザーモデル開発 #02
Railsユーザーモデルのバリデーション設定(has_secure_password解説)
8. ユーザーモデル開発 #03
Railsバリデーションエラーメッセージの日本語化(ja.yml設定方法)
8. ユーザーモデル開発 #04
EachValidatorクラスのカスタムバリデーション設定(Rails6/lib以下読込)
8. ユーザーモデル開発 #05
Rails環境ごとにSeedデータ切り替えるseeds.rbの書き方
8. ユーザーモデル開発 #06
Rails6から導入された並列テストを理解する
8. ユーザーモデル開発 #07
Railsユーザーモデルバリデーションテスティング(name/email/password)
8. ユーザーモデル開発 #08
Nuxt.jsからRailsのユーザーテーブルを取得しHerokuにデプロイする
9. Nuxt.jsフロント開発事前準備 #01
【Nuxt.js2.13超解説】バージョンアップ手順と6つの新機能+2つの変更点
9. Nuxt.jsフロント開発事前準備 #02
Docker AlpineベースのNode.js上で動くNuxt.jsにVuetifyを導入する
9. Nuxt.jsフロント開発事前準備 #03
VuetifyにカスタムCSSを導入してオリジナルブレイクポイントを作る
9. Nuxt.jsフロント開発事前準備 #04
Nuxt.jsにnuxt-i18nを導入して国際化に対応する
10. ログイン前のレイアウト構築 #01
Nuxt.jsのレイアウト・ページ・コンポーネントの役割を理解しよう
10. ログイン前のレイアウト構築 #02
Nuxt.js ウェルカムページを構成するコンポーネントファイル群を作成しよう(1/4)
10. ログイン前のレイアウト構築 #03
Nuxt.js ウェルカムページにアイキャッチ画像・アプリ名・メニューボタンを表示しよう(2/4)
10. ログイン前のレイアウト構築 #04
Nuxt.js addEventListenerでスクロールを検知しツールバーの色を変化させよう(3/4)
10. ログイン前のレイアウト構築 #05
Nuxt.js ウェルカムページをレスポンシブデザインに対応させよう(4/4)
10. ログイン前のレイアウト構築 #06
Nuxt.js 会員登録ページのレイアウトファイルを作成しよう(1/4)
10. ログイン前のレイアウト構築 #07
Nuxt.js 名前、メール、パスワードのコンポーネントファイルを作成しよう(2/4)
10. ログイン前のレイアウト構築 #08
Nuxt.js 親子コンポーネント間の双方向データバインディングを実装する(3/4)
10. ログイン前のレイアウト構築 #09
Nuxt.js Vuetifyのv-text-fieldを使った会員登録フォームのバリデーション設定(4/4)
10. ログイン前のレイアウト構築 #10
Nuxt.js ログインページ実装とHerokuデプロイまで(router. replaceとpushの違いとは)
11. ログイン後のレイアウト構築 #01
Nuxt.js ログイン後のツールバーを作成しよう(inject解説)
11. ログイン後のレイアウト構築 #02
Nuxt.js アカウントメニューページ・ログアウト機能を実装しよう(nuxt-child解説)
11. ログイン後のレイアウト構築 #03
Nuxt.js ログイン後のトップページにプロジェクト一覧を表示しよう
11. ログイン後のレイアウト構築 #04
Nuxt.js プロジェクトページにVuetifyのナビゲーションドロワーを追加しよう
11. ログイン後のレイアウト構築 #05
Nuxt.js paramsIDからプロジェクトを検索してVuexに保存しよう
12. サーバーサイドのログイン認証 #01
JWTとは何か?(ruby-jwtのインストール)
12. サーバーサイドのログイン認証 #02
【Rails×JWT】ログイン認証解説とJWT初期設定ファイルの作成
12. サーバーサイドのログイン認証 #03
【Rails×JWT】トークン発行とデコードを行うAuthTokenクラスの作成
12. サーバーサイドのログイン認証 #04
【Rails×JWT】 ログイン判定を行うAuthenticatorモジュールの作成
12. サーバーサイドのログイン認証 #05
【Rails×JWT】UserクラスからJWTを扱うTokenizableモジュールの作成
12. サーバーサイドのログイン認証 #06
【Rails×JWT】AuthTokenクラスとAuthenticatorモジュールをテストする
12. サーバーサイドのログイン認証 #07
【Rails×JWT】JWTをCookieに保存するログインコントローラーの実装
12. サーバーサイドのログイン認証 #08
【Rails×JWT】ログインコントローラーのテストとHerokuデプロイ
13. フロントエンドのログイン認証 #01
【Rails×Nuxt.js】クロスオリジン通信でのCookie共有設定
13. フロントエンドのログイン認証 #02
【Nuxt.js】Railsからのログイン成功レスポンスをVuexに保存する
13. フロントエンドのログイン認証 #03
【Nuxt.js】ローカルストレージの有効期限を暗号化する(crypto-js解説)
13. フロントエンドのログイン認証 #04
【Nuxt.js】JWT有効期限内のユーザーをログインしたままにする実装
13. フロントエンドのログイン認証 #05
【Nuxt.js】ログイン前後のリダイレクト処理をミドルウェアで実装する
13. フロントエンドのログイン認証 #06
【Nuxt.js】ログイン失敗時のトースターをグローバルイベントとして作成する
13. フロントエンドのログイン認証 #07
【Nuxt.js】エラーページを作成する
13. フロントエンドのログイン認証 #08
【Rails×Nuxt.js】デモプロジェクトの作成とHerokuデプロイ(ログイン認証完)
14. 本番環境への対応 #01
【Rails×Nuxt.js】SafariのクロスサイトCookie保存拒否に対応する
SPA開発
ブログ構築
小ネタ集