前提
Facebook ios sdk 在 17.0.0 版本後,要求必須實作 Limited Login,並使用 JWT token 進行驗證,否則在某些情況下會有非預期錯誤(ref)。
流程為:
client 端(ios App) 會將以下資訊:
- 登入嘗試要求的權限
- 追蹤偏好設定
- nonce(隨機生產的亂數,每次請求都須不同,用來做後續的驗證)
帶入請求,向 FB 取得用戶資料與一組 JWT token。(實作範例可參考官方文件)
接著 client 會將 token 傳給我們的後端 server,後端需要驗證 token 是否合法。
重點就在於後端驗證 token 這段,官方文件非常簡明概要,並沒有附上實作範例。所以特此紀錄一下踩坑過程。
如何驗證 JWT Token
首先,文件要我們確認以下三件事:
1. That the JWT is well formed
token 須為合法的 JWT token。也就是包含一組用 Base64Url-encoded 的 header, payload, signature,每個部分解碼後都必須為合法的 json 格式。
2. The signature
解碼後的 signature 部分需要和以下步驟產生的結果相同:
藉由呼叫 JWKS endpoint 取得一組 public key
打 JWKS endpoint 會得到一個 json 物件,回傳多組 pub key:
1 2 3 4 5 6 7 8 9 10
"keys": [ { "kid": "d458ab5237807dc6718901e522cebcd8e8157791", "kty": "RSA", "alg": "RS256", "use": "sig", "n": "uPyWMhNfNsO9EtiraYI0tr78vnkiJmzsmAAUd8hLHF5vPXDn683aQKZQ2Ny5lObigNmbHI5tt5y0o5m0RuZjJTj081uWm7Z901boO-p4VLwEONzjh4vTp2ZQ7aMjo17kMBzInHqz9iruWeB94dEu_LKYdQnDI6rweD_-chWWTR4mc7xbeaNozLHYzjEisSrIM3xIry2lZv5Mh334ZoahcTXGouFtU2XV_HvStXthwhoAtizQK7s2yJlBz8qlQK2lFNojRzd95f2bkynRnIvcpoF-qHZbOBTCIf-6TLp23qShs-XvbCkwHMhzvCPxcuZx3GNfCQkyTxeM5IGIMlWZ8w", "e": "AQAB" }, ...
而要使用哪一組 key,需要用解碼的 header 當中所帶的一個屬性
kid
去比對,找出相同kid
的那把 key。使用解碼後的 header 當中指定的演算法(欄位:
alg
)以及上述的 public key 對 Base64Url-encoded 的 header 跟 payload 連接的值(Base64url-encoded header + "." + Base64url-encoded payload
) 加密處理後的結果進行 Base64url-encode。
3. The standard claims
檢查解碼後的 payload 幾個屬性值是否符合預期:
檢查 token 有無過期(欄位:
exp
)Token issuer 是否正確(欄位:
iss
)文件上沒有說明 issuer 的值為何(照理來說會是 Facebook 的某個網域或端點),後來是在某篇討論串當中找到,在限制登入的 OIDC 權杖的 OIDC 端點 > 探索端點 裡面:
1 2 3 4
{ "issuer": "https://www.facebook.com", //... }
Audience 是否與 FB App ID 相同 (欄位:
aud
)App ID 可以到 Facebook developer 後台查看
Nonce 是否正確(欄位:
nonce
)就是上面前端傳給 FB 產 token 時帶的亂數,前端在發 request 給後端時需一併攜帶過來。(後端才能驗證 payload 當中的 nonce 是否與之相同)
實作
以上解析 JWT token 與驗證 signature 的部分都有現成的 JWT 套件可以替我們完成,在這邊使用 golang 搭配以下兩個套件來實作:
- golang-jwt/jwt/v5:解析 JWT token 與驗證 signature
- lestrrat-go/jwx/jwk:解析 JWKS public key
首先定義 structs 接收需要的資料:
|
|
接著實作驗證方法的部分:
|
|
驗證成功即可向前端回傳使用者登入成功。
碎碎念
猜測是因為後端語言太多,各語言也都有自己的 jwt 套件,所以官方才沒有放驗證 Token 的實作範例,但像上面提到的 kid、issuer 應該可以說明更清楚一點。不過也算是藉機學習了,感謝 Facebook。(?)