前言
在 Native applications 因為是安裝在個人的設備上面,例如 手機,個人電腦。
這些 Native applications 通常會跟作業系統註冊 App-Claimed https URL (https://app.exp.com 會開啟該 application) 或是使用 Custom URL Scheme(myapp://callback#token=…)。
因為OAuth 2.0 - Implicit Flow可以用在 public client,
因為 Implicit Flow 比較不安全,所以現在大多改使用 PKCE,它是擴充自 Authorization Code Flow 。
在 OAuth 2.0 - Authorization Code Flow 在取得 Token 時,需要將 client_secret 一併傳出來取得 access token ,如果是 public client 就有可能會被知道 client_secret 的值,所以 PKCE 就是動態建立 Code Verifier 來取代 client_secret。
以下使用OAuth 2.0 Playground - pkce來演示。
OAuth 2.0 - Authorization Code Flow With PKCE
1.建立 Code Verifier ( A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long)
可透過 IdentityModel Nuget 套件的 CryptoRandom.CreateUniqueId 來建立
1 | var codeVerifier = CryptoRandom.CreateUniqueId(50); |
2.建立 Code Challenge
1 | var codeChallenge = ""; |
3.組出跟 Authorization Server 取回 Code 的 URL(authorize),
response_type的值為code(表示我們要取回授權碼),client_id, redirect_uri為 Client 註冊的資訊,
state為防止 XSRF 準備的值,當 Authorization Server 回 Call 時,也會把接收到的 state 回傳回來,
scope 則為要求的 Resources,這裡給的值為 photo 及 offline_access ,這裡是用 **+**,有些是使用 空白 分隔,
code_challenge就是前面 codeVerifier 透過 Sha256 算出 Hash 值轉成 Base64 字串,
為的就是在取 Access Token 時可以透過 codeVerfier 而不需傳遞 client_secret 。
1 | https://authorization-server.com/authorize? |
4.轉址到上述建立的 URL(Authorization Server 的 Authorization endpoint),在 Authorization Server系統中,輸入使用者帳號及密碼
5.顯示同意 Client 存取要求的 scope 權限(consent 畫面可以設定顯示與否)。
6.當使用者同意後,Authorization Server 會把產生的 code (授權碼)及剛才傳過去的 state 一併傳回來給 redirect_uri 的網址(authorization-code-with-pkce.html)去驗證 state 值是否正確。
註:參數透過 QueryString ,所以 response_mode=query
7.組出要透過 Code 及 Code Verifier 來跟 Authorization Server 取回 AccessToken 的 URL(token),
grant_type為authorization_code,code為剛才從 Authorization Server 取回的 授權碼
1 | POST https://authorization-server.com/token |
- 註: 像 IdentityServer4 的 Client 可以設定 client_secret 不是必要的,所以 client_secret 就可以不需要傳遞
8.Post後就會得到 AccessToken
1 | { |
要呼叫 API 時,加入 access_token 就可以了
1 | HttpClient client = new HttpClient(); |
- 因為 scope 有 offline_access,所以 refresh_token 會一併回傳
適用情境
因為在取得 AccessToken 時,不會傳遞 client_secret 改以 code_verifier 傳遞,所以適用於 public client,例如 SPA, Native applications
而 Private Application 則可以加上 PKCE 讓它更安全,原本使用 Implicit Flow 的 Public Application 請改使用 Authorization Code Flow + PKCE ,
並設定 client_secret 不是必要值。
參考資源
Protecting Apps with PKCE
Redirect URLs for Native Apps
Registering an Application to a URI Scheme
Hyperlinking Beyond the Web
PKCE: What and Why?
Attacking and Defending OAuth 2.0 (Part 1 of 2: Introduction, Threats, and Best Practices)