API Gateway - Custom Authorizer using Lambda


Overview API Gateway 中提到,Amazon API Gateway 提供了客製化驗證的機制,讓我們可以個別設定驗證的方式,使用者認證 (Authentication) 可以使用 OAuth or SAML;授權 (Authorization) 可以使用 JSON Web Token (JWT) 或者 OAuth provider。

本文整理如何使用 Lambda 做 Custom Authorizers 的基本用法與流程。

Custom Authorizers

下圖是 官方文件 描述客製驗證流程:

圖中描述的關鍵在於中間紅色的 Policy is evaluted 要呈現的,實作上來講,透過上面的 Lambda Auth Function 回傳 IAM Policy 的資料結構,此 Policy 描述能否完成授權。

所以實際流程大概是這樣:

  1. 客製驗證:整合自行設計的認證、授權,或者既有的系統
  2. 依據驗證的結果回傳對應的 IAM Policy,回傳內容是 JSON 內容的字串

途中 Lambda 也可以使用 Cognito 作認證。

Authorizer Types

使用 Lambda 作為 Authorizer 提供兩種授權類型:

  1. TOKEN: 透過 HTTP HEADER 傳送 authorization token,通常配合 OAuth 使用
  2. REQUEST: 透過 request parameters 傳送授權資訊,像是用 headers, query strings, stage variables, or context parameters.

配置

  1. 建立 Custom Auth - Lambda Function
  2. 建立 IAM Role 作為 Custom Authorizer 的 Service Role
  3. 設定 Custom Authorizer
  4. 使用 Custom Authorizer

1. 建立 Custom Auth - Lambda Function

建立一個 Lambda Function,Code 如下。這段 code 是 官方文件 中簡化的版本,只有增加 customToken 作為驗證,通過驗證後,會回傳 IAM Policy 的 JSON 資料:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

// @see: https://docs.aws.amazon.com/apigateway/latest/developerguide/use-custom-authorizer.html

var customToken = ['abcde', 'wxyz1']

exports.handler = function(event, context, callback) {
console.log(`DEBUG event: ${JSON.stringify(event, null, 2)}`);
if (customToken.indexOf(event.authorizationToken) >= 0) {
callback(null, generatePolicy('user', 'Allow', event.methodArn));
} else {
callback("Error: Invalid token", generatePolicy('user', 'Deny', event.methodArn));
}
};

// Help function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
var authResponse = {};

authResponse.principalId = principalId;
if (effect && resource) {
var policyDocument = {};
policyDocument.Version = '2012-10-17';
policyDocument.Statement = [];
var statementOne = {};
statementOne.Action = 'execute-api:Invoke';
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
console.log(`DEBUG authResponse: ${JSON.stringify(authResponse, null, 2)}`);
return authResponse;
}

在 Lambda Console 可以用以下 JSON 測試:

1
2
3
4
5
{
"type": "TOKEN",
"methodArn": "arn:aws:execute-api:{REGION}:{AWS_ACCOUNT_ID}:{API_GATEWAY_ID}/null/GET/",
"authorizationToken": "12345"
}

測試結果如果正確,可以在 CloudWatch Logs 看到 DEBUG Log 顯示回傳內容是一段 IAM Policy:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"principalId": "user",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow",
"Resource": "arn:aws:execute-api:{REGION}:{AWS_ACCOUNT_ID}:{API_GATEWAY_ID}/null/GET/"
}
]
}
}

這段 Code 的目的有兩個:

  1. 認證、授權:這段可以自行依照需求客製化,或者串接既有的服務
  2. 回傳授權的 IAM Policy

注意,這個 Lambda 使用的 IAM Role 不同於下一步的 Custom Authorizer Service Role

2. 建立 Custom Authorizer 的 Service Role

這個 IAM Role 是要給 API Gateway 使用的 Service Role,主要是 Runtime 時,要執行 Custom Authorizer 的身份。

建立 IAM Role 時,選擇以下的 Managed Policy:

  • AWSLambdaRole: 允許執行 Lambda
  • AWSLambdaBasicExecutionRole: 主要是允許存取 CloudWatchLogs

注意:官方的 Managed Policy 命名不是很好,第一個一定要選到,否則無法執行

配置 Trust Relationship,主要是新增 apigateway.amazonaws.com 允許 API Gateway 執行此 policy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
{
"Sid": "APIGatewayAuthorizer",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}

建立完成後,複製 ARN,下一個步驟使用。

3. 設定 Custom Authorizer

回到 API Gateway,到 API 的 Authorizer ,選擇 Create New Authorizer,填入相關資訊:

  • Lambda Function Name: 這裡的名稱叫做 OpsAPI-CAuth-Token, 等一下設定會使用到。
  • Lambda Execution Role: 填入上個步驟建立的 ARN
  • Lambda Event Payload: 選 Token
  • Token Source: 填入 x-cauth-token
  • Authorization Caching: 關掉,方便測試

設定如下圖:

4. 使用 Custom Authorizer

到 API 設定,選擇要使用的 Method,在 Method Request 裡的 Authorization,選擇剛剛建立的 Custom Authorizer,如下圖:

沒問題就部署 API.

5. 測試

cURL 送出 HTTP POST

1
2
3
4
5
curl -X POST \
-H 'Content-Type:application/json' \
-H 'x-cauth-token:12345' \
--data '{ "message": "ok"}' \
https://{API_ENDPOINT}/{STAGE}/slack

順利的話,會得到正確的結果。

注意事項 (Troubleshooting)

  • Debug 不要開 Authorization Caching
  • Debug 如果更改 Custom Auth 設定,要重新部署 Rest API,像是開了 Auth Caching,關掉後,要記得重新部署。
  • 注意 Custom Auth 的 IAM Policy,很容易選錯。
  • 如果會間歇性的出現 200 / 402,然後收到這樣的 error message: { "Message": "User is not authorized to access this resource" },我的解法是,把 Custom Auth 的 TTL 調成 1s,然後重新部署就可以了。相關參考:Re: API gateway custom autorization

結論

本文整理如何在 API Gateway 使用 Lambda 做簡單的認證流程,屆下來繼續整理如何 Debug API Gateway


延伸閱讀

系列文章

參考資料


Comments