Amazon API Gateway で API キーを x-api-key ヘッダーとクエリ文字列どちらも使えるようにしてみた

2023.02.13

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

いわさです。

先日 API Gateway でカスタムオーソライザーを使って、x-api-key ヘッダーではなくクエリ文字列で設定する方法を紹介しました。

その際には API Gateway の設定で API キーはヘッダーとオーソライザーどちらを使うかを選択しました。
それによってオーソライザーで処理出来るようになったのですが、副作用として x-api-key ヘッダーが使えなくなっていました。

ただし、カスタムオーソライザーで API キーを決定出来ればどうにでもなるので、オーソライザー関数上で x-api-key ヘッダーから API キーを取得すれば問題なく使うことが出来ます。
そこで、本日は x-api-key ヘッダーとクエリ文字列パラメータのどちらでも API キーを使えるようにしてみました。

オーソライザー関数

仕組みは簡単でリクエストベースオーソライザーにはクエリ文字列もヘッダーも含む、body 以外の全ての情報を取り扱うことが出来ます。
そこで次のように指定された GET パラメータに API キーが設定されていればそれを使い、設定されていなければ x-api-key ヘッダーを使うというロジックに変更するだけです。
どちらにも設定されていない場合は API キー自体を使わないよう状態で処理を続行させます。

lambda_function.py

:
def lambda_handler(event, context):
    principalId = 'user|a1b2c3d4'

    tmp = event['methodArn'].split(':')
    apiGatewayArnTmp = tmp[5].split('/')
    awsAccountId = tmp[4]

    policy = AuthPolicy(principalId, awsAccountId)
    policy.restApiId = apiGatewayArnTmp[0]
    policy.region = tmp[3]
    policy.stage = apiGatewayArnTmp[1]
    policy.allowAllMethods()
    authResponse = policy.build()
    
    usageIdentifierKey = event.get('queryStringParameters', {}).get('hogeapikey')
    if usageIdentifierKey is None:
        usageIdentifierKey = event.get('headers', {}).get('x-api-key')
    if usageIdentifierKey is not None:
        authResponse['usageIdentifierKey'] = usageIdentifierKey
        
    print(authResponse)
    return authResponse

:

オーソライザーの ID ソースを設定しない

ここでポイントというか制限があります。
今回はヘッダーとクエリ文字列のどちらかに API キーが指定されていれば処理を続行したいです。
そこで、オーソライザーの ID ソースは今回指定しません。オーソライザーでは ID ソースを指定しないことも出来ます。

オーソライザーの ID ソースを指定すると、ID ソースが指定されていない場合に Lambda の実行前検証を行うことが出来て便利なのですが、これはやむを得ないですね。

また、ID ソースはオーソライザーの認可キャッシュのキーとして使われます。
さらに、オーソライザーの仕様として ID ソースを設定しない場合にキャッシュを有効化することは出来ません。
有効化すると「Invalid request input」エラーが発生します。

ちなみに、ここでヘッダーとクエリ文字列どちらも設定してしまうと、どちらも必須 ID として扱われるので設定しないでください。
以下のようにどちらも 401 UnauthorizedException が発生します。

% curl -i "https://rjw5cmkpf0.execute-api.ap-northeast-1.amazonaws.com/hoge0205stage/hoge1?hogeapikey=hoge0205key1aaaaaaaaaa"
HTTP/2 401 
date: Mon, 13 Feb 2023 08:22:47 GMT
content-type: application/json
content-length: 26
x-amzn-requestid: 54c4748d-71a5-4c1e-82a5-423705446ce0
x-amzn-errortype: UnauthorizedException
x-amz-apigw-id: ARL1wGyutjMFcHA=

{"message":"Unauthorized"}

% curl -i -H "x-api-key:hoge0205key1aaaaaaaaaa" "https://rjw5cmkpf0.execute-api.ap-northeast-1.amazonaws.com/hoge0205stage/hoge1"
HTTP/2 401 
date: Mon, 13 Feb 2023 08:22:50 GMT
content-type: application/json
content-length: 26
x-amzn-requestid: d00c30e5-cee4-4adf-b1cf-e7ca8209ddbf
x-amzn-errortype: UnauthorizedException
x-amz-apigw-id: ARL2IFZGNjMFktw=

{"message":"Unauthorized"}

リクエスト検証や必須チェックを見直す

また、API メソッドのリクエストの検証設定やパラメータの必須チェックなども見直しておきましょう。
これらのパラメータのいずれも、Lambda 関数の無駄な実行を抑制するための前処理として有効なのですが、任意パラメータになるのでオーソライザーに任せるしかありません。
私の知る限りでは API Gateway のメソッド設定上では複合ルールは設定出来ないはず。

どちらも使えるようになった

以上で利用可能になるはずです。

前回は x-api-key ヘッダーのみではUnauthorizedExceptionが発生していましたが、今回は正常に実行出来ています。

% curl -i "https://rjw5cmkpf0.execute-api.ap-northeast-1.amazonaws.com/hoge0205stage/hoge1?hogeapikey=hoge0205key1aaaaaaaaaa"    
HTTP/2 200 
date: Mon, 13 Feb 2023 08:18:36 GMT
content-type: application/json
content-length: 20
x-amzn-requestid: f916ef08-7484-4005-aa49-cd2279e6a8e4
x-amz-apigw-id: ARLOhEixNjMF5sA=
x-amzn-trace-id: Root=1-63e9f25c-73893a03586a5ae5442426d8;Sampled=0

"Hello from Lambda!"

% curl -i -H "x-api-key:hoge0205key1aaaaaaaaaa" "https://rjw5cmkpf0.execute-api.ap-northeast-1.amazonaws.com/hoge0205stage/hoge1"
HTTP/2 200 
date: Mon, 13 Feb 2023 08:18:39 GMT
content-type: application/json
content-length: 20
x-amzn-requestid: cac7311f-802b-4c06-9262-fc674e734a81
x-amz-apigw-id: ARLO7H1GNjMFiHg=
x-amzn-trace-id: Root=1-63e9f25f-2d5ca013050e884022ead1ee;Sampled=0

"Hello from Lambda!"

さいごに

本日は Amazon API Gateway で API キーを x-api-key ヘッダーとクエリ文字列どちらも使えるようにしてみました。

色々なクライアントから柔軟な API キーの受け方をしたいという場合は API キーはオーソライザーがやはり便利ですね。
デフォルトの x-api-key ヘッダーの場合と異なり Lambda の実行は発生してしまうので、料金など事前にシミュレーションしておいたほうが良さそうですが、なんでも出来ます。