OAuth 2.0與OpenID Connect協議的完整指南

本文由Haseeb Anwar發表在medium,經原作者授權由InfoQ中文站翻譯並分享。

我們都在網站或者手機應用中見過“谷歌登陸”和“綁定Facebook“這樣的按鈕。如果你點擊這個按鈕,就會有一個窗口彈出並顯示“這個應用想要訪問你的公共個人主頁、通訊錄……“,同時它會詢問你是否授權。概括而言,這就是OAuth。對於每個軟件工程師、安全專家甚至是黑客,理解這些協議都是非常重要的。

前言

本文是一篇關於OAuth 2.0與OpenID Connect協議的完整指南,這兩個協議是用於授權和認證的使用最廣泛的的協議。OAuth 2.0用於授權,OpenID Connect用於認證。有兩種OAuth 2.0授權流程最爲常見:服務端應用程序的授權碼流程和基於瀏覽器的應用程序的隱式流程。OpenID Connect是OAuth 2.0協議之上的標識層,以使OAuth適用於認證的用例。

爲什麼需要OAuth?

爲了更好地理解OAuth誕生的理由,我們需要理解一個術語:代理授權。

代理授權

代理授權是一種允許第三方應用訪問用戶數據的方法。

兩種代理授權的方式

有兩種代理授權的方式:一是你將賬號密碼提供給第三方應用,以便它們可以代表你來登陸賬號並且訪問數據;二是你通過OAuth授權第三方應用訪問你的數據,而無需提供密碼。(我相信我們都不會選擇交出我們的密碼!)

現在,我們知道了OAuth的必要性和重要性,讓我們更深入地研究這個協議。

什麼是OAuth?

OAuth(Open Authorization,即開放授權)是一個用於代理授權的標準協議。它允許應用程序在不提供用戶密碼的情況下訪問該用戶的數據。

OAuth 2.0術語表

爲理解這個協議,我們需要理解以下術語:

  • 資源所有者(Resource Owner):擁有客戶端應用程序想要訪問的數據的用戶。
  • 客戶端(Client):想要訪問用戶數據的的應用程序
  • 授權服務端(Authorization Server):通過用戶許可,授權客戶端訪問用戶數據的授權服務端。
  • 資源服務端(Resource Server):存儲客戶端要訪問的數據的系統。在某些情況下,資源服務端和授權服務端是同一個服務端。
  • 訪問令牌:訪問令牌是客戶端可用於訪問資源服務端上用戶授權的數據的唯一密鑰。

以下是OAuth 2.0抽象流程圖,讓我們一起看看上述術語在圖中的應用

OAuth2.0抽象流程圖

授權密鑰(Authorization Key)或者權限(Grant)可以是授權碼或者令牌的類型。下文我們將會提到不同的權限和授權密鑰。現在,讓我們先詳細解釋授權的流程。

  1. 用戶通過點擊按鈕啓動整個授權流程。這個按鈕通常類似於“谷歌登陸“、”Facebook登陸“或者通過其他的應用登陸。
  2. 然後客戶端將用戶重定向到授權服務端。在重定向的過程中,客戶端將類似客戶ID、重定向URI的信息發送給授權服務端。
  3. 授權服務端處理用戶認證,並顯示授權許可窗口,然後從用戶方獲得授權許可。如果你通過谷歌登陸,你必須向谷歌,而不是客戶端,提供登陸證書——例如向accounts.google.com提供登陸證書。
  4. 如果用戶授權許可,則授權服務端將用戶重定向到客戶端,同時發送授權密鑰(授權碼或令牌)。
  5. 客戶端向資源服務端發送包含授權密鑰的請求,要求資源服務端返回用戶數據。
  6. 資源服務端驗證授權密鑰,並向客戶端返回它所請求的數據。

這就是用戶在不提供密碼的情況下,允許第三方應用訪問用戶數據的過程。但與此同時,有一些問題出現了:

  • 我們如何限制客戶端只訪問資源服務端上的部分數據?
  • 如果我們只希望客戶端讀取數據,而沒有權限寫入數據呢?

這些問題將我們引導至OAuth技術術語中另一部分很重要的概念:授權範圍(Scope)。

OAuth 中的授權範圍(Scope)

在OAuth 2.0中,授權範圍用於限制應用程序訪問某用戶的數據。這是通過發佈僅限於用戶授權範圍的權限來實現的。

當客戶端向授權服務端發起權限請求時,它同時隨之發送一個授權範圍列表。授權客戶端根據這個列表生成一個授權許可窗口,並通過用戶授權許可。如果用戶同意了其授權告知,授權客戶端將發佈一個令牌或者授權碼,該令牌或授權碼僅限於用戶授權的範圍。

舉個例子,如果我授權了某客戶端應用訪問我的谷歌通訊錄,則授權服務端向該客戶端發佈的令牌不能用於刪除我的聯繫人,或者查看我的谷歌日曆事件——因爲它僅限於讀取谷歌通訊錄的範圍。

OAuth 2.0的設置

在討論OAuth流程之前,最好先了解一些OAuth的配置。當發起授權權限的請求時,客戶端將一些配置數據作爲查詢參數發送給授權服務端。這些基本的查詢參數包括:

  • 響應類型(response_type):我們希望從授權服務端獲得的響應類型
  • 授權範圍(scope):客戶端希望訪問的授權範圍列表。授權服務端將使用這個列表爲用戶產生同意授權許可窗口。
  • 用戶ID(client_id):由授權服務在爲OAuth設置客戶端時提供。此ID可幫助授權服務端確定正在發送OAuth流程的客戶端。
  • 重定向通用資源標識符(redirect_uri):用於告知授權服務器當OAuth流程完成後重定向的地址
  • 客戶密碼(client_secret):由授權服務提供,根據OAuth流程,這個參數可能需要也可能不需要。我們將在授權碼流程中會了解到它的重要性。

瞭解不同的OAuth流程

兩種最常用的OAuth2.0流程是:基於服務器的應用程序所使用的授權碼流程,以及純JavaScript單頁應用所使用的隱式流程。

爲了解釋OAuth的各類流程,接下來我將用谷歌作爲OAuth服務提供者。

授權碼流程

授權碼流程,或者說授權碼權限,是理想的OAuth流程。它被認爲是非常安全的,因爲它同時使用前端途徑(瀏覽器)和後端途徑(服務器)來實現OAuth2.0機制。

OAuth2.0授權碼流程

客戶端通過將用戶重定向到授權服務端來發起一個授權流程,其中,response_type需被設置成code。這告知了授權服務端用授權碼來響應。該流程的URI如下所示:

https://accounts.google.com/o/oauth2/v2/auth?
 response_type=code&
 client_id=your_client_id&
 scope=profile%20contacts&
 redirect_uri=https%3A//oauth2.example.com/code

在上述請求中,客戶端請求能夠訪問該用戶公共主頁和聯繫人的用戶許可,這是在scope請求參數中設置的。這個請求的結果是授權碼,客戶端可以使用該授權碼來交換訪問令牌。一個授權碼如下所示:

4/W7q7P51a-iMsCeLvIaQc6bYrgtp9

爲什麼用授權碼來交換令牌?

訪問令牌是唯一能用於訪問資源服務端上的數據的東西,而不是授權碼。所以爲什麼在客戶端實際需要訪問令牌的情況下,將response_type設置成授權碼呢?這是因爲這樣做能使OAuth流程非常安全。

OAuth2.0授權碼流程

問題:訪問令牌是我們不希望任何人能訪問的祕密信息。如果客戶端直接請求訪問令牌,並將其存儲在瀏覽器裏,它可能會被盜,因爲瀏覽器並不是完全安全的。任何人都能看見網頁的代碼,或者使用開發工具來獲取訪問令牌。

解決方案:未了避免將訪問令牌暴露在瀏覽器中,客戶端的前端從授權服務端獲得授權碼,然後發送這個授權碼到客戶端的後端。現在,爲了用授權碼交換訪問令牌,我們需要一個叫做客戶密碼(client_secret)的東西。這個客戶密碼只有客戶端的後端知道,然後後端向授權服務端發送一個POST請求,其中包含了授權碼和客戶密碼。這個請求可能如下所示:

POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded
code=4/W7q7P51a-iMsCeLvIaQc6bYrgtp9&
client_id=your_client_id&
client_secret=your_client_secret_only_known_by_server&
redirect_uri=https%3A//oauth2.example.com/code

授權服務端會驗證客戶密碼和授權碼,然後返回一個訪問令牌。後端程序存儲了這個訪問令牌並且可能使用此令牌來訪問資源服務端。這樣一來,瀏覽器就無法讀取訪問令牌了。

隱式流程

當你沒有後端程序,並且你的網站是一個僅使用瀏覽器的靜態網站時,應該使用OAuth2.0隱式流程。在這種情況下,當你用授權碼交換訪問令牌時,你跳過發生在後端程序的最後一步。在隱式流程中,授權服務端直接返回訪問令牌。

OAuth2.0授權碼流程

客戶端將瀏覽器重定向到授權服務端URI,並將response_type設置成token,以啓動授權流程。授權服務端處理用戶的登錄和授權許可。請求的返回結果是訪問令牌,客戶端可以通過這個令牌訪問資源服務端。

隱式流程被認爲不那麼安全,因爲瀏覽器負責管理訪問令牌,因此令牌有可能被盜。儘管如此,它仍然被單頁應用廣泛使用。

認證與授權

正如我們所知,OAuth解決了代理授權的問題,但是它沒有提供一個認證用戶身份的標準方法。你可以這樣認爲:

  • OAuth2.0用於授權
  • OpenID Connect用於認證

如果你無法區分這些術語,則以下是它們之間的區別:

  • 認證(Authentication)是確保通信實體是其所聲稱的實體。
  • 授權(Authorization)是驗證通信實體是否有權訪問資源的過程。

換言之,認證關注的是你是誰,授權關注的是你有什麼權限。

OpenID Connect

OpenID Connect是在OAuth2.0協議之上的標識層。它拓展了OAuth2.0,使得認證方式標準化。

OAuth不會立即提供用戶身份,而是會提供用於授權的訪問令牌。 OpenID Connect使客戶端能夠通過認證來識別用戶,其中,認證在授權服務端執行。它是這樣實現的:在向授權服務端發起用戶登錄和授權告知的請求時,定義一個名叫openid的授權範圍。在告知授權服務器需要使用OpenID Connect時,openid是必須存在的範圍。

客戶端發起的用於OpenID Connect認證請求URI會是如下的形式:

https://accounts.google.com/o/oauth2/v2/auth?
 response_type=code&
 client_id=your_client_id&
 scope=openid%20contacts&
 redirect_uri=https%3A//oauth2.example.com/code

該請求的返回結果是客戶端可以用來交換訪問令牌和ID令牌的授權碼。如果OAuth流程是隱式的,那麼授權服務端將直接返回訪問令牌和ID令牌。
ID令牌是JWT,或者又稱JSON Web Token。JWT是一個編碼令牌,它由三部分組成:頭部,有效負載和簽名。在獲得了ID令牌後,客戶端可以將其解碼,並且得到被編碼在有效負載中的用戶信息,如以下例子所示:

{
  "iss": "https://accounts.google.com",
  "sub": "10965150351106250715113082368",
  "email": "[email protected]",
  "iat": 1516239022,
  "exp": 1516242922
}

聲明(Claim)

ID令牌的有效負載包括了一些被稱作聲明的域。基本的聲明有:

  • iss:令牌發佈者
  • sub:用戶的唯一標識符
  • email:用戶的郵箱
  • iat:用Unix時間表示的令牌發佈時間
  • exp:Unix時間表示的令牌到期時間

然而,聲明不僅限於上述這些域。由授權服務器對聲明進行編碼。客戶端可以用這些信息來認證用戶。

如果客戶端需要更多的用戶信息,客戶端可以指定標準的OpenID Connect範圍,來告知授權服務端將所需信息包括在ID令牌的有效負載中。這些範圍包括個人主頁(profile)、郵箱(email)、地址(address)和電話(phone)。

結語

練習你所學習的內容總是好的。你可以訪問Google OAuth 2.0 Playground來使用OAuth2.0的授權範圍、授權碼和令牌。

英文原文:

The Complete Guide to OAuth 2.0 and OpenID Connect Protocols

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章