135
132

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.

【Nuxt.js】Auth Moduleでログイン機能を実装する ( + Ruby on Rails・devise_token_authで認証を行う)

Last updated at Posted at 2020-03-06

この記事の目的

Nuxt.jsアプリのAuth Module使ってログイン機能を実装するための記事です。

また、記事の最後にはRuby on Railsのdevise_token_authを使ってトークンベースで認証する方法も記載しています。

前提

  • Nuxt.jsのアプリが用意されていること
  • Nuxt.jsアプリとログインAPI用のエンドポイントはPOSTメソッド・/auth/sign_inとする

ログインの流れ

  1. 【ユーザ】 Nuxtアプリのログイン画面でEメールとパスワードを入力し、「送信」ボタンを押す
  2. 【Nuxtアプリ】 パスワードとEメール情報を付与し、APIの/auth/sign_inにHTTPリクエストを送信する
  3. 【API】 NuxtアプリからHTTPリクエストを受け取り、パスワードとEメールで認証をする。認証が完了し、HTTPレスポンスを返す
  4. 【Nuxtアプリ】 APIからHTTPレスポンスを受け取る
  5. 【Nuxtアプリ】 レスポンスヘッダー情報をVuex storeに保存する
  6. 【Nuxtアプリ】 ユーザをログイン画面からホーム画面へリダイレクトする
  7. 【Nuxtアプリ】 Vuex storeを確認し、ログイン済みであればログインしていないとアクセスできないページへのアクセスを許可する。ログイン済みでなければ、ログインページにリダイレクトする。

事前知識

Auth Moduleとは

**Auth Module**とは、Nuxt.jsプロジェクトにJWT(JSON Web Tokens)またはOAuth認証を使ってログイン機能を簡単に実装できるライブラリです。

ログイン機能は一から実装すると色々なことに気を使わないといけなくて大変なのですが、複雑な仕事を担ってくれているのがこのAuth Moduleです。

JWTとは

JWT(JSON Web Token)とは、JSONというデータ構造で情報が表現されたフォーマットです。安全にデータを運ぶために使用されます。JWTの詳しい説明は、下記の記事がわかりやすいです。
JSON Web Token の効用
認証におけるJWTの利用について

ステップ

Axiosを導入

Axiosを使用するので、Axiosを導入していない場合は下記のコマンドを実行してください。

$ yarn add @nuxtjs/axios
または
$ npm install @nuxtjs/axios

Auth Moduleを導入

下記コマンドでAuth Moduleを導入します。

$ yarn add @nuxtjs/auth
または
$ npm install @nuxtjs/auth

Vuex store用にindex.jsファイルを作成

Auth moduleはVuex storeを使ってユーザの情報やログイン情報を管理します。そのためVuex store用のファイルを用意しておく必要があります。nuxtアプリのルートディレクトリにVuex storeというディレクトがあるので、そこにindex.jsという名前のファイルを作成しておきます。index.jsの中はからっぽで大丈夫です。

image.png

nuxt.config.jsを修正

nuxt.config.jsのmodulesに下記を加えます。

nuxt.config.js
modules: [
  '@nuxtjs/axios',
  '@nuxtjs/auth'
]

さらに、nuxt.config.jsに下記を追加します。

nuxt.config.js
  axios: {
    baseURL: 'http:localhost:3000'
  },
  auth: {
    redirect: {
      login: '/login', 
      logout: '/login',
      callback: false,
      home: '/'
    },
    strategies: {
      local: {
        endpoints: {
          login: { url: '/auth/sign_in', method: 'post', propertyName: false },
          logout: false,
          user: false
        }
      }
    }  
  }

axios

baseURLを設定する事で、HTTPリクエストを送信するベースとなるURLが自動で設定されます。
ログインする際のRailsアプリのURLは/auth/sign_inのみですが、NuxtアプリでHTTPリクエストを送信する時に、axiosがhttp:localhost:3000/auth/sign_inにURLを自動的に変えてくれます。これを設定しないとうまくいかないので、忘れない様にしましょう。

redirect

  • redirect: ユーザの状態(ログインしているか否か)でどこにリダイレクトするかなどを設定します。
  • login: ユーザが未ログイン時、ログインしていないとアクセスできないページにアクセスした際のリダイレクトURLです。
  • logout: ユーザがログアウトした時のリダイレクトURLです。
  • callback: Oauth認証等で必要となるコールバック用URLです。今回はOauth認証は使わないのでfalseです。
  • home: ログイン成功後にリダイレクトするURLです。

strategies

  • strategies: Auth Moduleのどの認証ロジックを使うかを指定します。JWTとCookieを使うlocalと、OAuthを使うsocialの2種類があります。今回はJWTを使って認証を行うので、localです。
  • endpoints: どのメソッドが呼ばれた際にAPIのどのエンドポイントに飛ばすかを指定します。(logoutはログアウト用のAPIエンドポイントを指定しますが、今回は実装しないのでfalseです。同様にユーザ情報を取得するためのuserもfalseにしています。)

ログイン画面の作成

次にログイン画面を作っていきます。(今回は、Vuetifyというマテリアルデザインフレームワークを使っているので、HTMLタグはVuetify仕様になっています。)

login.vue
<template>
  <div class="mt-3">
    <v-card class="mt-5 mx-auto" max-width="600">
      <v-form ref="form" v-model="valid" lazy-validation>
        <v-container>
          <v-row justify="center">
            <p cols="12" class="mt-3 display-1 grey--text">
              ログイン
            </p>
          </v-row>
          <v-row justify="center">
            <v-col cols="12" md="10" sm="10">
              <v-text-field
                v-model="email"
                label="Eメールアドレス"
              />
              <p class="caption mb-0" />
            </v-col>
          </v-row>
          <v-row justify="center">
            <v-col cols="12" md="10" sm="10">
              <v-text-field
                v-model="password"
                type="password"
                label="パスワード"
              />
            </v-col>
          </v-row>
          <v-row justify="center">
            <v-col cols="12" md="10" sm="10">
              <v-btn
                block
                class="mr-4 blue white--text"
                @click="loginWithAuthModule"
              >
                ログイン
              </v-btn>
            </v-col>
          </v-row>
        </v-container>
      </v-form>
    </v-card>
  </div>
</template>

<script>
export default {
  data () {
    return {
      password: '',
      email: ''
    }
  },
  methods: {
    async loginWithAuthModule () {
      await this.$auth.loginWith('local', {
        data: {
          email: this.email,
          password: this.password
        }
      })
        .then((response) => {
          return response
        },
        (error) => {
          return error
        })
    }
  }
}
</script>


すると、下記の様な画面が出来上がります。
image.png

ここでAPIのUsersテーブルに保存してあるメールアドレスとパスワードを入力しログインボタンを押します。パスワードとメールアドレスは下記の様なJSONとしてAPIに送信されます。

{
  "email": "momoko@test.xom",
  "password": "password"
}

認証が通りAPIからHTTPレスポンスが返ってくると、ホーム(/)ページにリダイレクトされるはずです。

Image from Gyazo

ブラウザのディベロッパーズツールのコンソールで$nuxt.$storeを見てみます。ログイン後は$nuxt.$store.state.auth.loggedIntrueになっていることがわかります。

ログイン前
image.png

ログイン後
image.png

middlewareを設定する

middlewareを設定する事で、下記を自動化することができます。

  • ログインしていなかったら、強制的にログイン画面へリダイレクトする
  • ログインしていている状態でログイン画面にアクセスしようとするとホームへリダイレクトする

nuxt.config.jsでmiddlewareの設定を加えます。

nuxt.config.js
  router: {
    middleware: ['auth']
  }

これだけ!これでログインしている状態で/loginにアクセスしようとしてもホームにリダイレクトされるはずです。(サーバの再起動を忘れずに!)

ログインしてない時もこのページにはアクセスさせたい!という場合があると思います。そんなときは、そのページのVueファイルのscriptにauth: falseを入れてあげましょう。

<script>
export default {
  auth: false,
}
</script>

Ruby on Railsのdevise_token_authでログイン実装・認証をする

ログイン・認証の流れ

Auth Moduleとdevise_token_authを使ってのログイン・認証の流れは下記です。

  1. 【ユーザ】 Nuxtアプリのログイン画面でEメールとパスワードを入力し、「送信」ボタンを押す
  2. 【Nuxtアプリ】 パスワードとEメール情報を付与し、Railsアプリの/auth/sign_inにHTTPリクエストを送信する
  3. 【Nuxtアプリ】 認証されるとRailsアプリからレスポンスが返ってくる。レスポンスヘッダーのaccept-tokenuidclientを情報をVuex storeに保存する
  4. 【Nuxtアプリ】 ユーザをログイン画面からホーム画面へリダイレクトする
  5. 【Nuxtアプリ】 axiosでHTTPリクエストを送る時にaccept-tokenuidclientをリクエストヘッダーに付与し認証済みであることをRailsアプリに教える

これが基本的な認証の流れです。

HTTPレスポンスのヘッダーにアクセスしたい・・・!

さて、ここから超ハマったパートです。Ruby on Railのdevise_token_authでユーザ認証をやりとりするには、ログイン成功後にRailsアプリから返ってくるHTTPレスポンスのaccess-tokenclientuidを取り出しVuex storeに保存、axiosでHTTPレスポンスを送信する度にこの3つをヘッダーに付与する必要があります。

この3つを付与せずにHTTPリクエストを送信する場合、Railsアプリは認証されていないと判断し、401 Unauthorizedのエラーを返します。

ということで、ログインが成功したらHTTPレスポンスのヘッダーにあるaccess-tokenclientuidにアクセスし、Vuex storeに保存してあげたい。でも、この3つにどうやってアクセスしたらええんや!?

・・・めちゃくちゃはまりました。

調べた結果、Auth ModuleのIssueに対応策が書いてありました。

image.png

どうやらAuth ModuleはlocalStorage(Vuex store)access-tokenclientuidなどの認証情報を自動的に保存してくれる様です。そのため、ログイン成功時は、何もする必要はありません。

ただし、HTTPリクエストを送る時にlocalStorageからこの3つのデータを取り出し、HTTPリクエストのヘッダーにセットする必要がありますよね。

ということで、pluginsディレクトリにaxios.jsを作成し下記のコードを書いたところ、無事認証がされる様になりました!

nuxt.config.js
 // configの設定も忘れずに!
  plugins: [
    { src: '~/plugins/axios.js', ssr: false }
  ]
plugins/axios.js
export default function({ $axios }) {
  $axios.onRequest(config => {
    config.headers.client = window.localStorage.getItem("client")
    config.headers["access-token"] = window.localStorage.getItem("access-token")
    config.headers.uid = window.localStorage.getItem("uid")
    config.headers["token-type"] = window.localStorage.getItem("token-type")
  })

  $axios.onResponse(response => {
    if (response.headers.client) {
      localStorage.setItem("access-token", response.headers["access-token"])
      localStorage.setItem("client", response.headers.client)
      localStorage.setItem("uid", response.headers.uid)
      localStorage.setItem("token-type", response.headers["token-type"])
    }
  })
}

他にも方法があるかとは思いますが、今のところはこれが一番簡単そうです。別の方法知ってるよ〜という方がいらっしゃったらぜひ教えてください。

135
132
2

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
135
132

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?