本文是系列文章用 ASOS 創建自己的 OpenID Connect 服務器的第二部分:
ASOS 為 和 核心規範定義的所有標準流提供了內置的支持:,,,和。
雖然不是 ASOS 特有的,但是要實現自己的授權服務器,為你的應用選擇一個合適的認證流程是一個很重要的前提(important prerequisite);在這裏,讓我們快速的瀏覽一下不同的 OAuth2 / OpenID Connect 流程。
無交互流程(Non-interactive flows)
資源持有者密碼授權(resource owner password credentials grant)
資源持有者密碼授權(縮寫 ROPC),受基本身份驗證啟示(basic authentication),可能是最簡單的 OAuth2 流程了: 客戶端應用詢問用戶的帳號/密碼,向授權服務器發送帶用戶憑證的令牌(token)請求(並取決於授權服務器定義的客戶端授權策略,以及客戶端憑證),然後獲取到可用於檢索用戶資源的訪問令牌。
POST /connect/token HTTP/1.1Host: server.example.comContent-Type: application/x-www-form-urlencodedgrant_type=password&username=johndoe&password=A3ddj3w
HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Cache-Control: no-storePragma: no-cache{ "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"bearer", "expires_in":3600}
儘管 OAuth2 規範並不推薦這種方式,因為它是唯一一個讓用戶的密碼直接暴露給客戶端的流程(它破壞了最低權限準則),但是它確是最受歡迎的流程之一。
尤其是 SPA 作者,因為在 JavaScript 中,它沒有涉及任何重定向或者確認表單,不像交互方式流,不需要實現令牌驗證或者跨站點請求偽造(XSRF)對策來防止攻擊。
如果你不喜歡其它流程(以及其所要求的客戶端安全對策),那麽使用 ROPC 將會是一個不錯的選擇。但矛盾的是,使用 ROPC 的客戶端缺遠比使用交互流程但是不實現安全驗證的客戶端的安全性要差。
,你永遠不應當在第三方應用上使用它。如果你想支持那些你並不完全信任的第三方應用,你需要考慮使用交互方式來代替(比如,授權碼流或者隱式流)。
客戶端證書授權(client credentials grant)
客戶端證書授權和 ROPC 幾乎相同,但是它是專門為 client-to-server 場景設計的(該流程中沒有用戶): 客戶端發送包含客戶端證書的令牌請求,然後獲取到可用於檢索用戶資源的訪問令牌。
POST /connect/token HTTP/1.1Host: server.example.comContent-Type: application/x-www-form-urlencodedgrant_type=client_credentials&client_id=s6BhdRkqt3&client_secret=gX1fBat3bV
HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Cache-Control: no-storePragma: no-cache{ "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"bearer", "expires_in":3600}
和 ROPC 不同,在使用客戶端證書授權時,客戶端的驗證是必須的,ASOS 會拒絕所有的未經授權令牌請求。
換而言之,你不能在公共應用上使用客戶端證書授權,例如 JS,移動端,或者桌面應用,因為它們沒辦法保存它們的證書。
交互流程
授權碼流(Authorization code flow)
授權碼流有可能是最復雜的流程了,因為它涉及到用戶代理重定向(user-agent redirections)和反向通道通信(backchannel communication),好處是,在客戶端使用這種方式,access_token 不會被用戶代理截獲。
授權碼流有兩個基本步驟:授權請求/響應和令牌請求/響應
- 步驟1:授權請求
在該流程中,客戶端應用在發起認證流程的時候,生成的認證請求必須包括強制性的 response_type=code
參數,它的 client_id
,redirect_uri
以及可選的 scope
和 state
,這使得該流程可以。
大多數情況下,客戶端應用只會簡單的返回一個帶
Location
頭的 302 響應,以將用戶代理重定向到認證端點,不過得也看你用的 OpenID Connect ,也有可能支持 POST 請求,用於發送大授權請求。該功能通常使用 auto-post HTML 表單實現。
HTTP/1.1 302 FoundLocation: https://server.example.com/authorize?response_type=code&client_id=s6BhdRkqt3&state=af0ifjsldkj&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
GET /connect/authorize?response_type=code&client_id=s6BhdRkqt3&state=af0ifjsldkj&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1Host: server.example.com
處理請求授權的方式在很多情況下都是需要個別實現,但大多數情況下,都是向客戶顯示一個表單,詢問用戶是否同意和客戶端應用共享他們的私人數據。
當用戶同意之後,用戶代理重定向回客戶端應用,病句有一個唯一並且短暫的令牌,我們稱之為授權碼(authorization code),客戶端可以通過發送令牌請求,換回一個授權令牌。
HTTP/1.1 302 FoundLocation: https://client.example.org/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=af0ifjsldkj
為了防止 XSRF/Session 攻擊,客戶端應用必須確保從 identity provider 返回的
state
參數和原始的state
參數一致。並在兩者不一致的時中止認證流程。。
- 步驟2:令牌請求
當客戶端應用獲取到授權碼,它必須立刻發送 grant_type=authorization_code
令牌請求來獲取訪問令牌。
為幫助 identity provider 降低,必須發送原始的
redirect_uri
。
如果客戶端應用是機密應用(比如,已分配客戶端證書的應用),則需要進行身份驗證。
POST /connect/token HTTP/1.1Host: server.example.comContent-Type: application/x-www-form-urlencodedgrant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb&client_id=s6BhdRkqt3&client_secret=gX1fBat3bV&scope=openid
HTTP/1.1 200 OKContent-Type: application/json;charset=UTF-8Cache-Control: no-storePragma: no-cache{ "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"bearer", "expires_in":3600}
隱式流(Implicit flow)
隱式流和授權碼流類似,但是它沒有令牌請求/響應步驟,訪問令牌作為授權響應URI 片段的一部分(或者通過請求表,當你使用 response_mode=form_post
的時候)直接返回給客戶端應用。
GET /connect/authorize?response_type=token&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb&scope=openid&state=af0ifjsldkj&nonce=n-0S6_WzA2Mj HTTP/1.1Host: server.example.com
HTTP/1.1 302 FoundLocation: https://client.example.org/cb#access_token=SlAV32hkKG&token_type=bearer&expires_in=3600&state=af0ifjsldkj
該流程的安全性本身並不如授權碼方式,但是它的實現更簡單,並且是專為 JS 應用場景設計的。不建議在機密應用上使用該方式,並且授權服務器應該慎重考慮,在
client_id
命中機密應用時拒絕隱式流請求,以防止降级攻击和令牌泄漏。
為了防止 XSRF/Session 攻擊,客戶端應用必須確保從 identity provider 返回的
state
參數和原始的state
參數一致。並在兩者不一致的時中止認證流程。。
當使用隱式流,應用客戶端**必須確保授權令牌沒有被發放到另外一個應用上,以防止。**使用 OpenID Connect ,它可以通過設置
response_type=id_token
token
以及檢查 JWT 身份令牌的aud
聲明來實現,它必須對應或者包含客戶端應用的client_id
。
還是搞不清楚到底哪個流程適合你的應用?那麽看下面的速查表吧:
Flow/grant | Requires user interaction? | For server-side apps? | For public apps (JS/mobile)? | For third-party apps? |
---|---|---|---|---|
Resource owner password credentials | No | Yes | Yes | No |
Client credentials | No | Yes | No | Yes |
Authorization code | Yes | Yes | Yes | Yes |
Implicit | Yes | No | Yes | Yes |
下一章: