API Gatewayを通してBasic認証を使う方法


課題

今回は、単純なFlaskのAPIアプリを作って、全世界に公開したくないので、単純なユーザ・パスワード認証(BasicAuth)をかけたいなと思いました。
ZappaというAWS LambdaにDeployできるフレームワークを使って、Flaskのアプリは、AWS上にすぐにDeployとAPIの確認ができました〜。

APIには、プログラムからBasicAuthに必要なヘッダーを付けておけば無事にAPIとの通信ができました。

pythonのrequestsを使って、API叩く例:

from requests.auth import HTTPBasicAuth
requests.get('https://api.example.com/', auth=HTTPBasicAuth('user', 'pass'))

HTTPヘッダー例:

GET /private/index.html HTTP/1.1
Host: example.com
Authorization: Basic ABCEF1234567890abcdef

ただし、開発者が簡易にAPI参照できるように、OpenAPI(swagger)表示できるページも用意しました。
そこでブラウザから、ページをアクセスしてみると。。。ダメだ。


モダンブラウザは、BasicAuthのログインをダイアログをだしてくれるのですが、出てこない様子。
どうにか、API Gatewayから返される401 Unauthorizedのレスポンスヘッダーには、必要なWWW-Authenticate: Basicがないようです。

確認すると、Flask側から正しくWWW-Authenticate: Basicを返しているようですが、APIGatewayがそのヘッダーを消しているっぽい。

さって、API Gatewayからは、ブラウザのログインダイアログが表示されるように、401 UnauthorizedのレスのヘッダーにWWW-Authenticate: Basicを付けるようにしましょう。

HTTP/1.1 401 Authorization Required
Date: Wed, 11 May 2005 07:50:26 GMT
Server: Apache/1.3.33 (Unix)
WWW-Authenticate: Basic realm="SECRET AREA"
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=iso-8859-1

対策方法

もうしかして、UnauthorizedApiGatewayResponseだけでいくかなと思うのですが、今回は、FlaskのRequestへ流す前、CUSTOM Authorizerを導入して、BasicAuthヘッダーを確認するようにしています。

BasicAuth用の設定と関数のリポジトリにまとめたので、既存のAPIGateway(rest-api)により楽にBasicAuthを追加することができたかと思います。ここまでくるには、色々大変だったのですが、この手順で10分以内にほしいBasicAuth設定が終わるだろう。

  1. リポジトリ取得:

    git clone https://github.com/monkut/lambda-basicauth-authorizer.git
    
  2. 環境準備(完全にAWSCLIでの用意は可能かと思うのですが、一部はBOTO3で書いちゃいました):

    # pipenvでpython環境を用意
    pipenv install
    
  3. 環境変数を設定、CUSTOM Authorizerの関数を置いて、既存ApiGatewayのrest-apiを更新:

    RESTAPI_IDは、既存のものからこのコマンドで取得: aws apigateway get-rest-apis
    PROJECTID は、ユニーク性を保つためのものだけです(なんでも良い)

    export AWS_PROFILE={my profile}
    export BASIC_AUTH_USERNAME={YOUR USERNAME}
    export BASIC_AUTH_PASSWORD={YOUR PASSWORD}
    export PROJECTID={YOUR PROJECT IDENTIFIER}
    export RESTAPI_ID={既存のRESTAPI ID}
    # CUSTOM Authorizer用のBucketを用意
    make createfuncbucket
    
    # CUSTOM Authorizerの関数をDeployして、既存のREST-APIにインストール
    make deploy
    make install
    

これで、APIだけじゃなくて、ユーザ(人間)がブラウザ経由でUSER/PASSのログインできるようになりました〜

FlaskにBasicAuth処理を残すちゃいば、ちょっと重複するのですが、BasicAuthなので、残っていても大した処理時間がかからないだろう。

最近、BasicAuthを使って、zappa と組み合わせて、開発用のDUMMY APIを提供したりしています。