LoginSignup
11
12

More than 3 years have passed since last update.

モダンな技術を全く知らないSIer5年目題Webサービスを作ることになったため0から勉強する〜Nuxt +メール認証+パスワードリセット〜

Posted at

はじめに

現在SIer5年目でjavascript(Jqueryのみ)、PHP(フレームワーク無し)を2年ほど、C#(Windowsアプリ)3年ほどやってきました。
色々なご縁があり、個人で最近Webサービスの立ち上げをやることになったのですが何せ本当にWebサービスを立ち上げるための知識がほぼ0に等しいです:sob:

ただ今後のキャリアを考えた時に今のままではいけないと思いチャレンジすることにしました。

まずは最初に技術を習得しないといけないので、学ぶ&アウトプットするために毎回投稿していこうと思います。
今後身についていこうと思ってるのは下記のような技術です。
AWS
Docker
CI/CD環境の構築
Laravel
Nuxt.js
今回はLaravel+Nuxtについて学んでいきます。

今回学ぶこと

Laravel+Nuxtでのメール認証機能とパスワードリセット機能を作成していこうかと思います。
今回はLaravel API側の実装をしていきます

参考サイト

jwt-auth公式サイト

こちらのサイトを参考にさせていただきました

メール認証の概要は個々がわかりやっすかったです。

前提

Laravel 5.8
Nuxt 2.5.4
jwt-auth

メール認証とパスワードリセットのAPI側実装はこちらで実装してるのでこちらを参照ください

最終的なソースコードはこちらになります。
※今回は、Nuxt側の実装でLaravel側も変更している箇所もあるのでそちらもソースコード確認してもらえたらと思います。

Nuxt側のvueファイルを作成する

フロント側はメール認証+パスワード再発行を実現するための画面とミドルウェアを作成します。

vue_file 概要
client/pages/auth/resetpassword.vue パスワードを変更する画面、パスワード変更時にトークンも一緒に送る
client/pages/auth/resendverify.vue メール認証のリンクを送るための画面
client/pages/auth/forgotpassword.vue メールアドレスを入力し、パスワード再発行メールを送信する画面
client/pages/auth/emailverification.vue メール認証中の際に表示する画面
client/middleware/emailVerify.js メール認証済みかチェックを行うミドルウェア

パスワードリセットを実装する

まずはパスワードリセットを実装するにあたっての処理の流れはこんな感じになります。

  1. ログイン画面からForgot Your Passwordリンクをクリックし、パスワード再発行のメールアドレスを入力するforgotpassword.vueを表示する
  2. メールアドレスを入力し、再発行ボタンを押下するとlocalhost/api/auth/password/emailAPIに対してリクエストが投げられ指定したアドレスにパスワード再発行メールを送る
  3. メールのリンクをクリックするとresetpassword.vueを表示し、リセットするパスワードを入力し、リンクに乗ってるqueryURLに対してリクエストを投げる
  4. パスワードリセットが完了したら、ログイン画面に遷移する

ログイン画面からforgotpassword.vueに遷移するリンクを作成

client/pages/auth/login.vue
<div class="form-group">
  <input type="submit" value="Login" class="btn btn-default w-100">
  <nuxt-link class="nav-link" to="/auth/forgotpassword">Forgot Your Password</nuxt-link>
</div>

パスワードリセット対象のメールアドレス入力画面を実装する

client/pages/auth/forgotpassword.vue
<template>
  <div class="container">
    <div class="col-md-6 offset-md-3">
      <div class="card mt-4">
        <div class="card-header">
          <p class="mb-0">ForgotPassword</p>
        </div>
        <div class="card-body">
          <b-alert variant="success" v-model="showSuccessAlert">I have sent a password reissue email</b-alert> <!-- パスワードリセットメールを送れたことをメッセージで表示する -->
          <form @submit.prevent="ReSendVerifyEmail">
            <div class="form-group">
              <label>Email</label>
              <input v-model="form.email" type="email" class="form-control" :class="{ 'is-invalid': errors.email }" placeholder="Email">
            </div>
            <div class="form-group">
              <input type="submit" value="SendResetLinkEmail" class="btn btn-default w-100">
            </div>
          </form>
        </div>
      </div>
    </div>

  </div>
</template>

<script>
  export default {
    middleware: 'guest', //ログインであればリダイレクトする
    data() {
      return {
        form: {
          email: '',
        },
        showSuccessAlert: false

      }
    },
    methods: {
      async ReSendVerifyEmail(){
        // パスワードリセットのメール送信APIを実行する
        await this.$axios.post('/auth/password/email', this.form)
          .then(data => {
            // 送信完了メッセージ表示
            this.showSuccessAlert = true;

          })
          .catch(err=> {

            console.log(err);
          });


      }
    }

  }
</script>

リセットするパスワード入力する画面

client/pages/auth/resetpassword.vue
<template>
  <div class="container">
    <div class="col-md-6 offset-md-3">
      <div class="card mt-4">
        <div class="card-header">
          <p class="mb-0">Register</p>
        </div>
        <div class="card-body">
          <form @submit.prevent="ResetPassword"> <!-- 標準のsubmitは実行しない -->
            <div class="form-group">
              <label>Email</label>
              <input v-model="form.email" type="email" class="form-control" :class="{ 'is-invalid': errors.email }" placeholder="Email">
              <div class="invalid-feedback" v-if="errors.email">
                {{ errors.email[0] }}
              </div>
            </div>
            <div class="form-group">
              <label>Password</label>
              <input v-model="form.password" type="password" class="form-control" :class="{ 'is-invalid': errors.password }" placeholder="Password">
              <div class="invalid-feedback" v-if="errors.password">
                {{ errors.password[0] }}
              </div>
            </div>
            <div class="form-group">
              <label>Password Confirmation</label>
              <input v-model="form.password_confirmation" type="password" class="form-control" :class="{ 'is-invalid': errors.password }" placeholder="Password">
              <div class="invalid-feedback" v-if="errors.password">
                {{ errors.password[0] }}
              </div>
            </div>
            <div class="form-group">
              <input type="submit" value="Register" class="btn btn-default w-100">
            </div>
          </form>
        </div>
      </div>
    </div>

  </div>
</template>

<script>
  export default {
    middleware: 'guest', //ログイン状態であればリダイレクトする
    data() {
      return {
        form: {
          email: '',                  // リセット対象のメールアドレス
          password: '',               // 新しいパスワード
          password_confirmation: '',  // 新しいパスワード確認
          token: ''                   // パスワードリセット実行するための一時的なトークン
        },
        requestUrl: ''
      }
    },

    created() {
      this.setQuery()
    },

    methods: {
      async ResetPassword(){ // パスワードリセットリクエストを投げる関数
        await this.$axios.$post(this.requestUrl, this.form)
          .then(data => {
            this.$router.push('/auth/login');
          })
          .catch(err=> {
            console.log(err);
          });
      },
      setQuery() { // getリクエストのパラメータを取得する関数
        this.requestUrl = this.$route.query.queryURL || ''; // パスワードリセットAPIのURL
        this.form.token = this.$route.query.token || '';    // パスワードリセットするために必要なToken
      },
    }

  }
</script>

メール認証を実装する

メール認証を実装するにあたっての処理流れはこんな感じになります

  1. ユーザ登録後にメール認証するためのメールを送信する
  2. メールからメール認証画面に遷移する
  3. メール認証画面では、queryURLにそのままリクエストを投げてメール認証を行い、成功したら画面遷移を行う
client/pages/auth/emailverification.vue
<template>
  <div class="container">
    <div class="col-md-6 offset-md-3">
      <div class="card mt-4">
        <div class="card-header">
          <p class="mb-0">During Verification Your Email Address</p>
        </div>
        <div class="card-body">
          <label>Validating your email address.</label>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  export default {
    middleware: 'auth', //未ログイン状態であればリダイレクトする
    data() {
      return {
        queryURL: ''
      }
    },

    async mounted() {
      const queryURL = this.$route.query.queryURL || '';
      if (queryURL != '') {
        await this.$axios.$get(queryURL)
          .then(data => {
            this.$auth.fetchUser();             // メール認証が完了したため、ユーザ情報を再取得する
            this.$router.push({name: 'index'});
          })
          .catch(err => {
            alert('メール認証が失敗しました。再度メール認証を行ってください。');
            this.$router.push('/auth/resendverify');
          });
      }
    },
  }
</script>

メール認証チェックを行い、メール認証が必要な画面にアクセスした場合、認証メールを再送する画面にリダイレクトする

メール認証チェックを行うミドルウェアを実装する

email_verified_atはLaravelのUserモデルにはデフォルト項目であり
メール認証された場合はここに日時が入ってるため
nullの場合は未認証と判断し、リダイレクトを行う

client/middleware/emailVerify.js
//メールアドレスが認証されていなければ、メール認証の送信画面に遷移する
export default function({ store, redirect, app }) {
  if(app.$auth.user['email_verified_at'] == null) {
    return redirect('/auth/resendverify');
  }
}

メール認証チェックを行う

ダッシュボードは未認証ユーザはアクセスできないようにする

client/pages/dashboard.vue
<template>
  <div class="container">
    <h1>Welcome to the dashboard</h1>
  </div>
</template>

<script>
  export default {
    middleware: 'auth',
    middleware: 'emailVerify' // メール認証チェックを行う
  }
</script>

認証メールの再送信画面を実装する

client/pages/auth/resendverify.vue
<template>
  <div class="container">
    <div class="col-md-6 offset-md-3">
      <div class="card mt-4">
        <div class="card-header">
          <p class="mb-0">Verify Your Email Address</p>
        </div>
        <div class="card-body">
          <b-alert variant="success" v-model="showSuccessAlert">I have sent a password reissue email</b-alert>
          <label>Email verification has not been completed yet.
            Please press the button below to complete e-mail authentication</label>
          <div>
            <b-button block variant="primary" @click="ReSendVerifyEmail">Verify Email</b-button>
          </div>
        </div>
      </div>
    </div>

  </div>
</template>

<script>
  export default {
    middleware: 'auth', //ログインしてなければリダイレクトする
    data() {
      return {
        form: {
          email: '',
        },
        showSuccessAlert: false,
      }
    },

    methods: {
      async ReSendVerifyEmail(){
        await this.$axios.post('/auth/email/resend', this.form)
          .then(data => {
            this.showSuccessAlert = true;
          })
          .catch(err=> {
            console.log(err);
          });
      },

    }

  }
</script>

まとめ

フロント側はLaravelのmake:authで作成されるViewを参考に作りました
独学なので、おそらくもっと良い実装方法はあるかと思います。。。
もしこうした方がいいとかこのサイトは参考になるよなどあれば教えてもらえると幸いです。

あと、ログイン情報を保持するなどの機能はまだ実装してないのでそちらも実装できたらと思います。

11
12
0

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
11
12