Try   HackMD

Google Identity登入筆記

官方詳細說明:
https://developers.google.com/identity/gsi/web/guides/display-button?hl=zh-tw#html

登入方式:

1. 點擊“使用google帳戶登入”按鈕都登入

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

2. 使用One Tap(一鍵登入):

  • 有登入的工作階段:
    會看cookie當中有否紀錄使用者登入狀態,有的話會顯示以XXX身份繼續
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • 沒有登入或是使用者在「具有您帳戶存取權的應用程式」停用google one 登入
    若沒有登入的cookie的話,ITP 瀏覽器會顯示WElCOME PAGE,非itp則不會跳出任何畫面

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

*補充:預設會開啟的隱私權保護功能,稱為智慧型追蹤保護或 ITP。 ITP 會封鎖跨網域要求的 Cookie。
https://learn.microsoft.com/zh-tw/azure/active-directory/develop/reference-third-party-cookies-spas

引入方式:

1. HTML靜態網頁可以直接引入(g_id_signin 的元素會顯示為「使用 Google 帳戶登入」按鈕)

<html>
  <body>
      <script src="https://accounts.google.com/gsi/client" async defer></script>
      <div id="g_id_onload"
         data-client_id="YOUR_GOOGLE_CLIENT_ID"
         data-login_uri="https://your.domain/your_login_endpoint"
         data-auto_prompt="false">
      </div>
      <div class="g_id_signin"
         data-type="standard"
         data-size="large"
         data-theme="outline"
         data-text="sign_in_with"
         data-shape="rectangular"
         data-logo_alignment="left">
      </div>
  </body>
</html>

2. JavaScipt

<html>
  <body>
      <script src="https://accounts.google.com/gsi/client" async defer></script>
      <script>
        function handleCredentialResponse(response) {
          console.log("Encoded JWT ID token: " + response.credential);
        }
        window.onload = function () {
          google.accounts.id.initialize({
            client_id: "YOUR_GOOGLE_CLIENT_ID",
            callback: handleCredentialResponse
          });
          google.accounts.id.renderButton(
            document.getElementById("buttonDiv"),
            { theme: "outline", size: "large" }  // customization attributes
          );
          google.accounts.id.prompt(); // also display the One Tap dialog
        }
    </script>
    <div id="buttonDiv"></div> 
  </body>
</html>

網域設定:

1. 第一步檢查該google ID設定:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

由於google One Tap必須在https網域才能操作,所以本機測試必須先讓網址變成:https://XXXXX:3000
雖然後來有針對authorized redirect Urls多設定其他組但不知道為什麼打開瀏覽器還是會報錯(如下圖)

[GSI_LOGGER]: The given origin is not allowed for the given client ID.

懷疑可能原因是網域必須分別加入網域跟port

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

2. 更改本機連線成Https

brew install mkcert
brew install nss # if you use Firefox

在想要放置.pem檔的地方執行mkcert XXXXX
就會產生兩個檔案
key.pem/.pem
在server中加入與修改以下

const https = require('https');
const fs = require('fs');
const options = {
  key: fs.readFileSync('../../XXXXX-key.pem'),
  cert: fs.readFileSync('../../XXXXX.pem'),
};
const httpsServer = https.createServer(options, app);
//...最後加入
Loadable.preloadAll().then(() => {
  httpsServer.listen(sitePort, () => {
    console.log(`Listening to port ${sitePort}`);
  });
});
  • 方法二:直接透過openssl產生:
openssl req -new -x509 -key privatekey.pem -out cert.pem

一樣產生兩個.pem加入server

不過這兩種認證的ssl都被google視為不安全的憑證所以依舊無法打開
google chrome打開的error:

NET::ERRCERTCOMMONNAMEINVALID
NET::ERRCERTAUTHORITYINVALID*

React實際執行:

1. 初始化

初始化google登入 通常寫在useEffect之中 會得到google這個object以進行後來登入的initialize

export const initGoogleLogin = () => {
  const src = 'https://accounts.google.com/gsi/client';
  new Promise((resolve, reject) => {
    if (document.querySelector(`script[src="${src}"]`)) return resolve();
    const script = document.createElement('script');
    script.src = src;
    script.async = true;
    script.defer = true;
    script.onload = () => resolve();
    script.onerror = err => reject(err);
    document.body.appendChild(script);
  });
};

2. 點擊按鈕之後呼叫登入方法

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

google

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

舊的js方法:

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

export const handleGoogleLogin = (callbackSuccess = (userInfo) => { }, callbackFailed = (error) => { }) => {
function handleCallbackResponse(res) {
//由於credential是JWT格式所以要解碼 解完的object撈出需要的資料
 const userObject = jwt_decode(res.credential);
 const userInfo = {
        thirdPartyId: userObject.sub,
        thirdPartyEmail: userObject.email,
        thirdPartyFirstName: userObject.given_name,
        thirdPartyLastName: userObject.family_name,
        thirdPartyType: '02',
        };
        callbackSuccess(userInfo);
      }

      google.accounts.id.initialize({
        client_id: 'XXX.apps.googleusercontent.com',
        callback: handleCallbackResponse, //使用者點擊google登入以XX身份繼續後做的事情
        // itp_support: false,// itp瀏覽器預設true 測試時用false
      });
    //預設one tap login(呼叫prompt)
      google.accounts.id.prompt((notification) => {
        if (notification.isNotDisplayed()) {
          // One-Tap exponential cool-down 所以設定cookie時間 不然使用者關閉後要隔一段時間才會再顯示
          document.cookie = 'g_state=;path=/;expires=Thu, 01 Jan 1970 00:00:01 GMT';
          google.accounts.id.prompt((noti) => {
            // One-Tap 若沒顯示則render google login btn 點擊隱藏的google按鈕 跳出登入視窗
            if (noti.isNotDisplayed()) {
              google.accounts.id.renderButton(
                document.getElementById('signIn'),
                { size: 'large', theme: 'filled_blue', text: 'signin' },
              );
              document.querySelector('#signIn div[role=button]').click();
            }
          });
        }
      });
    };

指數冷卻
如果使用者手動關閉 One Tap 提示,系統就會隱藏 One Tap 提示。使用者輕觸提示右上角的「Close」圖示 close 時,會關閉 One Tap。且同一段瀏覽器在一段時間內不會顯示於同一個瀏覽器或上次造訪網站。

連續關閉時間 「一按」功能停用的時間範圍

1 2小時
2 1 天
3 1 週
4+ 4 週

在使用 One Tap 或「使用 Google 帳戶登入」按鈕成功登入後,會重設冷卻狀態。

3. 客製化按鈕

在客製化按鈕的段落加入

<div id="signIn" style={{ display: 'none' }} />

用以生成隱藏的google按鈕(由於google預設的按鈕能改動幅度有限,所以如此使用以套用現有的按鈕)