0%

【AWS】使用 API Gateway Authorizer 做使用者驗證 feat. Serverless

API Gateway Lambda authorization workflow

前言

在 AWS 的 API gateway 上就有一個項目(Authorizer)是支援 Cognito 以及客制認證的方法,好處就是在進我們寫的 API 之前有個可以做身份確認的 Lambda Function 幫我們好前處理,而本篇則會介紹如何在 Serverless framework 上設定 API gateway 的 Authorizer 以及成功讓 API 回應,串接 Cognito 做使用部分就留給之後吧!

👉 本篇的範例全都在 GitHub

API Gateway - Authorizer

擁有 Authorizer 的一個 Serverless 服務會長這樣:

User Request - [Authorizer] - API - Response user

好處除了前言所提到的前處理外,還有如果設定是 Edge 模式 (不是透過 region 從網路層傳) 的話其實在認證完後傳到 API 時速度是很快的。

以下就開始好好的介紹它 😆

實際的位置

如果在這邊設定覺得不知道在哪邊的話可以參考 - 純手動用法官方教學

中文的話左邊名為授權方,英文則是Authorizer
API Gateway Authorizer

Serverless 實作

Yaml settings

原始如果串接 python WSGI 的話設定檔應該會長得如下:

1
2
3
4
5
6
functions:
api:
handler: wsgi_handler.handler
events:
- http: ANY /
- http: ANY {proxy+}

設定 Authorizer Lambda function,先在跟 api 同層加上 Authorizer Lambda,指定 handler 為 handler 資料夾下的 authorizer.pyauth_handler function,並且設定 cors 為 true,避免呼叫 api 時被 AWS 擋下來無法使用。

接著在把 events 裡的兩個事件加上 authorizer 的 Function,名為 ResourceAuthorizer,改編過後入下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
functions:
api:
handler: wsgi_handler.handler
events:
- http:
path: /
method: ANY
authorizer: ResourceAuthorizer
- http:
path: /{proxy+}
method: ANY
authorizer: ResourceAuthorizer
ResourceAuthorizer:
handler: handler/authorizer.auth_handler
cors: true

只要透過簡單的設定 function 就可以互相溝通是不是很像 docker compose 呀 😏

處理 Lambda function handler

參考並複製官方的範例程式會看到我的開頭要從 event 取得 token:

1
event_token = event.get('authorizationToken')

🐛🐛 這裡雖然是取 authorizationToken,不過呼叫 api 時 header 是要帶 Authorization,如此一來 Api gateway 才會收到對的值,這邊是需要注意 AWS 比較不一樣的地方!
參考支援在這啦 QQ

取得 token 後一般就會做身份驗證的部分,以我的例子來說我就是解 JWT 下來做驗證,這邊為了 Demo 就先不處理它。

產生 Auth Policy

官方提供的範例版本是 Node.js[參考資料],但畢竟我們是用 python 看著 javascript 我怎麼用啊!!

因此查了一番找到了範例: aws-apigateway-lambda-authorizer-blueprints,只是說沒有任何文件使用起來只能靠本能了 😭,這邊他很佛心的提供 dotnetgolangpythonjavanode.js以及 rust 的範例,基本上常在 Serverless 上跑的語言都有了,就只好放過它一馬了 😇

基本上從官方的範例終止需要改寫成像我專案這樣只要注意 auth_handler 裡面的內容需要填什麼即可。

其他需要注意的就如第 25 行的 policy以及 28 行的 build policy所產出的東西需要確認清楚才行:

1
2
3
4
policy.allowAllMethods()

# Finally, build the policy
authResponse = policy.build()

而下面的 class code 就可以先忽略不管,因為那些官方是為了產生出 Authorizer 回應狀態 pattern 所使用的 code,即便我們去改也出不了什麼作用:

1
2
3
4
5
6
7
class HttpVerb:
GET = "GET"
...

class AuthPolicy(object):
awsAccountId = ""
...

Lambda 回應狀態

Authorizer 認證成功之後 Lambda 會回傳給 API Gateway 的 pattern:

1
2
3
4
5
6
7
8
9
10
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:ivdtdhp7b5/ESTestInvoke-stage/GET/"
}
]
}

認證失敗回傳的樣子:

1
2
3
4
5
6
7
8
9
10
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Deny",
"Resource": "arn:aws:execute-api:us-east-1:123456789012:ivdtdhp7b5/ESTestInvoke-stage/GET/"
}
]
}

這些都會是透過 AuthPolicy 來產生的,直接對 Function return value 是部會作用的。

使用心得 (踩坑)

  • Header 收的名稱是 Authorization,但程式是收authorizationToken參考
  • 只能 raise Exception(‘Unauthorized’),多一字少一字都不行,真的很奇怪,參考
  • 好不容易找到的範例預設是會先 deny 掉使用者,但這在測行為會很困擾,官方範例 code

結論

以往都要使用框架(Flask, Django…)裡提供的功能實作 middleware 來做處理,但若使用像 AWS、Google、Azure 他們 Serverless 服務的話不妨使用 Authorizer 來做驗證,除了認證使用者以外還可以藉由這些 Function Service 來降低 API 的負載,讓它們可以更專心地處理後端邏輯的問題。

如果真的很想知道 Authorizer 的行為機制的話可以看看這篇官方文件 - 使用 postman 去測試 authorizer

參考