概要、実施したいこと
実施したいこととしては以下になります。
- AWS Chalice で API Gateway + Lambda をデプロイ
- ビルド・デプロイは CodePipeline で実行
- 1つの API Gateway で、API Key 認証と Cognito 認証を使い分けたい
1, 2点目は、開発効率の点を考慮したよくある構成かと思います。
3点目については、
アプリケーションユーザーからのリクエストに対しては Cognito 認証を、
外部システムからのリクエストに対しては API Key 認証を使いたい
…といった場合を想定した構成です。
上記3点を実装した際のポイントを紹介します。
ソースコードの内容
ソースコード (app.py) については以下のように実装しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import os from chalice import Chalice, Response, CognitoUserPoolAuthorizer from chalicelib import sample app = Chalice(app_name = 'chalice_api' ) cognito_authorizer = CognitoUserPoolAuthorizer( 'ChaliceUserPool' , provider_arns = [ os.environ[ 'COGNITO_USERPOOL_ARN' ] ] ) @app .route( '/app-user' , authorizer = cognito_authorizer) def function(): return sample.hello_world() @app .route( '/ext-system' , api_key_required = True ) def function(): return sample.hello_world() |
API Key 認証と Cognito 認証を使い分けるために URL パスを分けました。
アプリケーションユーザー用パス(/app-user)には authorizer=cognito_authorizer
で Cognito 認証を設定し、
外部システム用パス(/ext-system)には api_key_required=True
で API Key 認証を設定しています。
ビルドの設定
CodePipeline の設定としては以下のようになっています。
Build ステージで CloudFormation テンプレートを作成し Deploy ステージに渡す必要があります。

そのため Build ステージで chalice package を実行しテンプレートを生成しています。
以下のような buildspec を使いました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
version: 0.2 artifacts: discard-paths: no files: - '**/*' phases: install: runtime-versions: python: 3.9 commands: - pip install --upgrade chalice build: commands: - pip install -r requirements.txt -t vendor/ - python3 build.py --cognito-userpool-arn ${COGNITO_USERPOOL_ARN} # 独自のスクリプト、後述 - chalice --debug package . --template-format yaml |
chalice package の実行時にソースコード(app.py
)が参照されるため
ビルド時点で、ソースコードに定義された環境変数(COGNITO_USERPOOL_ARN)が利用できる必要があります。
このような環境変数はプロジェクトディレクトリの .chalice/config.json
に記載する必要があります。
Configuration File - environment_variables
ですが、ソースコード内に環境固有の値(COGNITO_USERPOOL_ARN)を記載することに抵抗があったので、
ソースコード外から COGNITO_USERPOOL_ARN を挿入する仕組みにしました。
それが上記 buildspec 内で実施している build.py
です。
プロジェクトディレクトリルートに config.json
の元になる chalice-config.json
を配置し、
build.py
がそこに COGNITO_USERPOOL_ARN (CodeBuild の環境変数) を追加し .chalice/config.json
として保存しています。
1 2 3 4 5 6 |
. ├── .chalice │ └── config.json (build.py で生成) ├── app.py ├── build.py └── chalice-config.json (config.json の元になるファイル) |
build.py
の内容は以下の通りです。
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 34 35 36 | import argparse import os import json ROOT_PATH = os.path.dirname(os.path.abspath(__file__)) class Build(): def __init__( self , args): self .cognito_userpool_arn = args.cognito_userpool_arn def main( self ) - > None : self .config = self .load_base_config() self .generate_config() self .output_config() def load_base_config( self ) - > dict : with open (f '{ROOT_PATH}/chalice-config.json' , 'r' ) as file : base_config = json.load( file ) return base_config def generate_config( self ) - > None : self .config[ 'environment_variables' ] = { 'COGNITO_USERPOOL_ARN' : self .cognito_userpool_arn } def output_config( self ) - > None : with open (f '{ROOT_PATH}/.chalice/config.json' , 'w' ) as file : file .write(json.dumps( self .config, indent = 2 )) def main(): parser = argparse.ArgumentParser() parser.add_argument( '--cognito-userpool-arn' , required = True ) args = parser.parse_args() return Build(args).main() main() |
本当はこのような独自のスクリプトを作りたくなかったのですが、
そこまで複雑なスクリプトでもないので、これはこれで良しとしました。
まとめ
- Cognito 認証 / API Key 認証 を使い分けるには URL パスを分ける必要がある
- Chalice で Cognito 認証を設定する場合は
authorizer=...
で設定する - Chalice で API Key 認証を設定する場合は
api_key_required=True
で設定する - ビルド(chalice package)実行時に Cognito ユーザープール ARN が必要になるが、ARN をソースコードに含めたくない場合は今回紹介した
build.py
のような工夫が必要
共同著者: 神津
投稿者プロフィール
- 2015年8月入社。弊社はインフラ屋ですが、アプリも作ってみたいです。