昨天有提到一個問題,但並沒有特別解釋相關的處理方法,是不同的撤銷情境,該怎麼實作。
Token 撤銷權限的情境主要有三個:
使用 Access Token 時,在第二和第三個情境下,通常沒有什麼差別。主要是因為 Token 的狀態是存在授權伺服器上,屬於中央集權式,因此所有服務都無法自己確認 Token 的狀態,都必須要問授權伺服器才能知道 Token 是否還有效等狀態。但 ID Token 就不是這樣了,之前提到 ID Token 有簽章可以保護 Claim 的資訊不被亂改;同時 Claim 也有過期時間可以知道何時失效,所以應用程式只要有公鑰,就能自行驗證 ID Token 是否有效。
ID Token 非常方便,而且可以有效節省網路傳輸資源,但隨之帶來的問題就是撤銷 Token 的情境。
在第二個情境下沒有問題,使用者主動跟應用程式說這個 ID Token 不想使用了,想提早撤銷權限,這時應用程式只要確保 ID Token 有移除即可。問題在於第三個情境,當今天擁有 ID Token 的是攻擊者,伺服器包括應用程式或授權伺服器,都無法主動撤銷這個 Token--因為 ID Token 的狀態是存在 Token 上,而不像 Access Token 是存在授權伺服器上。
ID Token 代表的是登入的憑證,若要把 ID Token 的權限撤銷,代表的意義其實就是登出。今天先說明第二個情境的實作方法,也就是應用程式該如何登出。
OpenID Connect 對此有定義應用程式該如何啟動登出,也就是 OpenID Connect RP-Initiated Logout 1.0 這份協定。不過這份協定裡面還依賴了很多資訊,這部分之後再來說明,先來看 Hydra 官方文件是如何實作登出的。
Hydra 是定義一個叫 Logout Flow 的文件,時序圖如下:
http://127.0.0.1:4444/oauth2/sessions/logout
,可以用 GET(302)或是 POST(200 + Form)。可以使用的參數後面會說明。logout_challenge
在控制整個登出流程。登出端點跟流程比較有關係的欄位如下(詳細欄位說明可以參考協定):
欄位 | 必填 | 說明 |
---|---|---|
id_token_hint |
RECOMMENDED | 這裡要給當初登入的 ID Token,可以提示授權伺服器目前登入的身分等資訊。 |
client_id |
OPTIONAL | 應用程式的唯一識別碼。 |
post_logout_redirect_uri |
OPTIONAL | 登出完後要導回應用程式的哪個地方。 |
state |
OPTIONAL | 與登入所使用的 state 欄位意義相同。 |
首先從這裡的資訊可以了解,就協定的定義來說,其實不傳參數也是能動的,只是轉導去哪,必須要由授權伺服器設定。而直接打開這頁的話,Hydra 會提示要設定 urls.post_logout_redirect
為預設轉導網址。
若想要使用 post_logout_redirect_uri
轉導回應用程式就必須額外註冊對應 URL,對 Hydra 來說,這是一開始註冊應用程式就要提供的。文件還有提到如果有提供這個欄位的話,建議也要給 id_token_hint
。這個推測應該是為了確保不被 CSRF 攻擊,因為實際上,如果只有 post_logout_redirect_uri
欄位的話,那登出網址是可以散布的,但如果含有 id_token_hint
的話就沒辦法了。
而對於使用者來說,使用同個身分驗證中心,並進入不同的應用程式,是件很方便的事。相反地,對於授權伺服器來說,控制身分驗證的狀態就不是那麼容易,尤其是要清除登出狀態的時候。協定裡有提到當應用程式啟動登出之後,其他應用程式應該如何接受到這個訊息,並把使用者狀態一同清除,主要有下面三個協定:
該使用哪個協定,由應用程式和授權伺服器約定好方法執行,因此也是註冊的時候設定即可。Hydra 這三個協定都有實作,筆者只有實作過第三種方法,因此後續會用這個協定來做說明。
今天只跟大家說明清除 ID Token 遇到的問題,以及該如何做登出,明天來實作 Logout Provider,以及協定裡面提到很多檢查項目,Hydra 是如何實作的。