[PHP]Firebase phone auth 使用教學

前言

還記得之前寫過一篇 [PHP]Account Kit 使用教學 文章來教導怎麼利用 Facebook 提供的服務來驗證手機號碼

很可惜的是,寫完那篇文章後沒有多久

Facebook 發佈了要停止 Account Kit 的消息

很無奈~只能繼續找尋類似的服務

最後在社團 程式人雜誌 -- 公益出版 內的網友提供了建議

也就是今天這篇文章的主題

Firebase 是 Google 旗下的服務,功能非常強大

甚至可以單靠 Firebase 就能創建一個功能非常完整的後端應用

在一定額度內都是免費使用,挺適合小專案來開發

這次要使用的是其中的一項登入功能

Step 1

登入 Google 帳號後進入 Firebase首頁

選擇建立專案

01

輸入專案名稱

02

如果想要有分析報表的話可以啟用

03
04

等待一會兒

05

專案建立完成

06

Step 2

接下來要開啟登入項目

選擇左邊的 開發 -> Authentication

選擇登入方式

除了手機之外也有很多種登入方式,非常方便

07

選擇電信業者,啟用後儲存

08

將頁面往下拉到已授權網域,將需要用到的網域名稱加入

否則被別人刷配額那就不太好了

09

Step 3

之後回到首頁新增一個應用程式,因為這次要使用的是網頁

所以點下網頁的圖案

10

輸入應用程式名稱,因為不需要託管,所以不打勾

11

之後會產生一組設定檔,裡面包括 API Key,請先複製下來

12

這時候可能會有疑惑,這麼公開 Key 好嗎?

雖然我們在上一步有設定授權網域,但 Key 也是可以設定使用限制的

打開 Google Cloud

選擇好專案後,開啟左邊的選單,選擇 API和服務 -> 憑證

13

開啟列表頁後,找到同樣的 API Key 後點下旁邊的鉛筆圖案

14

就可以在金鑰限制的地方限制存取原則

比如某個網域才能使用

15

Step 4

事前準備完成了,接下來就開始進入程式碼環節

大概流程為

  1. 建立頁面讓使用者輸入電話號碼
  2. 利用 Firebase JS SDK 寄送驗證碼到使用者手機
  3. 使用者將驗證碼輸入後進行驗證
  4. 後端利用 Firebase SDK 進行手機號碼查詢

因為是測試用,所以就隨便弄兩個按鈕一個輸入框

<!DOCTYPE html>
<html>
<head>
	<title>Test</title>
</head>
<body>
	<button id="button_1" onclick="send()">SEND</button>
	<br />
	<input type="text" id="input_1">
	<br />
	<button onclick="go()">GO</button>

	<!-- 需要的SDK -->
	<script src="https://www.gstatic.com/firebasejs/6.6.1/firebase-app.js"></script>
	<script src="https://www.gstatic.com/firebasejs/6.6.1/firebase-auth.js"></script>

	<script>
		/* Firebase 設定檔 */
		var firebaseConfig = {
			apiKey: "",
			authDomain: "",
			databaseURL: "",
			projectId: "",
			storageBucket: "",
			messagingSenderId: "",
			appId: ""
		};

		/* 初始化 */
		firebase.initializeApp(firebaseConfig);

		/* 設定語系,這邊使用裝置的預設語系 */
		firebase.auth().useDeviceLanguage();

		/* 建立 reCAPTCHA 驗證(這是強制需要),帶入發送按鈕的ID */
		window.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('button_1', {
			'size': 'invisible',
			'callback': function(response) {
				onSignInSubmit();
			}
		});

		function send() {
			var phoneNumber = '含國碼的手機號碼 Ex. +886912345678';
			var appVerifier = window.recaptchaVerifier;

			firebase.auth().signInWithPhoneNumber(phoneNumber, appVerifier).then(function (confirmationResult) {
				/* 發送成功 */
				window.confirmationResult = confirmationResult;
			}).catch(function (error) {
				/* 發送失敗 */
				console.log(error);
				/* 重置驗證 */
				grecaptcha.reset(window.recaptchaWidgetId);
			});
		}

		function go() {
			var code = document.getElementById('input_1').value;
			/* 檢查驗證碼 */
			confirmationResult.confirm(code).then(function (result) {
				/* 驗證成功 */
				var user = result.user;
				console.log(user);
			}).catch(function (error) {
				/* 驗證失敗 */
				console.log(error);
			});
		}
	</script>
</body>
</html>

按下 SEND 後手機會收到一封驗證碼的簡訊

16

在頁面上輸入驗證碼後送出,若成功將會收到使用者的資訊

但目前沒有找到相對應的欄位資訊,所以回傳的資料應該沒什麼作用

若有讀者手上有相關資料請通知我

17

Step 5

前端的使用者跑完這整個流程之後,後端的部分應該要怎麼去驗證呢?

首先回到 Firebase 的專案設定,選擇服務帳戶

點選下方的 產生新的私密金鑰

18

按下 產生金鑰 後會下載一個 JSON 檔案,這份檔案將可以操作 Firebase

請務必收好

19

這邊比較可惜的是, Firebase 並沒有提供官方的 SDK

所以需要安裝第三方的 SDK,安裝步驟請參閱網頁

驗證的方式也很簡單

<?php
	require __DIR__.'/vendor/autoload.php';

	use Kreait\Firebase\Factory;
	
	$firebase = (new Factory) -> withServiceAccount('金鑰檔案路徑') -> create();

	$auth = $firebase->getAuth();

	try {
		$user = $auth->getUserByPhoneNumber('手機號碼(含國碼)');

		print_r($user);

	} catch (Exception $e) {
		echo $e->getMessage();
	}

如果有成功的話,將可以取到使用者資料

20

失敗的話會直接拋出例外

21

結尾

其實這服務與 Account Kit 是一樣的方式

但跟 Facebook 比起來,Firebase 提供了更多的登入選項靈活運用

整合度也是非常不錯,從網站託管到後端程式、資料庫、儲存空間、資料分析

一系列的服務確確實實可以讓開發者不用去顧及太多東西及風險

對於小型公司也能省去需要找太多專業人士的窘境

若這邊教學有幫助到你的話~請多多分享轉發出去給更多的人知道

謝謝大家的觀看

Read more

[LeetCode] #12 Integer to Roman 解題

題目連結 題型解說 這是一題難度為普通的題目 需要設計一個方法,此方法會傳入一個整數 num 要求是把整數轉換成羅馬字母,轉換清單如下 I => 1 V => 5 X => 10 L => 50 C => 100 D => 500 M => 1000 但羅馬字母有一些特殊規則 4 並非 IIII 而是 IV,9 並非 VIIII 而是 IX 這規則同樣可以套用到 40 90 400 900 解題思路 既然知道特殊規則是一樣的,變得是使用的符號,那麼先從 num 取個位數開始 轉換完成後,把 num 除上 10,消除個位數,

By Michael

[LeetCode] #11 Container With Most Water 解題

題目連結 題型解說 這是一題難度為中等的題目 需要設計一個方法,此方法會傳入一個數字陣列 height 陣列中的元素代表每一個柱子的高度 現在需要計算出,該陣列中以某兩隻柱子為邊界,最多可以裝多少水 以範例來說 height = [1,8,6,2,5,4,8,3,7] 最多可以裝 7 * 7 = 49 單位的水 解題思路 計算面積就是底乘上高 底的計算方式為 「右邊柱子的 index」 減去 「左邊柱子的 index」 高就取最短的那一根柱子高度 拿題目給的例子來當範例 建立三個變數 result、left、right left、right 代表左右兩邊的 index result 代表目前最大容量,初始值 0 第一步,找出最短的柱子高度,

By Michael

[LeetCode] #941 Valid Mountain Array 解題

題目連結 題型解說 這是一題難度為簡單的題目 需要設計一個方法,此方法會傳入一個數字陣列 arr 判斷陣列中的元素是不是由低到高再從高到低(山形)的排序,且不連續一個以上數字 比如說 [1,2,3,2] 就是一個山形陣列,但 [1,2,2,3,2] 不是,因為有兩個 2 [1,2,3,4,5] 和 [5,4,3,2,1] 也不算是山形陣列,前者只有往上沒有往下,後者相反 解題思路 準備一個數字變數(temp)和布林變數(asc),跑一次迴圈,有可能遇到如下狀況 1. 某個數字與前一個數字相同,這時候直接回傳 false

By Michael

[LeetCode] #944 Delete Columns to Make Sorted 解題

題目連結 題型解說 這是一題難度為簡單的題目 需要設計一個方法,此方法會傳入一個字串陣列 strs 這個陣列中每個字串的長度都相同,字串內容都是小寫英文 需要檢查每個元素的第 N 個字元是不是由小至大排列,並回傳有幾個錯誤排列 比如傳入的陣列長這樣 ["cba","daf","ghi"] 取第一個字元 = cdg 取第二個字元 = bah 取第三個字元 = afi 其中第二組的結果(bah)並不是由小至大排列,故回傳 1 解題思路 這一題就用兩個迴圈各別把字元取出來,並比較是否比上一個字元大(Java 中的字元可以直接比較),如果不是就將結果+1 程式碼 Java class Solution { public int minDeletionSize(String[] strs) { int result = 0; for (int i = 0,

By Michael