ALB + Cognito認証で付与されるユーザー情報をEC2サイドから眺めてみる

ALBはCognitoと組み合わせることで、簡単にWebサーバーの認証機能を実現できます。超便利。

ALBとCognitoを組み合わせた認証については、弊社ブログで解説しているのでこちらを御覧ください。

本ブログでは、ここからもう一歩踏み込んで、ALBが認証後EC2に何を渡しているのか?EC2はユーザー情報をどう受けとっているのか?)を眺めてみます。

構成図

こんな感じのシンプルな構成を作ります。ALBへのアクセス時に、Cognitoと連携して認証を行います。CognitoでログインできたユーザーだけがEC2上のコンテンツにアクセスできます。

ALB + Cognito認証のおさらい

ALB + Cognitoの認証がどういったフローで動いているか、ここで一度おさらいしましょう。
OIDC認証がわかっていると、似たような動作をしているので理解しやすいです。
だいたい、ALBリライング・パーティCognitoIDプロバイダの動きをしています。

フローを図にするとこんな感じです。

まずは、ALBに対してアクセスすると、Cognitoのログイン画面にリダイレクトされます。(フロー図の1〜4)

ログイン画面でID,パスワードを入力してログインすると、認証レスポンスがALBのコールバックURLにリダイレクトされます。(フロー図の5〜7)

その後、ALBとCognito間の裏側でトークンリクエストやIDトークンの検証をして(フロー図の8〜10)、ユーザークレームをCognitoから取得して(フロー図の11、12)、認証が完了したらもともとアクセスをしていたURLへリダイレクトして(フロー図の13〜14)、ユーザー情報をヘッダーに付与してEC2へ転送してログイン後の画面が表示される(フロー図の15〜17)といったフローになっています。

このフローを経て、ユーザーの認証に成功すると、ロードバランサは次のHTTPヘッダを追加してEC2に渡します。(フロー図の15)

x-amzn-oidc-accesstoken
トークンエンドポイントからのアクセストークン (プレーンテキスト)。

x-amzn-oidc-identity
ユーザー情報エンドポイントからの件名フィールド (sub) (プレーンテキスト)。

x-amzn-oidc-data
ユーザークレーム (JSON ウェブトークン (JWT) 形式)

参考:ユーザークレームのエンコードと署名の検証 | Application Load Balancer を使用してユーザーを認証する - Elastic Load Balancing

この追加されたHTTPヘッダ(ユーザー情報)をEC2サイドから覗いてみます。

DockerでPHPを動かす

EC2が受け取るHTTPリクエストヘッダを覗きたいので、phpinfoを使って、HTTPリクエストヘッダをべろっと出してみます。

PHPの環境をまともに作るのは面倒くさいので、Dockerを使ってさくっと作ります。

EC2インスタンスにSSHログインして、Dockerをインストールしていきます。

$ sudo yum install docker -y
$ sudo service docker start
$ sudo usermod -a -G docker ec2-user
$ sudo systemctl enable docker

phpinfoを表示するためのphpファイルを作ります。

$ mkdir /home/ec2-user/www
$ chmod 755 /home/ec2-user/www
$ echo "<?php phpinfo();" > /home/ec2-user/www/info.php

Dockerでphpを動かします。

$ docker run -d -p 8080:80 --name php -v /home/ec2-user/www:/var/www/html php:7.2-apache

ブラウザからALB経由でEC2へアクセスしてみると、べろっとHTTPリクエストヘッダをだすことができました。

EC2サイドからユーザー情報を眺めてみる

ALBに追加された次の3つのヘッダ情報が、どういう情報なのか実際に確認してみます。

x-amzn-oidc-accesstoken

x-amzn-oidc-accesstokenアクセストークンです。

アクセストークンを利用すると、Cognitoからユーザー情報を取得できます。
CogintoのUSERINFOエンドポイントは、アクセストークンを利用して認証されたユーザーに関する情報を返します。

よって、次のようにヘッダを利用してアクセストークンを渡すことで、認証されたユーザーのユーザー情報を受け取ることができます。

$ ACCESS_TOKEN=<<x-amzn-oidc-accesstokenをコピー&ペースト>>
$ COGNITO_URL=<<Cognitoドメインをコピー&ペースト>>
$ curl -H "Authorization: Bearer ${ACCESS_TOKEN}" \
  ${COGNITO_URL}/oauth2/userInfo
{
   "sub": "248289761001",
   "name": "Jane Doe",
   "given_name": "Jane",
   "family_name": "Doe",
   "preferred_username": "j.doe",
   "email": "janedoe@example.com"
}

ちなみにCognitoドメインは、Cognitoユーザープールの画面で参照できます。

x-amzn-oidc-identity

x-amzn-oidc-identityユーザーの一意の識別子 (UUID)で、Cognitoのsubクレームです。

Cognitoでログインしたユーザーのユーザー画面と比較してみると、確かに一致していることがわかります。

x-amzn-oidc-data

x-amzn-oidc-dataユーザークレーム(ユーザー情報)です。

ユーザークレームがJWT形式で保持されています。JWT形式ではありますが、IDトークンではありません
JWT形式のため、ペイロード部分を切り出してbase64デコードすると、ユーザー情報の内容を確認することができます。

$ OIDC_DATA=<<x-amzn-oidc-dataをコピー&ペースト>>
$ echo $OIDC_DATA | cut -d'.' -f 2 | base64 -D
{
   "sub": "1234567890",
   "name": "name",
   "email": "alias@example.com",
   ...
}

終わりに

ALBとCognitoの認証をEC2サイドから見てみました。

ALBとCognitoの認証の動きを知ることで、アプリケーションのあるEC2側でユーザー情報がどうやって渡されるのかが理解できると思います。

本ブログがその際の理解の助けになれば幸いです。