avatar
發佈

實作 Google 第三方登入(Vue / Express)

Table of Contents

前言

最近公司需要串接第三方登入,因為第一次做,踩了不少坑,也發現 Google 又改版了,爬到的許多文都已經不堪用的,於是來分享紀錄一下串接的流程。

基本上,如果你是用 Google Sign-in JavaScript library 串接的話,Google 已經棄用(deprecated)了,並且將在 2023/03/31 完全捨棄,也就是不讓人下載跟使用,所以之前按照 這個流程 實作的要趕快重構啦!

這篇文章會用 Vue3 和 Express 來帶大家來手把手實作簡易的 Google  第三方登入!

(除了使用新版的 Sign In With Google button ,也會有新的 Google One Tap 功能)

因為我只有使用 Google 原生的 Library,而沒有使用別人包過的,所以如果是 React 的話語法稍微改一下就也可以用囉!

現在 Google 登入越來越好串,建議不要用別人包裝過的 library 來減少耦合度,因為敝司最近就是因為之前用的 library 沒在維護了才重新串了一遍。

Overview

串接的流程會分成四個步驟:

  1. 在 Google Cloud 設定 OAuth Consent Screen 和 Credentials
  2. 前端:初始化 & 顯示 Google Button
  3. 前端:使用者按下同意後,會 POST Google Credential 給後端,並拿著回傳過來的 Authorization Token 後就可以順利登入並取得 User Data 啦!
  4. 後端:在 POST 的 Route 接到 Google Credential 後,用 Google Auth Library 驗證,並得到 Google User Data,把這些 User Data 存到 DB 或回傳對應的 Authorization Token 跟 User Data 給前端。

相信看完這個步驟,還是會有些疑惑,以下就來手把手實作囉!

一、Google Cloud  設定

  1. Google Cloud 選擇或新增一個你要使用 Google Login 專案 (img)

  1. 接著進去 OAuth 同意畫面( OAuth Consent Screen )的地方

  1. 註冊一個 App,照著步驟填寫欄位就好!

Scopes 是你允許 App 來存取已授權使用者的哪些資料。 基本的就是像圖上的 auth/userInfo.emailauth/userInfo.profile (img)

Test User 就是在測試時,你允許登入的帳號們。

  1. 建立完 OAuth Consent Screen 的 APP 之後,就要來設定 憑證(Credential)

點左側的 憑證 orCredential 之後,再點擊上面的 CREATE CREDENTIALS

  1. 設定 Authorized JavaScript origins 和 Authorized redirect URIs

Authorized JavaScript origins:是指前端的網域 Authorized redirect URIs:是指使用者按下同意後,會向後端的哪個 URI 發送 POST Request 來接 Google Credential。

以待會的 Demo 來說,前端會是在 http://localhost:5173 ,後端則是用 http://localhost:3000/verify-token 來接收 POST Request 需要注意的是在網域這邊,本地測試時需要特別加上 http://localhost 然後 Google 不允許用 IP 當作來源,像是 127.0.0.1 是不行的。

(然後正式上線後,記得要改成正式的網域跟 URI 噢)

  1. 建立完成後就可以在列表上看到你的 Client ID,待會會用到它!

(Client 的意思是,我們對 Google 來說是 Client,Google 是我們要跟他們拿憑證跟使用者資料的 Server)

恭喜,我們成功地完成了 Google Cloud  的設定。

取得了 Client ID 和 授權我們的前端 Origin 跟後端 URI 後,接下來就分別來實作前後端啦!

二、前端:初始化 & 顯示 Google Button / On Tap Login

基本上是照著這流程: Display the Sign in with Google Button,但以下也會走一遍給大家看!

在入口 index.html 載入 google library script

// index.html

<body>
  <script src=”https://accounts.google.com/gsi/client" async defer></script>
  <div id="app" />
</body>

這時候 window 會被註冊一個 google 的物件,後續的操作都會使用到這個物件!

讓我們用瀏覽器直接 log window.google 看看底下有哪些東西

  1. 初始化設定直接調用 window.google.accounts.id.initialize

    • Client ID:直接從 Credentials 上複製就好
    • callback:就是當使用者在 Google 的登入 Popup 按下同意後會觸發的 Callback Function
    • Cancel On Tap Outside:點擊 Tap 外的區塊能不能關掉 Tap Login Model
    • Context: signin | signout | login
  2. 渲染 Button 會使用 window.google.accounts.id.renderButton

  3. 顯示 On Tap Login 則是靠呼叫 window.google.accounts.id.prompt()

onMounted(() => {
  window.onload = () => {
    if (CLIENT_ID) {
      window.google.accounts.id.initialize({
        client_id: CLIENT_ID, // required
        callback: onLogin, // invoke while user login in the popup
        cancel_on_tap_outside: true, // optional
        context: 'signin', // optional
      });

      window.google.accounts.id.renderButton(
        document.getElementById('googleButton'),
        { theme: 'outline', size: 'large' } // customization attributes
      );

      window.google.accounts.id.prompt(); // show one-tap popup
    } else {
      console.error("client_id doesn't exist!");
    }
  };
});

三、前端:使用者同意後取得 Token  並登入

這步驟就是在實作上面 initialize 時會丟進去的 callbcak。

而這也是使用者在 Google 的登入畫面上登入後會執行的 function。

因此我們會在這邊實作我們自己的登入邏輯,像這樣:

const onLogin = (res) => {
  const axiosOptions = {
    headers: { 'Access-Control-Allow-Origin': CLIENT_URL },
  };
  axios
    .post(`${API_URL}/verify-token`, res, axiosOptions)
    .then((res) => {
      console.log('res', res);
      userData.value = res.data;
    })
    .catch((error) => {
      console.log('error', error);
    });
};

callback 會接到來自 Google 的 response( onLogin 的 res),response 裡又會包含這些資料,我們會拿其中的 credential 打去後端來做驗證。

{
  "clientId": "123456789.apps.googleusercontent.com",
  "credential": "",
  "select_by": "btn" // or 'user'
}

前端的處理到此結束啦!

簡單複習一下,我們引入 gsi client,在 window 底下建立了 google 物件,接著用 google 物件提供的 initializerenderButton 來初始化跟渲染 Google Button,而我們自己的登入邏輯 onLogin function 則是在 initialize 時傳進 callback 來讓使用者登入後調用。

四、後端:接收、驗證,回傳 User Data ( and Authorization Token )

在上面我們看到了 onLogin 是 POST 到 /verify-token,接著我們就要在這個 Route 去驗證並取得 Google User Data。

基本上就是照著 Google 的 範例 去做,然後記得處理 CORS 就好!

自此,第三方串接就完成了!

接下來後端讓前端知道已登入成功這件事即可。

而在這邊就單純把 User Data 回傳給前端。

在公司的專案裡則採用跟一般註冊或登入一樣去把資料存到 DB 或是取得 Authorization Token 、User Data 給前端的作法。

const cors = require('cors');
app.use(cors());
const CLIENT_ID = 'YOUR CLIENT ID';
/** Handle the POST request from onLogin callback in frontend */
app.post('/verify-token', (req, res) => {
  // use google-auth-library to verify token
  const { OAuth2Client } = require('google-auth-library');
  const client = new OAuth2Client(CLIENT_ID);
  async function verify() {
    // get credential from google
    const token = req.body.credential;
    console.log('token from credential', token);
    const ticket = await client.verifyIdToken({
      idToken: token,
      audience: CLIENT_ID,
    });
    const payload = ticket.getPayload();
    // You can store user data in DB and return Authorization Token here.
    res.json({
      email: payload.email,
      email_verified: payload.email_verified,
      picture: payload.picture,
      name: payload.name,
    });
  }
  verify().catch(console.error);
});

Demo

下面提供的 Repo 都跑起來後會像這樣:

初始的登入畫面

使用者登入後

Frontend: Vue Sign In With Google

  • clone 下來後 npm install + npm run dev 會跑在 port:5173,這也正是我們在 Google Cloud — Credentials 那邊設定的 Authorized JavaScript origins
  • 自己開發時,記得要去改成自己的 Port ,上線後也要改成正式的網域哦!

Backend: Express Sign In With Google

  • clone 下來後 npm install + npm run start 會跑在 port:3000,這也正是我們在 Google Cloud — Credentials 那邊設定的 Authorized redirect URIs

結語

第三方串接讀了一堆文件,做的懵懵懂懂, 剛寫完沒幾天,要來寫文章時還是有些細節會有點忘記,但是整理成一篇文章就清楚多了!

希望這篇文章對有串接 Google 登入需求的大家有幫助啦!

我要接下來繼續串 Facebook 登入了,不得不說串接 Google 與 Facebook 和 Twitter 的比較起來,真的是友善很多很多很多呢。