こんにちは。AWS CLIが好きな福島です。
はじめに
Webアプリを実装する際に、ALB+Cognitoを使って認証機能を実装しつつ、 Web上にユーザー情報を表示させたり、アプリ側でユーザー情報を基に操作を制御(認可)したいケースがあるかと思います。
そこで今回は、ALB+Cognito+Flaskの構成でユーザー情報を取得する方法をご紹介いたします。
※Flaskとは、PythonのWebフレームワークになります。
参考情報
構成図
今回の検証における構成図は、以下の通りになります。
Webアプリ上でユーザー情報を取得する方法
Webアプリ上でユーザー情報を取得するには、ALBからのリクエストに含まれるヘッダーを取得します。
ざっくりとした流れは以下の通りです。
- ①Webアプリ上でALBからのリクエストヘッダーを取得
- ②ヘッダーの検証
- ③ヘッダーのデコード
ユーザー情報を確認できるALBのリクエストヘッダー
ユーザー情報を確認できるALBのリクエストヘッダーは、以下の2つがあります。
x-amzn-oidc-accesstoken(Cognitoが発行)
トークンエンドポイントからのアクセストークン
x-amzn-oidc-data(ALBが発行)
ユーザークレーム (JSON ウェブトークン (JWT) 形式)
補足
ユーザーが所属するグループ情報は、x-amzn-oidc-accesstokenからしか取得できないため、基本的にはこのデータを使うことが多いのかなと思います。
ポイント
どのサービスが発行したデータなのか(ALB or Cognito)
セキュリティの観点からWebアプリで受け取ったリクエストヘッダーは、想定している発行元から払い出されたデータなのか、検証することが多いかと思います。
そのため、どのサービスが発行したデータなのかを意識する必要があります。
Cognitoのトークンを検証する場合の参考情報 https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
ALBのトークンを検証する場合の参考情報 https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/listener-authenticate-users.html#user-claims-encoding
ユーザーが所属するグループ情報が含まれているか
Webアプリ上で操作を制御(認可)したい場合、ユーザー名よりユーザーが所属するグループ名を使いたいケースが多いと思います。 しかし、x-amzn-oidc-dataからは、ユーザー情報しか取得できないため、 グループ情報を取得したい場合は、x-amzn-oidc-accesstokenを確認する必要があります。
検証
ここからは検証として、実際にALBからのリクエストヘッダーを覗いてみたいと思います。
前提
- DNSを管理していること
- ACMで証明書を発行済みであること
Git Clone
GitからソースコードをCloneします。
git clone https://github.com/kazuya9831/blog-sample.git
cd blog-sample
samconfig.tomlの更新
samconfig.tomlの更新します。
vi alb-cognito-flask/samconfig.toml
version = 0.1 [default.deploy.parameters] stack_name = "alb-cognito-flask" resolve_s3 = true s3_prefix = "alb-cognito-flask" region = "ap-northeast-1" capabilities = "CAPABILITY_IAM" parameter_overrides = [ "ApplicationName=alb-cognito-flask", "AllowSourceIp=xxx.xxx.xxx.xxx/xx", "DNSNameForALB=xxxx", "ACMArn=xxxxx", "VpcCIDR=192.168.0.0/24", "PublicSubnet1CIDR=192.168.0.0/27", "PublicSubnet2CIDR=192.168.0.32/27" ] image_repositories = []
変更する値は、以下の3点です。
AllowSourceIp
ALBにアクセス可能なIPを設定します。DNSNameForALB
払い出している証明書にマッチしたFQDNを指定します。ACMArn
ACMのARNを指定します。
リソースのデプロイ
cd alb-cognito-flask
sam build
sam deploy
Cognito Userの作成
cognito_user_pool_id=$(aws cloudformation describe-stacks \ --stack-name alb-cognito-flask \ --query "Stacks[].Outputs[?OutputKey=='CognitoUserPoolId'].OutputValue" \ --output text)
python tools/setup_cognito_user_and_group.py ${cognito_user_pool_id} tools/cognito_user_and_group.csv
DNS設定
以下のコマンドの実行結果を基にDNSにレコードを設定します。
aws cloudformation describe-stacks \ --stack-name alb-cognito-flask \ --query "Stacks[].Outputs[?OutputKey=='DNSNameForALB' || OutputKey=='ALBFQDN'].OutputValue" \ --output text
DNSにレコードを登録する方法は割愛します。
動作確認
ブラウザからDNSレコードに登録したエンドポイントにアクセスします。 アクセスすると、CognitoのホストUIの画面にリダイレクトされ、以下の画面が表示されると思います。
Cognitoには以下のユーザーを作成しているので、好きなユーザーでアクセスします。
- TestUser-1 (adminグループに所属)
- TestUser-2 (generalグループに所属)
※パスワードはユーザー名と同一です。
認証が通り、以下のような画面が表示されればOKです。
上記は項目名通り、X-Amzn-Oidc-AccesstokenまたはX-Amzn-Oidc-Dataから取得したユーザー情報を表示させています。
Request Headersには、ALBから送られてきたリクエストヘッダーを全て表示させています。
X-Amzn-Oidc-Accesstokenには、デコード前のデータを表示し、 X-Amzn-Oidc-Accesstoken(decode)には、デコード後のデータを表示しています。
1番上に表示していたユーザーおよびグループ名は、赤枠の部分から取得しています。
X-Amzn-Oidc-Dataには、デコード前のデータを表示し、 X-Amzn-Oidc-Data(decode)には、デコード後のデータを表示しています。
1番上に表示していたユーザー名は、赤枠の部分から取得しています。 このデータからX-Amzn-Oidc-Data(ALBが発行)には、グループ情報が含まれていないことが分かるかと思います。
終わりに
今回は、ALB+Cognito+Flaskで認証されたユーザー情報を取得する方法をご紹介いたしました。 どなたかのお役に立てれば幸いです。