Nuxt.jsのauth moduleとrails APIモードを使用したJWT認証の実装方法について②

今回、Nuxt.jsのauth moduleとrails APIモードを使用したJWT認証実装のパート2としてNuxt.js(クライアントサイド)の実装手順を紹介していきます。前回のrails(サーバサイド)側の実装手順はこちらにまとめてあります。

Nuxt.jsのauth moduleとrails APIモードでJWT認証の実装方法について

今回はNuxt.jsとrails APIモードを使ったJWT認証(JSON Web Tokens)の実装についてです。JWT実装はサーバーサイド(rails)とクライアントサイド(Nuxt.jsの)二つのパートに分けて書いていきます。

環境

  • ruby 2.6.3
  • rails  6.0.2
  • bcrypt 3.1.7
  • nuxt 2.0.0
  • nuxtjs/auth 4.9.1

JWT認証の流れ

JWT認証の流れは以下の図のようになります。今回、クライアントサイト側の①ユーザーがメールアドレスとパスワードを入力のためのフォーム作成と③JWTトークンを生成しクライアント側に渡す部分のトークン受け取りとAuth Moduleを使用した認証部分の実装を中心にしていきます。

  1. ユーザーがメールアドレスとパスワードを入力
  2. サーバーサイド側でメールアドレスが存在するかDBを検索、signupの場合新しいレコードを作成。
  3. JWTトークンを生成しクライアント側に渡す。
  4. ユーザーが認証が必要なリソースが必要な時トークンを検証

Auth Moduleのインストールと初期設定

最初にauth moduleを使用するので、npm install @nuxtjs/authをインストールしていきます。auth moduleを使うと認証系の実装が簡単にできます。

npm install @nuxtjs/auth

インストールした後はnuxt.confing.jsにコードを追加していきます。modules内に‘@nuxtjs/auth’を追加するのと細かい設定の詳細はコードの下部にまとめてます。

modules: [
    // Doc: https://buefy.github.io/#/documentation
    'nuxt-buefy',
    '@nuxtjs/axios',
    '@nuxtjs/auth',
  ],
  auth: {
    redirect: {
      login: '/login',   // 未ログイン時に認証が必要なページにアクセスした際のリダイレクトURL
      logout: '/login',  // ログアウト時のリダイレクトURL
      callback: '/callback',   // Oauth認証等で必要となる コールバックルート
      home: '/',         // ログイン後のリダイレクトURL
    },
    strategies: {
      local: {
        endpoints: {
          login: { url: '/api/v1/login', method: 'post', propertyName: 'jwt' },
          user: false,
          logout: false
        }
      },
    }
  },

Redirect

  • login・・・未ログイン時に認証が必要なページにアクセスした際のリダイレクトURL
  • logout・・・ログアウト時のリダイレクトURL
  • callback・・・Oauth認証等で必要となる コールバックルート
  • home・・・ログイン後のリダイレクトURL

strategies endpoints

  • login・・・ログインメソッドが呼ばれた時のAPIのエンドポイント指定
  • user・・・ユーザー情報取得時のAPIのエンドポイント指定
  • logout・・・ログアウト時のAPIのエンドポイント指定

今回、callback,strategies endpointsのuser,logoutは使わないのでfalseでも問題ありません。

Formの作成

続いて、冒頭でも紹介した通り、ユーザーがメールアドレスとパスワードを入力するためのFormを作成していきます。ほぼHTMLなのでコードはシンプルです。簡単に説明しておくと@submit.prevent=”login()”はsubmitされた時にJavaScriptのEvent.preventDefaultと同じで意図しない画面遷移を防いでくれます。v-modelは双方向データバインディングを実現してくれるvue.jsの文法です。@click=”login()”はsubmitされた時にログインメソッドが発火します。

<template>
<div>
<form @submit.prevent="login()">
    <input id="name" v-model="email" type="email" name="name">
    <input v-model="password" type="password">
    <button type="submit" @click="login()">ログイン</button>
    

</form>
<button @click="user_is_authed()">auth 確認</button>
<button @click="auto_login()">User 情報</button>
<button @click="logout()">ログアウト</button>

</div>
</template>

データオブジェクトのプロパティにはemailとpasswordを記述します。

export default {
  data() {
    return {
        email: '',
        password: ''
      }
  },
}

rails consoleでユーザー作成

今回、ログイン機能のみの実装なので、rails consoleでユーザー作成する必要があります。User.create(email: “daiki@gmail.com”,password: “password”)これでDBにレコードが追加されます。

rails c
irb(main):005:0> User.create(email: "daiki@gmail.com",password: "password")
 (0.3ms)  BEGIN
  User Create (9.0ms)  INSERT INTO "users" ("email", "password_digest", "created_at", "updated_at") 
VALUES ($1, $2, $3, $4) RETURNING "id"  [["email", "daiki@gmail.com"], ["password_digest", "$2a$12HKZoS78iug524pn8Hauzd82"], ["created_at", "2020-05-12 01:19:13.478413"], ["updated_at", "2020-05-12 01:19:13.478413"]]
   (0.6ms)  COMMIT
=> #<User id: 13, email: "daiki@gmail.com", password_digest: [FILTERED],
 created_at: "2020-05-12 01:19:13", updated_at: "2020-05-12 01:19:13", login_token: nil, login_token_valid_until: nil>

ログインメソッド作成とリスポンス の確認

先ほどのFormでメールアドレスとパスワード入力後にsubmitボタンが押下した時にログインメソッドが発火します。サーバーサイドでメールアドレスとパスワードの検証をクリアするとリスポンス でJWTトークンが返されます。auth moduleを使うとtokenなどをlocal storageに保存してくれるので便利です。console.logでthis.$auth.loggedInを記述してTrueであればログインできている状態です。

login() {
        this.$auth.loginWith('local',{
        data: {
              email: this.email,
              password: this.password
          }
        }).then((response) => {
          console.log(response)
        },
        (error) => {
          console.log(error)
        })
        
      
    },

JWTトークンを元にユーザー情報をGETする

ログインができたので次はログイン成功後、ユーザー情報を取得できるか確認していきます。エンドポイントは前回作成した/api/v1/auto_login’を指定します。Authorization: Bearer <token>の形でサーバーサイドにリクエストを送ります。localStorage.idTokenを指定してあげるとログイン時に保存されたトークンを取得できます。下の画像がパスワードを含めた全てのユーザーを取得しています。セキュリティ上よくないので必要なユーザー情報だけを取得するようにしましょう。

auto_login () {
   const ret = await this.$axios.$get('/api/v1/auto_login',
       { headers:{"Authorization" :`Bearer ${localStorage.idToken}`
   }}) 
 }
user_is_authed () {
      const ret = await this.$axios.$get('/api/v1/user_is_authed',
        { headers:{"Authorization" :`Bearer ${localStorage.idToken}`
        }}) 
       console.log(ret)

      },

ログアウトメソッドの作成

最後にログアウト用のメソッドを用意してあげます。Auth moduleを使用するとthis.$auth.logout();を記述するだけでログアウトが完了します。

logout() {
      this.$auth.logout();
        },

以上がNuxt.jsのauth moduleとrails APIモードを使用したJWT認証の実装方法についてついてでした。JWT認証を実装するまではあまり理解していなかったので、概要把握できたのは良かったです。一方でJWTを利用する際は、JWTトークンを保存する場所によって発生する脆弱性についても深掘りして理解指定く必要があります。

>文系エンジニア大学生の技術ブログ

文系エンジニア大学生の技術ブログ

社会が多様化していく中、大学生の学生生活も多様であるべきと考えています。主にエンジニア向けにITやプログラミングなどの技術系と大学生向けに休学、留学、海外生活、トビタテ留学、長期インターンに関する記事を書いています。