このチャプターで達成すること
チャプター「フロントエンドのログイン認証」では、ログインページの「ログインする」ボタンをクリック後のアクションを完成させます。
処理の流れは、
- 「ログインする」をクリック
- Railsの
/api/v1//user_token
にメールとパスワードを送信 - 認証に成功した場合、
- Vuexにユーザーを保存
- ローカルストレージに有効期限を保存
- ログイン後ページにリダイレクト
- 認証に失敗した場合
- 404エラーは、トースターを出力
- その他のエラーはエラーページにリダイレクト
となります。
今回達成すること
今回は、
- Nuxt.jsからRailsへログインリクエストを送る設定と、
- クロス(異なる)オリジン通信でのCookie共有設定を行います。
オリジンとは?
ウェブコンテンツにアクセスするために使われる、
- プロトコル(http)+ ドメイン(localhot)+ ポート番号(8080)
の3つを組み合わせたもの(http://localhost:8080)をオリジンと言います。
ブランチ切っとく
作業に入る前に「front」ディレクトリでブランチを切ります。
root $ cd front
front $ git checkout -b 20201002_login_authentication
front $ git branch
* 20201002_login_authentication
master
front $ cd ..
root $
ログインページからRailsにリクエストする
Nuxt.jsのログインページである「pages」ディレクトリの
login()
メソッド をまるっと書き換えてください。
front/pages/login.vue
...
export default {
layout: 'beforeLogin',
data () {
return {
isValid: false,
loading: false,
params: { auth: { email: '', password: '' } }
}
},
methods: {
// 書き換え
async login () {
this.loading = true
if (this.isValid) {
await this.$axios.$post('/api/v1/user_token', this.params)
.then(response => this.authSuccessful(response))
.catch(error => this.authFailure(error))
}
this.loading = false
},
// 追加
// ログイン成功
authSuccessful (response) {
console.log(response)
},
// ログイン失敗
authFailure ({ response }) {
console.log(response)
}
// 削除
// login () {
// this.loading = true
// setTimeout(() => {
// this.$store.dispatch('login')
// this.$router.replace('/')
// this.loading = false
// }, 1500)
// }
}
}
クリックしたと同時に、loading
フラグがtrue
になり、バリデーションが通った場合のみRailsにリクエストを送ります。
Railsからのレスポンスを確認する
それではコンテナを起動して、レスポンスが返ってきているか確認しましょう。
root $ docker-compose up
有効なパラメーターを投げる
http://localhost:8080/login にアクセスして、有効なパラメーターを投げます。
ログインフォームにはデータベースに保存されている
- ユーザーメールアドレス「user0@example.com」と
- パスワード「password」を入力してください。
コンソールを確認し、レスポンスが200、Railsからはexp
とuser
が返ってこれば成功です。
Cookieを確認する
RailsがCookieを保存しているかを確認するには、デベロッパーツールの「Application」タブを開きます。
「Storage」=>「Cookies」=>「http://localhost:8080」を確認します。
「access_token」というキーとトークンが保存されていれば成功です。
うん、無い。失敗ですね。
クロスオリジン通信でのCookie共有設定
今回の仕様では、
- Cookieを発行するドメイン「localhost:3000」
- Cookieを保存するドメイン「localhost:8080」
となっており、異なるオリジン間でCookieを共有します。
このようなクロスオリジン通信でのCookie共有は、双方に設定が必要です。
Nuxt.jsからのリクエスト設定
Nuxt.js側では、axios
プロパティに、credentials: true
を追加します。
front/nuxt.config.js
export default {
...
axios: {
// 追加
// クロスサイトリクエスト時にCookieを使用することを許可する
// Doc: https://axios.nuxtjs.org/options/#credentials
credentials: true
},
...
}
-
credentials(クレデンシャル)
... リクエストヘッダーのwithCredentials(ウィズ クレデンシャル)
フラグをtrue
にする。withCredentials
は、クロスオリジンのクライアント資格情報を使用して、リクエストを行うことを許容するか否かを設定するフラグです。(デフォルトはfalse
)true
にすることで、- クロスオリジンリクエストの際に、Cookieを送信することができ、
- また、クロスオリジンレスポンスのCookieを受け取ることができます。
今回のアプリではリクエストの際に、ほぼほぼCookieを使用するのでaxios
全体に設定を行いました。
個別にcredentials
の設定を行いたい場合は、リクエスト時にオプションを設定します。
axios参考コード
this.$axios.$post('/api/v1/user_token', this.params, { withCredentials: true })
RailsのCookie受取り設定
Rails側で、クロスオリジンリクエストのCookieを使用するには、gem cors(コルス)
の設定ファイル、credentials
フラグを追加します。
api/config/initializers/cors.rb
# Be sure to restart your server when you modify this file.
# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests.
# Read more: https://github.com/cyu/rack-cors
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins ENV["API_DOMAIN"] || ""
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head],
# 追加
# クロスオリジンのCookieを使用するための設定
credentials: true
end
end
RailsのAPIモードではCookieを処理するミドルウェアがありません。
api/config/application.rb
...
module App
class Application < Rails::Application
...
# 追加
# Cookieを処理するmeddlewareを追加(APIモードにはデフォルトで入っていない)
config.middleware.use ActionDispatch::Cookies
config.api_only = true
end
end
ここまでの変更ができたら、新しいターミナルを開き、apiサービスを再起動しましょう。
root $ 「Command」+「T」# 新規タブを開く
root $ docker-compose restart api
もう一度ログインする
再起動が確認できたら、もう一度http://localhost:8080/login にアクセスして、ログインします。
access_token
がセットされました。
- Expires(有効期限)は2週間後
- HttpOnlyにはチェックが入っていること
を確認してください。
Secure(https通信時のみCookieを使用する)フラグはチェックが入っていませんが、本番環境では有効になります。
Cookieの有効期限のタイムゾーン
Cookieの法定タイムゾーンはGMT(グリニッジ標準時)と決められており、expires
はGMTで保存されます。
また、有効期限を判断するために使用されるタイムゾーンもGMTです。
GMTは日本時間マイナス9時間で、UTC(世界標準時)と一致します。
参考: Cookie仕様 日本語訳
Cookieを削除する
確認が取れたらaccess_token
にカーソルを合わせ、「deleteキー」でCookieを削除してください。
axiosのグローバル設定ファイルを本番環境に対応
「plugins」ディレクトリにある
このログを開発環境のみ出力するよう編集します。
開発環境の場合にtrue
を返す、isDev
プロパティを追加してください。
front/plugins/axios.js
// isDev追加
export default ({ $axios, isDev }) => {
// リクエストログ
$axios.onRequest((config) => {
// if (isDev) 追加
if (isDev) { console.log(config) }
})
// レスポンスログ
$axios.onResponse((config) => {
// if (isDev) 追加
if (isDev) { console.log(config) }
})
// エラーログ
$axios.onError((e) => {
console.log(e.response)
})
}
コミットする
今回はここまでにしておきましょう。
「api」「front」それぞれをコミットします。
root $ cd api
api $ git commit -am "add_credentials_to_cors"
api $ git push
api $ cd ../front
front $ git commit -am "add_login_request_to_login.vue" && cd ..
root $
まとめ
今回は、Nuxt.jsからログインリクエストを送り、正しくCookieが保存できるよう、Nuxt.jsとRails双方にcredentials
の設定を行いました。
Railsは、このCookieのトークンを使ってNuxt.jsのリクエストが正しいかを判断します。
ただ、Nuxt.jsからはこのトークンにアクセスできないので、ログイン状態を他の値を使って判定する必要があります。
この辺りはもう少し先で実装します。
次回は?
Railsから返ってきた有効期限(exp
)とユーザーをNuxt.jsに保存します。
- 有効期限はローカルストレージに、
- ユーザーはVuexに
それぞれ保存します。
それではまた次回お会いしましょう。