[Google Sign-In for Websites] 整合Google帳號登入 Javascript SDK 的使用方式,範例程式碼 with ASP.net MVC

前言

昨天收到Google寄來的信,Google+ API快要收掉,於是趕緊花一個下午研究,試出目前最新Web版Google Sign-In 的使用方式

同時也發現Google身份驗證,有好多種方式選擇:Google Identity Platform

本文主要挑Google Sign-In for Web來實作

其實範例程式碼只是把官網這一頁 https://developers.google.com/identity/sign-in/web/reference 去蕪存菁整理而已XD

對於程式碼各變數名稱、參數意義,想瞭解的人請自行點選上面連結

像這種整合第三方登入功能,個人現在比較推薦使用Javascript SDK實作,不要使用後端實作,原因:

1.每當 ASP.net 改版,後端程式碼架構幾乎變得不一樣(網路文章教學雜亂,很難知道該用哪一套),反觀Javascript SDK只要去官網文件(Google、Facebook…等)複製貼上,幾乎都能跑

2.後端實作登入一定搭配Redirect_Url參數,然後系統就會把用戶導頁來導頁去,體驗不太好,反觀Javascript SDK有popup window(預設)和redirect兩種登入流程可選擇

實作

本文後端程式碼為 ASP.net MVC 5

一開始先到 Google Developers Console

確保已經建立好Project、憑證(擁有了OAuth 2.0 用戶端 ID( Client ID)設定好JavaScript授權來源、API 金鑰無須建立(它在Javascript SignIn API是多餘的))

這部份的設定,由於Google的介面一直改來改去,我懶得截圖XD,請參考黑暗執行緒網友的文章:筆記:使用Google帳號登入ASP.NET MVC網站

↑ 文章裡的Google+ API 可以移除不理它(因為2019年3月就會收掉)

由於資安關係,Google不建議把前端取得的user_id直接發送至你的後端網站:Add Google Sign-In to Your Web App

取而代之,前端Javascript要把id_token送給後端處理,參考Stackoverflow討論:Validate Google Id Token

後端(ASP.net MVC、ASP.net Core)必須先從Nuget安裝 Google.Apis.Auth 套件(Google API Client Library),以驗證前端送來的id_token合法性

或你想從後端自己透過HttpClient物件發出Request到 https://oauth2.googleapis.com/tokeninfo?id_token={user_id_token} 來取得JSON結果也是可以

請見官網: Authenticate with a backend server

前提環境都準備好後,以下是範例程式碼,說明都在註解

ASP.net MVC的View

@using System.Web.Configuration
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Test Google Login</title>
</head>
<body>
    <!--用戶一鍵Google登入或綁定Google帳戶時使用↓-->
    <button type="button" id="btnSignIn">Google登入</button>
    <!--用戶解除Google帳戶綁定時使用↓-->
    <button type="button" id="btnDisconnect">斷連Google App</button>
     

    <!-- 引用jQuery-->
    <script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.js"></script>
    <!--從 Web.config檔 抓取Googl App Client ID--> 
    <script type="text/javascript">
        let GoolgeApp_Cient_Id = "@WebConfigurationManager.AppSettings["Google_clientId_forLogin"]";
        let id_token_to_userIDUrl = "@Url.Action("Test","Home")";  
    </script>
    <!--引用Google Sign-in必須的.js,callback function(GoogleSigninInit)名稱自訂 -->
    <script src="https://apis.google.com/js/platform.js?onload=GoogleSigninInit" async defer></script>

    <!--以下請放置到*.js檔-->
    <script type="text/javascript">
        //jQuery處理button click event 當畫面DOM都載入時....
        $(function () {
            $("#btnSignIn").on("click", function () {
                GoogleLogin();//Google 登入
            });
            $("#btnDisconnect").on("click", function () {
                Google_disconnect();//和Google App斷連
            });
        });

        function GoogleSigninInit() {
            gapi.load('auth2', function () {
                gapi.auth2.init({
                    client_id: GoolgeApp_Cient_Id//必填,記得開發時期要開啟 Chrome開發人員工具 查看有沒有403錯誤(Javascript來源被禁止)
                });
            });//end gapi.load
        }//end GoogleSigninInit function

        
        function GoogleLogin() {
            let auth2 = gapi.auth2.getAuthInstance();//取得GoogleAuth物件
            auth2.signIn().then(function (GoogleUser) {
                console.log("Google登入成功"); 
                let user_id =  GoogleUser.getId();//取得user id,不過要發送至Server端的話,請使用↓id_token   
                let AuthResponse = GoogleUser.getAuthResponse(true) ;//true會回傳access token ,false則不會,自行決定。如果只需要Google登入功能應該不會使用到access token
                let id_token = AuthResponse.id_token;//取得id_token
                $.ajax({
                    url: id_token_to_userIDUrl,
                    method: "post",
                    data: { id_token: id_token },
                    success: function (msg) {
                        console.log(msg);
                    }
                });//end $.ajax 
               
            },
                function (error) {
                    console.log("Google登入失敗");
                    console.log(error);
                });

        }//end function GoogleLogin
         

         
        function Google_disconnect() {
            let auth2 = gapi.auth2.getAuthInstance(); //取得GoogleAuth物件

            auth2.disconnect().then(function () {
                console.log('User disconnect.');
            });
        } 
    </script>
</body>
</html> 

ASP.net MVC的Controller

  public ActionResult Test()
        {
            return View();
        }

        /// <summary>
        /// Javascript取得id_token,透過Ajax發送至這個Action,後端 ASP.net MVC 把 id_token 轉成 user_id
        /// </summary>
        /// <param name="id_token"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<ActionResult> Test(string id_token)
        { 
            string msg = "ok";
            GoogleJsonWebSignature.Payload payload = null;
            try
            {
                payload = await GoogleJsonWebSignature.ValidateAsync(id_token, new GoogleJsonWebSignature.ValidationSettings()
                {
                    Audience = new List<string>() { WebConfigurationManager.AppSettings["Google_clientId_forLogin"] }//要驗證的client id,把自己申請的Client ID填進去
                }); 
            }
            catch (Google.Apis.Auth.InvalidJwtException ex)
            {
                msg = ex.Message;
            }
            catch (Newtonsoft.Json.JsonReaderException ex)
            {
                msg = ex.Message;
            }
            catch (Exception ex)
            {
                msg = ex.Message;
            }
             
            if (msg=="ok" && payload!=null)
            {//都成功
                string user_id = payload.Subject;//取得user_id
                msg = $@"您的 user_id :{user_id}";
            }
             
            return Content(msg);
        }

 

結語

由於之前我實作Google登入,參考的國外文章:Google Login with Javascript API

該篇文章使用到 plus.me 的 scope權限要求,才導致我不得不修改程式