はじめに
AWSでは、Webサービスの認証機能は、ALBを使うのが超便利ですね!
ここでは、まず準備段階として認証機能を実装します。(→「準備」の段落)
つぎに、ログアウト処理を追加します。(→「ログアウトの実装」の段落)
ALBに認証させる方法は、いろいろな方が書いてくれています。そちらを参考にしてください。
以下、クラスメソッドさんの記事です。
準備
まず、ALBにCognitoを連携し、ログインの実装まで。
上のリンクのとおりに、ALB、Cognitoユーザープールなどは実装してください。
WebサーバはLambdaでベタ書きします。EC2は面倒なので。ログアウトの処理がやりたいので手抜きです。
Webサーバ
Lambda関数を、Python3.8で実装する。
import json
def lambda_handler(event, context):
return {
'statusCode': 200,
'headers': {
'Content-Type': 'text/html; charset=utf-8'
},
'body': '''
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>title</title>
</head>
<body>
テスト
</body>
</html>
'''
}
ターゲットグループの作成
ターゲットをこのLambda関数にしたターゲットグループを作成します。
Cognitoユーザープールの作成、ALBの作成、ターゲットと認証の登録
上にあるリンク先のとおりに。
ここまでで、認証をもったWebサイトができたとします。
ログアウトの実装
方法
認証からログアウトさせる方法は、以下に記載があります。
認証セッション Cookie の有効期限を -1 に設定し、クライアントを IdP ログアウトエンドポイントにリダイレクトする
① Cookieの有効期限を-1に設定する。
認証セッションのCookieは、ALBで設定しています。
どのCookieを使っているの?
→ 下の、セッションCookieのところにあります。(※ここはCookie名の確認のみ。)
変更していなかったので、デフォルトですね(^_^;)
「AWSELBAuthSessionCookie-0」「AWSELBAuthSessionCookie-1」が使われていました。
これらCookieの有効期限を、プログラムから-1に設定します。
② クライアントをIdPログアウトエンドポイントにリダイレクトする。
IdPはCognitoです。Cognitoのログアウトエンドポイントは以下に記載あり。
プログラムから、ログアウトエンドポイントに、クライアントIDとログアウトURLを渡してあげればいいわけです。
例のとおり、こんな感じ。
GET
https://mydomain.auth.us-east-1.amazoncognito.com/logout?client_id=ad398u21ijw3s9w3939&logout_uri=https://myclient/logout
クライアントIDは、ALBがヘッダーに渡してくれているので、そこから取り出します。
ユーザークレーム(x-amzn-oidc-data)のJWTヘッダーにクライアントID(client_id)があります。
ログアウトURLは、ログアウト後に遷移するURLのことです。ここではまたログイン画面を出したいと思います。つまり、WebサーバにアクセスするURL(=ALBのエンドポイント)とします。
Lambdaにログアウト処理を追加
パスに"logout"があるときにログアウトをさせます。
import json
import base64
import urllib.parse
def lambda_handler(event, context):
# print(event)
headers = event['headers']
# JwtヘッダをDict型に変換
encoded_jwt = headers['x-amzn-oidc-data']
jwt_headers = encoded_jwt.split('.')[0]
decoded_jwt_headers = base64.urlsafe_b64decode(jwt_headers)
decoded_jwt_headers = decoded_jwt_headers.decode("utf-8")
decoded_json = json.loads(decoded_jwt_headers)
path = event['path']
# ログアウト処理
if path.find('logout') > 0:
logout_uri = 'https://<ELBのドメイン>.ap-northeast-1.elb.amazonaws.com'
redirect_url = 'https://<ユーザープールのドメインのプレフィックス>.auth.ap-northeast-1.amazoncognito.com/logout?client_id=' \
+ decoded_json['client'] + '&logout_uri=' + urllib.parse.quote(logout_uri, encoding='utf-8')
return {
'statusCode': 302,
'headers': {
'Set-Cookie': 'AWSELBAuthSessionCookie-0=; max-age=0',
'Set-CookiE': 'AWSELBAuthSessionCookie-1=; max-age=0',
'Access-Control-Allow-Origin': logout_uri,
'Access-Control-Allow-Methods': 'GET',
'Location': redirect_url
}
}
return {
'statusCode': 200,
'headers': {
'Content-Type': 'text/html; charset=utf-8'
},
'body': '''
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>title</title>
</head>
<body>
テスト
</body>
</html>
'''
}
<>の箇所は、自分の値を設定します。
ユーザープールへのリダイレクトの追加
ユーザープールのアプリクライアントに、サインアウト(ログアウト)のURLを設定します。上に書いたように、ALBのエンドポイントとしました。
動作確認
ELBのDNS名にブラウザでアクセスしてログインからサイトの表示
https://<ELB名>.ap-northeast-1.elb.amazonaws.com
ログアウトするとき → ログイン画面に遷移
https://<ELB名>.ap-northeast-1.elb.amazonaws.com/logout
おわりに
100%理解していないところもありますが、動くので掲載しました!!
- ユーザークレームの署名の検証は省略しています。本来は、必要に応じて検証を実施すべきです。(コードが長くなったのと、ライブラリのインポートの説明が必要になるので、省略しました。)
- リダイレクトのステータスコードは302にしていますが、301とか、ちゃんと理解できてないです(^_^;)
- MacOS+Safariで確認しています。MacOS+Chromeだと、httpsの自己証明書ではALBのドメインにアクセス出来ませんでした。
- リージョンはTokyo(ap-northeast-1)に固定してしまっています。