Sign in With Google之服務(wù)端驗證

前言

關(guān)于使用Google第三方登錄服務(wù)端如何進行登錄驗證。Google登錄流程中,服務(wù)器的主要工作為驗證用戶信息以確保此次登錄為有效登錄,然后讓當前登錄的用戶進行應(yīng)用服務(wù)器的相應(yīng)流程。另一方面,用戶在授權(quán)登錄后,應(yīng)用服務(wù)器可以通過用戶的授權(quán)獲取到access_token,以便后續(xù)訪問Google的其他API。

登錄流程

根據(jù)上面的圖所示:

  1. 在用戶點擊"Sign in With Google"的按鈕后,客戶端將會收到一個來自O(shè)Auth服務(wù)器并且只能使用一次的code,客戶端將該code發(fā)送至應(yīng)用服務(wù)器,這里由客戶端處理。

  2. 應(yīng)用服務(wù)器用該code向Google API服務(wù)器請求access_token和refresh_token,這里需要注意:

a. 每個code只能使用一次
b. 只要code有效,那么每次請求Google都將返回access_token
c. 在獲取到refresh_token之后,需要將其保存起來用于后續(xù)刷新access_token使用(access_token存在過期時間),因為在后續(xù)的refresh_token交換請求將返回空

請求access_token和refresh_token的API為https://oauth2.googleapis.com/token
請求參數(shù)有:

參數(shù)名 解釋
code 用戶授權(quán)后客戶端獲取到的code
client_id 應(yīng)用ID
client_secret 應(yīng)用密鑰
redirect_uri API控制臺頁面 配置的授權(quán)重定向URI
grant_type 固定值:authorization_code

Google返回的參數(shù)有:

參數(shù)名 解釋
access_token 用于訪問Google API的token
expires_in access_token有效期, 單位: 秒
id_token Google數(shù)字簽名了的用戶身份信息
scope access_token授予的訪問范圍,以空格分隔的、區(qū)分大小寫的字符串列表表示。
token_type token類型,這里總是返回Bearer
refresh_token 用于刷新access_token的token

下面以golang為例, 發(fā)送請求:

    var (
        clientId     = "your clientId"
        clientSecret = "your clientSecret"
        code         = "your code"
        redirectUri  = "your redirectUri"
        url          = "https://oauth2.googleapis.com/token"
    )
    
        reader := strings.NewReader(fmt.Sprintf("client_id=%s&client_secret=%s&redirect_uri=%s&grant_type=authorization_code&code=%s", clientId, clientSecret, redirectUri, code))
        response, err := http.Post(url, "application/x-www-form-urlencoded", reader)
        handleResponse(response)
        handleErr(err)

刷新access_token的API為: https://oauth2.googleapis.com/token

請求參數(shù)有:

參數(shù)名 解釋
client_id 應(yīng)用ID
client_secret 應(yīng)用密鑰
refresh_token access_token值
grant_type 固定值:refresh_token

返回的參數(shù)有:

參數(shù)名 解釋
access_token 用于訪問Google API的token
expires_in access_token有效期
token_type token類型,這里總是返回Bearer
scope access_token授予的訪問范圍,以空格分隔的、區(qū)分大小寫的字符串列表表示。
  1. 服務(wù)器獲取到access_token和refresh_token之后便可以開始驗證用戶信息(idToken)是否有效。

如何驗證用戶身份信息

用戶的身份信息是包含在idToken中的,idTokenJWT 格式。按照 Google官網(wǎng) 介紹,idToken需要驗證的內(nèi)容有:

  1. 使用Google公鑰確認idToken被正確的簽名
  2. aud的值應(yīng)該與你應(yīng)用的client_id相同
  3. iss的值為accounts.google.com或者https://accounts.google.com
  4. 有效期尚未過

官網(wǎng)建議的驗證方式有兩種:

  1. 使用Google的驗證庫,包括Java, Node.js, PHP, Python, 當然, Go語言的庫為: Golang, 如果使用Go語言的庫,需要注意如果你的項目中引入了etcd,那么你需要注意關(guān)于etcd GRPC版本所引起的一個老生常談的問題了,我在使用過程中解決辦法是降低了Golang的版本,對應(yīng)go.mod中為:
replace (
    google.golang.org/grpc => google.golang.org/grpc v1.26.0
    google.golang.org/api => google.golang.org/api v0.14.0
)

關(guān)于驗證idToken的代碼為:

import (
    "http"
    "github.com/dghubble/oauth1"
    "google.golang.org/api/option"
    "google.golang.org/api/oauth2/v2"
)
    
    oatuService, err := oauth2.NewService(context.Background(), option.WithHTTPClient(http.DefaultClient))
    if err != nil {
        log4go.Errorf("%v\n", err)
        return nil, err
    }
    tokenInfoCall := oatuService.Tokeninfo()
    tokenInfoCall.IdToken(googleToken)
    tokenInfo, err := tokenInfoCall.Do()
    //根據(jù)tokenInfo中的字段進行驗證

注意:Google建議生產(chǎn)環(huán)境使用此方法進行驗證

  1. 調(diào)用Google的API進行驗證, 驗證API為: https://oauth2.googleapis.com/tokeninfo?id_token={idToken}, 如:

https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123

請求方法既可以是POST也可以是GET, 需要注意的是, 在收到API回復(fù)后只有當StatusCode為http.StatusOK時才能進行下一步校驗。

idToken校驗API返回的結(jié)果為JSON鍵值對,如:

{
 // 這6個字段是所有idToken都包含的
 "iss": "https://accounts.google.com",  //token簽發(fā)者,值為https://accounts.google.com或者accounts.google.com
 "sub": "110169484474386276334", //用戶在該Google應(yīng)用中的唯一標識,類似于微信的OpenID
 "azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com", //具體我也不知道,猜測與aud相同,都是應(yīng)用的client_id
 "aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com", //client_id
 "iat": "1433978353", //簽發(fā)時間
 "exp": "1433981953", //過期時間

 // 下面的字段只有當用戶授權(quán)了"profile"和"email"權(quán)限后才會出現(xiàn)
 "email": "testuser@gmail.com", //用戶郵箱
 "email_verified": "true", //郵箱是否已驗證
 "name" : "Test User", //用戶名
 "picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg", //用戶頭像
 "given_name": "Test", //名
 "family_name": "User", //姓
 "locale": "en"  //所屬地區(qū)
}

注意: 因為在授權(quán)過程中和獲取access_token的過程中都會獲取到idToken,所以服務(wù)器驗證的idToken既可以由客戶端傳給服務(wù)器,也可以是服務(wù)器自己獲取。但是在登錄過程中,客戶端獲取到的授權(quán)code必須傳給服務(wù)器

參考資料

Google Sign-In for server-side apps
Authenticate with a backend server
Using OAuth 2.0 to Access Google APIs

原文鏈接

Sign in With Google之服務(wù)端驗證

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容