AWSでロードバランサにぶら下がったEC2やECSなどのWebサービスに対して、認証機能をつけたい場合、ALBとCognitoを組み合わせることで簡単に実現できますね。
Cognitoでの認証結果に対して、PHPのサービス側でそのユーザに対するロール設定などをしたい場合、認証結果をPHPで取得して処理をする必要があります。 この記事では、その処理について書いていきたいと思います。
Amazon Cognitoとは
Amazon Cognito を使用すれば、ウェブアプリケーションおよびモバイルアプリに素早く簡単にユーザーのサインアップ/サインインおよびアクセスコントロールの機能を追加できます。Amazon Cognito は、数百万人のユーザーにスケールし、Facebook、Google、Amazon などのソーシャル ID プロバイダー、および SAML 2.0 によるエンタープライズ ID プロバイダーを使用したサインインをサポートします。(公式サイトより)
なので、ALBを利用したWebサービスに対して、Cognitoを追加するだけでFacebookやGoogleなどのソーシャルIDプロバイダの認証システムを導入できること。また、その管理をCognito上で行うことができるってことなのです。
システムの全体像
今回はALBとCognitoではよくある構成で行います。
ALB上でCognitoとの連携設定を行いますが、この記事ではこの部分は省略します。
PHPでログイン情報を取得する
Cognitoでログインが完了すると、ALBからEC2に以下のHTTPヘッダ情報を渡してくれます。
パラメータ | 内容 |
---|---|
x-amzn-oidc-accesstoken | トークンエンドポイントからのアクセストークン (プレーンテキスト)。 |
x-amzn-oidc-identity | ユーザー情報エンドポイントからの件名フィールド (sub) (プレーンテキスト)。 |
x-amzn-oidc-data | ユーザークレーム (JSON ウェブトークン (JWT) 形式) |
Application Load Balancer を使用してユーザーを認証するのユーザークレームのエンコードと署名の検証より。 docs.aws.amazon.com
上記のJWT形式のユーザークレームを利用して、認証されたユーザ情報を抽出したいと思います。
PHPソース
AWSの公式サイトに書いてあるPythonのソースコードを参考にPHPで書いてみたいと思います。
import jwt import requests import base64 import json # Step 1: Get the key id from JWT headers (the kid field) encoded_jwt = headers.dict['x-amzn-oidc-data'] jwt_headers = encoded_jwt.split('.')[0] decoded_jwt_headers = base64.b64decode(jwt_headers) decoded_jwt_headers = decoded_jwt_headers.decode("utf-8") decoded_json = json.loads(decoded_jwt_headers) kid = decoded_json['kid'] # Step 2: Get the public key from regional endpoint url = 'https://public-keys.auth.elb.' + region + '.amazonaws.com/' + kid req = requests.get(url) pub_key = req.text # Step 3: Get the payload payload = jwt.decode(encoded_jwt, pub_key, algorithms=['ES256'])
上記がドキュメントに書いてあるサンプルソースコードですが、これを参考にLaravelを想定したPHPのプログラムに変換してみたいと思います。
<?php function action(Request $request) { // step 1: Get the key id from JWT headers (the kid field) $encoded_jwt = $request->header('x-amzn-oidc-data'); $jwt_headers = explode('.', $encoded_jwt)[0]; $decoded_jwt_headers = base64_decode($jwt_headers); $decoded_json = json_decode($decoded_jwt_headers); // step 2: Get the public key from regional endpoint $kid = $decoded_json->kid; $region = 'ap-northeast-1'; $url = 'https://public-keys.auth.elb.' . $region . '.amazonaws.com/' . $kid; $pub_key = file_get_contents($url); // step 3: Get the payload $result = JWT::decode($encoded_jwt, $pub_key, ['ES256']); var_dump($result); }
JWTはAWSを使っているのであれば例えばFirebaseのuse Firebase\JWT\JWT;
でも良いかなと思います。
実行結果
CognitoでGoogleログインを成功させると、以下のようにログインしたユーザの情報を取得することができました。
array(7) { ["sub"]=> string(36) "aaa00000-aaa0-0000-aa0a-aa0a00aaa00a" ["identities"]=> string(141) "[{"userId":"000000000000000000000","providerName":"Google","providerType":"Google","issuer":null,"primary":true,"dateCreated":0000000000000}]" ["email_verified"]=> string(5) "false" ["email"]=> string(25) "abcdefg@gmail.co.jp" ["username"]=> string(28) "Google_000000000000000000000" ["exp"]=> int(0000000000) ["iss"]=> string(73) "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-0_a0aaaaaaa" }
この取得できたemailとusernameなどを利用してLaravelでのユーザ認証を行うことができるようになると思います。
さいごに
ということで、今回紹介したPHPソースコードだけでGoogle認証だったりFacebookの認証を介したWebアプリケーションを実現することができました。簡単ですね!