豆腐とコンソメ

豆腐とコンソメ

もろもろのプログラム勉強記録

Vue.js + FIrebaseのEメール+パスワード認証をためす

日記

Nuxt.js + Firebaseで、お酒の写真とコメントを管理するWebアプリをつくってる。

f:id:konoemario:20181119174520p:plain
作成中のWebアプリ
使用ユーザーは奥さんの1名だけ。

とりあえずなにかしらWebアプリを完成させたい、という目標を達成するために必要最低限の機能は一通り実装できた。

使用ユーザーは奥さん1名だけなので、ログイン機能なんかいらないんだけれども、FirebaseのRealtimeDatabseのルールが誰でも書き込める、だとさすがにどうなのかなという思いから認証機能を実装することにした。
※認証機能よりも画像の表示をなんとかしたほうがいいのはわかってるんだ。

本題


ユーザーの管理をFirebaseで行う

FIrebaseでの認証は以前、Udemyの以下のNuxt.jsの講習でさわったことがあったので、以下の講習内容と

https://www.udemy.com/nuxtjs-vuejs-on-steroids/

Firebase公式の内容で復習を行うことにしました。
Firebase でユーザーを管理する  |  Firebase


Eメールとパスワードでユーザー管理する

Firebaseの認証方法はいろいろあって、一番初歩的なログイン画面をつくって、Eメール、パスワードを入力させて認証するものから、Google認証のようにOAuth2をベースにした認証やらなんやらがいっぱいある。

このWebアプリのために、ユーザーとパスワードを管理してもらうのは申し訳ないので、最終的にはGoogle認証等に移行するつもりですが、とりあえずは初歩的なEメールとパスワード認証を使ってみることにします。

Firebaseの認証について

FirebaseのAuthenticationという機能で特段サーバ等を用意せずともユーザーの管理ができてしまう恐ろしい子。

Firebaseのコンソールで「Authencication」⇒「ユーザー」を参照すると、後述する処理で登録したユーザーの一覧を参照することができる。

f:id:konoemario:20181119121402p:plain
登録済みのユーザー

こちらのFirebaseのAuthenticationの機能と、データを管理するFirebaseのRealtimeDatabaseを組み合わせて、ログイン機能を実装していく。

 まずはログイン画面を作成する

とりあえずは、こんな感じのログイン画面を作成します、

f:id:konoemario:20181119120339p:plain
ログイン画面

画面は、login.vueというVueコンポーネントで実装しました。

login.vue

<template lang="pug">
.login
  label.label email
  input.input(type="text" v-model="email")
  label.label password
  input.input(type="text" v-model="password")
  button.button(@click="login") login
</template>

<script>
  export default {
    data() {
      return {
        email: '',
        password: '',
      }
    },
    methods: {
      async login() {
       //ボタンを押したときの処理をかく  
     }
   }

firebase.authを使わない方法

認証を行うにあたっては、firebase.authという便利なパッケージがあるのでこちらを使うのが基本だ思う。 だけど、前述のUdemyの講習ではfirebase.authを使っていなかったので、復習の意味を込めて一旦firebase.authを使わないでやってみます。

といっても、firebase.authの内部でやっていることを、直接やるだけの話です。


さきほどのlogin.vueについて、ログインボタンを押した場合の処理を追記します。

login.vueを抜粋

//HTMLは省略  
<script>
  export default {
    data() {
      return {
        email: '',
        password: '',
      }
    },
    methods: {
      async login() {
        // 既存ユーザーの認証はこっち
        //const url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=' + process.env.fbAPIKey    

        // 新規ユーザーの認証
        const url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=' + process.env.fbAPIKey    

        try {
          const token = await this.$axios.$post(url ,{
            email: this.email,
            password: this.password,
            returnSecureToken: true
          })

          //token取得して保存する
          localStorage.setItem('token', token.idToken)
          localStorage.setItem('tokenExpiration', token.expiresIn)
        } catch(error) {
          console.log(error)
        }
      }
    }
  }
</script>


Firebaseのユーザーの作成 or ユーザーの認証のエンドポイントに対して、EメールとパスワードとfirebaseのAPIKeyをつけてなげるだけです。

上記APIは以下のドキュメントを参考にしています。
https://firebase.google.com/docs/reference/rest/auth

APIのキーは、Firebaseのコンソール画面にある↓のような箇所に書いてあるやつですね。

f:id:konoemario:20181119115914p:plain
FirebaseのAPIキー


さて、上記login.vueに記載されている処理を実行すると、以下のようなレスポンスが返ってきました。

ユーザーの新規作成APIの結果

{
  "error": {
    "code": 400,
    "message": "OPERATION_NOT_ALLOWED",
    "errors": [
      {
        "message": "OPERATION_NOT_ALLOWED",
        "domain": "global",
        "reason": "invalid"
      }
    ]
  }
}


どうやらFirebaseのAuthenticationの設定でログイン方法の設定を行っておく必要があるみたいです。

f:id:konoemario:20181119120111p:plain
ログイン方法のメール + パスワードを有効にする


上記設定を行った後に、再度適当なメールアドレス、パスワードを設定したところ、無事リクエストが通りました。

ユーザーの新規作成APIの結果

{
  "kind": "identitytoolkit#SignupNewUserResponse",
  "idToken": "省略",
  "email": "tekitouna@email.com",
  "refreshToken": "省略",
  "expiresIn": "3600",
  "localId": "b7NUgSEZIZavcPVADGR27Fg0XgZ2"
}


リクエストの結果、idTokenなるものが返ってきたので、これをlocalstorageに保存しておくことにします。
このtokenをRealTimeDataBaseを参照するREST APIに渡して、tokenをもとに認証されたユーザーのみデータベースの内容を書き込むことができる、という設定に変更することで今回やりたいことが実現できそうです。



firebase.authを使う方法

さきほどは、https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword等のAPIを直接叩いていましたが、通常はfirebase.authの機能を使えばよさそうです。

なので、さっそくfirebaseのパッケージをインストールします。

firebaseをインストール

$ npm install --save firebase

今回はNuxt.jsを使用しているので、pluginとしてFirebaseの初期設定処理を行っています。

初期設定の値は、ウェブアプリに Firebase を追加で取得できる値を設定します。
また、firebaseにはいろんなパッケージが含まれているので、必要最低限のものだけimportするようにします。
ここではfirebaseのコアであるfirebase/appと認証のパッケージであるfirebase/authをimportします。

firebase.js

import firebase from 'firebase/app'
import 'firebase/auth'
import 'firebase/database'

if (!firebase.apps.length) {
  firebase.initializeApp({
    apiKey: process.env.fbAPIKey,
    //以下省略
  })
}

export default firebase


あとは、さきほど同様にlogin.vueでfirebase.authの認証用のメソッドを呼ぶだけになります。

login.vueを抜粋

<script>
  import firebase from '~/plugins/firebase'

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

        try {
          // 新規ユーザーの作成
          //const user = await firebase.auth().createUserWithEmailAndPassword(this.email, this.password)

          // 既存ユーザーの認証 
          const user = await firebase.auth().signInWithEmailAndPassword(this.email, this.password)

          const token = await firebase.auth().currentUser.getIdToken(true)
          localStorage.setItem('token', token.idToken)
        } catch(error) {
          console.log(error)
        }
      }
    }
  }
</script>


一点、firebase.authを使わなかった場合、tokenの値は直接取得できていましたが、firebase.authではtokenを取得するためにfirebase.auth().currentUser.getIdToken()を使う必要があるみたいです。
こちらですが、tokenの有効期限が切れた後に、もっかい呼ぶと、リフレッシュトークンをもとに、新しいtokenを返却してくれるという素晴らしいものになっているみたいです。

当初はtokenの有効期限もクライアント側に保持して、意識する必要があるのかななんて思ってたんですが、getIdTokenを使えば、有効期限を気にしなくてもよくなるのかしら。


tokenを使ってRealtimeDatabaseのREST APIの認証を行う

さきほど取得したtokenを使って、RealtimeDatabaseの認証の設定を行ってみます。

RealtimeDatabaseはデータを取ってくるだけであれば、KEY名に.jsonをつけてあげるだけで簡単に取得できますね。 私の場合、articlesとうキーにオブジェクトをぶらさげているので、以下のような形になります。

REST APIでデータを取得する

https://自分のrealtimedatabaseのdomain/articles.json


現状、上記にアクセスすると誰でもarticles.jsonの内容を参照することができます。
これをログインしたユーザーのみ参照できる、という設定に変えてみます。

Firebaseのコンソール画面より、「Database」⇒「RealtimeDatabase」⇒「ルール」を開き、以下のようにreadの設定にauth!=nullを設定しています。

authは、認証されたユーザーの情報が設定されるみたいです。

f:id:konoemario:20181119125008p:plain
ruleを設定する

実際に試してみると、さきほどまでアクセスできていたhttps://自分のrealtimedatabaseのdomain/articles.jsonを参照すると、Permission Deniedと言われます。

しかしhttps://自分のrealtimedatabaseのdomain/articles.json?auth=取得したidTokenをつけてアクセスすることで、REST APIが認証され、articles.jsonの内容を取得することができました。

ものすごく簡単にできちゃいますね。
この調子でGoogle認証もやってみたいと思います。