3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cognito の Managed Login を ALB と連携する

Last updated at Posted at 2024-12-23

はじめに

Amazon Cognito で Managed Login 機能を 2024 年 11 月に提供を開始しました。今までの Hosted UI が進化してリッチな見た目になり、日本語対応が可能で、MFA やパスキーの登録や認証機能が付属されるようになり、アプリケーション側の実装負担を削減できる、といったうれしさがあります。

Hosted UI のログイン画面

image-20241222125148384.png

Managed Login のログイン画面

image-20241222125155590.png

今回は、ALB と Cognito の Managed Login を連携する設定方法を紹介します。

構成図

image.png

事前設定

テーマとは外れるので、以下の内容は記事の中では触れません。

  • EC2 インスタンスで Nginx と Flask を動かす
  • ALB と EC2 を紐づける
  • ACM と連携して HTTPS を構成

Cognito User Pool の作成

Cognito User Pool を作成します。

image-20241221202333562.png

名前などは適当に指定します。

image-20241221204759228.png

Callback URL の指定

作成した Cognito Userpool で、Callback URL を指定します。これは、Cognito がログイン処理を行ったあとに表示する Web サイトの URL です。ALB に指定しているドメインを指定します。

Cognito Userpool の画面の中で、作成した App Client を開きます。

image-20241221205103792.png

Login pages のタブから Edit を押します。

image-20241221210327951.png

エンドユーザーに提供する Web サイトの URL である、ALB のドメイン名を指定します。ドメインの末尾に /oauth2/idpresponse を付与します。これは、ALB 側と Cognito 連携する際に指定する、固定されているパスです。

https://cognito-nginx01.sugiaws.tokyo/oauth2/idpresponse

image-20241221211649415.png

Custom Domain の指定

任意の設定項目になりますが、Cognito のログイン URL を独自のドメインで指定できます。組織内で Managed Login を利用する場合には、独自のドメインを利用したいことが多いと思います。

Domain のメニューから、Create custom domain を選択します。

image-20241221212411509.png

エンドユーザーに提供したい Web サイトの URL が、以下の URL だとします。

今回の手順では、Cognito の Custom Domain は、auth サブドメインを追加するものとします。

なので、以下のパラメーターで指定をします。ACM の証明書設定が別途設定しておく必要があります。

image-20241221215756227.png

Create ボタンを押すと、以下の画面に変わります。CNAME レコードを作成するように、という指示があります。

image-20241221215942412.png

以下のように auth サブドメインの大して、 CNAME レコードを作成します。以下の画像は、Route 53 の画面ですが、独自の DNS サーバーに合わせて変更ください。

image-20241221220246904.png

ALB と Cognito の連携設定

ALB と Cognito を連携する設定を入れていきます。Listener のルールを指定します。

image-20241221220752311.png

Authentication から、Cognito の連携設定を入れていきます。

image-20241221221313605.png

動作確認

動作確認をしていきます。以下の URL にアクセスします。

image-20241221221924297.png

Cognito の Managed Login の画面が自動的に開かれます。Create an account を選択します。

image-20241221221942300.png

自分自身のメールアドレスやパスワードなどを指定します。

image-20241221222042008.png

指定したメールアドレスに Code が届きます。

image-20241221222110277.png

Code を入力します。

image-20241221222123939.png

ログインされました。Nginx が稼働しているサーバーが受け取る、Request Header を表示させています。

image-20241222225918125.png

EC2 インスタンスが受け取る JWT について

ALB と Cognito を連携しているとき、ALB から EC2 インスタンスへ送信される HTTP Request の Request Header の X-Amzn-Oidc-Data に JWT (JSON Web Token) が付与されている。これを利用して正常な JWT の検証をしながら、ユーザーのメールアドレスなどを確認可能

image-20241223140138877.png

以下がこちらのサイトをつかって JWT を decode したときの例。アクセスしたユーザーのメールアドレスを確認ができるので、アプリケーション側で認可処理を実装可能。

image-20241223135650852.png

付録 : Cookie

ALB と Cognito を連携したあと、ユーザーが Managed Login を開いて、ログイン成功した場合、自動的にブラウザ側に Cookie が保存される。この Cookie を削除すると、ログアウトされている状態として扱われる。

image-20241221222404589.png

付録 : Nginx と Flask の導入方法

以下に簡単にメモをする。

Nginx をインストール

sudo dnf install nginx

起動

sudo systemctl restart nginx
sudo systemctl enable nginx

必要なパッケージのインストール

sudo dnf install python3-pip
pip3 install flask gunicorn

アプリケーションディレクトリの作成

mkdir ~/flaskapp
cd ~/flaskapp

アプリケーションコードの作成

cat << EOF > app.py
import json
import base64
from flask import Flask, request, redirect, make_response
from datetime import datetime

app = Flask(__name__)

COGNITO_DOMAIN = "https://auth.cognito-nginx01.sugiaws.tokyo"
CLIENT_ID = "5ehctf8ikmlvo1grqk7id5v4a2"
LOGOUT_URI = "https://cognito-nginx01.sugiaws.tokyo"

def decode_jwt(token):
    # JWTのペイロード部分(2番目の部分)を取得
    payload = token.split('.')[1]
    # Base64デコード(パディングの調整が必要な場合がある)
    payload += '=' * ((4 - len(payload) % 4) % 4)
    decoded = base64.b64decode(payload)
    # JSONとしてパース
    return json.loads(decoded)

@app.route('/logout')
def logout():
    logout_url = f"{COGNITO_DOMAIN}/logout?client_id={CLIENT_ID}&logout_uri={LOGOUT_URI}"
    response = make_response(redirect(logout_url))
    
    # Delete ALB auth cookies by setting them to empty and expired
    response.set_cookie('AWSELBAuthSessionCookie-0', '', expires=0, domain=None, path='/', httponly=True)
    response.set_cookie('AWSELBAuthSessionCookie-1', '', expires=0, domain=None, path='/', httponly=True)

    return response

@app.route('/')
def home():
    headers = dict(request.headers)
    headers.pop('X-Forwarded-For', None)
    
    formatted_headers = '\n'.join([f"{key}: {value}" for key, value in headers.items()])
    
    # JWTからメールアドレスを抽出
    jwt_data = headers.get('X-Amzn-Oidc-Data')
    email = "Not found"
    if jwt_data:
        try:
            decoded = decode_jwt(jwt_data)
            email = decoded.get('email', 'Email not found in JWT')
        except Exception as e:
            email = f"Error decoding JWT: {str(e)}"

    return f"""
        <h1>Request Headers</h1>
        <pre>{formatted_headers}</pre>
        <h2>Email from JWT</h2>
        <p>{email}</p>
        <a href="/logout">Logout</a>
    """

if __name__ == '__main__':
    app.run()

EOF

Gunicorn サービスファイルの作成

sudo tee /etc/systemd/system/flaskapp.service << EOF
[Unit]
Description=Gunicorn instance to serve flask application
After=network.target

[Service]
User=ec2-user
Group=nginx
WorkingDirectory=/home/ec2-user/flaskapp
Environment="PATH=/home/ec2-user/.local/bin"
ExecStart=/home/ec2-user/.local/bin/gunicorn --workers 3 --bind unix:flaskapp.sock -m 777 app:app

[Install]
WantedBy=multi-user.target
EOF

Nginx の設定ファイル作成

sudo tee /etc/nginx/conf.d/flaskapp.conf << EOF
server {
    listen 80;
    server_name _;

    location / {
        proxy_pass http://unix:/home/ec2-user/flaskapp/flaskapp.sock;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
    }
}
EOF

ec2-user を nginx グループに追加

sudo usermod -a -G nginx ec2-user

flaskapp ディレクトリに nginx ユーザーがアクセスできるようにする

sudo chmod 755 /home/ec2-user
sudo chmod 755 /home/ec2-user/flaskapp

Gunicorn サービスの起動

sudo systemctl daemon-reload
sudo systemctl restart flaskapp
sudo systemctl enable flaskapp

status

$ sudo systemctl status flaskapp
● flaskapp.service - Gunicorn instance to serve flask application
     Loaded: loaded (/etc/systemd/system/flaskapp.service; disabled; preset: disabled)
     Active: active (running) since Sun 2024-12-22 00:28:08 UTC; 4s ago
   Main PID: 62726 (gunicorn)
      Tasks: 4 (limit: 9346)
     Memory: 52.4M
        CPU: 495ms
     CGroup: /system.slice/flaskapp.service
             ├─62726 /usr/bin/python3 /home/ec2-user/.local/bin/gunicorn --workers 3 --bind unix:flaskapp.sock -m 007 app:app
             ├─62728 /usr/bin/python3 /home/ec2-user/.local/bin/gunicorn --workers 3 --bind unix:flaskapp.sock -m 007 app:app
             ├─62729 /usr/bin/python3 /home/ec2-user/.local/bin/gunicorn --workers 3 --bind unix:flaskapp.sock -m 007 app:app
             └─62730 /usr/bin/python3 /home/ec2-user/.local/bin/gunicorn --workers 3 --bind unix:flaskapp.sock -m 007 app:app

Dec 22 00:28:08 ip-10-0-1-135.ap-northeast-1.compute.internal systemd[1]: Started flaskapp.service - Gunicorn instance to serve flask application.
Dec 22 00:28:08 ip-10-0-1-135.ap-northeast-1.compute.internal gunicorn[62726]: [2024-12-22 00:28:08 +0000] [62726] [INFO] Starting gunicorn 23.0.0
Dec 22 00:28:08 ip-10-0-1-135.ap-northeast-1.compute.internal gunicorn[62726]: [2024-12-22 00:28:08 +0000] [62726] [INFO] Listening at: unix:flaskapp.sock (62726)
Dec 22 00:28:08 ip-10-0-1-135.ap-northeast-1.compute.internal gunicorn[62726]: [2024-12-22 00:28:08 +0000] [62726] [INFO] Using worker: sync
Dec 22 00:28:08 ip-10-0-1-135.ap-northeast-1.compute.internal gunicorn[62728]: [2024-12-22 00:28:08 +0000] [62728] [INFO] Booting worker with pid: 62728
Dec 22 00:28:08 ip-10-0-1-135.ap-northeast-1.compute.internal gunicorn[62729]: [2024-12-22 00:28:08 +0000] [62729] [INFO] Booting worker with pid: 62729
Dec 22 00:28:08 ip-10-0-1-135.ap-northeast-1.compute.internal gunicorn[62730]: [2024-12-22 00:28:08 +0000] [62730] [INFO] Booting worker with pid: 62730

Nginx の再起動

sudo systemctl restart nginx

付録 : 日本語の表示について

付録 : セルフサインアップの停止

Managed Login は、デフォルトでユーザー側のセルフアインアップ機能が提供されているので、これを停止することが可能。

image-20241222124601110.png

image-20241222124616344.png

無くなっている

image-20241222124647108.png

検証を通じてわかったこと

  • ALB と Cognito を連携して Managed Login を利用したログイン画面を提供可能
  • Managed Login で認証成功したとき、ブラウザ側に Cookie が保存される。この Cookie の名前は、デフォルトで AWSELBAuthSessionCookie となっているが、任意の名前を指定可能
3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?