ポンコツエンジニアのごじゃっぺ開発日記。

いろいろポンコツだけど、気にするな。エンジニアの日々の開発などの記録を残していきます。 自動で収入を得られるサービスやシステムを作ることが目標!!

【PHP】ALB + Cognitoで認証されたユーザ情報をWebサーバから取得してみる。

AWSでロードバランサにぶら下がったEC2やECSなどのWebサービスに対して、認証機能をつけたい場合、ALBとCognitoを組み合わせることで簡単に実現できますね。

Cognitoでの認証結果に対して、PHPのサービス側でそのユーザに対するロール設定などをしたい場合、認証結果をPHPで取得して処理をする必要があります。 この記事では、その処理について書いていきたいと思います。

Amazon Cognitoとは

Amazon Cognito を使用すれば、ウェブアプリケーションおよびモバイルアプリに素早く簡単にユーザーのサインアップ/サインインおよびアクセスコントロールの機能を追加できます。Amazon Cognito は、数百万人のユーザーにスケールし、Facebook、Google、Amazon などのソーシャル ID プロバイダー、および SAML 2.0 によるエンタープライズ ID プロバイダーを使用したサインインをサポートします。(公式サイトより)

aws.amazon.com

なので、ALBを利用したWebサービスに対して、Cognitoを追加するだけでFacebookやGoogleなどのソーシャルIDプロバイダの認証システムを導入できること。また、その管理をCognito上で行うことができるってことなのです。

システムの全体像

今回はALBとCognitoではよくある構成で行います。

f:id:ponkotsu0605:20200730220802p:plain

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で書いてみたいと思います。

docs.aws.amazon.com

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アプリケーションを実現することができました。簡単ですね!

参考サイト

dev.classmethod.jp

お問い合わせプライバシーポリシー制作物