cloud service, nodejs

[Node.js] 使用 Firebase 對於 Web Authentication 帳號建立與認證

81 / 100

[Node.js] 使用 Firebase 對於 Web Authentication 帳號建立與認證

Vuetify SPA 開發 建置了基本的框架,接下來將與 Firebase 的認證機制作整合。首先,依 Firebase 對於 Realtime Database 的初步使用 先申請一個專案,並在此專案加入對 web app 的支援,此時應該可以得到 Firebase 對此的 config 資料。

展示網址 : https://polinwei.github.io/vue2-vuetify/

 

  • 設定 Realtime Database & 認證機制

建立一個節點 logs 可以供帳號註冊及登入時,記錄帳號的資訊

設定對 節點: logs 的讀寫規則

這裡設定對於 logs 這個節點的規則:只有註冊的帳號可以讀寫。

{
  "rules": {
    ".read": false,
    ".write": false,
    "vue2-spa" :{
      ".read":"true",
      ".write":"false",      
      "logs" :{
        ".read": false,
        ".write": false,
        "$data": {
          ".read": "auth.uid != null",
          ".write": "auth.uid != null",
        },
      }
    }
  }
}

若不想控管權限,只要有資料就可以寫入的話,可以設定規則如下

{
  "rules": {
    ".read": false,
    ".write": false,
    "$data": {
      ".read": true,
      ".write": "!data.exists()",
    },
  }
}

進階的規則,可以參考官方文件: https://firebase.google.com/docs/database/security

啟用認證以 “電子郵件/密碼”

  • 建立一個註冊及登入的畫面

HTML 的部份如下:
<template>
  <v-container>
    <v-card width="500" class="mx-auto my-auto">
      <v-card-title>
        <h1>Login</h1>
      </v-card-title>
      <v-card-text>
        <v-form ref="form">
          <v-text-field
            v-model="loginFormData.email"
            :rules="emailRules"
            label="Email"
            prepend-icon="mdi-account-circle"
            clearable
          />
          <v-text-field
            v-model="loginFormData.password"
            :rules="[(v) => !!v || 'Password is required']"
            :type="showPassword ? 'text' : 'password'"
            label="Password"
            clearable
            prepend-icon="mdi-lock"
            :append-icon="showPassword ? 'mdi-eye' : 'mdi-eye-off'"
            @click:append="showPassword = !showPassword"
          />
        </v-form>
      </v-card-text>
      <v-divider></v-divider>
      <v-card-actions>
        <v-btn color="error" @click="reset">Reset</v-btn>
        <v-btn color="warning" @click="btnRegister">Register</v-btn>
        <v-btn color="info" @click="btnLogin">Login</v-btn>
      </v-card-actions>
    </v-card>

    <v-snackbar
      v-model="snackbar"
      :timeout="msg.timeout"
      top
      right
      color="pink"
    >
      {{ msg.text }}
      <template v-slot:action="{ attrs }">
        <v-btn text v-bind="attrs" @click="snackbar = false">
          Close
        </v-btn>
      </template>
    </v-snackbar>
    <v-overlay :value="overlay">
      <v-progress-circular
        indeterminate
        size="64"
      ></v-progress-circular>
    </v-overlay>
  </v-container>
</template>

針對於 JavaScript ,作細部的說明

安裝 Firebase 套件 與 vue-monent
/** https://firebase.google.com/docs/web/setup#node.js-apps */
$ npm install firebase --save
$ npm install vue-moment
import 必要的 Firebase
// Firebase App (the core Firebase SDK) is always required
// and must be listed first
import firebase from "firebase/app";

// Add the Firebase products that you want to use
import "firebase/auth";
import "firebase/firestore";
import "firebase/database";
頁面建立時,要初始 Firebase
created() {
    // Initialize Firebase
    firebase.initializeApp(this.firebaseConfig);
}
帳號註冊時的作業

要使用 firebase.auth().createUserWithEmailAndPassword(email, password)來作帳號註冊。註冊後,使用 firebase.auth().currentUser.sendEmailVerification() 來發送驗證信件。為了不讓申請者重覆送出註冊資料,在這裡需要使用 async & await ,並且利用遮罩 v-overlay 來作屏避。

async btnRegister() {
  if (this.$refs.form.validate()) {
 this.overlay=true;
 await firebase
   .auth()
   .createUserWithEmailAndPassword(
  this.loginFormData.email,
  this.loginFormData.password
   )
   .then((authCredential) => {
  this.fbUser = authCredential.user;
  firebase
    .auth()
    .currentUser.sendEmailVerification()
    .then(async () => {
   // 取得註冊當下的時間
   //let date = new Date();
   //let now = date.getTime();
   let now = this.$moment().format("YYYY-MM-DD HH:MM");
   let YM = this.$moment().format("YYYY-MM");
   firebase
     .database()
     .ref("/vue2-spa/logs/" + YM + "/" + this.fbUser.uid)
     .set({
    signup: now,
    email: this.loginFormData.email,
     })
     .catch((error) => {
    // Handle Errors here.
    this.msg.text = error;
    this.snackbar = true;
     });
   // Verification email sent.
   this.snackbar = true;
   this.msg.text = "註冊成功, 已發出信件";
   await setTimeout(() => {
     this.$router.push("/home");
     this.overlay=false;
   }, this.msg.timeout);
    })
    .catch(function(error) {
   // Handle Errors here.
   this.msg.text = error;
   this.snackbar = true;
   this.overlay=false;
    });
   })
   .catch((error) => {
  // Handle Errors here.
  this.msg.text = error;
  this.snackbar = true;
  this.overlay=false;
   });
  } else {
 this.msg.text = "送出錯誤";
 this.snackbar = true;
  }
}

當帳號以 email 註冊後,會發驗證信件

帳號登入時的作業

登入時,利用 firebase.auth().signInWithEmailAndPassword 來作認證,在此會使用屬性 authCredential.user.emailVerified 來驗證註冊的帳號是否有點選驗證連結來啟用。

async btnLogin() {
  if (this.$refs.form.validate()) {
 this.overlay=true;
 await firebase
   .auth()
   .signInWithEmailAndPassword(
  this.loginFormData.email,
  this.loginFormData.password
   )
   .then((authCredential) => {            
  // POST TO SERVER
  this.fbUser = authCredential.user;            
  if (this.fbUser.emailVerified) {

    // 取得登入當下的時間
    let now = this.$moment().format("YYYY-MM-DD HH:MM");
    let YM = this.$moment().format("YYYY-MM");
    firebase
   .database()
   .ref("/vue2-spa/logs/" + YM + "/" + this.fbUser.uid)
   .update({
     signin: now,
     email: this.loginFormData.email,
   })
   .then(async () => {
     // signed in and go to home page.
     this.snackbar = true;
     this.msg.text = "登入成功, 將轉至首頁";
     await setTimeout(() => {
    this.$router.push("/home");
    this.overlay=false;                   
     }, this.msg.timeout);
     
   })
   .catch((error) => {
     // Error occurred. Inspect error.code.
     this.msg.text = error;
     this.snackbar = true;
     this.overlay=false;
   });
  } else {
   this.snackbar = true;
   this.msg.text = "請先驗證信箱, 再登入";
   this.overlay=false;
  }
  
   })
   .catch((error) => {
  // Handle Errors here.
  this.msg.text = error;
  this.snackbar = true;
  this.overlay=false;
   });        
  } else {
 this.msg.text = "送出錯誤";
 this.snackbar = true;
  }
}

帳號註冊與登入的記錄

Firebase 對於  Realtime Database 提供 firebase.database().ref().update() 函數來對某一節點作存取,在一開始的存取規則是對節點: /vue2-spa/logs 且必需是有註冊的帳號才有權限讀取,所以程式段是這麼寫的. 這樣就可以了.

let now = this.$moment().format("YYYY-MM-DD HH:MM");
let YM = this.$moment().format("YYYY-MM");
firebase.database().ref("/vue2-spa/logs/" + YM + "/" + this.fbUser.uid)
.update({
  signin: now,
  email: this.loginFormData.email,
})

 

 

參考:

https://firebase.google.com/codelabs/firebase-get-to-know-web

https://firebase.google.com/docs/reference/node

https://www.oxxostudio.tw/articles/201904/firebase-realtime-database-rules.html