[アップデート]API GWのHTTP APIでIAMによる認可とLambdaオーソライザーが利用可能になりました

REST APIからHTTP APIへの移行がさらに加速しそうです!!
2020.09.10

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

CX事業本部@大阪の岩田です。

2020/9/9付のアップデートにより、API GatewayのAPIタイプとしてHTTP APIを選択した場合でもオーソライザーとして

  • IAM
  • Lambdaオーソライザー

が選択可能になりました。

さっそく各機能を試してみたので、内容を簡単にご紹介します。

何がうれしいの?

API GatewayのAPIタイプ HTTP APIはre:invent2019で発表された比較的新しい機能です。従来のREST APIと比較して

  • 低コスト
  • 低レイテンシ

というメリットがありますが、REST APIの上位互換というわけではなくREST APIに存在するいくつかの機能は利用できず、なんでもかんでもHTTP APIに移行することはできません。

このREST APIには存在するものの、HTTP APIでは利用できない機能の代表格としてオーソライザー関連の機能が挙げられます。REST APIでは認可メソッドとして

  • IAM
  • Cognito
  • Lambdaオーソライザー

が選択できますが、HTTP APIで選択可能なオーソライザーはJWTオーソライザー一択でした。REST APIの認可メソッドとしてCognitoを利用していた場合はHTTP APIのJWTオーソライザーで代替可能ですが、IAMもしくはLambdaオーソライザーを利用している場合は代替手段が存在しないため、HTTP APIに移行することはできませんでした。

今回のアップデートによってREST APIの認可メソッドとしてIAMもしくはLambdaオーソライザーを利用していたケースでもHTTP APIに移行できる可能性が高くなりました。是非REST APIからHTTP APIに移行できないか検討してみて下さい。

やってみる

実際にIAMによる認可とLambdaオーソライザーを試してみます。

IAM

まずはIAMからです。

マネコンからHTTP APIを作成し、「認可」→ 「オーソライザーを管理」と進むと「IAM(built-in)」という選択肢が増えていることが分かります。

適当にルートを作成し、オーソライザーとしてIAMをアタッチします。

普通にAPIを呼び出してみます。

$curl -i https://<API GWのID>.execute-api.ap-northeast-1.amazonaws.com/iam
HTTP/2 403
date: Thu, 10 Sep 2020 00:48:07 GMT
content-length: 23
apigw-requestid: Sn-nMhmnNjMEJLg=
{"message":"Forbidden"}

403エラーになりました。

今度は適当にexecute-api:Invokeの権限を持ったIAMユーザーを作成し、Postmanを使ってSIGV4の署名付きでAPIを呼び出してみます。

今度は200 OKでレスポンスが返却されてきました。想定通りに動作しています。

Lambdaオーソライザー

続いてLambdaオーソライザーを試してみます。

HTTP APIのLambdaオーソライザーではHTTP APIの「統合」と同様にペイロード形式として1.0もしくは2.0いずれかを選択可能です。さらに、ペイロード形式2.0を選択した場合はLambdaオーソライザーからのレスポンスとして

  • Simple
  • IAM Policy

の2つが選択可能になります。

IAM Policyを選択した場合は、REST APIにおけるLambdaオーソライザーと同様のフォーマットでLambdaからレスポンスを返却する必要があります。以下の公式ドキュメントからの引用ですが、以下のようなフォーマットです。

{
  "principalId": "abcdef", // The principal user identification associated with the token sent by the client.
  "policyDocument": {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": "execute-api:Invoke",
        "Effect": "Allow|Deny",
        "Resource": "arn:aws:execute-api:{regionId}:{accountId}:{apiId}/{stage}/{httpVerb}/[{resource}/[{child-resources}]]"
      }
    ]
  },
  "context": {
    "exampleKey": "exampleValue"
  }
}

このResourceの部分を組み立てるのが地味に面倒なんですよね。。。

https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-lambda-authorizer.html

HTTP APIで選択可能なSimpleフォーマットの場合は以下のようなフォーマットになります。こちらも公式ドキュメントの引用となります。

{
  "isAuthorized": true/false,
  "context": {
    "exampleKey": "exampleValue"
  }
}

contextは後続処理に引き渡したい情報をセットできる任意項目です認可OK/NGの判定だけであればSimpleフォーマットのレスポンスは

{"isAuthorized": true/false}

だけ返却すればOKです。IAM Policyと比較すると非常にシンプルですね!!

実際にLambdaオーソライザーを作成していきます。まずはオーソライザーとして指定するLambda Functionを作成します。公式ドキュメント記載のSimpleフォーマットのサンプルをそのまま拝借しました。リクエストヘッダのAuthorizationsecretTokenという文字列が設定されていた場合に認可OKとするロジックです。

exports.handler = async(event) => {
    let response = {
        "isAuthorized": false,
        "context": {
            "stringKey": "value",
            "numberKey": 1,
            "booleanKey": true,
            "arrayKey": ["value1", "value2"],
            "mapKey": {"value1": "value2"}
        }
    };
    
    if (event.headers.authorization === "secretToken") {
        response = {
            "isAuthorized": true,
            "context": {
                "stringKey": "value",
                "numberKey": 1,
                "booleanKey": true,
                "arrayKey": ["value1", "value2"],
                "mapKey": {"value1": "value2"}
            }
        };
    }

    return response;

};

作成したLambda Functionを指定してLambdaオーソライザーを作成します。ペイロード形式を2.0に、Response modeをSimpleにするのを忘れないで下さい。

オーソライザーの準備ができたので、適当なルートを用意してアタッチします。

準備ができたので呼び出してみます。

curl -i https://<API GWのID>.execute-api.ap-northeast-1.amazonaws.com/lambda
HTTP/2 401
date: Thu, 10 Sep 2020 01:10:25 GMT
content-length: 26
apigw-requestid: SoB4OgwttjMEJSA=

{"message":"Unauthorized"}

401エラーになりました。

今度はHTTPヘッダーのAuthorizationsecretTokenという文字列をセットして呼び出してみます。

curl -i  https://<API GWのID>.execute-api.ap-northeast-1.amazonaws.com/lambda -H "Authorization: secretToken"
HTTP/2 200
date: Thu, 10 Sep 2020 01:16:18 GMT
content-type: text/plain; charset=utf-8
content-length: 1045
apigw-requestid: SoCvXinnNjMEPOQ=

{"version": "2.0", ...略

今度は200 OKでレスポンスが返却されてきました!!今回はバックエンドのLambdaとしてeventデータをそのままレスポンスするLambdaを紐付けたのですが、レスポンスボディを整形すると以下のような形でした。

{
  "version": "2.0",
  "routeKey": "GET /lambda",
  "rawPath": "/lambda",
  "rawQueryString": "",
  "headers": {
    "accept": "*/*",
    "authorization": "secretToken",
    "content-length": "0",
    "host": "<API GWのID>.execute-api.ap-northeast-1.amazonaws.com",
    "user-agent": "curl/7.64.1",
    "x-amzn-trace-id": "Root=1-5f597e8d-8ffbfd88e6f2dd12aa495e08",
    "x-forwarded-for": "126.83.27.201",
    "x-forwarded-port": "443",
    "x-forwarded-proto": "https"
  },
  "requestContext": {
    "accountId": "<AWSアカウントIDEA>",
    "apiId": "<API GWのID>",
    "authorizer": {
      "lambda": {
        "arrayKey": [
          "value1",
          "value2"
        ],
        "booleanKey": true,
        "mapKey": {
          "value1": "value2"
        },
        "numberKey": 1,
        "stringKey": "value"
      }
    },
    "domainName": "<API GWのID>.execute-api.ap-northeast-1.amazonaws.com",
    "domainPrefix": "<API GWのID>",
    "http": {
      "method": "GET",
      "path": "/lambda",
      "protocol": "HTTP/1.1",
      "sourceIp": "<アクセス元IPアドレス>",
      "userAgent": "curl/7.64.1"
    },
    "requestId": "SoC2EivRNjMEPyw=",
    "routeKey": "GET /lambda",
    "stage": "$default",
    "time": "10/Sep/2020:01:17:01 +0000",
    "timeEpoch": 1599700621197
  },
  "isBase64Encoded": false
}

注目したいのはrequestContext.authorizer.lambda配下のデータ構造です。Lambdaオーソライザーのレスポンスでセットしたcontextの情報が後続のLambdaまで渡っていることが分かります。また文字列やbool値だけでなく、arrayやmapのようなデータ構造も渡せていることが分かります。例えばですが、Lambdaオーソライザー側でDBから何かしらの情報を取得して後続のLambdaに引き渡して...といった制御が簡単に行えることが分かります。

まとめ

今回のアップデートによりREST APIからHTTP APIへの移行がさらに加速しそうです。現在REST APIを利用している方は、HTTP APIに移行できないか、是非再検討してみて下さい。