從Google第三方登入看OAuth2.0

前言

ZHIH轉職人森
9 min readAug 25, 2021

剛開始直接硬嗑Google 官方文件,根本搞不清楚是怎運作授權這件事,直接在文件裡面大迷路,於是覺得還是要了解原理,才會搞得懂授權是怎麼運作的,讀了幾篇文章後跟實際操作後才整個恍然大悟整個過程,那就開始吧。

OAuth2.0

用文謅謅的說法就是

👤使用者授權給💻某程式/服務去跟🔑 另一網站/服務 換取授權去資源中心🏢拿回使用者同意給你的資料💾

白話文的實例

小明是老闆👤,要請他的員工A💻去服務中心🔑索取資料,所以小明👤寫了一份授權書給員工A💻,員工A💻去了服務中心🔑跟櫃台出示授權書,換得一張代理權證明,並前往存放資料中心🏢的所在地,向資料料中心🏢的櫃台出示代理權的證明,換得一份密碼,最後前往資料所在地,用密碼解開鎖拿回資料💾交差。

那麼來看看老方法怎麼用,並用白話的方式

小明是老闆👤,要請他的員工A💻去資料中心🏢索取資料,所以小明👤直接給了員工A💻帳號密碼,員工A💻單刀直入前往存放資料中心🏢的所在地,用密碼解開鎖拿回資料💾交差,途中忽然心生歹念,把小明的另 一個放貴重物品的箱子也抄了,想想小明平時很機八,想搞他一下,於是利用拿到的資料,假冒身分亂搞他,超爽der~

是不是很可怕(x,這種方式等同你要非常相信要幫你取資料的人,於是乎才有現在OAuth2.0 的方式,該授權單位本身的防衛機制良好,加上換取授權換取資料的方式,再加上只限定給你使用者願意給你的資料,這使得個人敏感資料的防護更加的有保障。

接下來提及整個流程的重要的角色吧

角色介紹

👤資料所有者Resource Owner

就是使用者本人,當client向你要求使用某些資料(ex:email、姓名去建立帳戶,必須要先經過本人同意。

💻 Client

意即使用者使用的APP、網站等等,以Leetcode為例,可以用github來授權登入,Leetcode就是client。
client端必須事先向授權中心註冊拿到client_ID、client_Secret,並指定你要索取的資料,透過scope來標示,並指定redirect_url(很重要很重要很重要!!),使用者同意後會把授權轉到redirect_url來

🔑授權中心Authorization Server

顧名思義掌管授權的單位,給使用者本人進行確認動作,確認完成後發放Grant(授權狀)給client端,會回到你指定的redirect_url(很重要很重要很重要!!再說一次),client拿到Grant(授權狀)才能去要token

🏢資料中心Resource Server

管理資料的單位就好比web server API,當client透過授權中心拿到token,就能夠向資料中心索取資料。

OAuth2.0 共分4種授權類型流程

  1. Authorization Code Grant Flow
  2. Implicit Grant Flow
  3. Resource Owner Password Credentials Grant Flow
  4. Client Credentials Grant Flow

以下會實際串接google來看看第一種Authorization Code Grant Flow長怎樣,所以僅介紹Authorization Code Grant Flow

Authorization Code Grant Flow流程

廢話不多說先上流程圖

取自Yu-Cheng Chuang’s Blog的簡報

A)💻 client會預先設置授權連結並用query的方式夾帶在url中。
B) 👤使用者點擊授權連結後,轉到🔑授權中心並詢問你要不要授權這 些資料給💻client,按下同意後。
C) 🔑授權中心就會回傳Grant(授權狀)(是以query的形式夾帶),送到💻client指定的 redirect_url。
D)💻client收到後會以用Grant(授權狀),去🔑授權中心索取token
E)🔑授權中心確認無虞後就會發放token給client使用
後續💻client就可以token去向🏢資料中心索取資料啦

詳細的細節,就交給實際串一次再來講,GOGOOGO

實作

  1. 先去https://console.cloud.google.com/apis/credentials,新增OAuth2.0的憑證跟設定redirect_url
  2. 建立簡易前端頁面

3. 建立使用者點擊下去,轉到🔑授權中心的url

A)💻 client會預先設置授權連結並用query的方式夾帶在url中。

B) 👤使用者點擊授權連結後,轉到🔑授權中心並詢問你要不要授權這 些資料給💻client,按下同意後。

query夾帶需要的選項

  • redirect_uri ,送出去請求後🔑Google會去確認你設定的redirect_uri有沒有相同,不相同就會失敗
  • client_id,🔑Google會去確認你的client_id是否存在
  • response_type,一定要標"code"才會知道要走"Authorization Code Grant Flow",不然🔑Google沒辦法分辨到底要走哪一個流程
  • scope,client端開出想要的資料,🔑Google會去確認你的格式、內容是否正確

4. 將Grant(授權狀)回傳到先前設定的redirect_uri

C) 🔑授權中心就會回傳Grant(授權狀)(是以query的形式夾帶),送到💻client指定的 redirect_url。

(這邊要注意的是,這個由🔑Google回傳的url透過query給你grant,瀏覽器是看得到這個response的,query是明碼相對來說是不安全的,是能被人利用的,所以才會有下一步,後端收到後,由後端去請求token,而這段過程是瀏覽器是看不到的。)

那我們來看看回傳的東西長什麼樣

/auth/google/callback?code=4/0AXWgctXynOPlUNJUOsg&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+openid&authuser=0&prompt=consent

有點亂?沒關係可以看到回傳的/auth/google/callback ,是預先設定的redirect_uri。

query 部分看下面,另外用object呈現

{ code:
'4/0AXWgctXynOPlUNJUOsg',
scope:
'email profile https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email openid',
authuser: '0',
prompt: 'consent' }

可以看到我們拿到了關鍵的Grant(授權狀)也就是code裡面的內容。

=====以下動作皆在 app.get(auth/google/callback)內執行=======

5. 以這組code去向🔑Google換取token

D)💻client收到後會以用Grant(授權狀),去🔑授權中心索取token

要索取token必須要POST夾帶以下資料才能夠成功換取

  • code、clientId、clientSecret、redirectUri、grant_type
    🔑Google會去確認clientId、clientSecret、是否正確redirectUri是否與client設定的相同,若無問題就會回傳token

其中grant_type這個用意一樣,是告知🔑Google要用什麼樣的流程索取,"Authorization Code Grant"要設"authorizaion_code"

那回傳的data會以json檔回傳,並包含以下

  • access_token,用來索取資料
  • refresh_token,當access_token過期時,可以透過POST refresh token 去做更新access_token,其過期時限也會設定的比access_token久
  • scope,client要請求的資料選項
  • expires_in,access_token的過期時間
  • id_token:這部分是google自己的設定,請求資料需要放在header內使用Bearer 夾帶進去
{ 
access_token:'yUPj01aw4Eh0TWscGAu8CQUW4fr9DKp',
expires_in: 3599,
refresh_token:'1//0egm404TGA4SNwF',
scope:'https://www.googleapis.com/auth/userinfo.profile
https://www.googleapis.com/auth/userinfo.email openid',
token_type: 'Bearer',
id_token:
'eyJhbGciOiJSUzI1NiIsImtp'
}

6.使用token向google的資料中心索取使用者同意給的資料

回傳後的資料為以下

{ id: '10451',
email: 'example@gmail.com',
verified_email: true,
name: 'ZHIH',
given_name: 'ZHIH',
picture:
'https://lh3.googleusercontent.com/a/123tyr4',
locale: 'zh-TW' }

接下來就看要怎麼運用這筆資料啦~~如果是建立帳戶,就把相關資訊存到資料庫裡。

總結

實際跑過一次對於整個運作真的清晰許多,對於以後串接大公司的API (google、facebook)閱讀上難度應該會降低不少,就這樣啦~~~

886

--

--