API Gateway custom authorizer
這一篇不放在這之前的API Gateway節章,而是額外提出在這裡討論,主要的原因是卡一個Cognito的服務,所以才會在談完Cognito這個服務後才來談。
API Gateway custom authorizer 是什麼
因為API Gateway本身是對外公開的網址,所以會有一種情況是權限控管的需求,而custom authorizer則是讓使用者可以透過這層控制,讓API Gateway具有權限控管的功能。
建立API Gateway custom authorizer
進到API Gateway的頁面中,選擇我們的API後,即可以看到「Authorizers」的選項。
點選進入後,點選「Create」按鈕後,可以看到有二個選項:
我們分別來述說這二種的處理方式。
Custom Authorizer
流程
首先Custom Authorized的部份,這個部份是讓使用者客制化自己的API Gateway權限,不過要談論如何處理之前,我們先來看看它的作業流程:
上圖截圖至AWS官網,我們根據上圖,來說明一下Custom Authorizer的流程:
-
首先是左邊的Clinet,發出Request至API Gateway。
-
API Gateway收到Request後,會根據Client帶來的Token,先到下方的Cache確認是否有資料,如果沒有資料的話,就轉傳至上方的Lambda Auth function;由此可知,Custom Authorizer只能透過Lamdba來作業。
-
Lambda Auth function接收到Token資料後,判斷這個Request是誰,並決定該使用者的權限。
-
Lambda Auth function將Auth資料回傳給API Gateway。這裡的Auth資料是有定義的,待會會再說明。
-
API Gateway依據收到權限資料,判斷這個Request是否擁有權限,如果沒有權限,則回傳403給使用者,否則就執行原本API Gateway應該執行的事;並且將Auth的資料儲存進Cache中。
設定
瞭解整個流程後,我們來看看API Gateway上的Custom Authorizer要填的資料:
上圖中,關於Lambda的事應該不用再提了,這些選項中,比較重要的部份有二個:
-
Identity token source:這裡的設定是要取Client中的那一個值當成Token,並傳送給Lambda Auth function,以這裡的範例來說,會將header中的Authorization內容,轉給Lambda Auth function去做驗証。
-
Result TTL in seconds:這裡設定的數值即是當Lambda Auth function回傳給API Gateway的值,要在Cache中保留多久,以範例上的設定來說,目前是保留3分鐘。
關於TTL的值要多才適合,主要是看自己的需求來決定,如果太短,會變成每次都會需要Lambda Auth function進行處理才能得知權限,但若是保留太久,會有一種情況是:
系統上明明已經給特定的使用者設定權限了,但回應卻還是403(權限不足)。
這是因為API Gateway的Cache所導至,而且目前並沒有一個好的方法可以去刪除API Gateway Cache中的資料,所以只能等它過期;所以數字的多寡,必須由讀者自行去確認自己的需求,才能決定出最適合的數字。
資料格式(API Gateway to Lambda)
瞭解整個流程與設定後,接下來的問題就是這個Lambda Auth function了,剛剛在流程的部份有談到,關於API Gateway與Lambda Auth function中的格式,都是有被定義的,所以我們必須照著這個規範走,首先我們來看由API Gateway至Lambda Auth function的格式,這個格式可以在Lambda中的Configure test event中找到:
從這裡可以看到會有三個值從API Gateway中帶過來,說明如下:
-
authorizationToken:這個就是我們剛剛在API Gateway中看到的「Identity token source」設定,也就是Client Request中的header的Authorization的值。
-
methodArn:這個值代表是由那一個API Gateway中觸發的,通常會從這個值來取得API Gateway中的restApiId與stage的值,再搭配該使用者可使用的Resource,組回後的資料再回傳給API Gateway。
-
type:這個沒什麼好說的,目前它只會固定是「TOKEN」這個值。
有了authorizationToken就可以知道是誰,有了methodArn就可以知道來源的API Gateway,這樣就可以組成這個Request的權限資料了,不過資料格式是?現在才剛要說而已。
資料格式(Lambda to API Gateway)
接下來是要由Lambda Auth function將權限的資料回傳給API Gateway,回應的資料當然是JSON格式的資料,而內容大概如下:
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": [
"arn:aws:execute-api:[region]:[account_id]:[restApiId]/[stage]/[method]/[resourcePath1]",
"arn:aws:execute-api:[region]:[account_id]:[restApiId]/[stage]/[method]/[resourcePath2]",
"arn:aws:execute-api:[region]:[account_id]:[restApiId]/[stage]/[method]/[resourcePath3]"
]
}
]
}
不知道讀者對於這種格式有沒有很眼熟,沒錯,這就是IAM的Policy格式,換句話說,我們Lambda Auth function要產生這樣的格式回給API Gateway,而API Gateway會根據Resource內的資料,來判斷這個Request要不要繼續進行。
這邊有一點要特別談的是,這段JSON格式,實際上與IAM的Policy是一樣的,所以*(星號)是可以被使用的,例如:
原本的:
"arn:aws:execute-api:[region]:[account_id]:[restApiId]/[stage]/[method]/[resourcePath2]",
修改為
"arn:aws:execute-api:[region]:[account_id]:[restApiId]/*/*/user/*",
就會變成這個[restApiId]下的所有stage,且是所有method,只要Resource Path是user/*的,都會被允許。
測試
完成上述的Lambda Auth function並設定完API Gateway後,就可以在API Gateway中的Custom Authorizers中進行測試:
在Identity token中貼上給Lambda Auth function的token資料後,按「Test」,就可以得到Lambda執行後的結果,下圖是正常執行的回應。
Cognito User Pool Authorizer
接下來我們來談談另外一種Authorizer,不過基本上也沒什麼好談,因為它是搭配AWS的Cognito的服務,所以只需要設定即可,所有程式的部份都由AWS幫我們處理好了;下圖是API Gateway針對Cognito User Pool Authorizer的設定:
雖說Cognito User Pool Authorizer的設定很簡單,但是它也有它的缺點,那就是它辦法做細微的區隔;Cognito User Pool Authorizer只能幫我們檢查這個使用者是不是有存在Cognito的Uesr Pool裡,有就放行,沒有就擋掉,它不能像是Custom Authorizer一樣,可以針對單一使用者進行細微的權限設定。
至於那一種比較好用,當然是看需求了,不過,大部份的情況,還是Custom Authorizer吧,畢竟一個網站不太可能用一個帳號走完全部需求,通常都會有權限的控管。
Cognito User Pool + Custom Authorizer
假設我們使用了Cognito User pool,並且想要像Custom Authorizer這樣,可以做細微調整的話,能不能辦到呢?答案是可以的,不過在API Gateway的設定上,一樣要走Custom Authorizer的路。
如果我們要達到Cognito User Pool + Custom Authorizer的做法,首先我們必須能夠從Token反解出Cognito中的user,整個Verify token的流程可以在官網中找到,而範例程式在這裡,取得user後,就可以根據使用者組出該使用者的權限。
結論
API Gateway中的Custom Authorizer非常實用,它可以幫我們在API Gateway這一層就完成權限的判斷,而不是放行所有的Request後,再到Lambda上進行權限判斷;再加上搭配的Cache,讓Lambda Auth function的運行負擔可以降到最低,所以讀者勿必熟悉這個部份。